属性的简洁表示法
ES6允许直接写入变量和函数作为对象的属性和方法
var foo = 'bar';
var obj = {foo};
obj //{foo:"bar"}
var o = {
method() {
return "hello!";
}
};
//等同于
var o = {
method: function() {
return "hello!";
}
};
这种写法用于函数的返回值将会非常方便
function getPoint(){
var x = 1;
var y = 2;
return {x,y};
}
getPoint(); //{x:1,y:2}
属性的赋值器(setter
)和取值器(getter
)也可以采用这种写法
var cart = {
_wheels: 4,
get wheels () {
return this._wheels;
},
set wheels (value) {
this._wheels = value;
}
}
如果某个方法的值是一个Generator
函数,则其前面需要加上星号
const obj = {
* m() {
yield 'hello world';
}
};
属性名表达式
ES6允许以字面量形式定义变量时,用表达式作为对象属性名或方法名,即把表达式放在方括号里
let propkey = "foo";
let obj = {
[propkey] : true,
['a' + 'bc'] : 123,
['h' + 'ello'] () {
return 'hi';
}
};
但属性名表达式和简洁表示法不能同时使用,否则会报错
var foo = 'bar';
var bar = 'abc';
var obj = { [foo] }; //报错
//正确
var obj = {[foo]: 'abc'};
方法的name属性
方法的name
属性返回方法名
const person = {
sayName() {
console.log('hello!');
},
};
person.sayName.name // "sayName"
若对象的方法使用取值函数(getter)和存值函数(setter),则name
属性不在该方法上,而是在该方法属性描述符对象(descriptor)的get
和set
属性上,,返回值是方法名前加上get或set
const obj = {
get foo() {},
set foo(x) {}
};
obj.foo.name
// TypeError: Cannot read property 'name' of undefined
const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');
descriptor.get.name //"get foo"
descriptor.set.name //"set foo"
bind
方法创造的函数,name
属性返回bound
加上原函数的名字;Function
构造函数创造的函数,name
属性返回anonymous
如果对象的方法是一个Symbol
值,那么name
属性返回的是这个Symbol
值的描述
const key1 = Symbol('description');
const key2 = Symbol();
let obj = {
[key1]() {},
[key2]() {},
};
obj[key1].name // "[description]"
obj[key2].name // ""
Object.is()
该方法用于比较两个值是否是相等的,用来弥补===
运算符对于两个NaN
不等,以及+0
和-0
相等的情况
Object.is('foo','foo'); //true
Object.is({}, {}); //false
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
Object.assign()
Object.assign
方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target,source1,source2);
target // {a:1, b:2, c:3}
如果只有一个参数,Object.assign
会直接返回,如果该参数不是对象,则会自动转换为对象。若参数是null
或undefined
,且其为第一个参数,则会报错。
其他类型的值(数值、字符串、布尔值)不在首参数也不会报错,但是除了字符串会以数组形式复制到目标对象外,其他值都不会产生效果。
const v1 = 'abc';
const v2 = true;
const v3 = 10;
const obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
上述只有字符串才被复制的原因在于,只有字符串包装对象具有可枚举属性,而Object.assign
方法只复制源对象自身的可枚举属性(不复制继承属性和不可枚举属性),属性名为Symbol
值的属性也会被复制
Object.assign
方法复制的对象属性是浅复制,若源对象属性是对象,则目标对象得到的是这个对象的引用
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
Object.assign
也可以用来处理数组,但是会把数组视为对象,即索引为属性名
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
Object.assign
的用途很多,可以为对象添加属性和方法、克隆对象、合并对象、为属性指定默认值
//为对象添加属性和方法
var obj = {};
var x = 1,
y = 2;
Object.assign(obj, {x,y}, { someMethod () {return 'abc';} });
//上述利用了简洁表示法,为obj对象添加了x,y属性和someMethod方法
//克隆对象
var objProto = Object.getPrototypeOf(obj);
var clone = Object.assign(Object.create(objProto),obj);
//上述先获取对象原型,再复制属性,保证了原对象的继承属性也被复制
//合并对象
const merge = (target, ...sources) => Object.assign(target, ...sources);
//为对象指定默认属性
var Default = {
abc: 0,
text: 'html'
};
function(options){
options = Object.assign({}, Default, options);
//....
}
//上述代码将默认属性放置在Default对象中,用户输入的属性在options对象中
//如果两者有同名属性,则options中属性会覆盖Default中的属性
属性的可枚举性和遍历
若某个属性的描述符对象中enumerable
属性为false
,则表示该属性不可被枚举。(关于描述符对象https://blog.csdn.net/zjw_python/article/details/79526443)。
目前有4个操作会忽略enumerable
为false
的属性
for...in
循环:只会遍历对象自身和继承的可枚举属性Object.keys()
:只返回对象自身的可枚举属性键名JSON.stringify()
:只串行化对象自身的可枚举属性Object.assign()
:只复制源对象自身的可枚举属性
另外,ES6 规定,所有 Class 的原型的方法都是不可枚举的。因此尽量不要使用for...in
循环,而用Object.keys()
代替
ES6一共有5种方法可以遍历对象的属性:
(1)for...in
遍历对象自身和继承的所有可枚举属性(不包含Symbol属性)
(2)Object.keys(obj)
遍历对象自身所有可枚举属性的键名(不包含Symbol属性)
(3)Object.getOwnPropertyNames(obj)
返回一个数组,包含对象自身的所有属性的键名(不包含Symbol属性)
(4)Object.getOwnPropertySymbols(obj)
返回一个数组,包含对象自身所有Symbol属性的键名
(5)Reflect.ownKeys(obj)
返回一个数组,包含对象自身的所有属性,不管是否可枚举或者是Symbol属性
Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptor
方法会返回某个对象属性的描述对象(descriptor)。ES2017 引入了Object.getOwnPropertyDescriptors
方法,返回指定对象所有自身属性(非继承属性)的描述对象。
const obj = {
foo: 123,
get bar() { return 'abc' }
};
Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
该方法的引入目的,主要是为了解决Object.assign()
无法正确拷贝get
属性和set
属性的问题。这是因为Object.assign
方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法
const source = {
set foo(value) {
console.log(value);
}
};
const target1 = {};
Object.assign(target1, source);
Object.getOwnPropertyDescriptor(target1, 'foo')
// { value: undefined,
// writable: true,
// enumerable: true,
// configurable: true }
这时,Object.getOwnPropertyDescriptors
方法配合Object.defineProperties
,就可以实现正确拷贝了
const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
Object.getOwnPropertyDescriptor(target2, 'foo')
// { get: undefined,
// set: [Function: set foo],
// enumerable: true,
// configurable: true }
除此之外,还可以配合Object.create
方法,克隆对象属性到一个新对象
const clone =
Object.create(Object.getPrototypeOf(obj),Object.getOwnPropertyDescriptors(obj));
另外,Object.getOwnPropertyDescriptors
方法可以实现一个对象继承另一个对象
//以前的写法
const obj = {
__proto__: prot,
foo: 123,
};
//现在
const obj = Object.create(
prot,
Object.getOwnPropertyDescriptors({foo:123})
);
__proto__属性、Object.getPrototypeOf()、Object.setPrototypeOf()
__proto__
属性(前后各两个下划线),用来读取或设置当前对象的prototype
对象。目前,所有浏览器(包括 IE11)都部署了这个属性。
// es5 的写法
const obj = {
method: function() { ... }
};
obj.__proto__ = someOtherObj;
// es6 的写法
var obj = Object.create(someOtherObj);
obj.method = function() { ... };
Object.setPrototypeOf
方法的作用与__proto__
相同,用来设置一个对象的prototype
对象,返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。
//格式
Object.setPrototypeOf(object, prototype);
//示例
const obj = Object.setPrototypeOf({}, null);
第一个参数若不是对象,则自动会转换为对象。若第一个参数为null
或undefined
,就会报错
Object.getPrototypeOf()
用于获取对象的原型
Object.getPrototypeOf(obj);
super关键字
this
关键字总是指向函数所在的当前对象,ES6又新增了另一个类似的关键字super
,指向当前对象的原型对象
const proto = {
foo: 'hello'
};
const obj = {
foo: 'world',
find() {
return super.foo;
}
};
Object.setPrototypeOf(obj, proto);
obj.find() // "hello"
值得注意的是,super
关键字表示原型对象时,只能用在对象的方法之中,且必须使用对象方法的简写形式才可以让JavaScript 引擎确认,定义的是对象的方法,否则会报错。
// 报错
const obj = {
foo: super.foo
}
// 报错
const obj = {
foo: () => super.foo
}
// 报错
const obj = {
foo: function () {
return super.foo
}
}
Object.keys(),Object.values(),Object.entries()
Object.keys()
返回一个数组,包含对象自身所有可枚举属性的键名。Object.values()
则是返回对象自身所有可遍历对象的键值。类似的Object.entries()
返回键值对。
var obj = {foo:'bar', num: 42};
Object.keys(obj); //["foo" , "num"];
Object.values(obj); //["bar", 42];
Object.entries(obj); //[ ["foo","bar"], ["num", 42] ]
对象的扩展运算符
对象的扩展运算符用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
在解构赋值中,扩展运算符必须是最后一个参数,否则会报错。解构赋值的拷贝是浅拷贝,若属性值是一个对象,而只是拷贝的这个对象的引用。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
扩展运算符可用于合并两个对象,与Object.assign
方法类似,后面的属性会覆盖前面的属性
let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);
如果扩展运算符的参数对象之中,有取值函数get
,这个函数是会执行的
// 会抛出错误,因为 x 属性被执行了
let runtimeError = {
...a,
...{
get x() {
throw new Error('throw now');
}
}
};
Null传导运算符
Null
传导运算符简化了读取对象内部某个属性时,对该对象是否存在的判断。例如对message.body.user.firstName
读取,可使用
const firstName = message?.body?.user?.firstName || 'default';
上述代码中,只要有一个?.
运算符返回null
或undefined
,则就不载向下运算,而是返回undefined
该运算符有4种用法
obj?.prop
:读取对象属性obj?.[expr]
:读取对象属性func?.(...args)
:函数或方法的调用new C?.(...args)
:构造函数的调用