概述
可以将本文理解为《你不知道的javascript》(上卷)观后感,尽管在我读这本书之前看到很多人将此书奉上神坛,但还是不影响我每次翻阅此书都想直呼牛x!!
1. 属性描述符
前言:在ES5之前,JS没有提供可以直接检测属性特性的方法,比如判断属性是否是只读;从ES5开始,所有的属性都具备了属性描述符。
属性描述符的定义和配置:
如果要为一个属性定义它的属性描述符,可以通过Object.defineProperty
:
let obj = {};
obj.a = 1;
Obeject.defineProperty(obj, 'a', {
value: 2, // 相当于 obj.a = 2; 会覆盖上方赋值的 1
writable: ture, // 默认为true,
enumerable: true, // 默认为true
configurable: true, // 默认为true
})
- writable:该属性是否能被重新赋值;
- enumerable:该属性能否被枚举;
- configurable:该属性的属性描述符能否被重新配置和删除;
有了这几个描述符,我们就能对对象的属性进行更丰富的定义,例如对象常量:
只需将属性描述符的writable和configurable都设置为false,那么该属性就会变成一个无法更改且无法删除的常量。
属性描述符的细节和其他定义对象的方法:
除此之外,ES5还提供了一些直接定义整个对象的方法,例如Object.preventExtensions(objName)
,调用改方法能禁止一个对象添加新属性并且保留已有属性:
let obj = {
a: 1,
}
Object.preventExtensions(obj);
obj.b = 1;
console.log(obj.b); // undefined
并且如果是在严格模式下,强行创建属性b会提示TypeError;
在此基础上,如果想将obj里的所有属性都更改为不可配置性,(不可配置表示的是该属性的属性描述符不可被修改(writable例外),并且也无法被删除。)那么就可以调用Object.seal(objName)
。该方法的底层原理就是在Object.preventExtension(objName)的基础上,再将存在于obj上的所有属性的configurable都修改为false,不过与直接修改configurable不同的是,一个所有属性都为configurable:false的对象调用改方法不会报错:
let obj = {}
obj.a = 1;
Object.defineProperty(obj,'a',{
value:2,
configurable:false,
})
Object.seal(obj)
console.log(obj.a); // 2
而如果一个属性的configurable为false的话,再次配置该属性的属性配置符会提示TypeError:
let obj = {};
Object.defineProperty(obj, 'a', {
value: 1,
configurable: false,
enumerable: true
})
Object.defineProperty(obj, 'a', {
configurable: true,
enumerable: false,
})
// TypeError: Cannot redefine property: a
不过一个属性例外,那就是writable。如果某个属性的configurable为false,那么它的writable仍可以修改为false,并且切可以将false赋值给writable(虽然赋值多次的操作没什么用)
let obj = {};
Object.defineProperty(obj, 'a', {
value: 1,
configurable: false,
})
Object.defineProperty(obj, 'a', {
writable:false
})
Object.defineProperty(obj, 'a', {
writable:false
})
Object.defineProperty(obj, 'a', {
writable:false
})
console.log(obj.a); // 1
但如果将writable的值由false改为true,就会提示TypeError:
let obj = {};
Object.defineProperty(obj, 'a', {
value: 1,
configurable: false,
})
Object.defineProperty(obj, 'a', {
writable:false
})
Object.defineProperty(obj, 'a', {
writable:true
})
// TypeError: Cannot redefine property: a
绕了一圈,继续聊回对象,还有一个比Object.seal(objName)
更高级别的方法,那就是Object.freeze(objName)
,该方法是在seal
的基础上将存在于对象上的所有属性的writable都配置为false。
说完这么多,在这简单总结一下ES5三个定义对象的方法:
- 禁止扩展——
Object.preventExtensions(objName)
:保留对象的原有属性,禁止对象再新增属性。在普通模式下新增属性会被js无视,严格模式下会抛出TypeError。 - 密封——
Object.seal(objName)
:在Object.preventExtensions(objName)
的基础上,将所有存在于对象上的属性配置为configurable:false。 - 冻结——
Object.freeze(objName)
:在Object.seal(objName)
的基础上,将所有存在于对象上的属性配置为writable:false
。
值得一提的是,上述的三个方法对对象的限制只局限于浅层属性:
let obj = {
a: 1,
b: {
c: 2,
}
}
Object.preventExtensions(obj);
obj.d = 3;
obj.b.e = 4;
console.log(obj.d, obj.b.e); // undefined 4
可以看到obj无法添加新的对象,而obj里的对象b可以继续添加新的对象
let b = {
c: 2,
e: 3
}
let obj = {
a: 1,
b
}
Object.seal(obj);
for (const objKey in b) {
console.log(b[objKey]); // 2, 3
}
Object.defineProperty(b, 'c', {
enumerable: false
})
for (const objKey in b) {
console.log(b[objKey]); // 3
}
let b = {
c: 2,
}
let obj = {
a: 1,
b
}
Object.freeze(obj);
console.log(b.c); // 2
b.c = 3;
console.log(b.c); // 3
2. 访问描述符
当我们对一个属性进行赋值和查找时,调用的都是对象内置默认的[[Put]] 和[[Get]]。接下来我们要介绍的是属性中的getter和setter,如果一个属性中至少有一个getter或setter,那么该属性就从属性描述符转变为访问描述符。
访问描述符不同于属性描述符,value和writable都会被访问描述符忽视,其关心的只有getter、setter、enumerable和configurable:
let obj = {
get a (){
return 2
}
}
obj.a = 3;
console.log(obj.a); // 2
console.log(Object.getOwnPropertyDescriptor(obj, 'a'));
// 访问描述符
// {
// get: [Function: get a],
// set: undefined,
//
enumerable: true,
//
configurable: true
// }
// 可以看到Object.getOwnPropertyDescriptor返回的对象中不再有value和writable,取而代之的是get和set。
如果说getter对应的是获取某个属性的值(类似于变量的RHS查询),那么setter对应的就是给某个属性赋值(类似变量的LHS查询):
let obj = {
get a(){
return this.val
},
set a(param){
this.val = param * 2
}
}
obj.a = 1;
console.log(obj.a); // 2
可以看到setter还可以自定义属性的赋值方法。
3. 存在性
如果访问对象的某个属性,返回一个undefined的值,那么要如何判断这个属性是否真实地存在于该对象上?
答案是可以通过in、obj.hasOwnProperty(objName)和Object.getOwnPropertyNames(obj):
let obj = {
a: undefined
}
console.log('a' in obj); // ture
console.log('b' in obj); // false
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('b')); // false
console.log(Object.getOwnPropertyNames(obj));; // ['a']
先简单讲述一下这三个方法的异同:
- objName in obj:返回一个布尔值,查找范围是整个对象及其原型链;
- obj.hasOwnProperty(objName):返回一个布尔值,查找范围仅限于对象上;
- Object.getOwnPropertyNames(obj):返回一个数组,数组里每一项为对象的属性名。
4.枚举性
如果一个属性的enumerable为false,那我们能通过什么方法判断?
答案是obj.propertyIsEnumberable(objKey)和Object.keys(obj)
let obj = {
a: 1
}
Object.defineProperty(obj,'b',{
value:2,
enumerable:false,
})
console.log(obj.propertyIsEnumerable('a')); // true
console.log(obj.propertyIsEnumerable('b')); // false
console.log(Object.keys(obj)); // ['a']
老规矩,看完例子我们在继续讲述一下这两个方法的异同:
- obj.propertyIsEnumerable(objKey):传入一个obj对象上的属性名,返回一个布尔值表示这个属性是否为可枚举属性;
- Object.keys(obj):返回一个数组,数组中的每一项为obj对象上可枚举的属性的属性名。
这两个方法只会查询存在于对象上的属性,不会涉及到原型链。
function Father() {
this.a = 1;
}
Son.prototype = new Father();
function Son() {
this.b = 2;
}
let son = new Son();
console.log('a' in son); // true
console.log(son.propertyIsEnumerable('a')); // false
console.log(Object.keys(son)); // ['b']
最后
以上就是欣喜往事为你收集整理的对象小谈,让你更清楚的认识javascript里的对象的全部内容,希望文章能够帮你解决对象小谈,让你更清楚的认识javascript里的对象所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复