• ES6 入门教程 15 Proxy 15.2 Proxy 实例的方法 15.2.10 ownKeys() ~ 15.2.12 setPrototypeOf()


    ES6 入门教程

    ECMAScript 6 入门

    作者:阮一峰

    本文仅用于学习记录,不存在任何商业用途,如侵删

    15 Proxy

    15.2 Proxy 实例的方法

    拦截方法的详细介绍。

    15.2.10 ownKeys()

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

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

    下面是拦截Object.keys()的例子。

    let target = {
      a: 1,
      b: 2,
      c: 3
    };
    
    let handler = {
      ownKeys(target) {
        return ['a'];
      }
    };
    
    let proxy = new Proxy(target, handler);
    
    Object.keys(proxy)
    // [ 'a' ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    上面代码拦截了对于target对象的Object.keys()操作,只返回abc三个属性之中的a属性。

    下面的例子是拦截第一个字符为下划线的属性名。

    let target = {
      _bar: 'foo',
      _prop: 'bar',
      prop: 'baz'
    };
    
    let handler = {
      ownKeys (target) {
        return Reflect.ownKeys(target).filter(key => key[0] !== '_');
      }
    };
    
    let proxy = new Proxy(target, handler);
    for (let key of Object.keys(proxy)) {
      console.log(target[key]);
    }
    // "baz"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注意,使用Object.keys()方法时,有三类属性会被ownKeys()方法自动过滤,不会返回。

    • 目标对象上不存在的属性
    • 属性名为 Symbol 值
    • 不可遍历(enumerable)的属性
    let target = {
      a: 1,
      b: 2,
      c: 3,
      [Symbol.for('secret')]: '4',
    };
    
    Object.defineProperty(target, 'key', {
      enumerable: false,
      configurable: true,
      writable: true,
      value: 'static'
    });
    
    let handler = {
      ownKeys(target) {
        return ['a', 'd', Symbol.for('secret'), 'key'];
      }
    };
    
    let proxy = new Proxy(target, handler);
    
    Object.keys(proxy)
    // ['a']
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    上面代码中,ownKeys()方法之中,显式返回不存在的属性(d)、Symbol 值(Symbol.for('secret'))、不可遍历的属性(key),结果都被自动过滤掉。

    ownKeys()方法还可以拦截Object.getOwnPropertyNames()

    var p = new Proxy({}, {
      ownKeys: function(target) {
        return ['a', 'b', 'c'];
      }
    });
    
    Object.getOwnPropertyNames(p)
    // [ 'a', 'b', 'c' ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    for...in循环也受到ownKeys()方法的拦截。

    const obj = { hello: 'world' };
    const proxy = new Proxy(obj, {
      ownKeys: function () {
        return ['a', 'b'];
      }
    });
    
    for (let key in proxy) {
      console.log(key); // 没有任何输出
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    上面代码中,ownkeys()指定只返回ab属性,由于obj没有这两个属性,因此for...in循环不会有任何输出。

    ownKeys()方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错。

    var obj = {};
    
    var p = new Proxy(obj, {
      ownKeys: function(target) {
        return [123, true, undefined, null, {}, []];
      }
    });
    
    Object.getOwnPropertyNames(p)
    // Uncaught TypeError: 123 is not a valid property name
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    上面代码中,ownKeys()方法虽然返回一个数组,但是每一个数组成员都不是字符串或 Symbol 值,因此就报错了。

    如果目标对象自身包含不可配置的属性,则该属性必须被ownKeys()方法返回,否则报错。

    var obj = {};
    Object.defineProperty(obj, 'a', {
      configurable: false,
      enumerable: true,
      value: 10 }
    );
    
    var p = new Proxy(obj, {
      ownKeys: function(target) {
        return ['b'];
      }
    });
    
    Object.getOwnPropertyNames(p)
    // Uncaught TypeError: 'ownKeys' on proxy: trap result did not include 'a'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    上面代码中,obj对象的a属性是不可配置的,这时ownKeys()方法返回的数组之中,必须包含a,否则会报错。

    另外,如果目标对象是不可扩展的(non-extensible),这时ownKeys()方法返回的数组之中,必须包含原对象的所有属性,且不能包含多余的属性,否则报错。

    var obj = {
      a: 1
    };
    
    Object.preventExtensions(obj);
    
    var p = new Proxy(obj, {
      ownKeys: function(target) {
        return ['a', 'b'];
      }
    });
    
    Object.getOwnPropertyNames(p)
    // Uncaught TypeError: 'ownKeys' on proxy: trap returned extra keys but proxy target is non-extensible
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    上面代码中,obj对象是不可扩展的,这时ownKeys()方法返回的数组之中,包含了obj对象的多余属性b,所以导致了报错。

    15.2.11 preventExtensions()

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

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

    var proxy = new Proxy({}, {
      preventExtensions: function(target) {
        return true;
      }
    });
    
    Object.preventExtensions(proxy)
    // Uncaught TypeError: 'preventExtensions' on proxy: trap returned truish but the proxy target is extensible
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    上面代码中,proxy.preventExtensions()方法返回true,但这时Object.isExtensible(proxy)会返回true,因此报错。

    为了防止出现这个问题,通常要在proxy.preventExtensions()方法里面,调用一次Object.preventExtensions()

    var proxy = new Proxy({}, {
      preventExtensions: function(target) {
        console.log('called');
        Object.preventExtensions(target);
        return true;
      }
    });
    
    Object.preventExtensions(proxy)
    // "called"
    // Proxy {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    15.2.12 setPrototypeOf()

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

    下面是一个例子。

    var handler = {
      setPrototypeOf (target, proto) {
        throw new Error('Changing the prototype is forbidden');
      }
    };
    var proto = {};
    var target = function () {};
    var proxy = new Proxy(target, handler);
    Object.setPrototypeOf(proxy, proto);
    // Error: Changing the prototype is forbidden
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    上面代码中,只要修改target原型对象,就会报错。

    注意,该方法只能返回布尔值,否则会被自动转为布尔值。另外,如果目标对象不可扩展(non-extensible),setPrototypeOf()方法不得改变目标对象的原型。

  • 相关阅读:
    链式-父类中返回子类对象
    25.Ubuntu旧硬盘挂载
    Google浏览器插件推荐
    Flask实现注册登录模块
    leetcode 刷题 log day 52(子序列系列
    Java“牵手”lazada商品列表页数据采集+lazada商品价格数据排序,lazadaAPI接口申请指南
    用Nodejs 实现一个简单的 Redis客户端
    七、 循环
    【Python】OpenCV-图像滤波
    【clickhouse笔记】 查询表或列的磁盘占用大小
  • 原文地址:https://blog.csdn.net/weixin_44226181/article/details/127944924