Object.defineProperty()、Proxy
Object.defineProperty()
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
Object.defineProperty(obj, prop, descriptor)有三个参数。
obj 是定义属性的对象
prop 用来定义或修改属性的名称
descriptor 是将被定义或修改的属性描述符
下面通过案例来看一下Object.defineProperty()具体的用法。
基本使用
var obj = {};
Object.defineProperty(obj, 'a', {
value: 1,
});
console.log(obj.a);
// 1
定义一个空对象,使用Object.defineProperty()来定义一个a属性。
属性描述符
Object.defineProperty()方法目前存在的属性描述符主要有两种形式。
数据描述符和存取描述符。
数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。
存取描述符是使用getter-setter函数来实现描述的功能。
属性描述符可以是数据描述符,也可以是存取描述符,不能两种同时使用。
Object.defineProperty()方法定义的属性,默认不可修改、不可删除。
可以通过定义configurable、enumerable、writable来更改Object.defineProperty()定义对象的默认状态。
value:定义属性的值,可以是任何有效的值,默认为undefined。
configurable:当该属性为true时,属性值才能被改变或者删除,默认为false。
enumerable:当该属性为true时,属性值才能够出现在对象的枚举属性中,默认为false。
writable:当该属性为true时,value属性才能被修改,默认为false。
1. writable
属性值默认是不可修改的
var obj = {};
Object.defineProperties(obj, {
a: {
value: 1,
},
b: {
value: 2
}
});
console.log(obj.a);
obj.a = 2;
console.log(obj.a);
// 1
// 1
配置writable属性后,属性值可修改。
var obj = {};
Object.defineProperties(obj, {
a: {
value: 1,
writable: true
},
b: {
value: 2
}
});
console.log(obj.a);
obj.a = 2;
console.log(obj.a);
// 1
// 2
2. enumerable
默认是不可枚举的。
var obj = {};
Object.defineProperties(obj, {
a: {
value: 1
},
b: {
value: 2
}
});
for (const key in obj) {
console.log(key + ':' + obj[key]);
}
配置enumerable属性后,属性值可枚举。
var obj = {};
Object.defineProperties(obj, {
a: {
value: 1,
enumerable: true
},
b: {
value: 2
}
});
for (const key in obj) {
console.log(key + ':' + obj[key]);
}
// a:1
3. configurable
默认属性是不可删除的。
var obj = {};
Object.defineProperties(obj, {
a: {
value: 1,
enumerable: true
},
b: {
value: 2,
enumerable: true
}
});
delete obj.a;
for (const key in obj) {
console.log(key + ':' + obj[key]);
}
// a:1
// b:2
配置configurable属性后,属性值可删除。
var obj = {};
Object.defineProperties(obj, {
a: {
value: 1,
enumerable: true,
configurable: true
},
b: {
value: 2,
enumerable: true
}
});
delete obj.a;
for (const key in obj) {
console.log(key + ':' + obj[key]);
}
// b:2
4. get、set
对于一个对象,它的取值和赋值有一系列的配置和阻止的方法,用以阻拦数据的输出和输入。
get、set方法是存取描述符,它和value、writable、enumerable、configurable互斥。
可以使用get和set方法实现数据劫持的效果。
每一个属性被定义时,都存在getter、setter机制。
var obj = {},
a = 1;
Object.defineProperties(obj, {
a: {
get () {
return '"a"\'s value is ' + a + '.';
},
set (newVal) {
console.log('The value "a" has been designed a new value "' + newVal + '".');
}
}
});
console.log(obj.a);
obj.a = 1;
// "a"'s value is 1.
// The value "a" has been designed a new value "1".
Object.defineProperty() 操作数组
function DataArr () {
var _val = null,
_arr = [];
Object.defineProperty(this, 'val', {
get: function () {
return _val;
},
set: function (newVal) {
_val = newVal;
_arr.push({val: _val});
console.log('A new value ' + _val + ' has been pushed to _arr');
}
})
this.getArr = function () {
return _arr;
}
}
var dataArr = new DataArr();
dataArr.val = 123;
dataArr.val = 234;
console.log(dataArr.getArr());
// A new value 123 has been pushed to _arr
// A new value 234 has been pushed to _arr
// [ { val: 123 }, { val: 234 } ]
Proxy
Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。
Object.defineProperty() 劫持数据,给对象进行扩展,对属性进行设置;
Proxy 用来给对象进行扩展;
Proxy对象有两个参数。
target:用Proxy包装的目标对象(可以是任何类型的对象)。
handler:一个对象,属性是当执行一个操作时定义代理的行为的函数。
对象使用proxy
var target = {
a: 1,
b: 2
}
let proxy = new Proxy(target, {
get (target, prop) {
return `This is property value ${target[prop]}`;
},
set (target, prop, value) {
console.log(`The property ${prop} set value ${value}`);
target[prop] = value;
}
});
console.log(proxy.a);
proxy.b = 3;
// This is property value 1
// The property b set value 3
数组使用proxy
let arr = [
{ name: '小明', age: 18 },
{ name: '小宏', age: 23 },
{ name: '小青', age: 34 },
{ name: '小黄', age: 21 },
{ name: '小王', age: 37 },
{ name: '小李', age: 23 }
];
let persons = new Proxy(arr, {
get (arr, prop) {
return arr[prop];
},
set (arr, prop, value) {
arr[prop] = value;
}
})
console.log(persons[0]);
persons[0] = { name: '小杨', age: 21 }
console.log(persons[0]);
// { name: '小明', age: 18 }
// { name: '小杨', age: 21 }
函数使用proxy
const fn = function () {
console.log('i am a function.');
}
fn.a = 123;
let newFn = new Proxy(fn, {
get (fn, prop) {
return fn[prop] + ' This is a Proxy return.';
}
});
console.log(newFn.a);
// 123 This is a Proxy return.
总结
const target = { a: 1, b: 2 };
const proxy = new Proxy(target, {
get (target, prop) {
return 'GET:' + prop + ' = ' + target[prop];
},
set (target, prop, value) {
target[prop] = value;
console.log('SET:' + prop + ' = ' + value);
},
has (target, prop) {
console.log(target[prop]);
return target[prop];
},
deleteProperty (target, prop) {
console.log('DELETE:' + target[prop]);
delete target[prop];
}
});
Proxy中代理的方法有下面描述的14种对象操作的方法。
Object.defineProperty()与Proxy
1. defineProperty和Proxy都可以实现拦截的功能,但是本质是不同的;
2. defineProperty原则上是给对象增加属性使用的,它在操作数组的长度,
操作数组的值等,无法触发defineProperty的setter方法。
vue中使用的数组的操作不是原生的,是自己实现的。
3. Proxy对于操作数组,会触发setter方法,数组元素的添加、移除等。
对象操作的14种方法
ECMAScript 委员会,对于对象操作,定义了14种方法。
const obj = { a: 1, b: 2 };
1. 获取原型 [[GetPrototypeOf]]
var proto = Object.getPrototypeOf(obj);
console.log(proto);
console.log(obj.__proto__);
console.log(Object.prototype);
2. 设置原型 [[SetProtypeOf]]
Object.setPrototypeOf(obj, { c: 3, d : 4 });
Object.prototype.e = 5;
obj.__proto__.f = 6;
console.log(obj);
3. 获取对象的可扩展性 [[Is Extensible]]
var extensible = Object.isExtensible(obj);
console.log(extensible); // true
Object.freeze(obj); // 冻结对象
var extensible2 = Object.isExtensible(obj);
console.log(extensible2); // false
seal 封闭对象、不可修改、不可删除、可写、可读
Object.seal(obj);
obj.c = 3;
console.log(obj);
delete obj.a;
console.log(obj);
obj.b = 4;
console.log(obj);
for (var key in obj) {
console.log(obj[key]);
}
// { a: 1, b: 2 }
// { a: 1, b: 2 }
// { a: 1, b: 2 }
// 1
// 4
freeze 冻结对象、不可修改、不可删除、不可写、可读
Object.freeze(obj);
obj.c = 3;
console.log(obj);
delete obj.a;
console.log(obj);
obj.b = 4;
console.log(obj);
for (var key in obj) {
console.log(obj[key]);
}
// { a: 1, b: 2 }
// { a: 1, b: 2 }
// { a: 1, b: 2 }
// 1
// 2
4. 获取自由属性 [[GetOwnProperty]]
Object.setPrototypeOf(obj, { c: 3, d: 4});
console.log(Object.getOwnPropertyNames(obj));
// [ 'a', 'b' ]
5. 禁止扩展对象 [[PreventExtension]]
Object.preventExtensions(obj);
obj.c = 3; // 禁止增加属性
console.log(obj);
delete obj.a; // 可删除属性
console.log(obj);
// { a: 1, b: 2 }
// { b: 2 }
6. 拦截对象操作 [[DefineOwnProperty]]
Object.defineProperty()
7. 判断是否是自己属性 [[HasProperty]]
console.log(obj.hasOwnProperty('a')); // true
8. [[GET]]
console.log('c' in obj); // false
console.log('a' in obj); // true
console.log(obj.a); // 1
9. [[SET]]
obj.a = 3;
obj['b'] = 4;
console.log(obj); // a: 3, b: 4 }
10. [[Delete]]
console.log(obj); // { a: 1, b: 2 }
delete obj.a;
console.log(obj); // { b: 2 }
11. [[Enumerate]]
for (var k in obj) {
console.log(obj[k]);
}
// 1
// 2
12. 获取键集合 [[OwnPropertyKeys]]
console.log(Object.keys(obj)); // [ 'a', 'b' ]
13.
function test () {}
obj.test = function () {}
obj.test();
14.
function Test () {}
new Test();