前置知识
对象中方法的定义
看如下代码对象中的方法定义是可以简写的
const obj = {
fnn () {
return 'fn-->fnn';
},
testOne: function () {
return 'fn-->testOne';
},
testTwo: () => {
return 'fn-->testTwo';
},
async fnns () {
return 'fn-->fnns';
},
testOnes: async function () {
return 'fn-->testOnes';
},
testTwos: async () => {
return 'fn-->testTwos';
},
['test' + 'three'] () {
return `fn-->${['test' + 'three']}`;
},
};
console.log('fnn:', obj.fnn());
console.log('testOne:', obj.testOne());
console.log('testTwo:', obj.testTwo());
console.log('fnns:', obj.fnns());
console.log('testOnes:', obj.testOnes());
console.log('testTwos:', obj.testTwos());
console.log('test' + 'three', obj['test' + 'three']());
console.log('test' + 'three', obj.testthree());
getter 与 setter
get
:将对象属性绑定到查询该属性时将被调用的函数。
- 语法:
get [prop]() { ... }
prop
: 要绑定到给定函数的属性名。
set
:将对象属性绑定到要调用的函数。
- 语法:
set [prop](val) { ... }
prop
: 要绑定到给定函数的属性名。val
: 用于保存尝试分配给prop
的值的变量的一个别名。
使用示例
const obj = {
a: '我是 a 初始的值,稍后我将改变',
// 相当于一个普通函数
get () {
console.log('调用了此对象的 get 方法');
return 'get'
},
// getter 绑定了 test 属性
get testGetter () {
return this.a
},
set testGetter (name) {
this.a = name
},
};
console.log('第一次获取 testGetter:', obj.testGetter);
obj.a = '第一次获取 testGetter 后 a 的值变成此文字'
console.log('第二次获取 testGetter:', obj.testGetter);
obj.testGetter = '这是一个新值'
console.log('第三次获取 testGetter:', obj.testGetter);
Object.defineProperty 作用
直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
语法: Object.defineProperty(obj, prop, descriptor)
obj
:要在其上定义属性的对象。prop
:要定义或修改的属性的名称。descriptor
:将被定义或修改的 属性描述符
返回值: 被传递给函数的对象。
属性描述符
-
对象里目前存在的属性描述符有两种主要形式:数据描述符 和 存取描述符。
-
数据描述符和存取描述符均具有以下可选键值
configurable
:当且仅当该属性的configurable
为true
时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为false
。enumerable
:当且仅当该属性的enumerable
为true
时,该属性才能够出现在对象的枚举属性中。默认为false
。
-
数据描述符同时具有以下可选键值:
value
:该属性对应的值。可以是任何有效的JavaScript
值(数值,对象,函数等)。默认为undefined
。writable
:当且仅当该属性的writable
为true
时,value
才能被赋值运算符改变。默认为false
。
-
存取描述符同时具有以下可选键值:
get
:一个给属性提供getter
的方法,如果没有getter
则为undefined
。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this
对象(由于继承关系,这里的this
并不一定是定义该属性的对象)。默认为undefined
。set
:一个给属性提供setter
的方法,如果没有setter
则为undefined
。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认为undefined
。
-
如果一个描述符不具有
value
,writable
,get
和set
任意一个关键字,那么它将被认为是一个数据描述符。 -
如果一个描述符同时有(
value
或writable
)和(get
或set
)关键字,将会产生一个异常。 -
拥有布尔值的字段的默认值都是
false
。value
,get
和set
字段的默认值为undefined
。 -
当描述符中省略某些字段时,这些字段将使用它们的默认值。
存储描述符示例
const obj = {
firstName: 'A',
lastName: 'B'
}
Object.defineProperty(obj, 'fullName', {
// 当读取对象此属性值时自动调用, 将函数返回的值作为属性值, this 为 obj
get () {
return this.firstName + '-' + this.lastName
},
// 当修改了对象的当前属性值时自动调用, 监视当前属性值的变化, 修改相关的属性, this 为 obj
set (value) {
const names = value.split('-')
this.firstName = names[0]
this.lastName = names[1]
}
})
console.log(obj.fullName) // A-B
obj.fullName = 'w-fly'
console.log('fullName:', obj.fullName) // w-fly
console.log('firstName:', obj.firstName) // w
console.log('lastName:', obj.lastName) // fly
使用 Object.defineProperty 创建属性
如果对象中不存在指定的属性,Object.defineProperty()
就创建这个属性。
var o = {}; // 创建一个新对象
// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(o, "a", {
value: 37,
writable: true,
enumerable: true,
configurable: true
});
// 对象 o 拥有了属性 a,值为 37
console.log(o);
// 在对象中添加一个属性与存取描述符的示例
var bValue;
Object.defineProperty(o, "b", {
get: function () {
return bValue;
},
set: function (newValue) {
bValue = newValue;
},
enumerable: true,
configurable: true
});
// 对象 o 拥有了属性 b,值为 38
// o.b 的值现在总是与 bValue 相同,除非重新定义 o.b
o.b = 38;
// 数据描述符和存取描述符不能混合使用
Object.defineProperty(o, "conflict", {
value: 'aaa',
get: function () {
return 'bbb';
}
});
// throws a TypeError: value appears only in data descriptors, get appears only in accessor descriptors
修改属性
- 如果属性已经存在,
Object.defineProperty()
将尝试根据描述符中的值以及对象当前的配置来修改这个属性。 - 如果旧描述符将其
configurable
属性设置为false
,则该属性被认为是“不可配置的”,并且没有属性可以被改变(除了单向改变writable
为false
)。 - 当属性不可配置时,不能在数据和访问器属性类型之间切换。
- 当试图改变不可配置属性(除了
value
和writable
属性之外)的值时会抛出TypeError
,除非当前值和新值相同。 Writable
属性:当writable
属性设置为false
时,该属性被称为“不可写”。它不能被重新分配。
如示例所示,试图写入非可写属性不会改变它,也不会引发错误。
var o = {}; // Creates a new object
Object.defineProperty(o, 'a', {
value: 37,
writable: false
});
console.log(o.a); // 37
o.a = 25; // 不会报错,但是修改不会生效
console.log(o.a); // 37
Enumerable
:enumerable
定义了对象的属性是否可以在 for...in
循环和 Object.keys()
中被枚举。
var o = {};
Object.defineProperty(o, "a", { value: 1, enumerable: true });
Object.defineProperty(o, "b", { value: 2, enumerable: false });
Object.defineProperty(o, "c", { value: 3 }); // enumerable defaults to false
o.d = 4; // 如果使用直接赋值的方式创建对象的属性,则这个属性的 enumerable 为 true
for (var i in o) {
// 'a' 和 'd'
console.log(i);
}
Object.keys(o); // ["a", "d"]
o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false
Configurable
特性:configurable
特性表示对象的属性是否可以被删除,以及除 value
和 writable
特性外的其他特性是否可以被修改。如果 o.a
的 configurable
属性为 true
,则不会抛出任何错误,并且该属性将在最后被删除。
var o = {}
Object.defineProperty(o, 'a', {
get: function () {
return 1
},
configurable: false
})
// throws a TypeError
Object.defineProperty(o, 'a', { configurable: true })
// throws a TypeError
Object.defineProperty(o, 'a', { enumerable: true })
// throws a TypeError (set was undefined previously)
Object.defineProperty(o, 'a', {
set: function () {
}
})
// throws a TypeError (even though the new get does exactly the same thing)
Object.defineProperty(o, 'a', {
get: function () {
return 1
}
})
// throws a TypeError
Object.defineProperty(o, 'a', { value: 12 })
console.log(o.a) // 1
delete o.a // false
console.log(o.a) // 1
添加多个属性和默认值:考虑特性被赋予的默认特性值非常重要,通常,使用点运算符和 Object.defineProperty()
为对象的属性赋值时,数据描述符中的属性默认值是不同的,如下例所示。
var o = {};
o.a = 1;
// 等同于 :
Object.defineProperty(o, "a", {
value: 1,
writable: true,
configurable: true,
enumerable: true
});
// 另一方面,
Object.defineProperty(o, "a", { value: 1 });
// 等同于 :
Object.defineProperty(o, "a", {
value: 1,
writable: false,
configurable: false,
enumerable: false
});
一般的 Setters
和 Getters
:下面的例子展示了如何实现一个自存档对象。 当设置temperature
属性时,archive
数组会获取日志条目。
function Archiver () {
var temperature = null;
var archive = [];
Object.defineProperty(this, 'temperature', {
get: function () {
console.log('get!');
return temperature;
},
set: function (value) {
temperature = value;
archive.push({ val: temperature });
}
});
this.getArchive = function () { return archive; };
}
var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]