本文讨论了js中对象的属性与属性之间的区别。介绍了Object.defineProperty()的用法。
1. 属性与属性的差别
js中的对象是属性的集合。换句话说,js中的对象中什么都没有,只有属性。
虽然同样是对象的属性,其实属性与属性之间还是有很多的区别的。
举个例子:
a = 1;
var b = 2;
如上定义的两个变量有什么区别之处吗? 你可以先思考一下,然后在往下看。
区别之处:
- 如果它们写在函数的内部的话,a是不加var就直接定义的全局变量,这种写法已经被鄙视的不行了,因为会导致不可控的全局变量的污染。但并不是不可以使用。b是一个局部变量。以下代码可以证明这一点。
function f(){
a = 1;
var b = 2;
}
f();
console.info(a) // 1
console.info(b) // 报错
顺便提一句,如果开启了严格模式,则a=1
这中写法就会直接报错了。
"use strict"
function f(){
a = 1; // 这里直接报错
var b = 2;
}
f();
- 如果他们写在函数的外部的话,它们都是全局变量,都是加在window对象上的属性。但a属性不能删除,b属性可以删除。
a = 1;
var b = 2;
console.info(window.a)
console.info(window.b)
如果上面的代码并不能正常输出1,2。建议你不要使用chrome浏览器。
下面看下可否删除的例子
a = 1;
var b = 2;
delete window.a // true
delete window.b // false
console.info(window.a); // 无法访问
console.info(window.b); // 还可以访问
同样是属性,为什么一个可以删除,一个不可以? 属性之间地位有差别吗? 答案是:有区别。
我们可以通过Object.getOwnPropertyDescriptor() 来查看这两个属性之间的区别:
a = 1;
var b = 2;
Object.getOwnPropertyDescriptor(window,"a")
//输出:Object { value: 2, writable: true, enumerable: true, configurable: true }
Object.getOwnPropertyDescriptor(window,"b")
//输出:Object { value: 2, writable: true, enumerable: true, configurable: false }
window.a 的 configurable是true
window.b的configurable是false。 这就是区别。
那么其他的三个分别表示:
- value: 值
- writable : 是否可以修改
- enumerable : 是否可以枚举
更详细的说明可以看这里
2. 两种定义对象的属性的方式
- 初始值/ 赋值
- Object.defineProperty
1.1 初始值/ 赋值
var obj = {
name: "fan"
}
这是比较常见的一种做法:对象字面量。
var obj = {}
obj.name = "fan"
// or obj["name"] = "fan"
这种写法也比较常见。它们产生的效果是一样。
Object.getOwnPropertyDescriptor(obj,"name")
// { value: "fan", writable: true, enumerable: true, configurable: true }
这种方式定义的属性都是:
- 可删除
- 可修改
- 可枚举
1.2 Object.defineProperty
我们可以更加精确地控制属性的特性。例如:让一个属性不能被修改。
使用数据描述符定义对象的属性
var obj = {};
Object.defineProperty(obj, 'name', {
value: 37,
writable: true, //如果是false 则不可修改
enumerable: true,
configurable: true
});
另一种
使用存取描述符定义对象的属性
var nameValue = "fan";
Object.defineProperty(obj, 'name', {
get() { return nameValue; },
set(newValue) { nameValue = newValue; },
enumerable: true,
configurable: true
});
注意,这两种方式不能混在一起使用。 以下代码报错:
var obj = {}
var nameValue = "fan";
Object.defineProperty(obj, 'name', {
value:"abc",
get() { return nameValue; },
set(newValue) { nameValue = newValue; },
enumerable: true,
configurable: true
});
报错的内容可能是:
Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>
at Function.defineProperty (<anonymous>)
3. Object.defineProperty 的应用
3.1 定义特殊的属性,例如只读的
var obj = {};
Object.defineProperty(obj, 'name', {
value: "fan",
writable: false
});
obj.name = "jake" // 悄悄地失败
console.info(obj.name) // fan
当然,如果开启了严格模式,就会抛出错误了。
"use strict"
var obj = {};
Object.defineProperty(obj, 'name', {
value: "fan",
writable: false
});
obj.name = "jake" // 这里会报错
console.info(obj.name)
// Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
3.2 特殊的属性的处理
在通过存取描述符定义属性时,我们可以在set,get中添加额外的自定义处理逻辑,以达到特殊的效果。
例如:我们希望在给obj.name
赋值时,必须是字符串类型,如果是不字符串,就直接报错。
var obj = {}
var nameValue = "fan";
Object.defineProperty(obj, 'name', {
get() { return nameValue; },
set(newValue) {
if((typeof newValue) === "string")
nameValue = newValue;
else
throw new Error("name 必须是字符串...")
},
enumerable: true,
configurable: true
});
obj.name = "jake"
console.info(obj.name) // jake
obj.name = 123 // 报错
以上。