目录
3.1 定义多个属性 Object.defineProperties( )
Object.getOwnPropertyDescriptor( )
Object.getOwnPropertyDescriptors( )
1. 定义
ECMA-262 将对象定义为一组属性的无序集合,可以把对象想象成一张散列表。对象的每个属性或方法都由一个名称来标识,这个名称映射到一个值。(也就是键值对)
1.1 创建对象
- 通常方法
先创建一个 Object 实例,然后给它添加属性和方法(有点麻烦)
let person = new Object();
person.name = 'mike';
person.age = 18;
person.sayName = function() {
console.log(this.name);
};
- 对象字面量方法
let person = {
name: 'mike',
age: 18,
sayName() {
console.log(this.name);
}
// 也可以写成这样
// sayName: function() {
// console.log(this.name);
// }
};
2.属性的类型
属性包括两种
- 数据属性
- 访问器属性
特性的定义:ECMA-262 使用一些内部特性来描述属性的特征。这些特性是由为 JavaScript 实现引擎的规范定义 的。因此,开发者不能在 JavaScript 中直接访问这些特性。为了将某个特性标识为内部特性,规范会用两个中括号把特性的名称括起来,比如 [[ Enumerable ]] 。
2.1 数据属性
数据属性包含一个保存数据值的位置。值会从这个位置读取,也会写入到这个位置。数据属性有4个特性描述它们的行为。
- [[ configurable ]] : 表示属性是否可以通过 delete 删除并重新定义,是否可以修改他的特性。以及是否可以把它变成访问器属性。默认是 true
- [[ Enumerable]] : 表示属性是否可以通过 for-in 循环返回。默认是 true
- [[ Writable ]] : 表示属性是否可以被修改。默认是 true
- [[ Value ]] : 表示属性的实际值,默认未赋值的情况下是 undefined
例如:
let person = {
name: 'mike'
}
这里创建了一个名为 name 的属性,且赋值为 mike ,那么前面三个特性 [[ configurable ]] [[ Enumerable]] [[ Writable ]] 都会是默认值 true , [[ Value ]] 为 ' mike '
2.2 修改默认属性
使用 Object.defineProperty() 方法,接收三个参数:属性对象、属性名称、描述符对象
let person = {
name: 'mike'
}
Object.defineProperty(person,"name", {
writable: false
})
person.name = 'alice'
console.log(person.name); // mike
上面的例子,在非严格模式下尝试重新赋值会被忽略,在严格模式下会报错
Tips:
- 如果把 [[ configurable ]] 设置为 false,该属性变成不可配置后,就不能再变回可配置了。再次调用 Object.defineProperty() 修改除 [[ writable ]] 之外的所有属性都会报错
- 在调用 Object.defineProperty() 时如果没有给 configurable enumberable writable 指定值,就会默认是 false (例如 3.1 的例子)
- 多数情况下,可能不需要 Object.defineProperty() 提供这些强大的设置,但要知道这些概念
2.3 访问器属性
访问器属性不包含数值。它们包含一个获取函数(getter)和一个设置函数(setter),不过这两个函数不是必需的。在读取时会调用 getter ,在写入时调用 setter
访问器属性不是直接定义的,必须通过 Object.defineProperty()
访问器属性有4个特性
- [[ configurable ]] : 表示属性是否可以通过 delete 删除并重新定义,是否可以修改他的特性。以及是否可以把它变成访问器属性。默认是 true
- [[ Enumerable]] : 表示属性是否可以通过 for-in 循环返回。默认是 true
- [[ Get ]] : 获取函数,在读取属性时使用,默认为 undefined
- [[ Set ]] : 设置函数,在写入属性时使用,默认为 undefined
例如:
let book = {
year_: 2023, // 数据属性,通常情况下下划线表示该属性并不希望在对象方法的外部使用
edition: 1 // 数据属性
};
// 定义访问器属性
Object.defineProperty(book,"year", {
get() {
return this.year_;
},
set(value) {
if(value > 2023) {
this.year_ = value;
this.edition += value - 2023;
}
}
});
// 使用访问器属性
book.year = 2025;
console.log(book.edition); // 3
在上面的例子中,修改 year 属性会导致 year_ 和 edition 都会进行更新。这个就是访问器属性的典型使用场景,设置一个属性会导致其他属性发生一些变化。
Tips:
- 虽然获取函数和设置函数并不是一定需要定义的。但是如果你只定义了获取函数,也就意味着该属性是只读的,当你想要尝试修改属性时,非严格模式会忽略,严格模式会报错。反之也是如此
3.对象的操作
3.1 定义多个属性 Object.defineProperties( )
Object.defineProperties( ) 接收两个参数,第一个是添加或修改属性的对象,第二个是描述符对象
let book = {};
Object.defineProperties(book,{
year_: { // 数据属性
value: 2023
},
edition: { // 数据属性
value: 1
},
year: { // 访问器属性
get() {
return this.year_;
},
set(value) {
if(value > 2023) {
this.year_ = value;
this.edition += value - 2023;
}
}
}
})
这个例子中定义的 book 对象跟 2.3 例子中的 book 对象是一样的。区别就是数据属性 year_ 和edition 由于没有指定 configurable enumerable 和 writable 特性值,所以全都设置为 false
3.2 读取属性的特性
-
Object.getOwnPropertyDescriptor( )
Object.getOwnPropertyDescriptor( ) 接收两个参数,第一个是要访问的对象名,另一个是属性名(要以字符串的形式)。返回值是一个对象,返回数据属性和访问器属性的四个特性
let book = {};
Object.defineProperties(book,{
year_: { // 数据属性
value: 2023
},
edition: { // 数据属性
value: 1
},
year: {
get() {
return this.year_;
},
set(value) {
if(value > 2023) {
this.year_ = value;
this.edition += value - 2023;
}
}
}
})
let descriptor = Object.getOwnPropertyDescriptor(book,"year_");
console.log(descriptor);
// {
// value: 2023,
// writable: false,
// enumerable: false,
// configurable: false
// }
let descriptor1 = Object.getOwnPropertyDescriptor(book,"year");
console.log(descriptor1);
// {
// get: [Function: get],
// set: [Function: set],
// enumerable: false,
// configurable: false
// }
-
Object.getOwnPropertyDescriptors( )
该函数接收一个参数为要访问的对象,返回值是一个对象。该函数实际上会在对象的每个自有属性上调用 Object.getOwnPropertyDescriptor( ) ,合并在一个新对象后返回。
let book = {};
Object.defineProperties(book, {
year_: { // 数据属性
value: 2023
},
edition: { // 数据属性
value: 1
},
year: {
get() {
return this.year_;
},
set(value) {
if (value > 2023) {
this.year_ = value;
this.edition += value - 2023;
}
}
}
})
let descriptors = Object.getOwnPropertyDescriptors(book);
console.log(descriptors);
// {
// year_: {
// value: 2023,
// writable: false,
// enumerable: false,
// configurable: false
// },
// edition: {
// value: 1,
// writable: false,
// enumerable: false,
// configurable: false
// },
// year: {
// get: [Function: get],
// set: [Function: set],
// enumerable: false,
// configurable: false
// }
// }
3.3 合并对象 Object.assign( )
- Object.assign( ) 这个方法接收一个目标对象和一个或多个源对象作为参数。返回值是修改后的目标对象。
- 将源对象中可枚举和自有的属性复制到目标对象中。(使用源对象的 [[ Get ]] 特性方法获取值,使用目标对象的 [[ Set ]] 特性方法设置属性值
简单复制
let dest = {
a: 'aaa'
}
let src = {
b: 'bbb'
}
let k = Object.assign(dest,src, { c: 'ccc'} );
console.log(dest);
// { a: 'aaa', b: 'bbb', c: 'ccc' }
console.log(k);
// { a: 'aaa', b: 'bbb', c: 'ccc' }
获取函数和设置函数
let dest = {
set (val) {
console.log(`This is ${val}`);
}
}
let src = {
get () {
console.log('src getter');
return 'foo';
}
}
Object.assign(dest,src);
dest.get(); // src getter
覆盖属性,重复的属性会进行覆盖
let dest = {
a: 'aaa',
b: 'bbb'
}
let src = {
a: 'zzz'
}
Object.assign(dest, src, { a: 'kkk' });
console.log(dest);
// { a: 'kkk', b: 'bbb' }
4.增强的对象语法
4.1 属性值简写
当属性名和变量名相等时,可以直接简写属性名即可。当简写时,如果没有找到同名变量,则会抛出 ReferenceError
let name = 'mike';
function sayName() {
console.log(this.name);
}
let dest = {
name, // 等价于 name: name
age: 18,
sayName // 函数也适用
}
console.log(dest);
// { name: 'mike', age: 18, sayName: [Function: sayName] }
dest.sayName(); // mike
4.2 可计算属性
- 如果想要用一个变量来定义属性名的话,要使用中括号语法,不然会直接识别成字符串。
Tips
- 变量中途发生改变,要重新定义 person 才会在 person 上生效
let key = 'job';
let person = {
[key]: 'good'
}
console.log(person.job); // good
key = 'class';
console.log(person.class); // undefined 要重新定义对象才有用
console.log(person.job); // good
// 重新定义对象 person 变量改动后的值才生效
person = {
[key]: 'bad'
}
console.log(person.class); // bad
console.log(person.job); // undefined
- 函数返回值也可以充当属性名
let nameKey = 'name';
function getKey(count) {
return `${nameKey}_${count}`;
}
let person = {
[getKey(1)] : 'aaa'
}
console.log(person);
// { name_1: 'aaa' }
5. 对象解构
ES6 新增了对象结构语法,可以在一条语句中使用嵌套数据实现一个或多个赋值操作。
- 不解构时
let person = {
name: 'mike',
age: 18
}
let personName = person.name;
let personAge = person.age;
console.log(personName); // mike
console.log(personAge); // 18
- 解构时,简单轻松
let person = {
name: 'mike',
age: 18
}
let { name: personName, age: personAge } = person;
console.log(personName); // mike
console.log(personAge); // 18
- 如果属性名和要赋值的变量名相等,甚至可以简写
let person = {
name: 'mike',
age: 18
}
let { name, age, job } = person;
console.log(name); // mike
console.log(age); // 18
console.log(job); // undefined 当属性不存在时 undefined
Tips:
- 解构时会自动帮你生成变量,如果要给事先声明过的变量赋值,则表达式必须包含在一对括号中。
let personName,personAge;
let person = {
name: 'mike',
age: 18
};
({ name: personName, age: personAge } = person);
console.log(personName); // mike
console.log(personAge); // 18
终于写完了,不过未完待续......