JavaScript中的代理和反射

JavaScript中Reflect

JavaScript在ES6提供了反射内置对象Reflect,但JavaScript里面的反射和Java反射有所不同。先看下Reflect提供的13个静态方法。

       Reflect.apply(target, thisArg, args)

       Reflect.construct(target, args)

       Reflect.get(target, name, receiver)

       Reflect.set(target, name, value, receiver)

       Reflect.defineProperty(target, name, desc)

       Reflect.deleteProperty(target, name)

       Reflect.has(target, name)

       Reflect.ownKeys(target)

       Reflect.isExtensible(target)

       Reflect.preventExtensions(target)

       Reflect.getOwnPropertyDescriptor(target, name)

       Reflect.getPrototypeOf(target)

       Reflect.setPrototypeOf(target, prototype)

Reflect.get(target, name, receiver)

Reflect.get方法查找并返回target对象的name属性,如果没有该属性,则返回undefined。

const obj = {
  name: 'jack',
  age: 12,
  get userInfo() {
    return this.name + ' age is ' + this.age;
  }
}

Reflect.get(obj, 'name') // jack
Reflect.get(obj, 'age') // 12
Reflect.get(obj, 'userInfo') // jack age is 12

// 如果传递了receiver参数,在调用userInfo()函数时,this是指向receiver对象。
const receiverObj = {
  name: '小明',
  age: 22
};

Reflect.get(obj, 'userInfo', receiverObj) // 小明 age is 22

Reflect.set(target, name, value, receiver)

const obj = {
  name: 'jack',
  age: 12,
  set updateAge(value) {
    return this.age = value;
  },
}
Reflect.set(obj, 'age', 22);
obj.age // 22

// 如果传递了receiver参数,在调用updateAge()函数时,this是指向receiver对象。
const receiverObj = {
  age: 0
};

Reflect.set(obj, 'updateAge', 10, receiverObj) // 
obj.age         // 22
receiverObj.age // 10

Reflect.has(obj, name)

Reflect.has方法相当于name in obj里面的in运算符。


const obj = {
  name: 'jack',
}
obj in name // true
Reflect.has(obj, 'name') // true

Reflect.deleteProperty(obj, name)

Reflect.deleteProperty方法相当于delete obj[name],用于删除对象的属性。如果删除成功,或者被删除的属性不存在,返回true;删除失败,被删除的属性依然存在,返回false。


const obj = {
  name: 'jack',
}
delete obj.name 
Reflect.deleteProperty(obj, 'name')

Reflect.construct(target, args)

Reflect.construct方法等同于new target(...args)。


function User(name){
  this.name = name;
}
const user = new User('jack');
Reflect.construct(User, ['jack']);

Reflect.getPrototypeOf(obj)

Reflect.getPrototypeOf方法用于读取对象的__proto__属性。

Reflect.setPrototypeOf(obj, newProto)

Reflect.setPrototypeOf方法用于设置目标对象的原型(prototype)。返回一个布尔值,表示是否设置成功。

const obj = {
  name: 'jack',
}
Reflect.setPrototypeOf(obj, Array.prototype);
obj.length // 0

Reflect.apply(func, thisArg, args)

Reflect.apply方法相当于Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数。


const nums = [1,2,3,4,5];
const min = Math.max.apply(Math, nums);
// 通过 Reflect.apply 调用
const min = Reflect.apply(Math.min, Math, nums);

Reflect.defineProperty(target, propertyKey, attributes)

Reflect.defineProperty方法相当于Object.defineProperty,用来为对象定义属性。


const obj = {};
Object.defineProperty(obj, 'property', {
  value: 0,
  writable: false
});

Reflect.defineProperty(obj, 'property', {
  value: 0,
  writable: false
});

Proxy中支持的拦截操作

handler.get(target, property, receiver)

handler.set(target, property, value, receiver)

handler.has(target, property)

handler.defineProperty(target, property, descriptor)

handler.deleteProperty(target, property)

handler.getOwnPropertyDescriptor(target, prop)

handler.getPrototypeOf(target)

handler.setPrototypeOf(target, prototype)

handler.isExtensible(target)

handler.ownKeys(target)

handler.preventExtensions(target)

handler.apply(target, thisArg, argumentsList)

handler.construct(target, argumentsList, newTarget)

get()

用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身,其中最后一个参数可选。


const user = {
  name: 'jack'
}
// 只有属性存在才返回值,否则抛出异常。
const proxy = new Proxy(user, {
  get: function(target, property) {
    if (!(property in target)) {
       throw new ReferenceError(`${property} does not exist.`);
    }
    return target[property];
  }
});
proxy.name // jack
proxy.age // ReferenceError: age does not exist.

set()

用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。

// 字符类型的属性长度校验
let sizeValidator = {
  set: function(target, property, value, receiver) {
    if (typeof value == 'string' && value.length > 5) {
       throw new RangeError('Cannot exceed 5 character.');
    }
    target[property] = value;
    return true;
  }
};

const validator = new Proxy({}, sizeValidator);
let obj = Object.create(validator);
obj.name = '123456' // RangeError: Cannot exceed 5 character.
obj.age = 12                 // 12

has()

用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。如in运算符。

它接受两个参数,分别是目标对象、需查询的属性名。


const handler = {
  has (target, key) {
    if (key[0] === '_') {
      return false;
    }
    return key in target;
  }
};
var target = { _prop: 'foo', prop: 'foo' };
var proxy = new Proxy(target, handler);
'_prop' in proxy // false

defineProperty()

defineProperty()方法拦截了Object.defineProperty()操作。

deleteProperty()

用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。

getOwnPropertyDescriptor()

getOwnPropertyDescriptor()方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined。

getPrototypeOf()

主要用来拦截获取对象原型,拦截的操作如下:

  Object.getPrototypeOf()

  Reflect.getPrototypeOf()

  __proto__

  Object.prototype.isPrototypeOf()

  instanceof


const obj = {};
const proto = {};
const handler = {
    getPrototypeOf(target) {
        console.log(target === obj);   // true
        console.log(this === handler); // true
        return proto;
    }
};

const p = new Proxy(obj, handler);
console.log(Object.getPrototypeOf(p) === proto);    // true

setPrototypeOf()

主要用来拦截Object.setPrototypeOf()方法。

const handlerReturnsFalse = {
    setPrototypeOf(target, newProto) {
        return false;
    }
};

const newProto = {}, target = {};

const p1 = new Proxy(target, handlerReturnsFalse);
Object.setPrototypeOf(p1, newProto); // throws a TypeError
Reflect.setPrototypeOf(p1, newProto); // returns false

isExtensible()

方法拦截Object.isExtensible()操作。


const p = new Proxy({}, {
  isExtensible: function(target) {
    console.log('called');
    return true;//也可以return 1;等表示为true的值
  }
});

console.log(Object.isExtensible(p)); // "called"

ownKeys()

用来拦截对象自身属性的读取操作。具体来说,拦截以下操作。

        Object.getOwnPropertyNames()

        Object.getOwnPropertySymbols()

        Object.keys()

        for...in循环。

const p = new Proxy({}, {
  ownKeys: function(target) {
    console.log('called');
    return ['a', 'b', 'c'];
  }
});

console.log(Object.getOwnPropertyNames(p)); // "called"

preventExtensions()

用来拦截Object.preventExtensions()。该方法必须返回一个布尔值,否则会被自动转为布尔值。

这个方法有一个限制,只有目标对象不可扩展时(即Object.isExtensible(proxy)为false),proxy.preventExtensions才能返回true,否则会报错。


const p = new Proxy({}, {
  preventExtensions: function(target) {
    console.log('called');
    Object.preventExtensions(target);
    return true;
  }
});

console.log(Object.preventExtensions(p)); // "called"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值