
前幾天,我偶然發(fā)現了一些代碼,我需要將一個對象作為常規(guī)數組處理幾次。這當然可以使用Object.keys(),Object.values()或來實現Object.entries(),但它很快就變得冗長了。
所以我想我可以創(chuàng)建某種包裝器來接收一個對象并為它定義一些類似數組的行為。我主要需要Array.prototype.map(),Array.prototype.find(). 使用方法創(chuàng)建所有這些功能都非常簡單 ??梢赃@么說,唯一棘手的部分是讓對象表現得像一個可迭代對象,這需要使用Array.prototype.includes()和Array.prototype.lengthObjectSymbol.iterator生成器函數。
將新功能注入到對象中可以像添加方法一樣簡單。這種方法的缺點是它們將成為實際對象的一部分,這可能是有問題的。如果我們想在少數對象上應用它,這也不是很可重用,這也無濟于事。
輸入Proxy 對象,它是 JavaScript 開發(fā)人員工具帶中鮮為人知的工具之一,但卻是一個非常強大的工具。它用于攔截對象的某些操作,例如屬性查找、賦值等。在這種情況下,它可以巧妙地將所需的功能包裝到一個圍繞對象創(chuàng)建代理的函數中。
最終代碼(可能是)可以在下面的示例中看到。它實現了我需要的功能,以及一些更好的Array測量方法:
const toKeyedArray = obj => {
? const methods = {
? ? map(target) {
? ? ? return callback =>
? ? ? ? Object.keys(target).map(key => callback(target[key], key, target));
? ? },
? ? reduce(target) {
? ? ? return (callback, accumulator) =>
? ? ? ? Object.keys(target).reduce(
? ? ? ? ? (acc, key) => callback(acc, target[key], key, target),
? ? ? ? ? accumulator
? ? ? ? );
? ? },
? ? forEach(target) {
? ? ? return callback =>
? ? ? ? Object.keys(target).forEach(key => callback(target[key], key, target));
? ? },
? ? filter(target) {
? ? ? return callback =>
? ? ? ? Object.keys(target).reduce((acc, key) => {
? ? ? ? ? if (callback(target[key], key, target)) acc[key] = target[key];
? ? ? ? ? return acc;
? ? ? ? }, {});
? ? },
? ? slice(target) {
? ? ? return (start, end) => Object.values(target).slice(start, end);
? ? },
? ? find(target) {
? ? ? return callback => {
? ? ? ? return (Object.entries(target).find(([key, value]) =>
? ? ? ? ? callback(value, key, target)
? ? ? ? ) || [])[0];
? ? ? };
? ? },
? ? findKey(target) {
? ? ? return callback =>
? ? ? ? Object.keys(target).find(key => callback(target[key], key, target));
? ? },
? ? includes(target) {
? ? ? return val => Object.values(target).includes(val);
? ? },
? ? keyOf(target) {
? ? ? return value =>
? ? ? ? Object.keys(target).find(key => target[key] === value) || null;
? ? },
? ? lastKeyOf(target) {
? ? ? return value =>
? ? ? ? Object.keys(target)
? ? ? ? ? .reverse()
? ? ? ? ? .find(key => target[key] === value) || null;
? ? },
? };
? const methodKeys = Object.keys(methods);
? const handler = {
? ? get(target, prop, receiver) {
? ? ? if (methodKeys.includes(prop)) return methods[prop](...arguments);
? ? ? const [keys, values] = [Object.keys(target), Object.values(target)];
? ? ? if (prop === 'length') return keys.length;
? ? ? if (prop === 'keys') return keys;
? ? ? if (prop === 'values') return values;
? ? ? if (prop === Symbol.iterator)
? ? ? ? return function* () {
? ? ? ? ? for (value of values) yield value;
? ? ? ? ? return;
? ? ? ? };
? ? ? else return Reflect.get(...arguments);
? ? },
? };
? return new Proxy(obj, handler);
};
// Object creation
const x = toKeyedArray({ a: 'A', b: 'B' });
// Accessing properties and values
x.a;? ? ? ? ? // 'A'
x.keys;? ? ? // ['a', 'b']
x.values;? ? // ['A', 'B']
[...x];? ? ? // ['A', 'B']
x.length;? ? // 2
// Inserting values
x.c = 'c';? ? // x = { a: 'A', b: 'B', c: 'c' }
x.length;? ? // 3
// Array methods
x.forEach((v, i) => console.log(`${i}: ${v}`)); // LOGS: 'a: A', 'b: B', 'c: c'
x.map((v, i) => i + v);? ? ? ? ? ? ? ? ? ? ? ? // ['aA', 'bB, 'cc]
x.filter((v, i) => v !== 'B');? ? ? ? ? ? ? ? ? // { a: 'A', c: 'c' }
x.reduce((a, v, i) => ({ ...a, [v]: i }), {}); // { A: 'a', B: 'b', c: 'c' }
x.slice(0, 2);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // ['A', 'B']
x.slice(-1);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // ['c']
x.find((v, i) => v === i);? ? ? ? ? ? ? ? ? ? ? // 'c'
x.findKey((v, i) => v === 'B');? ? ? ? ? ? ? ? // 'b'
x.includes('c');? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // true
x.includes('d');? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // false
x.keyOf('B');? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 'b'
x.keyOf('a');? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // null
x.lastKeyOf('c');? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 'c'