您的位置:首页 > 移动开发 > Objective-C

从零开始学_JavaScript_系列(39)——对象的扩展(2)Object对象的扩展方法

2017-06-04 19:26 597 查看

对象的扩展

0、一句话总结

属性名,以及是属性的函数的简洁写法,写起来简单易阅读

属性名可以用变量字符串拼接起来(话说以前也有吧?)

函数都有name属性,但set和get也要加前缀

Object.is判断两个变量是否相等

Object.assign可以合并对象的非原型链上,且可枚举属性

Object.getOwnPropertyDescriptor查看属性是否可枚举、可修改、可赋值

Object.keys获取对象非原型链上,且可枚举属性的key

Object.values获取对象非原型链上,且可枚举属性的值

Object.entries同时获取上面两个

当属性在原型链上,或者不可枚举时,会被很多方法忽视(参照6.1)

__proto__和prototype的关系(参照7.1)

Object.setPrototypeOf(obj, prototype)设置__proto__属性

Object.getPrototypeOf(obj)获取__proto__属性

es7新增对对象有效的扩展运算符…(三个点)

本篇没有的请翻看【对象的扩展】系列的其他篇

4、Object的扩展

4.1、判断两个变量是否相等(Object.is)

Object.is(value1, value2);

简单的说,判断2个值是否相等,相等返回true,不等返回false。

类似===,但与其在某些表现略有差异,如代码:

Object.is(+0, -0);   //false
+0 === -0;  //true

Object.is(NaN, NaN);    //true
NaN === NaN;  //false


另外需要在注意的是,按引用传递类型,比如{}或者[],哪怕看起来都是空数组、空对象,但2个不同的变量作为参数时,他们是不相等的。

(我似乎提升

4.2、将对象的属性合并到另外一个对象之中(Object.assign)

4.2.1、定义

Object.assign(target, …sources)

简单来说,就是将参数2,3,4……里面的对象的所有 可枚举 属性,添加到参数1这个对象之中。

返回值是参数1这个对象(但因为对象是按引用传递,所以返回值和参数1是一样的);

需要注意的是,被添加对象的属性的数据类型,如果是按引用传递,那么该属性将以按引用传递的形式添加到第一个参数之中。

var foo = {
a: "1"
}
var child = {
name: "child"
}
var bar = {
child,
b: "2"
}
var target = {
name: "target"
}
var result = Object.assign(target, foo, bar);
console.log(target);    //Object {name: "target", a: "1", child: Object, b: "2"}
console.log(target.child === child);    //true
console.log(result === target);     //true


4.2.2、属性覆盖:

同名属性,参数位置靠后者覆盖靠前者。

var foo = {
name: "1"
}
var target = {
name: "target"
}
Object.assign(target, foo);
console.log(target.name);    //"1"


4.2.3、必须可枚举

可枚举,具体来说,就是该属性enumerable值为false时,如代码

var foo = Object.defineProperty({}, "name", {
value: "foo",
enumerable: false
})
console.log(foo.name);    //"foo"
var target = {
name: "target"
}
Object.assign(target, foo);
console.log(target.name);    //"target"


注意:

通过 Object.defineProperty 方法添加新属性,未设置的属性名,不然上面里不包含writable和configurable,其默认值为false

4.2.4、参数一必须是对象或者可以转为对象

参数一如果不是对象,那么至少要能转为对象,例如:

var num = Object.assign(1);
console.log(num);    //Number {[[PrimitiveValue]]: 1}

//作为对比
new Number(1);    //Number {[[PrimitiveValue]]: 1}


说明num是一个Number类型的对象。他可以像number一样进行正常的数学运算;

也可以像一个对象一样添加属性,只要不对它进行赋值,那么就会一直保持是对象状态。

如代码:

var num = Object.assign(1);
typeof num; //"object"
Object.assign(num, {name: "obj"});
num.name;   //"obj"
num + 10;   //11
Object.prototype.toString.call(num);    //"[object Number]"


类似可以推导到Boolean类型、String类型等基本类型,甚至可以对正则表达式使用,如代码:

var reg = Object.assign(/\d/, {name: "reg"})
reg;    // /\d/
reg.name;   //"reg"


但,基本类型里,
null
undefined
不行。

另外,参数一也不能是函数,不管是声明的还是变量赋值的都不行,除非通过new生成该函数的实例。

var test = function () {
}
var res = Object.assign(test, {name: "fun"});
//Uncaught TypeError: Cannot assign to read only property 'name' of function 'function () {}'


4.2.5、其他参数会隐式转换为对象,并且将可枚举属性添加进去

之前提过,只有 可枚举 的属性可以添加,因此假如其他参数是number或者boolean显然是不行的,即使被转换为对象,也无法添加。

因此,基本类型里只有string有意义,例如”string”转为对象后,key为”0”时,值为”s”。

而扩展类型里,Array也是有意义的,他的下标就是key,并且该key可以被枚举。

4.2.6、再次提示,这种拷贝方法是浅拷贝

如果只是要合并属性是可以用的,但注意是浅拷贝,也就是数据类型为按引用传递类型时,只拷贝引用,而不是创建一个新的。

如果有深拷贝的需求,请自行寻找办法。

另外,因为这种方式是浅拷贝,所以不会递归执行,

因此也不必担心参数2和参数3有同名属性且该属性是按引用传递,参数3覆盖参数2在参数1的属性时,导致参数2这个对象的属性的值受到影响。

具体来说,其实质就是:

//a,b,c都有属性name,且name是不同的对象
Object.assign(a,b,c);
a.name = b.name;
a.name = c.name;
//结果是a.name = c.name,但b.name的值因为不是递归,所以自身不受影响。


4.2.7、对setter和getter不会正确生效

简单来说,set和get复制过来后,复制的仅是值,而不包括setter和getter的函数。如代码:

let foo = {
_val: 0,
get val() {
return this._val + 1;
},
set val(val) {
this._val = val;
}
}
var bar = Object.assign({}, foo);
foo;    //{_val: 0}
bar;    //{_val: 0, val: 1}
foo.val = 1;
foo.val;    //2
bar.val = 1;
bar.val;    //1


注意看以上,从foo通过assign将属性合并到bar后,val属性变成一个普通变量了。

对val属性的赋值和获取,也没有正确生效。

解决办法:

通过
Object.getOwnPropertyDescriptors(obj)
(参照4.3)和
Object.defineProperties()
两个方法完成获取和定义。

如代码:

let foo = {
_val: 0,
get val() {
return this._val + 1;
},
set val(val) {
this._val = val;
}
}
var getAttributes = Object.getOwnPropertyDescriptors(foo);
console.log(getAttributes);  //这个自行查看吧,跟defineProperties的参数2是相同的,显示configurable、setter等属性
let bar = Object.defineProperties({}, getAttributes);
bar;    //{_val: 0}
bar.val = 1;
bar.val;    //2


4.3、查看是否可枚举、可修改、可赋值(Object.getOwnPropertyDescriptor)

Object.getOwnPropertyDescriptor(obj, prop)

Object.getOwnPropertyDescriptors(obj) //需要es7,IE11不支持

获取enumberable、configurable和writable属性,以及value,和通过Object.defineProperty设置其的行为类似。

第一个获取指定属性的,第二个获取所有属性的,就像设置他们一样的形式来获取;

let foo = {
name: "foo",
test: "test"
}
Object.defineProperty(foo, "name", {
enumerable: false
});
let one = Object.getOwnPropertyDescriptor(foo, "name");
console.log(one);   //{value: "foo", writable: true, enumerable: false, configurable: true}
let all = Object.getOwnPropertyDescriptors(foo);
console.log(all);   //Object {name: Object, test: Object}
//name和test也是对象,对象的属性参考上面的one


4.4、获取对象所有key(Object.keys)

Object.keys(obj)

获取obj对象,非原型链上,enumberable的值为true的所有属性的key,依次放到数组中并返回该数组。

var obj = Object.create({a: "A"}, {
b: {
value: "B",
enumerable: true
}
});
console.log(Object.keys(obj));  //["b"]
console.log(Object.values(obj));    //["B"]


4.5、获取对象所有值(Object.values)

Object.values(obj)

按照MDN说法,这个是实验性的,IE不兼容。但是我实测结果,IE11开IE9的文档模式,这个方法是可以正常执行的。

效果是获取obj对象,非原型链上,enumberable的值为true的所有属性的values。同样依次放到一个数组中并返回该数组。

代码参照4.4

4.6、同时获取对象的所有key和value(Object.entries)

Object.entries(obj)

兼容性不好,IE11才能跑,10都不行。

也是只对非原型链,以及enumerable值为true的才有效。

返回一个数组,数组的每个元素是一个key+val组合。

返回数组的每个元素也是一个数组,下标为0的地方为key,下标为1的为value。

如代码:

var obj = Object.create({a: "A"}, {
b: {
value: "B",
enumerable: true
},
c: {
value: "C",
enumerable: true
}
});
console.log(Object.entries(obj));   //[["b", "B"], ["c", "C"]]


5.属性的可枚举性

5.1、如何设置可枚举性

Object.defineProperty(obj, prop, descriptor)

Object.defineProperties(obj, props)

第一个设置单个属性的,第二个可以同时设置多个属性。

通过设置属性的enumerable属性来设置其是否可枚举,true可枚举,false不可枚举。

在未锁定前可以设置,锁定后不可修改(并且会报错)

var foo = {
name: "foo"
}
Object.defineProperty(foo, "name", {
configurable: false
});
var res = Object.getOwnPropertyDescriptor(foo, "name");
console.log(res);   //Object {value: "foo", writable: true, enumerable: true, configurable: false}
Object.defineProperty(foo, "name", {
enumerable: false
}); //Uncaught TypeError: Cannot redefine property: name


5.2、查看是否可枚举(以及其他)

参照4.3中的:

Object.getOwnPropertyDescriptor(obj, prop)

Object.getOwnPropertyDescriptors(obj) //需要es7,IE11不支持

5.3、当可枚举属性为false时,被忽视的情况

for in
遍历对象属性;

Object.keys()
获取所有key

JSON.stringify()
JSON序列化

原因是以上都会去遍历对象的key,然后根据key来获取值,如果不能枚举显然不能遍历咯。

var foo = Object.defineProperty({test: "test"}, "name", {
value: "foo",
enumerable: false,
writable: true,
configurable: true
})

for (var i in foo) {
console.log(i);
}
//test

console.log(Object.keys(foo));  //["test"]

console.log(JSON.stringify(foo));   //{"test":"test"}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐