hook array

 

// 保存原始的Array构造函数
const originalArray = Array;

// 创建一个代理Array构造函数
const hookedArray = new Proxy(originalArray, {
  construct: function(target, argumentsList, newTarget) {
    // 创建一个新的数组实例
    const arr = Reflect.construct(...arguments);

    // 返回一个代理数组,拦截下标访问
    return new Proxy(arr, {
      get: function(target, prop, receiver) {
        if (typeof prop === 'number' && !isNaN(prop)) {
          console.log(`正在访问数组下标: ${prop}`);
        }
        return Reflect.get(...arguments);
      }
    });
  }
});

// 替换全局的Array构造函数
globalThis.Array = hookedArray;

// 现在,所有新创建的数组都会被钩入
const newArray = [1, 2, 3];
console.log(newArray[1]); // 正在访问数组下标: 1,输出 2

// 但是,已经存在的数组不会受到影响
const existingArray = [4, 5, 6];
console.log(existingArray[1]); // 直接访问,不会输出任何信息,输出 5

 

 


 

 

const array = [1, 2, 3, 4, 5];

const hookedArray = new Proxy(array, {
get: function(target, property) {
if (isFinite(parseInt(property))) {
console.log(`正在访问下标 ${property}`);
// 在这里可以添加更多的逻辑
}
// 对于非数组下标的属性访问,直接返回属性值
return target[property];
}
});

// 测试访问下标
console.log(hookedArray[2]); // 正在访问下标 2, 输出 3
console.log(hookedArray.length); // 输出数组长度,没有被钩子,输出 5

 

 

 

const arr = [1, 2, 3];

const originalPush = Array.prototype.push;

Array.prototype.push = function(...args) {
  console.log('push 方法被调用了');
  // 执行我们自己的逻辑
  // ...

  // 调用原本的 push 方法
  return originalPush.apply(this, args);
}

arr.push(4); // 控制台输出 "push 方法被调用了"

 

 

 


 

 

如果不使用Proxy对象来钩入数组的下标取值,还有其他几种方法可以实现类似的功能。下面列举了几种不同的方案:

自定义数组类:
你可以创建一个自定义的数组类,并重写索引访问器(indexer)方法,如get和set,来拦截对数组元素的访问。
javascript
class HookedArray extends Array {
get(index) {
console.log(`正在访问下标 ${index}`);
return super.get(index);
}

set(index, value) {
console.log(`正在设置下标 ${index} 的值为 ${value}`);
return super.set(index, value);
}
}

const array = new HookedArray(1, 2, 3, 4, 5);
console.log(array[2]); // 正在访问下标 2,输出 3
数组包装函数:
你可以创建一个函数,该函数接收一个数组作为参数,并返回一个新的对象,该对象具有与原始数组相同的索引属性,并可以添加额外的逻辑。
javascript
function hookedArray(originalArray) {
const wrappedArray = {};
Object.defineProperty(wrappedArray, 'length', {
get: () => originalArray.length
});

for (let i = 0; i < originalArray.length; i++) {
Object.defineProperty(wrappedArray, i, {
get: () => {
console.log(`正在访问下标 ${i}`);
return originalArray[i];
},
set: (value) => {
console.log(`正在设置下标 ${i} 的值为 ${value}`);
originalArray[i] = value;
}
});
}

return wrappedArray;
}

const array = [1, 2, 3, 4, 5];
const hooked = hookedArray(array);
console.log(hooked[2]); // 正在访问下标 2,输出 3
数组原型扩展:
你可以扩展数组的原型,以添加自定义的访问器。但这种方法通常不推荐,因为它会改变所有数组的行为,可能导致与其他代码的冲突。
javascript
Array.prototype.getHooked = function(index) {
console.log(`正在访问下标 ${index}`);
return this[index];
};

const array = [1, 2, 3, 4, 5];
console.log(array.getHooked(2)); // 正在访问下标 2,输出 3

请注意,以上每种方法都有其优缺点。自定义数组类提供了最大的灵活性,但需要创建新的类实例。数组包装函数提供了对原始数组的封装,但创建了一个新对象。扩展数组原型是最简单的方法,但可能影响其他代码并导致不期望的副作用。

在选择方案时,请考虑代码的清晰性、维护性、以及与其他代码的兼容性。通常,Proxy对象是在不修改原始对象的情况下实现hook的最佳选择,因为它提供了强大的拦截功能,同时保持了对原始对象的封装。

posted @ 2023-10-22 17:49  AngDH  阅读(96)  评论(0)    收藏  举报