拦截JavaScript数组访问器

Ale*_*sov 5 javascript arrays metaprogramming intercept

我想将一些副作用与每个数组访问器相关联a[i].例如,如果副作用是将消息写入控制台,则以下程序:

var array = [1, 2, 3]
var total = 0;
for (var i in array) {
  total += array[i]
}
console.log(total);
Run Code Online (Sandbox Code Playgroud)
应该返回如下输出:

1 // access a[0]
2 // access a[1]
3 // access a[2]
6 // print original total
Run Code Online (Sandbox Code Playgroud)

如果我对拦截数组方法感兴趣push,我会使用此博客文章中的技术并提供拦截器:

var _push = Array.prototype.push;
Array.prototype.push = function( item ) {
    console.log( 'pushing ', item, ' into ', this );
    _push.apply( this, arguments );
}
Run Code Online (Sandbox Code Playgroud)

是否可以将相同的技巧应用于数组访问器?或者对于这个问题更好的解决方案是什么?一个重要的注意事项是我不想修改程序的原始代码.因此,使用JS proxies来拦截getter和setter似乎不是我的问题的有效选项.

我要介绍的一个特殊副作用是在访问值未定义的情况下引发异常(JS数组的索引超出范围异常的一些变化.)我将检查当前访问的值是否等于undefined,并抛出一个异常,在这种情况下,否则只返回原始值.

qui*_*mmo 4

您无法覆盖数组的访问器。这里有一个例子:

Object.defineProperty(Array.prototype, 0, {
  get: function () { return "my get on 0"; }
});
var a = [1,2,3];
console.log(a[0]); // output: 1
Run Code Online (Sandbox Code Playgroud)

但是,如果您尝试对数组中并不真正存在的属性执行相同的操作,您将实现它:

Object.defineProperty(Array.prototype, 5, {
  get: function () { return "my get on 5"; }
});
var a = [1,2,3];
console.log(a[5]); // output: my get on 5
Run Code Online (Sandbox Code Playgroud)

get您可以做的是通过数组方法访问元素的一些解决方法。

Array.prototype.get = function(i) { 
  console.log('my print'); 
  console.log(this[i]); 
  return "this is!"; 
};
var a = [1,2,3];
console.log(a.get(0)); // output: my print 1 this is!
Run Code Online (Sandbox Code Playgroud)

因此,回到你的问题,你可以做一些像你所做的事情,push但使用get,避免代理:

Array.prototype.get = function (i) {
  console.log('Accessing element: ' + this[i]);
  console.log(this);
  return this[i];
};
var array = [1, 2, 3];
var total = 0;
// be careful that now you cannot do anymore 
// for (var i in array), because inside the array there is also the property get defined and it will cycle also on that
// if you want to cycle again in that way, you need the check through hasOwnProperty method
/*
for(var i in array) {
  if (array.hasOwnProperty(i)){
    console.log(i);
    total += array.get(i);
  } 
}
*/
for(var i = 0; i < array.length; i++) {
  total += array.get(i);
}
console.log(total);
Run Code Online (Sandbox Code Playgroud)

只是为了完成答案,您想要做的事情可以使用reduce数组方法在一行中完成:

var array = [1, 2, 3];
var result = array.reduce(function (accumulator, actual) {
  return accumulator + actual;
}, 0);
console.log(result);
Run Code Online (Sandbox Code Playgroud)

我强烈建议您避免覆盖这些访问器。您将更改代码的基础,因此第三方人员在不阅读所有代码的情况下无法了解正在发生的情况。而且你会失去很多内置的有用方法。我希望这有帮助

ps 在您的编辑之后,为了检查未定义的值并引发异常,您可以在 get 方法的重写中添加检查。但我的建议只是过滤数组,检测未定义的值并删除它们。请注意,我使用的是双等号。因为undefined == null但是undefined !== null。通过这种方式,您将删除未定义的值和空值。如果您只想删除未定义的内容,请将其更改为if (typeof element === 'undefined').

像这样,仅使用一个带有数组filter方法的循环:

var data = [1, 2, undefined, 3, 4, undefined, 5];

data = data.filter(function( element, index ) {
   // note that I am using the double equal. because undefined == null but undefined !== null. 
   // in this way you will remove both undefined and null values
   // if you want to remove only undefined, change it to if (typeof element === 'undefined')
   if (element == null) {
     console.log('found and undefined null value at index: ' + index);
   }
   return element != null;
});
console.log(data); // array without undefined and null values
Run Code Online (Sandbox Code Playgroud)