对象--属性的类型
- 数据属性:包含一个保存数据值的位置,有四个特性描述它们的行为。
- [[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true.
- [[Enumreable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true.
- [[Writable]]:表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是 true.
- [[Value]]:包含属性实际的值。这个特性的默认值为 undefined。
想要修改属性的默认特性,必须使用Object.defineProperty()方法。方法接收三个 参数:要给其添加属性的对象、属性的名称和一个描述符对象。
let person = {}; Object.defineProperty(person, "name", { writable: false, value: "Nicholas" }); console.log(person.name); // "Nicholas" person.name = "Greg"; console.log(person.name); // "Nicholas"
一个属性被定义为不可配置之后,就不能再变回可配置的了。
let person = {}; Object.defineProperty(person, "name", { configurable: false, value: "Nicholas" }); // 抛出错误 Object.defineProperty(person, "name", { configurable: true, value: "Nicholas" });
对象--属性的类型
- 访问器属性:不包含数据值,包含一个获取函数(getter)和一个设置函数(setter)。它也有4个特性。
- [[Configurable]]:表示属性是否可以通过 delete 删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true.
- [[Enumreable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是 true.
- [[Get]]:获取函数,在读取属性时调用。默认值为 undefined。
- [[Set]]:设置函数,在写入属性时调用。默认值为 undefined。
访问器属性不能直接定义,必须使用Object.defineProperty().
// 定义一个对象,包含伪私有成员 year_和公共成员 edition let book = { year_: 2017, edition: 1 }; Object.defineProperty(book, "year", { get() { return this.year_; }, set(newValue) { if (newValue > 2017) { this.year_ = newValue; this.edition += newValue - 2017; } } }); book.year = 2018; console.log(book.edition); // 2
读取属性的特性
Object.getOwnPropertyDescriptor()方法可以获取指定属性的属性描述符。接收两个参数:属性所在的对象和要取得其描述符的属性名。
let book = {}; Object.defineProperties(book, { year_: { value: 2017 }, edition: { value: 1 }, year: { get: function() { return this.year_; }, set: function(newValue){ if (newValue > 2017) { this.year_ = newValue; this.edition += newValue - 2017; } } } }); let descriptor = Object.getOwnPropertyDescriptor(book, "year_"); console.log(descriptor.value); // 2017 console.log(descriptor.configurable); // false console.log(typeof descriptor.get); // "undefined" let descriptor = Object.getOwnPropertyDescriptor(book, "year"); console.log(descriptor.value); // undefined console.log(descriptor.enumerable); // false console.log(typeof descriptor.get); // "function"
合并对象
Object.assign()方法,接收一个目标对象和一个或多个源对象作为参数,然后将每个源对象中可枚举(Object.propertyIsEnumerable()返回true)和自有(Object.hasOwnProperty()返回true)属性复制到目标对象。
let dest, src, result; /** * 简单复制 */ dest = {}; src = { id: 'src' }; result = Object.assign(dest, src); // Object.assign 修改目标对象 // 也会返回修改后的目标对象 console.log(dest === result); // true console.log(dest !== src); // true console.log(result); // { id: src } console.log(dest); // { id: src } /** * 多个源对象 */ dest = {}; result = Object.assign(dest, { a: 'foo' }, { b: 'bar' }); console.log(result); // { a: foo, b: bar } /** * 获取函数与设置函数 */ dest = { set a(val) { console.log(`Invoked dest setter with param ${val}`); } }; src = { get a() { console.log('Invoked src getter'); return 'foo'; } }; Object.assign(dest, src); // 调用 src 的获取方法 // 调用 dest 的设置方法并传入参数"foo" // 因为这里的设置函数不执行赋值操作 // 所以实际上并没有把值转移过来 console.log(dest); // { set a(val) {...} }
Object.assign()实际上对每个源对象执行的是浅复制。如果多个源对象都有相同的属性,则使用最后一个复制的值。此外,从源对象访问器属性取得的值,比如获取函数,会作为一个静态值赋给目标对象。换句话说,不能在两个对象间转移获取函数和设置函数。let dest, src, result; /** * 覆盖属性 */ dest = { id: 'dest' }; result = Object.assign(dest, { id: 'src1', a: 'foo' }, { id: 'src2', b: 'bar' }); // Object.assign 会覆盖重复的属性 console.log(result); // { id: src2, a: foo, b: bar } // 可以通过目标对象上的设置函数观察到覆盖的过程: dest = { set id(x) { console.log(x); } }; Object.assign(dest, { id: 'first' }, { id: 'second' }, { id: 'third' }); // first // second // third /** * 对象引用 */ dest = {}; src = { a: {} }; Object.assign(dest, src); // 浅复制意味着只会复制对象的引用 console.log(dest); // { a :{} } console.log(dest.a === src.a); // true