ES6学习笔记十(Proxy)

Posted усил

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6学习笔记十(Proxy)相关的知识,希望对你有一定的参考价值。

get(target, key, receiver)

拦截对象属性的读取

  • target:目标对象

  • key:属性

  • receiver:proxy 实例本身

例子1:实现 get拦截器,对不属于该对象属性的抛出错误

var preson =  name: "haohao" 
var proxy = new Proxy(preson, 
    get: function(target, key, receiver) 
        if (key in target) 
            return target[key]
         else 
            throw new ReferenceError(`Prop name $key does not exist`)
        
    
);
console.log(proxy.name) // haohao
console.log(proxy.age) // 抛出一个错误

例子2:实现一个公共的拦截器(继承)

var proxy = new Proxy(, 
    get: function(target, key, receiver) 
        if (key in target) 
            return target[key]
         else 
            throw new ReferenceError(`Prop name $key does not exist`)
        
    
)
var preson =  name: "haohao" 
// 将 preson 的原型对象指向 proxy 
preson.__proto__ = proxy;

console.log(preson.name)// haohao
console.log(preson.age) // 抛出一个错误

例子3:实现数组读取负数的索引

function createArray(arr) 
    var target = [...arr];
    return new Proxy(target, 
        get: function(target, key, receiver) 
            var index = Number(key)
            if (index < 0) 
                key = String(target.length + index)
            
            return Reflect.get(target, key, receiver)
        
    )

var arr = createArray([1, 2, 3, 4]);
console.log(arr[1]); // 2
console.log(arr[-1]); // 4

例子4:实现链式调用

var pipe = function (value) 
    var funcStack = [];
    var proxy = new Proxy(, 
        get: function (target, key, receiver) 
            if (key === "get") 
                return funcStack.reduce(function (val, fn) 
                    return fn(val)
                , value)
            

            let func = n => n;
            switch (key) 
                case "double":
                    func = n => n * 2
                    break;
                case "pow":
                    func = n => n * n
                    break;
                case "reverseInt":
                    func = n => n.toString().split("").reverse().join("") | 0
            
            // 往方法栈中添加方法
            funcStack.push(func)
            // 返回代理对象
            return receiver
        
    )
    return proxy

console.log(pipe(3).double.pow.reverseInt.get) // 63

注意:当属性被配置为不可写,不可配置时,则Proxy不能修改该属性

var target = Object.defineProperties(, 
    foo: 
        value: 123,
        configurable: false, // 不可配置
        writable: false // 不可写
    
);

var proxy = new Proxy(target, 
    get: function(target, key) 
        return 'abc'
    
)
console.log(proxy.foo)

set(target, key, val, receiver)

拦截对象属性的设置

  • target:目标对象

  • key:属性

  • val:值

  • receiver:proxy 实例本身

例子1:实现范围及类型控制

var person = new Proxy(, 
    set: function(target, key, val, receiver) 
        if (key == "age") 
            if (!Number.isInteger(val)) 
                throw new TypeError('The age is not an integer')
            
            if (val > 200) 
                throw new RangeError('The age seems invalid')
            
        
        target[key] = val
        return true
    
)
person.age = 100
console.log(person.age)

person.age = 201 // 报错
person.age = "haohao" // 报错

注意:

  • 如果目标对象自身的某个属性不可写,那么set()将不起作用
  • set代理应当返回一个布尔值。严格模式下,set()代理如果没有返回true,就会报错。

apply(target, ctx, args)

拦截函数的调用

  • target:目标对象(函数)

  • ctx:目标对象的上下文(this)

  • args:参数(数组)

例子1:拦截返回指定字符串

var getStr = function()  return 'I am the target' 
var getStrProxy = new Proxy(getStr, 
    apply: function() 
        return 'I am the proxy'
    
)
console.log(getStrProxy()); // I am the proxy

例子2:将返回结果 * 2

var sum = function(left, right)  return left + right 
var sumProxy = new Proxy(sum, 
    apply: function(target, ctx, args) 
        // 将结果 * 2
        return Reflect.apply(...arguments) * 2;
    
)
console.log(sumProxy(1, 2)) // 6
console.log(sumProxy.call(null, 1, 2)) // 6

has(target, key)

拦截 HasProperty 操作(即判断对象是否具有每个属性时,这个方法会生效)

target:目标对象

key:需查询的属性名

例子1:拦截 in 运算符

var obj = 
    _prop: '不可读取属性',
    prop: '可以读取属性'

var objProxy = new Proxy(obj, 
    has(target, key) 
        if (key.charAt(0) == '_') 
            return false;
        
        return key in target
        
)
console.log('_prop' in objProxy) // false
console.log('prop' in objProxy)  // true

注意:

  • has() 方法拦截的是 HasProperty 操作,而不是HasOwnProperty操作,即 has() 不判断属性是对象自身的属性,还是继承的属性

  • has() 可以拦截 in 运算符,但不能拦截 for … in 循环

    let student =  name: '张三', score: 59 
    
    let studentProxy = new Proxy(student, 
        has: function(target, key) 
            if (key == 'score' && target[key] < 60) return false
            return target[key]
        
    )
    // in 运算符
    console.log('score' in studentProxy) // false
    // for .. in
    for(key in studentProxy) 
        console.log(studentProxy[key]) // 张三 59
    
    
  • 当源对象禁止扩展或者不可配置时,has() 拦截会报错

    var obj =  a: 10 
    // 禁止扩展
    Object.preventExtensions(obj)
    var objProxy = new Proxy(obj, 
        has: function(target, key) 
            return false
        
    )
    // TypeError: 'has' on proxy: trap returned falsish for property 'a' but the proxy target is not extensible
    console.log('a' in objProxy)
    

construct(target, args, newTarget)

拦截 new 命令

  • target:目标对象

  • args: 参数(数组)

  • newTarget 创建实例对象时, new 命令作用的构造函数

例子1:拦截 new 操作符

var p = new Proxy(function() , 
    construct: function(target, args, newTarget) 
        console.log('拦截到')
        return  value: args[0] * 10 
    
)
console.log(new p(1).value)

注意:

  • construct() 返回必须是一个对象

    var p = new Proxy(function () , 
        construct(target, args) 
            return 2
        
    )
    new p()
    // TypeError: 'construct' on proxy: trap returned non-object ('2')
    
  • construct() 拦截的对象必须是函数

    var p = new Proxy(, 
        construct(target, args) 
            return 2
        
    )
    new p() 
    //  Uncaught TypeError: p is not a constructor
    
  • construct() 中 this 的指向不是实例对象

    var p = new Proxy(function() , 
        construct: function(target, args) 
            //  construct: [Function: construct] 
            console.log(this)
            return new target(...args)
        
    )
    console.log(new p())
    
    var handler = 
      construct: function(target, args) 
        console.log(this === handler);
        return new target(...args);
      
    
    
    let p = new Proxy(function () , handler);
    new p() // true
    

deleteProerty(target, key)

拦截 delete 操作

  • target:目标对象
  • key:属性

例子1:拦截删除属性

var p = 
    name: 'haohao',
    _age: 20

var pProxy = new Proxy(p, 
    deleteProperty(target, key) 
        if (key.charAt(0) == '_') 
            throw new Error(`Invalid attempt to delete private "$key" property`)
        
        delete target[key]
        return true
    
)
delete pProxy.name
console.log(pProxy) //  _age: 20 
delete pProxy._age // Invalid attempt to delete private _age property

注:

  • 如果抛出错误或者返回false,当前属性就无法被 delete命令 删除
  • 当源对象不可配置时, 不能被deleteProperty()删除,否则报错。

### defineProperty(target, key, descriptor)

拦截 Object.Object.defineProperty()操作

  • target:目标对象
  • key:属性
  • descriptor:配置项

例子1:拦截 defineProperty

var proxy = new Proxy(, 
    defineProperty (target, key, descriptor) 
        console.log(descriptor)
        //  value: 'bar', writable: true, enumerable: true, configurable: true 
        return false
    
);
proxy.foo = 'bar' // 不会生效
console.log(proxy) // 

上面代码中,defineProperty()方法内部没有任何操作,只返回false,导致添加新属性总是无效。 跟返回 false 无关系

注意:

  • 当目标对象不可扩展时,defineProperty 不能添加目标对象上不存在的属性,否则会报错
  • 如果目标对象的某个属性不可写或不可配置,则defineProperty不得改变这两个设置

getOwnPropertyDesciptor(target, key)

拦截 Object.getOwnPropertyDescriptor()

  • target:目标对象
  • key:属性

返回 属性描述对象undefined

例子1:拦截 getOwnPropertyDescriptor

var proxy = new Proxy( _foo: 'bar', baz: 'tar' , 
    getOwnPropertyDescriptor (target, key) 
        if (key[0] === '_') return;
        return Object.getOwnPropertyDescriptor(target, key);
      
);
console.log(Object.getOwnPropertyDescriptor(proxy, 'wat')) // undefined
console.log(Object.getOwnPropertyDescriptor(proxy, '_foo'))  // undefined
console.log(Object.getOwnPropertyDescriptor(proxy, 'baz'))
//  value: 'tar', writable: true, enumerable: true, configurable: true 

getPrototypeOf(target)

拦截获取对象原型

  • Object.prototype.__proto__
  • Object.prototype.isPrototypeOf()
  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • instanceof

注意:

  • getPrototypeOf()返回值必须是对象或者null,否则报错。
  • 目标对象不可扩展, getPrototypeOf()必须返回目标对象的原型对象

isExtensible(target)

拦截Object.isExtensible()操作

  • target:目标对象

注:

  • 该方法只能返回布尔值,否则返回值会被自动转为布尔值

  • 它的返回值必须与目标对象的isExtensible属性保持一致,否则就会抛出错误。


### ownKeys(target)

来拦截对象自身属性的读取操作

  • Object.getOwnPropertyNames()
  • Object.getOwnPropertySymbols()
  • Object.keys()
  • for...in 循环

注: 有三类属性会被ownKeys()方法自动过滤,不会返回。

  • 目标对象上不存在的属性
  • 属性名为 Symbol 值
  • 不可遍历(enumerable)的属性

preventExtensions(target)

拦截Object.preventExtensions()

注:该方法必须返回一个布尔值,否则会被自动转为布尔值。


setPrototypeOf(target)

来拦截Object.setPrototypeOf()

注:该方法必须返回一个布尔值,否则会被自动转为布尔值。


Proxy.revocable()

返回一个可取消的 Proxy 实例。

let target = ;
let handler = ;

let proxy, revoke = Proxy.revocable(target, handler);

proxy.foo = 123;
proxy.foo // 123

revoke(); // 收回代理
proxy.foo; // 报错

以上是关于ES6学习笔记十(Proxy)的主要内容,如果未能解决你的问题,请参考以下文章

es6 Proxy简单使用

ES6-11学习笔记--Iterator

ES6知识点整理之----Proxy----API

js-ES6学习笔记-Proxy

es6代理 proxy 学习

es6之proxy学习