关于对象属性你应该知道的事

这篇文章是我读《你不知道的js》时做的笔记,如有错误和疑惑请在评论区指出,查看代码高亮优化版原文请点击链接,欢迎watch和star

属性描述符

Object.getOwnPropertyDescriptor

es5之前,js语言本身并没有提供可以直接检测属性特性的方法,但从es5开始,所有的属性都有了属性描述符。我们来定义一个对象,来看一下他有哪些属性。

const obj={a:1,b:2}
console.log(Object.getOwnPropertyDescriptor(obj,'a'))
// value: 1, writable: true, enumerable: true, configurable: true}

像你看到的那样,他不仅仅有一个有一个为1的值,还有三个特性:writable、enumerable、configurable。我们可以使用Object.defineProperty设置这几个特性。

Object.defineProperty

writable

当我们把writable属性置为false时,

const obj={a:1}
Object.defineProperty(obj,'a',{
    value:1,
    configurable:true,
    writable:false,
    enumerable:true
})
obj.a=6
console.log(obj.a) //1 对于属性值的修改静默失败了;在严格模式下会报错

configurable

注意,把configurable修改为false为单向操作,不能再次调用Object.defineProperty来修改这个属性的配置。我们只能把true改为false,不能从false变为true。

const obj={a:1}
Object.defineProperty(obj,'a',{
    value:1,
    configurable:false,
    writable:true,
    enumerable:true
})

Object.defineProperty(obj,'a',{
    value:1,
    configurable:true,
    writable:true,
    enumerable:true
})

// 报错
Uncaught TypeError: Cannot redefine property: a
    at Function.defineProperty ()
    at index.js:11

例外,在configurable为false并且writable为true的情况下,再次把writable修改为false是可行的。但是把writable的false变为true会报错。

const obj={a:1}
Object.defineProperty(obj,'a',{
    value:1,
    configurable:false,
    writable:true,
    enumerable:true
})
Object.defineProperty(obj,'a',{
    value:1,
    configurable:false,
    writable:true,
    enumerable:true
})

obj.a=6
console.log(obj.a) //1

不仅仅是无法修改,configurable为false时还会禁止删除属性。

Object.defineProperty(obj,'a',{
    value:1,
    configurable:false,
    writable:true,
    enumerable:true
})
delete a
console.log(obj) // {a:1}

delete删除语句静默失败了,因为是不可配置的。

Enumerable

这个描述符控制的是属性是否出现在对象的属性枚举中,如下面的例子,把enumerable设置为false,这个属性就不会出现在枚举中。调用Object.keys()for...in...都不会检测到a属性。如果你不希望某个属性出现在枚举中,可以把enumerable置为false。

const obj={a:1,b:2}
Object.defineProperty(obj,'a',{
    configurable:false,
    writable:true,
    enumerable:false
})
for(key in obj){
    console.log(key) //b
}
console.log(Object.keys(obj)) // ["b"]

不变性

有时候你会希望属性或对象是不可改变的。

1.对象常量

大家都知道const是无法定义一个对象常量的,但我们可以设置writeable:falseconfigurable:false就可以创建一个真正的常量属性。

const obj={a:1}
obj.a=2
console.log(obj.a)//2

Object.defineProperty(obj,"a",{
    value:1,
    writable:false,
    configurable:false
})
obj.a=3 // 这里静默失败,不会报错
console.log(obj.a) // 1

2.禁止扩展

const obj={a:2}
Object.preventExtensions(obj)
obj.b=1 // 这里静默失败,不会报错
console.log(obj)// {a:2}

3.密封

如果我们想要整个对象都无法扩展,并且把所有的现有属性标记为configuarble:false,即所有不能添加新属性,也不能重新配置,并且无法删除属性,这时可以使用Object.seal()

const obj={a:1}
Object.seal(obj)
delete obj.a // 静默失败了,并不会删除a属性
obj.b=2 // 同样静默失败
console.log(obj) // {a:1}

4.冻结

const obj={a:1}
Object.freeze(obj)
obj.a=2
obj.b=3
console.log(obj) // {a:1}

getter和setter

在es5中可以使用gettersetter部分改写改写默认操作,但是只能应用在单个属性上,无法在整个对象上使用。getter是隐藏函数会在获取属性值时调用,setter同样是隐藏函数,会在设置属性值时调用。

const obj={
    get a(){
        return 1
    }
}

Object.defineProperty(obj,'b',{
    // 给b设置一个getter
    get(){
        return this.a*2
    }, // 注意逗号
    // 确保b会出现在对象的属性列表中
    enumerable:true 
})

console.log(obj)// {a:1,b:2}

如上,不论是使用对象文字语法的get a(){}还是使用Object.defineProperty显式定义,两者都会在对象中创建一个不包含值的属性。对于这个的访问会自动调用一个隐藏函数,其返回值回座位属性访问的返回值。

const obj={
    get a(){return 1} //只定义了getter
}
obj.a=2 // 对a进行设置时,set操作会忽略赋值操作,set操作是没有意义的
console.log(obj.a)// 1

为了让属性更合理,应当设置setter,setter会覆盖单个属性的复制操作。一般来说,getter和setter是成对出现的。

const obj={
    get a(){
        return this._a
    },
    set a(val){
        this._a=val*2
    }
}
console.log(obj.a) //undefined
obj.a=1
console.log(obj.a)// 2

存在性

检测属性是否在对象上:

  1. hasOwnProperty 这个大家都知道,但他检测的只是对象上的属性,不会检查原型链上的。但也可以通过一种更强硬的方法来判断Object.prototype.hasOwnProperty.call(obj,'a')
  2. 使用in就可以检测到原型链上的属性了。
const obj={a:1}
console.log('a' in obj)// true

你可能感兴趣的:(关于对象属性你应该知道的事)