javascript进阶学习:对象属性全解,自定义属性特性,存取器属性,检测属性,继承属性

蛰伏已久 蛰伏已久 2019-01-24

我们在VUE源码中,可以看到大量使用Object.defineProperty的场景,在一些库的开发中,这是一个很重要的功能,因此也是进阶必须学习的内容

//vue观察者模式中使用Object.defineProperty
Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: function reactiveGetter () {
    const value = getter ? getter.call(obj) : val
    if (Dep.target) {
      dep.depend()
      if (childOb) {
        childOb.dep.depend()
        if (Array.isArray(value)) {
          dependArray(value)
        }
      }
    }
    return value
  },
  set: function reactiveSetter (newVal) {
    const value = getter ? getter.call(obj) : val
    /* eslint-disable no-self-compare */
    if (newVal === value || (newVal !== newVal && value !== value)) {
      return
    }
    /* eslint-enable no-self-compare */
    if (process.env.NODE_ENV !== 'production' && customSetter) {
      customSetter()
    }
    // #7981: for accessor properties without setter
    if (getter && !setter) return
    if (setter) {
      setter.call(obj, newVal)
    } else {
      val = newVal
    }
    childOb = !shallow && observe(newVal)
    dep.notify()
  }
})

属性的特性

一个对象拥有多个属性,每个属性其实也有自己的特性,比如下面一个非常简单的对象var obj={a:1},a属性不只是简单的有个value=1,还有其他3种特性,分别是可写(writable)、可枚举(enumerable)、可配置(configurable)

可以通过Object.getOwnPropertyDescriptor()来获取属性的特性描述,第一个参数为对象,第二个参数为要获取的属性名称

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

修改属性

我们可以通过Object.defineProperty()来添加新属性或者修改已有属性的特性

  • 第一个参数为一个对象

  • 第二个参数为对象的属性名称

  • 第三个参数为特性的描述

如果属性的名称不存在,则为添加新属性,如果属性名称存在,则为修改属性

var obj={}
//添加新属性a
Object.defineProperty(obj,'a',{
    value:1,
    writable:true,
    enumerable:true,
    configurable:true
})

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

//修改属性a的可写属性,只需传入writable即可,其他属性特性不变
Object.defineProperty(obj,'a',{
    writable:false
})
obj.a=2
console.log(obj.a)   //1,a不可写,因此仍然保持为初始值1

可写属性

我们可以通过定义属性的writable值来决定属性是否可以,如果writable=true,则可以修改,否则对属性的赋值操作无效,如上例

可枚举属性

我们可以通过定义属性的enumerable来定义属性是否可枚举

我们可以通过for/in 循环来遍历对象中所有可枚举的属性

var obj={
    a:1,
    b:2
}

for(let key in obj){
    console.log(key)    //先后打印出a  b
}

//将属性b改为不可枚举
Object.defineProperty(obj,'b',{
    enumerable:false,
})

for(let key in obj){
    console.log(key)  //只答应出a
}

可配置属性

我们可以通过定义属性的configurable来定义属性是否可配置

可配置的属性,可以通过delete运算符删除,而不可配置的属性不能被删除,严格模式下还会报错,非严格模式下返回false

var obj={
    a:1,
    b:2
}

Object.defineProperty(obj,'b',{
    configurable:false,
})

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

数据属性和存取器属性

像上面我们的举例,对象的属性由属性名、属性值和一组特性(可写、可枚举、可配置)构成,这样的我们称为“数据属性”

在ES6中,属性值可以用一个或两个方法替代(getter、setter方法),由getter和setter定义的属性称作“存取器属性”

var obj={
    get a(){
        console.log("进行了get操作")
    },
    set a(val){
        console.log("进行了set操作,set的值为:"+val)
    }
}

obj.a     //进行了get操作
obj.a=1   //进行了set操作,set的值为:1

console.log(Object.getOwnPropertyDescriptor(obj,'a'))

//get: ƒ a()
//set: ƒ a(val)
//enumerable: true
//configurable: true

可以看到存取器属性没有value和writable特性,取而代之的是get和set特性

  • 数据属性的特性:value、writable、enumerable、configurable

  • 存取器属性的特性:get、set、enumerable、configurable

同样的,我们也可以通过Object.defineProperty()来添加修改存取器属性

var obj={}

Object.defineProperty(obj,'a',{
    get:function(){
        console.log("进行了get 操作")
    },
    set:function(val){
        console.log("进行了set操作,set的值为:"+val)
    }
})
obj.a     //进行了get操作
obj.a=1   //进行了set操作,set的值为:1

定义多个属性

我们通过Object.defineProperty()每次只能定义一个对象属性,也可以通过Object.defineProperties()一次定义多个属性,Object.defineProperties()接受两个参数,第一个为要操作的对象,第二个为属性描述对象

var obj={}

Object.defineProperties(obj,{
   a:{
       value:1,
       writable:false
   },
   b:{
       value: 2,
       writable:true
   }
})
console.log(obj)  //{a:!,b:2}

检测对象是否拥有某个属性

直接通过判断对象的属性是否等于undefined,如果不等于undefined,则说明对象拥有这个属性

var obj={a:1}

if(obj.a!==undefined){
    console.log("obj拥有属性a")   //会打印
}

if(obj.b!==undefined){
    console.log("obj拥有属性b")   //不会打印 
}

当然这里有个问题就是对象的某个属性值本来就是undefined,则判断就出现问题了

var obj={a:undefined}

if(obj.a!==undefined){
    console.log("obj拥有属性a")   //不会打印
}

通过in运算符来检测,左侧是属性名,右侧是对象,属性属于对象则返回true,否则返回false。注意对象的自有属性和继承属性都可以通过in运算符返回true

var obj={a:undefined}

if("a" in obj){
    console.log("obj拥有属性a")
}

if("b" in obj){
    console.log("obj拥有属性b")
}

通过hasOwnProperty()来检测给定的属性是否是对象的自有属性,对于继承属性,则返回false

var obj={a:undefined}

if(obj.hasOwnProperty("a")){
    console.log("obj拥有属性a")
}

if(obj.hasOwnProperty("b")){
    console.log("obj拥有属性b")
}

继承属性

我们可以通过Object.create()来实现对象的继承,如下例,对象sub继承对象parent,并给自己增加了一个自有属性b

var parent={
    a:1
}

var sub=Object.create(parent,{
    b:{
        value:2
    }
})


console.log(sub)
console.log(sub.a)   //1
console.log(sub.b)   //2
console.log(sub.hasOwnProperty('a')) //false
console.log('a' in sub)              //true
  • 继承属性:在对象的原型对象中定义的属性

  • 自有属性:直接在对象中定义的属性

hasOwnProperty可以判断一个属性是否是自有属性,是返回true,而in运算符可以判断属性是否是自有属性或继承属性,自有属性或继承属性都返回true

复制对象

通过JSON先将对象转为字符串,再还原为对象,可完成对象的复制

var obj1={
    a:1,
    b:2
}

var obj2=JSON.parse(JSON.stringify(obj1))
console.log(obj2)  //{a:1,b:2}


分享到

点赞(0)