javascript进阶学习:对象属性全解,自定义属性特性,存取器属性,检测属性,继承属性
我们在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)