ES6 对象的扩展、Symbol
一、 ES6 对象的扩展
1、 属性的简洁表示法
- ES6允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
var some='aaa';
var obj={some};
console.log(obj); //{some:aaa}
//等同于 var obj={some:some}
//只写属性名,不写属性值。这时,属性值等于属性名所代表的变量
- 除了属性简写,方法也可以简写。
var obj={
func(){
return 'hi!';
}
}
//等同于
var obj={
func:function(){
return 'hi!';
}
}
- 如果某个方法的值是一个Generator函数,前面需要加上星号。
- 注意,简洁写法的属性名总是字符串
2、属性名表达式
js中定义对象属性的方法有两种,即
//方法一
obj.some:'aaa';
//方法二
obj['a',+'i']:'bbb';
上面代码的方法一是直接用标识符作为属性名,方法二是用表达式作为属性名,这时要将表达式放在方括号之内。
但是,如果使用字面量方式定义对象(使用大括号),在ES5中只能使用方法一(标识符)定义属性。
var obj={
some:'aaa';
num:123;
}
- ES6允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。
var str='zzz';
var obj={
[zzz]:'sss';
['a'+'b']:111;
}
- 属性名表达式还可以用来定义方法名
- 注:属性名表达式与简洁表示法,不能同时使用,会报错。
var num = 123;
var str = 'aaa';
var obj = {
[num]
}
//报错
//正确的写法如下
var num = 123;
var str = 'aaa';
var obj ={[num]:1111}
3,方法的name值
- 函数的name属性,返回函数名。对象方法也是函数,因此也有name属性
var obj = {
name: 'kykyky',
num: 111,
func1() {
console.log(this.name);
},
func2() {
console.log(this.num);
}
}
console.log(obj.func1.name); //func1
console.log(obj.func2.name); //func2
- 有两种特殊情况:bind方法创造的函数,name属性返回“bound”加上原函数的名字;Function构造函数创造 的函数,name属性返回“anonymous”。(anonymous 匿名)
- 如果对象的方法是一个Symbol值,那么name属性返回的是这个Symbol值的描述。
4. Object.is()
- Object.is() 用来比较两个值是否严格相等。它与严格比较运算符(===)的行为基本一致。
console.log(Object.is('foo', 'foo')); //true
console.log(Object.is({}, {})); //false
console.log(Object.is(+0, -0)); //false
console.log(Object.is(NaN, NaN)); //true
- 不同之处只有两个:一是+0不等于-0,二是NaN等于自身。如上。
//用ES5部署Object.is
Object.defineProperty(Object, 'is', {
value: function (x, y) {
if (x === y) {
//针对+0不等于-0的情况
return x !== 0 || 1 / x === 1 / y;
}
return x !== x && y !== y;
},
configurable: true,
enumerable: false,
writable: true
});
5. Object.assign()
(assign 分配 指派)
- Object.assign()方法用来将源对象(source)的所有可枚举属性,复制到目标对象(target)。它至少需要两 个对象作为参数,第一个参数是目标对象,后面的参数都是源对象。只要有一个参数不是对象,就会抛出 TypeError错误。
var target = { a: 1 };
var sour1 = { b: 2 };
var sour2 = { c: 3 };
Object.assign(target, sour1, sour2);
console.log(target); //{a:1,b:2,c:3}
- 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
var target = { a: 1 };
var sour1 = { b: 2 };
var sour2 = { b: 3 };
Object.assign(target, sour1, sour2);
console.log(target); //32 {a: 1, b: 3}
- Object.assign()只拷贝自身属性,不可枚举的属性(enumerable 为false)和继承的属性不会被拷贝。
- 对于嵌套的对象,Object.assign()的处理方法是替换,而不是添加。
var t = { a: { b: "111", c: 1223 } };
var s = { a: {b:"hello"} };
Object.assign(t, s);
console.log(t); // {a: {b:"hello"}}
6、属性的可枚举性
- 对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOenPropertyDescriptor方法可以获取该属性的描述对象。
var obj = { a: 111 };
console.log(Object.getOwnPropertyDescriptor(obj, 'a'));
// {
value: 111,
writable: true,
enumerable: true,
configurable: true
}
描述对象的enumerable属性,称为”可枚举性“,如果该属性为true ,就表示某些操作会忽略当前属性。
ES5有三个操作会忽略enumerable为false的属性:
for…in Object.keys() JSON.stringfy()
ES6新增了两个操作,会忽略enumerable为false的属性。
Object.assign():只拷贝对象自身的可枚举的属性
Reflect.enumerable() : 返回所有for… in循环会遍历的属性。
在以上五个操作中,只有 for…in和Reflect.enumerable() 会返回继承的属性。
7、属性的遍历
ES6一共有6种方法可以遍历对象的属性。
- for …in 循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
- Object.keys(obj) 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。
- Object.getOwnPropertyNames(obj) 返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚 举属性)。
- Object.getOwnPropertySymbols(obj) 返回一个数组,包含对象自身的所有Symbol属性。
5)Reflect.ownKeys(obj) 返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否 可枚举。
6))Reflect.enumerate(obj) 遍历对象自身的和继承的可枚举属性(不含Symbol属性)。 与for… in 循环相同。
- 以上的6种方法遍历对象的属性,都遵守同样的属性遍历的次序规则。
首先遍历所有属性名为数值的属性,按照数字排序。 其次遍历所有属性名为字符串的属性,按照生成时间排序。 后遍历所有属性名为Symbol值的属性,按照生成时间排序。
8、__ proto __属性,Object.setPrototypeOf(), Object.getPrototypeOf()
- __proto__属性(前后各两个下划线),用来读取或设置当前对象的 prototype 对象。
- Object.setPrototypeOf() 写操作
Object.getPrototypeOf() 读操作b
Object.create() 生成操作
9、对象的扩展运算符
- 1,Rest参数
Rest参数用于从一个对象取值,相当于将所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有 的键和它们的值,都会拷贝到新对象上面。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); //1
console.log(y); //2
console.log(z); //{a:3,b:4}
//上面代码中,变量z是Rest参数所在的对象。
//它获取等号右边的所有尚未读取的键(a和b),
//将它们和它们的 值拷贝过来。
注:
1)Rest参数的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么Rest参数拷 贝的是这个值的引用,而不是这个值的副本。
2)Rest参数不会拷贝继承自原型对象的属性。
- 2, 扩展运算符…
扩展运算符用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
let z = { a: 1, b: 2 };
let n = { ...z };
console.log(n);// {a: 1, b: 2}
这等同于使用Object.assign 方法。即
var k={a: 1, b: 2};
var obj={...k}
//即等于
var obj=Object.assign({},k);
二、Symbol
1、概述
ES5的对象属性名都是字符串,这容易造成属性名的冲突。ES6引入了一种新的原始数据类型Symbol,表示独一无二的值,它是JavaScript语言的第七种数据类型。
- Symbol值通过Symbol 函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串, 另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性 名产生冲突。
var m = Symbol();
console.log(typeof m); //symbol
注:Symbol函数前不能使用new 命令,否则会报错。
var s1 = Symbol('foo');
var s2 = Symbol('bar');
console.log(s1); //Symbol(foo)
console.log(s2); // Symbol(bar)
console.log(s1.toString()); // Symbol(foo)
console.log(s2.toString());// Symbol(bar)
上面代码中,s1和s2是两个Symbol值。如果不加参数,它们在控制台的输出都是Symbol() ,不利于区分。 有了参数以后,就等于为它们加上了描述,输出的时候就能够分清,到底是哪一个值。
注意,Symbol 函数的参数只是表示对当前Symbol值的描述,因此相同参数的Symbol函数的返回值是不相等的。
- Symbol值不能与其他类型的值进行运算,会报错。
- Symbol值可以显式转为字符串。
- Symbol值也可以转为布尔值,但是不能转为数值。
2、作为属性名的Symbol
- 由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符,用于对象的属性名,就能保证不会 出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
- Symbol值作为对象属性名时,不能用点运算符。
var mysymbol = Symbol();
var a = { [mysymbol]: 123 };
console.log(a.mysymbol); //undefined
console.log(a[mysymbol]); //123
- 同理,在对象的内部,使用Symbol值定义属性时,Symbol值必须放在方括号之中。如果mysymbol不放在方括号中,该属性的键名就是字符串mysymbol ,而不是mysymbol 所代表的那个Symbol值。
4. 属性名的遍历
- Symbol作为属性名,该属性不会出现在for…of for…in循环中。但是,它也不是私有属性,有一 个Object.getOenPropertySymbols()方法,可以获取指定对象的所有Symbol属性名。Object.getOenPropertySymbols()方法返回一个数组,成员是当前对象的所有用作属性名的Symbol值。
5. Symbol.for(),Symbol.keyFor()
- 有时,我们希望重新使用同一个Symbol值,Symbol.for()方法可以做到这一点。它接受一个字符串作为参数, 然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该 字符串为名称的Symbol值。
var s1 = Symbol.for('foo');
var s2 = Symbol.for('foo');
console.log(s1 === s2); //true
上面代码中,s1和s2都是Symbol值,但是它们都是同样参数的Symbol.for方法生成的,所以实际上是同一个 值。
注:Symbol.for ()与Symbol ()这两种写法,都会生成新的Symbol。它们的区别是,前者会被登记在全局环境中供 搜索,后者不会。Symbol.for ()不会每次调用就返回一个新的Symbol类型的值,而是会先检查给定的key是 否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for ()30次,每次都会返回同一个 Symbol值,但是调用Symbol () 30次,会返回30个不同的Symbol值。
- Symbol.keyFor方法返回一个已登记的Symbol类型值的key。
由于Symbol () 写法没有登记机制,所以每次调用都会返回一个不同的值。Symbol.keyFor方法返回一个已登记的Symbol类型值的key。
如下:
var s1 = Symbol.for('aaa');
console.log(Symbol.keyFor(s1)); //aaa
var s2 = Symbol('bbb');
console.log(Symbol.keyFor(s2)); //undefined
变量s2属于未登记的Symbol值,所以返回
undefined
6. 内置的Symbol值
除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
- Symbol.hasInstance
对象的Symbol.hasInstance 属性,指向一个内部方法。该对象使用 incetanceof 运算符时,会调用这个方法, 判断该对象是否为某个构造函数的实例。比如,foo incetanceof Foo在语言内部,实际调用的是 Foo[Symbol.hasInstance] (foo)。 - Symbol.isConcatSpreadable
对象的Symbol.isConcatSpreadable 属性等于一个布尔值,表示该对象使用Array.prototype.concat()时,是否可以展开。 - Symbol.species
对象的Symbol.species属性,指向一个方法。该对象作为构造函数创造实例时,会调用这个方法。即如果this.constractor[Symbol.species]存在,就会使用这个属性作为构造函数,来创造新的实例对象。 - Symbol.match
对象的Symbol.match 属性,指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返 回该方法的返回值。 - Symbol.replace
对象的Symbol.replace 属性,指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。 - Symbol.search
对象的Symbol.search属性,指向一个方法,当该对象被String.prototype.search 方法调用时,会返回该方 法的返回值。 - Symbol.split
对象的Symbol.split 属性,指向一个方法,当该对象被String.prototype.split 方法调用时,会返回该方法 的返回值。 - Symbol.iterator
对象的Symbol.iterator属性,指向该对象的默认遍历器方法,即该对象进行for…of循环时,会调用这个方 法,返回该对象的默认遍历器。 - Symbol.toPrimitive
对象的Symbol.toPrimitive 属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对 象对应的原始类型值。
Symbol.toPrimitive 被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。
Number:该场合需要转成数值
String:该场合需要转成字符串
Default:该场合可以转成数值,也可以转成字符串 - Symbol.toStringTag
对象的Symbol.toStringTag属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如 果这个属性存在,它的返回值会出现在toString 方法返回的字符串之中,表示对象的类型。也就是说,这个属 性可以用来定制 [object Object]或 [object Array]中object后面的那个字符串。 - Symbol.unscopables
对象的Symbol.unscopables属性,指向一个对象。该对象指定了使用 with 关键字时,哪些属性会被 with 环境 排除。