1.1属性、方法简洁表示
允许直接写入变量和函数作为对象属性和方法
let birth = '2000/01/01';
const Person = {
name: '张三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
1.2表达式作为属性名
在ES5中,字面量方式定义对象时,不可以使用表达式作为属性名。ES6字面量定义对象时允许这种方式,也就是将表达式放在方括号内,也可以定义方法名。
let lastWord = 'last word';
const a = {
'first word': 'hello',
[lastWord]: 'world',
['h' + 'ello']() {
return 'hi';
}
};
console.log(a['first word']); // "hello"
console.log(a[lastWord]); // "world"
console.log(a['last word']); // "world"
console.log(a.hello()); // hi
console.log(a['last word']); // "world"
1.3对象方法有name属性
方法的name属性返回函数名
const person = {
sayName() {
console.log('hello!');
},
};
person.sayName.name // "sayName"
特殊情况:
- 对象的方法使用了取值函数(getter)和存值函数(setter),name属性不在该方法上,而是在该方法的属性的描述对象的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
(new Function()).name // "anonymous"
var doSomething = function() {
// ...
};
doSomething.bind().name // "bound doSomething"
1.4属性可枚举、可遍历
1.可枚举性
对象每个属性都有描述对象,描述对象有enumerable属性,称可枚举性,如果属性为false,表示不可枚举。
四个忽略enumerable为false属性的操作 1.for...in循环;Object.keys();JSON.stringify();Object.assign()(ES6新增)
2.属性的遍历
(1) for...in:遍历对象自身和继承的可枚举属性(不含Symbol属性)
(2) Object.keys(obj):返回数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
(3) Object.getOwnPropertyNames(obj返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
(4) Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有 Symbol 属性的键名。
(5) Reflect.ownKeys(obj):返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
1.5 super关键字
super关键字指向当前对象的原型对象,super表示原型对象时,只能用在对象的方法中。
const proto = {
foo: 'hello'
};
const obj = {
foo: 'world',
find() {
return super.foo;
}
};
Object.setPrototypeOf(obj, proto);
console.log(obj.find()); // "hello"
1.6对象的扩展运算符
用于取出对象理所有可遍历的属性,拷贝到当前对象。
let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }
扩展运算符等同于使用Object.assign(),只拷贝对象实例的属性
let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);
扩展运算符可以用于合并两个对象。
let ab = { ...a, ...b };
// 等同于
let ab = Object.assign({}, a, b);
如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。方便修改对象部分现有属性。
let aWithOverrides = { ...a, x: 1, y: 2 };
// 等同于
let aWithOverrides = { ...a, ...{ x: 1, y: 2 } };
// 等同于
let x = 1, y = 2, aWithOverrides = { ...a, x, y };
// 等同于
let aWithOverrides = Object.assign({}, a, { x: 1, y: 2 });
如果把自定义属性放在扩展运算符前面,就变成了设置新对象的默认属性值。
let aWithDefaults = { x: 1, y: 2, ...a };
// 等同于
let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a);
// 等同于
let aWithDefaults = Object.assign({ x: 1, y: 2 }, a);
其他特点可参照数组的扩展运算符。
1.7对象的解构赋值
用于从一个对象取值
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
参照前面解构赋值
1.8对象的新增方法
1.8.1 Object.is()
比较两个值是否严格相等,与===行为基本一致,不同之处在于:===中,+0等于-0,NaN不等于自身。
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
1.8.2 Object.assign()
用法
用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。返回目标对象。
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
注意点
- 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性;
- 如果只有一个参数会直接返回该参数;
- 如果第一个参数不是对象,会转成对象,undefined和null无法转成对象,作为参数会报错;
- 如果非对象参数不是第一个参数,且无法转成对象,就直接跳过。
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" }
- 只拷贝源对象自身属性,不拷贝继承属性,也不拷贝不可枚举属性
- 属性名为Symbol值的属性,也会被拷贝
Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' })
// { a: 'b', Symbol(c): 'd' }
- 浅拷贝
- 对于嵌套的对象,一旦遇到同名属性,Object.assign方法会进行替换
const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }
- 可以处理数组,把数组视为对象
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
Object.assign把数组视为属性名为 0、1、2 的对象,因此源数组的 0 号属性4覆盖了目标数组的 0 号属性1。 10. Object.assign只能进行值的复制,如果要复制的值是一个取值函数,那么将求值后再复制。
const source = {
get foo() { return 1 }
};
const target = {};
Object.assign(target, source)
// { foo: 1 }
source对象的foo属性是一个取值函数,Object.assign不会复制这个取值函数,只会拿到值以后,将这个值复制过去。
常见用途
- 给对象添加属性
class Point {
constructor(x, y) {
Object.assign(this, {x, y});
}
}
- 给对象添加方法
Object.assign(obj, {
someMethod(arg1, arg2) {
···
},
anotherMethod() {
···
}
});
- 克隆对象(浅拷贝,不能克隆继承值)
Object.assign({}, origin);
- 合并多个对象
Object.assign(target, ...sources);
- 给属性指定默认值
const DEFAULTS = {
logLevel: 0,
outputFormat: 'html'
};
function processContent(options) {
options = Object.assign({}, DEFAULTS, options);
}
1.8.3 Object.getOwnPropertyDescriptors()
用来获取一个对象的==所有==自身属性的描述符。参数为任意对象。
解决了Object.assign()无法正确拷贝get和set属性的问题。Object.getOwnPropertyDescriptors()方法配合Object.defineProperties()方法,实现正确拷贝。
const source = {
set foo(value) {
console.log(value);
}
};
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));
实现一个对象继承另一个对象
const obj = Object.create(
prot,
Object.getOwnPropertyDescriptors({
foo: 123,
})
);
1.8.5 Object.keys(), Object.values(), Object.entries()
1.Object.keys()
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
参数:obj-要返回其枚举自身属性的对象
2.Object.values()
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
参数:obj-要返回其枚举自身属性键值的对象
Object.entries()
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
参数:obj-可以返回其可枚举属性的键值对的对象。
Object.keys,Object.values和Object.entries,作为遍历一个对象的补充手段,供for...of循环使用。
let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };
for (let key of keys(obj)) {
console.log(key); // 'a', 'b', 'c'
}
for (let value of values(obj)) {
console.log(value); // 1, 2, 3
}
for (let [key, value] of entries(obj)) {
console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}
1.8.6 ES5常用对象方法
1.Object.defineProperty()
会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
参数:
(1)obj-要在其上定义属性的对象
(2)prop-要定义或修改属性的名称
(3)descriptor-将被定义或修改的属性描述符
descriptor可以设置的值有
- [value]:属性的值。
- [writable]:该属性是否可写,如果设置成 false,则任何对该属性改写的操作都无效(但不会报错)。
- [configurable]:如果为false,则任何尝试删除目标属性或修改属性以下特性(writable, configurable, enumerable)的行为将被无效化。
- [enumerable]:可枚举性。
- [get]:一旦目标对象访问该属性,就会调用这个方法,并返回结果。
- [set]:一旦目标对象设置该属性,就会调用这个方法。
实际运用
优化对象获取和修改属性的方式
//加入有一个目标节点, 想设置其位移
var targetDom = document.getElementById('target');
var transformText = 'translateX(' + 10 + 'px)';
targetDom.style.webkitTransform = transformText;
targetDom.style.transform = transformText;
// 用defineProperty方法优化
Object.defineProperty(dom, 'translateX', {
set: function(value) {
var transformText = 'translateX(' + value + 'px)';
dom.style.webkitTransform = transformText;
dom.style.transform = transformText;
}
//这样再后面调用的时候, 十分简单
dom.translateX = 10;
dom.translateX = -10;
MVVM中数据‘双向绑定’实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>标题</title>
</head>
<body>
<h3>使用Object.defineProperty实现简单的双向数据绑定</h3>
<input type="text" id="input" />
<div id="div"></div>
<script>
var obj = {};
var inputVal = document.getElementById("input");
var div = document.getElementById("div");
Object.defineProperty(obj, "name", {
set: function(newVal) {
inputVal.value = newVal;
div.innerHTML = newVal;
}
});
inputVal.addEventListener('input', function(e){
obj.name = e.target.value;
});
</script>
</body>
</html>
2.Object.seal()
封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置, 当前属性的值只要可写就可以改变。
参数:obj-要被密封的对象
// 如果属性值可写
let obj = Object.defineProperty({},'name',{
value:'hello',
writable:true
})
Object.seal(obj);
console.log(obj.name); // hello
obj.name = 'world';
delete obj.name;
console.log(obj.name); // world
// 如果属性值不可写
let obj = Object.defineProperty({},'name',{
value:'hello',
writable:false,
})
Object.seal(obj);
console.log(obj.name); // hello
obj.name = 'world';
delete obj.name;
console.log(obj.name); // hello
3.Object.freeze()
可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。
参数:obj-要被冻结的对象
let obj = Object.defineProperty({},'name',{
value:'hello',
writable:true
})
Object.freeze(obj);
console.log(obj.name); // hello
obj.name = 'world';
delete obj.name;
console.log(obj.name); // hello
摘自阮一峰
参照MDN