Object.defineProperty
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。IE8不兼容。
Object.defineProperty(obj, prop, descriptor)
Object.defineProperty(obj, 'name', {
// configurable: false,//默认为false // enumerable: false,//默认为false value: 'kongzhi', // writable: false,/默认为false // get(){}, // set(v){} });
定义了 value 或 writable , 一定不能有 get 或 set, 反之亦然, 否则报错
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。
数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。
存取描述符是由 getter 函数和 setter 函数所描述的属性。
一个描述符只能是这两者其中之一;不能同时是两者。
这两种描述符都是对象。它们共享以下可选键值(默认值是指在使用 Object.defineProperty() 定义属性时的默认值):
默认值:
configurable
当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。
默认为 false。
enumerable
当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。
默认为 false。
数据描述符还具有以下可选键值:
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。
默认为 undefined。
writable
当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符改变。
默认为 false。
存取描述符还具有以下可选键值:
get
属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。
默认为 undefined。
set
属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。
默认为 undefined。
描述符默认值汇总
描述符可拥有的键值
| – | configurable | enumerable | value | writable | get | set |
|---|---|---|---|---|---|---|
| 数据描述符 | 可以 | 可以 | 可以 | 可以 | 不可以 | 不可以 |
| 存取描述符 | 可以 | 可以 | 不可以 | 不可以 | 可以 | 可以 |
let obj={
a:2,b:{
c:3}}; Object.defineProperty(obj,'a',{
configurable:true, enumerable:true, get(){
return obj['a'] }, set(value){
return obj['a']=value; } })
let obj={
a:2,b:{
c:3}}; Object.defineProperty(obj,'b',{
writable:false, configurable:false }) obj.b=3; obj.b.c=4; console.log(obj)//{a:2,b:{c:4}}
const obj = {
}; Object.defineProperty(obj, 'name', {
// configurable: false,//默认为false // enumerable: false,//默认为false value: 'kongzhi', // writable: false,/默认为false // get(){}, // set(v){} }); console.log(obj.name); // 输出 kongzhi // 改写obj.name 的值 obj.name = 111; console.log(obj.name); // 还是打印出 kongzhi,因为writable默认为false
var o = {
}; o.a = 1; // 等同于: Object.defineProperty(o, "a", {
value: 1, writable: true, configurable: true, enumerable: true }); // 另一方面, Object.defineProperty(o, "a", {
value : 1 }); // 等同于: Object.defineProperty(o, "a", {
value: 1, writable: false, configurable: false, enumerable: false });
全局变量Date不可修改,为了安全性考虑
Object.defineProperty(window,'Date',{
writable:false, configurable:false })
Object.defineProperties
Object.defineProperties本质上定义了obj 对象上props的可枚举属性相对应的所有属性。
var obj = {
}; Object.defineProperties(obj, {
'property1': {
value: true, writable: true }, 'property2': {
value: 'Hello', writable: false } // etc. etc. });
Proxy
Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。IE不兼容。
const p = new Proxy(target, handler)
handler 对象的方法
handler 对象是一个容纳一批特定属性的占位符对象。它包含有 Proxy 的各个捕获器(trap)。
所有的捕捉器是可选的。如果没有定义某个捕捉器,那么就会保留源对象的默认行为。
handler.getPrototypeOf() handler.setPrototypeOf() handler.isExtensible() handler.preventExtensions() handler.getOwnPropertyDescriptor() handler.defineProperty() handler.has()//in 操作符的捕捉器。 handler.get(target, property) handler.set(target, property, value) handler.deleteProperty()//delete 操作符的捕捉器。 handler.ownKeys() handler.apply() handler.construct()//new 操作符的捕捉器。
基本操作
const handler = {
get: function(obj, prop) {
return prop in obj ? obj[prop] : 37; } }; const p = new Proxy({
}, handler); p.a = 1; p.b = undefined; console.log(p.a, p.b); // 1, undefined console.log('c' in p, p.c); // false, 37
Proxy只代理对象外层属性
let obj={
a:1,b:{
c:2}}; let handler={
get:function(obj,prop){
const v = Reflect.get(obj,prop); return v; // 返回obj[prop] }, set(obj,prop,value){
return Reflect.set(obj,prop,value);//设置成功返回true } }; let p=new Proxy(obj,handler); p.a//会触发get方法 p.b.c//会触发get方法获取p.b,不会触发.c的set,因为c没被代理。
递归代理对象内部对象
let obj={
a:1,b:{
c:2}}; let handler={
get:function(obj,prop){
const v = Reflect.get(obj,prop); if(v !== null && typeof v === 'object'){
return new Proxy(v,handler);//代理内层 }else{
return v; // 返回obj[prop] } }, set(obj,prop,value){
return Reflect.set(obj,prop,value);//设置成功返回true } }; let p=new Proxy(obj,handler); p.a//会触发get方法 p.b.c//会先触发get方法获取p.b,然后触发返回的新代理对象的.c的set。
可以看出Proxy代理对象时只会在调用时递归,不会一开始就全部递归,优化了性能。
Proxy对象和原始对象
let target = {
}; let p = new Proxy(target, {
}); p.a = 37; // 操作转发到目标 console.log(target.a); // 37. 操作已经被正确地转发 target.a=4; console.log(p.a)//4 console.log(target==p)//false
总结
- Proxy使用上比Object.defineProperty方便的多。
- Proxy代理整个对象,Object.defineProperty只代理对象上的某个属性。
3. 如果对象内部要全部递归代理,则Proxy可以只在调用时递归,而Object.defineProperty需要在一开始就全部递归,Proxy性能优于Object.defineProperty。 - vue中,Proxy在调用时递归,Object.defineProperty在一开始就全部递归,Proxy性能优于Object.defineProperty。
- 对象上定义新属性时,Proxy可以监听到,Object.defineProperty监听不到。
- 数组新增删除修改时,Proxy可以监听到,Object.defineProperty监听不到。
- Proxy不兼容IE,Object.defineProperty不兼容IE8及以下。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/208794.html原文链接:https://javaforall.net
