JavaScript(3)之——对象的属性描述符

本文深入探讨了JavaScript中对象属性的描述符,包括value、writable、enumerable和configurable等特性,以及如何使用Object.getOwnPropertyDescriptor和Object.defineProperty方法进行属性操作。

  对象的属性描述符是一个初学者容易忽略但是非常重要的特性,像是vue的数据双向绑定就是用它做文章。且关于它的方法和属性也很多,今天我来总结一下。

属性描述符概述

  对象的每个属性都具备了属性描述符,它描述了属性的一些特性。一共有四个特性,分别为value(值)、writable(可写)、enumerable(可枚举)和configurable(可配置)。只设置属性值的属性描述符也被称为数据描述符。
  这里我们介绍两个方法,Object.getOwnPropertyDescriptor()以及Object.defineProperty()。

//Object.getOwnPropertyDescriptor()第一个参数为某个对象,第二个参数为某个对象上的某个属性,返回这个属性的属性描述符。  
//默认writable、enumerable和configurable都为true
let obj = {
    foo: 123,
    bar: 456
}
console.log(Object.getOwnPropertyDescriptor(obj, 'foo'))
输出为:
{ 
  value: 123,
  writable: true,
  enumerable: true,
  configurable: true 
}
复制代码
//Object.defineProperty()可以给某个对象添加一个新属性或者修改某个对象的一个已有属性(前提是configurable为true)
Object.defineProperty(obj, 'baz', {
    value: 789,
    writable: true,
    enumerable: true,
    configurable: true
})
console.log(obj.baz) //789
复制代码
writable

  writable决定是否可以修改属性的值,这个不可修改是指通过.或者[]在对象上修改该属性是会出错的,但是通过属性描述符还是可以修改的。

let obj = {}  
Object.defineProperty(obj, 'baz', {
    value: 1,
    writable: false,
    enumerable: true,
    configurable: true
})
console.log(obj) //{ baz: 1 }
Object.defineProperty(obj, 'baz', {
    value: 2,
    writable: false,
    enumerable: true,
    configurable: true
})
console.log(obj) //{ baz: 2 }
obj.baz = 3 //这样是无法修改baz属性的
console.log(obj) //{ baz: 2 }
复制代码
configurable

  configurable决定属性是否可以通过Object.defineProperty方法来修改属性描述符。configurable一旦设置是不可逆的,设置之后它除了不允许配置属性描述符以外,还不允许通过delete删除该属性。

let obj = {}  
Object.defineProperty(obj, 'baz', {
    value: 1,
    writable: true,
    enumerable: true,
    configurable: false
})
console.log(obj) //{ baz: 1 }
obj['baz'] = 3 //还是可以通过这种方式来修改属性的值
console.log(obj) //{ baz: 3 }
delete obj.baz
console.log(obj) //{ baz: 3 }没删掉,但前一句不会报错
Object.defineProperty(obj, 'baz', {  //TypeError: Cannot redefine property: baz
    value: 2,
    writable: true,
    enumerable: true,
    configurable: false
})
复制代码
enumerable

  enumerable被称为可枚举性,如果该属性为false,则表示某些操作会忽略当前属性。es5有三个操作:for...in、Object.keys、JSON.stringify,再加上es6的Object.assign方法,一共四个操作会忽略不可枚举的属性。

属性的遍历

  1. for...in:循环遍历对象自身的和继承的可枚举的属性。
  2. Objects.keys(obj):返回一个数组,包括对象自身(不含继承)的所有可枚举属性。所以说对象的遍历一般用该方法。
  3. Object.getOwnPropertyNames(obj):返回一个数组,包括对象自身(不含继承)的所有属性。包含不可枚举的属性。

let proto = { y: 1, z: 2 };
let obj = { x: 10 }
Object.defineProperty(obj, 'foo', {
    value: 3,
    writable: true,
    enumerable: false,
    configurable: false
})
Object.setPrototypeOf(obj, proto);
Object.getOwnPropertyNames(obj).forEach(key => console.log(obj[key]))  //10,3

for (let key in obj) {
    console.log(obj[key]); //10,1,2
}

Object.keys(obj).forEach(key => console.log(obj[key])) //10
复制代码

  4. Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有Symbol属性。
  5. Reflect.ownKeys(obj):返回一个数组,包含自身所有属性,包括Symbol属性和不可枚举属性。

[[Get]]和[[Put]]

  属性的[[Get]]操作会从该对象自身属性开始,沿着它的原型链寻找是否有名称相同的属性,如果找到就返回该属性的值,如果自始至终都没找到值,就返回undefined。   [[Put]]操作则涉及两种情况。一个是已经存在该属性,如果存在且有setter就调用setter,如果writable为false,则静默失败,如果这两者都不是,将该值设置为属性的值。如果对象中不存在该属性,就涉及到原型链的问题,下一节在原型及原型链中总结。
  getter和setter方法可以部分改写默认的[[Get]]和[[Put]]操作,getter在获取属性值时调用,setter在设置属性值时调用。当给一个属性设置getter和setter之后,这个属性的属性描述符也被称作访问描述符,Js会忽略掉它的writable和value特性,用get来取代value,用set取代writable。
  getter和setter有两种定义方式,一种在对象中直接定义,一种通过Object.defineProperty定义。

let val = 2;
let obj = {
    get a() {
        return val;
    },
    set a(newVal) {
        if (val === newVal) {
            return;
        }
        val = newVal * 2;
    }
}

obj.a = 4;
console.log(obj.a); //8
obj.a = 3;
console.log(obj.a); //6
复制代码

相当于:

let val = 2;
let obj = {};
Object.defineProperty(obj, 'a', {
    get: function() {
        return val;
    },
    set: function(newVal) {
        if (val === newVal) {
            return;
        }
        val = newVal * 2;
    }
})
obj.a = 4;
console.log(obj.a);//8
obj.a = 3;
console.log(obj.a);//6
复制代码
存在性

  例如'a' in myobj操作符和myobj.a/myobj[a]都是在整个原型链上查找查找该对象是否有该属性。相比之下,myobj.hasOwnProperty('a')方法只会在myobj这个对象中查找该属性,而不会去原型链中查找。
  hasOwnProperty是Object.prototype上的方法,通过Object.create(null)创建的对象是无法使用该方法的。

let myobj = {
    a: 1
}
let proto = {
    b: 2
}
Object.setPrototypeOf(myobj, proto);

console.log('a' in myobj); //true
console.log('b' in myobj); //true

console.log(myobj.hasOwnProperty('a')) //true
console.log(myobj.hasOwnProperty('b')) //false
复制代码

  部分内容参考《你不知道的JavaScript上》和《ES6标准入门》。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值