Object.defineProperty()方法可以直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
/*
* obj 要定义属性或修改属性的目标对象。
* prop 属性名称
* descriptior 属性描述符
*/
Object.defineProperty(obj, prop, descriptor)
属性描述符
- configurable 对象是否可通过Object.defineProperty修改,默认false
- enumerable 能否枚举(for..in 或 Object.keys),默认false
- writable 只有为true才能通过赋值来修改值,默认false
- value 属性的值,默认undefined
- get 当访问该属性时,该方法会被执行,默认undefined
- set 当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。默认undefined
(value或writable)和(get或set)不能同时存在
var a = {};
Object.defineProperty(a,'name',{configurable : false})
Object.defineProperty(a,'name',{value : 'xuriliang'}) //会抛出异常,如果configurable为true则不会
a.name = 'rlxu' // a得值不会发生改变,因为writable默认为false
Object.keys(a) //没有获取到name,因为enumerable默认为false
定义属性
通过赋值操作添加的普通属性是可枚举的,能够在属性枚举期间呈现出来(for...in 或 Object.keys 方法), 这些属性的值可以被改变,也可以被删除。这个方法允许修改默认的额外选项(或配置)。默认情况下,使用 Object.defineProperty() 添加的属性值是不可修改的。
var obj = {};
Object.defineProperty(obj,'name',{
configurable : true,
writable : true,
enumerable : true,
value : 'xuriliang'
})
Object.keys(obj) //enumerable为true,可枚举
obj.name = 'rlxu'; //writable为true,可赋值
同时定义多个属性可以使用Object.defineProperties
,如:
var obj = Object.create(null)
Object.defineProperties(obj, {
'_id': {
value: 1,
configurable : true,
writable : true,
enumerable : true
},
'name': {
value : 'Lucy',
configurable : true,
writable : true,
enumerable : true
}
});
修改现有属性
仅当属性描述configurable为true时,才可以修改属性。(通过赋值操作添加的普通属性configurable、enumerable、writable默认为true)
var obj = { name : 'xuriliang'}
var showLog = function(newval){
console.log('name change :'+newval)
}
Object.defineProperty(obj,'name',{
enumerable: true,
configurable: true,
set : function(newval){
showLog(newval)
}
})
obj.name = 'rlxu';
vue中的应用
1、把data中的属性代理到vm实例上,如:
function MyVue(options){
let data = this._data = options.data || {};
Object.keys(data).forEach(key => {
Object.defineProperty(this,key,{
enumerable: true,
configurable: true,
get: function(){
console.log('get execute...')
return this._data[key]
},
set: function(newval){
console.log('set execute...')
this._data[key] = newval
}
})
})
}
let v1 = new MyVue({
data: {
message: '你好',
borth:{
age: 18
}
}
})
这样就可以通过v1.message去访问data属性。需要注意的是,当我们设置v1.borth.age = 20会发现,get执行了一次,set没有执行。这是因为我们只重新定义了message和borth属性,get执行了一次是因为访问了borth,set没有执行时因为我们没有定义age的setter。没有设置会采用对象的默认行为。
2、当对象值发生改变触发操作,如:
function observe(val){
Object.keys(val).forEach(key => {
defineReactive(val,key)
})
}
function defineReactive(obj,key){
let val = obj[key]
if(Object.prototype.toString.call(val) === '[object Object]'){
observe(val)
return
}
Object.defineProperty(obj,key,{
get: function(){
return val
},
set: function(newval){
if(val == newval)
return
console.log(`oldval:${val} newval:${newval}`)
val = newval
}
})
}
function MyVue(options){
let data = this._data = options.data || {};
//把data代理到vm实例上
Object.keys(data).forEach(key => {
Object.defineProperty(this,key,{
enumerable: true,
configurable: true,
get: function(){
console.log('vm get exec...')
return this._data[key]
},
set: function(newval){
console.log('vm set exec...')
this._data[key] = newval
}
})
})
//监听data的值发生改变
observe(data)
}
let v1 = new MyVue({
data: {
message: '你好',
borth:{
age: 18
}
}
})
当我们修改data里的值时会触发set