ownKeys 陷阱函数

ownKeys 代理陷阱拦截了内部方法 ,并允许你返回一个数组用于重写该行为。返回的这个数组会被用于四个方法:Object.keys() 方法、 Object.getOwnPropertyNames() 方法、Object.getOwnPropertySymbols() 方法与 Object.assign() 方法,其中 Object.assign() 方法会使用该数组来决定哪些属性会被复制。

ownKeys 陷阱函数的默认行为由 Reflect.ownKeys() 方法实现,会返回一个由全部自有属性的键构成的数组,无论键的类型是字符串还是符号。Object.getOwnProperyNames() 方法与 Object.keys() 方法会将符号值从该数组中过滤出去;相反,Object.getOwnPropertySymbols() 会将字符串值过滤掉;而 Object.assign() 方法会使用数组中所有的字符串值与符号值。

ownKeys 陷阱函数接受单个参数,即目标对象,同时必须返回一个数组或者一个类数组对象,不合要求的返回值会导致错误。你可以使用 ownKeys 陷阱函数去过滤特定的属性,以避免这些属性被 Object.keys() 方法、Object.getOwnPropertyNames() 方法、Object.getOwnPropertySymbols() 方法或 Object.assign() 方法使用。假设你不想在结果中包含任何以下划线打头的属性(在 JS 的编码惯例中,这代表该字段是私有的),那么可以使用 ownKeys 陷阱函数来将它们过滤掉,就像下面这样:

let proxy = new Proxy({}, {
    ownKeys(trapTarget) {
        return Reflect.ownKeys(trapTarget).filter(key => {
            return typeof key !== "string" || key[0] !== "_";
        });
    }
});

let nameSymbol = Symbol("name");

proxy.name = "proxy";
proxy._name = "private";
proxy[nameSymbol] = "symbol";

let names = Object.getOwnPropertyNames(proxy),
    keys = Object.keys(proxy);
    symbols = Object.getOwnPropertySymbols(proxy);

console.log(names.length);      // 1
console.log(names[0]);          // "name"

console.log(keys.length);      // 1
console.log(keys[0]);          // "name"

console.log(symbols.length);    // 1
console.log(symbols[0]);        // "Symbol(name)"

这个例子使用了一个 ownKeys 陷阱函数,首先调用了 Reflect.ownKeys() 方法来获取目标对象的键列表;接下来,filter() 方法被用于将所有下划线打头的字符串类型的键过滤出去;这之后向 proxy 对象添加了三个属性:name、_name 与 nameSymbol。当 proxy 对象上的 Object.getOwnPropertyNames() 方法与 Object.keys() 方法被调用时,只获得了 name 属性;类似的,Object.getOwnPropertySymbols() 方法被调用时只获得了 nameSymbol 属性;而 _name 属性则始终没有出现在结果里,因为它被过滤了。

ownKeys 陷阱函数也能影响 for-in 循环,因为这种循环调用了陷阱函数来决定哪些值能够被用在循环内。