likes
comments
collection
share

es6 新特性 之 Proxy, 这次彻底搞懂

作者站长头像
站长
· 阅读数 24

es6提供了一些新特性,其中就有Proxy构造函数,用来生成Proxy实例的,它是一种拦截器。可以理解成,在目标对象之前假设一层”拦截“,外界对该对象饿访问,都必须先通过这层拦截。

因此提供了一种机制,可以对外界的访问进行过滤和改写。

what 是什么

proxy对象 用于 创建一个对象的 代理,从而实现基本操作的 拦截和自定义 (比如:属性查找赋值枚举函数调用等)。

how 怎么样用

const p = new Proxy(target, handler)
  • target: 要使用proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

  • handler: 一个通常以函数作为属性对象,各属性中的函数分别定义了在执行各种操作时代理p行为

其中Proxy的拦截方法共有13种。

es6 新特性 之 Proxy, 这次彻底搞懂

  • apply(target, object, args): 拦截Proxy实例作为函数调用的操作, 比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)

  • get(target, propKey, receiver)拦截对象属性的读取,比如proxy.foo和proxy[‘foo’]。

  • set(target, propKey, value, receiver)拦截对象属性的设置,比如proxy.foo = v或proxy[‘foo’] = v,返回一个布尔值。

  • has(target, propKey)拦截propKey in proxy的操作,返回一个布尔值。

  • deleteProperty(target, propKey)拦截delete proxy[propKey]的操作,返回一个布尔值。

  • ownKeys(target)拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。

  • getOwnPropertyDescriptor(target, propKey)拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。

  • defineProperty(target, propKey, propDesc)拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。

  • preventExtensions(target)拦截Object.preventExtensions(proxy),返回一个布尔值。

  • getPrototypeOf(target)拦截Object.getPrototypeOf(proxy),返回一个对象。

  • isExtensible(target)拦截Object.isExtensible(proxy),返回一个布尔值。

  • setPrototypeOf(target, proto)拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。

  • construct(target, args)拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。

下面着重分析一下apply、get、set这三种


handler.apply()

handler = {
    apply() // 拦截函数的调用
}

语法:

var p = new Proxy(target, {
  apply: function(target, thisArg, argumentsList) {
  }
});

target: 目标对象(函数)。

thisArg:被调用时的上下文对象。

argumentsList: 被调用时的参数数组。

返回值:apply 方法可以返回任何值。

拦截目标对象

  • proxy(...args)
  • Function.prototype.apply() 和 Function.prototype.call()
  • Reflect.apply()

示例1

function sum(a, b) {
    return a + b
}

const handler = {
    apply: function(target, thisArg, argumentsList) {
        console.log('target', target) // sum()这个函数本身
        console.log('thisArg', thisArg) // undefined
        console.log('argumentsList', argumentsList) // [1, 2]

        return target(argumentsList[0], argumentsList[1]) * 10
    }
}

const proxy1 = new Proxy(sum, handler)

console.log(sum(1, 2)) // expected output: 3
console.log(proxy1(1, 2)) // expected output: 30

示例2

var p = new Proxy(function() {}, {
    apply: function(target, thisArg, argumentsList) {
        console.log('called: ' + argumentsList.join(', ')) // called: 1, 2, 3
        
        return argumentsList[0] + argumentsList[1] + argumentsList[2]
    }
})

console.log(p(1, 2, 3)) // 6

handler.get()

handler = {
    get() // 拦截对象的读取属性操作
}

语法:

var p = new Proxy(target, {
  get: function(target, property, receiver) {
  }
});

target: 目标对象(函数)。

property:被获取的属性名。

receiver: Proxy或者继承Proxy的对象。

返回值:get方法可以返回任何值。

拦截目标对象

  • 访问属性:proxy[foo] 和 proxy.bar
  • 访问原型链上的属性:Object.create(proxy)[foo]
  • Reflect.get()

示例1

var p = new Proxy({}, {
    get: function(target, prop, receiver) {
        console.log('target', target) // {}
        console.log('called: ' + prop) // "called: a“
        console.log('receiver', receiver) // Proxy {}
        
        return 10
    }
})

console.log(p.a) // 10

示例2

var obj = {}

Object.defineProperty(obj, 'a', {
    configurable: false,
    enumerable: false,
    value: 10,
    writable: false
})

var p = new Proxy(obj, {
    get: function(target, prop) {
        return 20
    }
})

console.log(p.a) // 会抛出 TypeError 'get'上的代理:属性'a'是一个只读的和不可配置的数据属性

handler.set()

handler = {
    set() // 拦截对象的设置属性操作
}

语法:

var p = new Proxy(target, {
  set: function(target, property, receiver) {
  }
});

target: 目标对象(函数)。

property:被设置的属性名。

receiver: Proxy或者继承Proxy的对象。

返回值:set方法应当返回一个布尔值。

  • 返回 true 代表属性设置成功。
  • 在严格模式下,如果 set() 方法返回 false,那么会抛出一个TypeError异常。

拦截目标对象

  • 访问属性:proxy[foo] = bar 和 proxy.bar = foo
  • 访问原型链上的属性:Object.create(proxy)[foo] = bar
  • Reflect.set()

示例1

const monster1 = { eyeCount: 4 }

const handler1 = {
    set(obj, prop, value) {
        if ((prop === 'eyeCount') && ((value % 2) !== 0)) {
            console.log('Monsters must have an even numbers of eyes')
        } else {
            return Reflect.set(...arguments)
        }
    }
}

const proxy1 = new Proxy(monster1, handler1)

proxy1.eyeCount = 1 // expected Output: "Monsters must have an even numbers of eyes"

console.log(proxy1.eyeCount); // expected Output: 4

proxy1.eyeCount = 2
console.log(proxy1.eyeCount) // expected output: 2