【问题标题】:JS: is it possible to define getter functions on array members?JS:是否可以在数组成员上定义 getter 函数?
【发布时间】:2017-02-23 02:07:13
【问题描述】:

我没有找到关于这个主题的任何信息,如果这是一个非常奇怪的问题,请原谅我。

我知道 JS 允许将属性定义为访问器,这意味着它们在使用时会触发 getter 或 setter 函数。

我的问题是是否可以对数组成员做同样的事情。

例如,我希望在这样分配时触发一个 setter 函数:

myObj[2] = 2 /* set function(value, index) { console.log(value + index) } */

如果这不可能,还有其他方法可以扩展 [] 操作吗?

【问题讨论】:

  • 您的意思是仅针对属性“2”还是针对所有索引?
  • @Bergi 所有索引
  • 那么你想要一个代理,是的。另见here

标签: javascript arrays oop getter


【解决方案1】:

基本上,除非您对数组进行子类化,否则您不能。

即使是子类,数组也比对象动态得多。与对象不同,它们一直在添加和删除项目,您必须能够动态地将 getter 和 setter 与添加的项目一起附加到每个属性(键)。不实用。如果一个数组是子类的,您可以使用 getter 和 setter 修改它的长度属性,并尝试观察数组对其长度属性的更改,但这需要大量的数组差异。

我猜想数组最好使用 ES6 代理对象。你可以这样做;

function ProxyMaker(a){
    return new Proxy(a, {
        get: function(target, property, receiver) {
            console.log(target+"'s "+property+" property has been accessed");
            return target[property];
        },
        set: function(target, property, value, receiver) {
            console.log(target+"'s "+property+" property has been modified");
            return target[property] = value;
        },
        has: function(target, property) {
            console.log(target+"'s "+property+" property has been checked");
            return Reflect.has(target, property); // 10x to @Bergi
        }
    });
}

var p = ProxyMaker([]);
p[0] = 1;
p[0];
p.push(10);
p.unshift(7);
console.log(JSON.stringify(p))

【讨论】:

  • @Bergi yes :) Reflect 的正确方法是什么?
  • 可能是Reflect.has(target, property),afaik 它们被设计成与陷阱具有完全相同的签名,因此即使不记住细节也很容易
【解决方案2】:

如果知道值何时发生变化很重要,您将需要创建显式的 setter 和 getter。

正如建议的那样,如果可用,代理是一个不错的选择。但是,它可能不适用于所有浏览器。在这种情况下,您可以创建一个新对象,在设置时通知您。

这种方法有很多折衷,要实现数组本机提供的所有内容将是相当多的工作。下面的示例非常简单,但它确实提供了类似数组的交互和一个要监听的 setter 事件。

var observableArray = function(eventName) {
    var myArray = [];

    var api = {};

    api.get = function(index) {
        if (myArray.length && typeof index === 'number') {
            return myArray[index];
        }
    };

    api.set = function(item, index) {
        if (typeof index === 'number') {
            myArray[index] = item;
        }
        else {
            index = myArray.push(item) - 1;

        }
        var event = new CustomEvent(eventName, {detail: index});
        document.dispatchEvent(event);
        return index;
     };
    api.getLength = function() {
        return myArray.length;
    };

    api.empty = function() {
        myArray.splice(0, myArray.length);
    };

    return api;
};

var arrayListener = document.addEventListener('foo', function(e)     {console.log('The index modified: ',e.detail);},false);

var test = new observableArray('foo');
test.set('bar');
console.log('The # of items: ', test.getLength());
test.empty();
console.log('The # of items after empty: ', test.getLength());

如果您只需要一种能够在设置数组值时发生某些事情的方法,您可以扩展数组以具有触发回调的 set 方法。不过,这将取决于程序员使用它。

    Array.prototype.set = function(item, index, callback) {
    if (typeof index === 'number') {
        this[index] = item;
    }
    else {
        index = this.push(item) - 1;

    }
    if (callback) {
        callback.apply(this, [index]);
    }
    return index;
};

var test2 = [];
test2.set('foo', 4, function(i) {
    console.log('Callback value: ', i);
    console.log('The array: ', this);
});

【讨论】:

  • 我不知道该给哪一个答案标签...您的答案没有使用 ES6,但同时它没有扩展 [/*Number*/] 运算符,就像访问器扩展了= 操作员。
【解决方案3】:

你有两种访问数组的方式:

  1. 被视为具有命名 setter/getter 的对象 var x = pojoArray['firstName'];

  2. 通过数值索引 var x = pojoArray[10];

所以在我看来,你有两种方法可以解决这个问题:

不好的方式: 如果将数组视为对象,则可以手动循环整个数组,但在使用对象之类的数组时应避免这种情况,因为不能保证顺序。请参阅以下帖子: Loop through an array in JavaScript

其他选项: 根据您的要求,另一种方法是通过数字索引访问它。您可以创建某种辅助函数,这些函数必须将 getter 和 setter 键映射到数组中的正确索引,并确保该键只使用一次。

请注意,我没有说明 “好方法” 来满足您的要求。如果可能的话,我会尽量避免使用这些工作流程中的任何一个,因为它们很容易出错并在未来引起头痛。

【讨论】:

    【解决方案4】:

    您可以将对象包装在 ES6 proxy 中。

    var a = new Proxy({}, {get:function(target, name){return 42;}});
    
    console.log(a[0]); // ==> 42
    

    然后您可以捕获并将处理添加到get/set 操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-15
      • 2013-07-27
      • 2018-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多