参考博客
JS中的Object.assign()、Object.create()、Object.defineProperty()
首先看一下函数的定义:
函数参数为一个目标对象(该对象作为最终的返回值),源对象(此处可以为任意多个)。通过调用该函数可以拷贝所有可被枚举的自有属性值到目标对象中。
Object.assign(target, …sources)
这里我们需要强调的三点是:
- 可被枚举的属性:
在JavaScript中,对象的属性分为可枚举和不可枚举之分,它们是由属性的enumerable值决定的。可枚举性决定了这个属性能否被for…in查找遍历到。
js中基本包装类型的原型属性是不可枚举的,如Object, Array, Number等,如果你写出这样的代码使用for...in
遍历其中的属性,将会什么都不会发生,不会报错,能够继续往下执行,看起来就是直接跳过去了。
如果对象不可枚举,则方法for…in
JSON.stringify()
Object.keys()
都会失效。
- 自有属性:
区别于原型属性,不是从原型链继承而来的,例如:
function Obj () {
this.z = 3; //自有属性
}
//对象会继承原型里的属性
Obj.prototype.x = 1;//非自有属性
Obj.prototype.y = 2;//非自有属性
- string或者Symbol类型,可以被直接分配
示例一:
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, target object itself is changed.
此例子表明,Object.assign
不仅返回一个新拼接出来的值,同时也会改变第一个 参数(target)的值。
var obj = Object.create({ foo: 1 }, { // foo is an inherit property.
bar: {
value: 2 // bar is a non-enumerable property.
},
baz: {
value: 3,
enumerable: true // baz is an own enumerable property.
}
});
var copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
这个例子中,因为对象obj是使用Object.create()
构造,所以{foo:1}
是obj继承的原型,obj中的第二个属性显式声明为可枚举,所以最后copy
中拷贝过去的属性只有baz
Object.create(proto [, propertiesObject ])
是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型,如果不是一个子函数,可以传一个null,第二个参数是对象的属性描述符,这个参数是可选的。
function Car (desc) {
this.desc = desc;
this.color = "red";
}
Car.prototype = {
getInfo: function() {
return 'A ' + this.color + ' ' + this.desc + '.';
}
};
//instantiate object using the constructor function
var car = Object.create(Car.prototype);
car.color = "blue";
alert(car.getInfo());
数据属性
- writable:是否可任意写
- configurable:是否能够删除,是否能够被修改
- enumerable:是否能用 for in 枚举
- value:值
访问属性:
- get(): 访问
- set(): 设置
newObj = Object.create(obj,{
t1:{
value:'yupeng',
writable:true
},
bar: {
configurable: false,
get: function() { return bar; },
set: function(value) { bar=value }
}
})
理解Object.defineProperty方法
一、语法
Object.defineProperty(obj, prop, descriptor)
obj:需要定义属性的对象
prop:需要定义的属性
descriptor:属性的描述描述符
返回值:返回此对象
[举个例子]
let obj = Object.create(null);
let descriptor = {
configurable:false,
writable:false,
enumerable:false,
value:'hello world'
};
Object.defineProperty(obj,'hello',descriptor);
console.log(obj.hello);//hello world
二、描述符
在JS中对象具有两种属性,分别是数据属性和访问器属性,所以其描述符也根据属性分类,分为数据描述符和访问器描述符。
在使用描述符时,必须是两种形式之一,且两者不能同时使用。
数据描述符 是一个具有值的属性,该值可能是可写的,也可能是不可写的。
它具有以下可选的键值:
configurable:表示该属性能否通过delete删除,能否修改属性的特性或者能否修改访问器属性,默认为false。当且仅当该属性的configurable为true时,才能实现上述行为。
enumerable:表示该属性是否可以枚举,即可否通过for..in访问属性。默认为false。
value:表示该属性的值。可以是任何有效的JS值。默认为undefined。
writable:表示该属性的值是否可写,默认为false。当且仅当属性的writable为true时,其值才能被赋值运算符改变。
例子
// 在对象中添加一个属性与数据描述符的示例
Object.defineProperty(o, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
// 对象o拥有了属性a,值为37
访问器描述符 是一个有getter-setter函数对描述的属性的读写。
它具有以下可选的键值:
configurable:表示该属性能否通过delete删除,能否修改属性的特性或者能否修改访问器属性,默认为false。当且仅当该属性的configurable为true时,才能实现上述行为。
enumerable:表示该属性是否可以枚举,即可否通过for..in访问属性。默认为false。
get:在读取属性时调用的函数,默认值为undefined。
set:在写入属性时调用的函数,默认值为undefined。
例子
let obj = {
_hello:'hello world' //表示私有变量
};
Object.defineProperty(obj,'hello',{
get() {
console.log('get');
return this._hello;
},
set:function (value) {
console.log('set');
this._hello = value;
}
});
console.log(obj.hello);
obj.hello = 'goodbye';
console.log(obj.hello);
Object.getOwnPropertyDescriptor( obj, prop)
返回一个指定对象上的自有属性对应的属性描述 (自由属性指,直接赋值的属性,不需要从原型上查找的属性)
例子
let o = { get foo() { return 17; } };
let d = Object.getOwnPropertyDescriptor(o, "foo");
let o1 = { bar: 42 };
let d1 = Object.getOwnPropertyDescriptor(o1, "bar");
let o2 = {};
Object.defineProperty(o2, "baz", {
value: 8675309,
writable: false,
enumerable: false
});
let d2 = Object.getOwnPropertyDescriptor(o2, "baz");
console.log(d)// {configurable: true, enumerable: true, get: [Function: get foo],set: undefined}
console.log(d1)//{configurable: true, enumerable: true, value: 42, writable: true}
console.log(d2)// {value: 8675309, writable: false, enumerable: false, configurable: false}
三、应用
Vue.js实现数据双向绑定的原理就是用的Object.defineProperty
例子
const obj = {};
Object.defineProperty(obj, 'text', {
get() {
console.log('get val');
},
set(newVal) {
console.log('set val:' + newVal);
document.getElementById('input').value = newVal;
document.getElementById('span').innerHTML = newVal;
}
});
const input = document.getElementById('input');
input.addEventListener('keyup', function(e){
obj.text = e.target.value;
});
四、扩展
实现双向绑定Proxy比defineproperty优劣如何?
剖析Vue原理&实现双向绑定MVVM