【问题标题】:Modifying Array.forEach to Automatically Set the Context to be the Caller修改 Array.forEach 以自动将 Context 设置为调用者
【发布时间】:2014-07-25 22:47:45
【问题描述】:

是否可以创建一个替代 Array.forEach 自动将上下文“this”设置为与调用方法时相同的上下文?

例如(不工作,不知道为什么):

Array.prototype.each = function(fn) {
    return this.forEach(fn, arguments.callee.caller);
}


function myFunction() {
    this.myVar = 'myVar';

    [1,2,3].each(function() {
        console.log(this.myVar); // logs 'myVar'
    });
}

【问题讨论】:

  • 您为什么要这样做?对我来说似乎是一个 XY 问题......
  • 所以我不必记住每次循环遍历数组时都绑定或传递“this”
  • 答案显然是“否”,JavaScript 函数无法确定调用者中this 的值。这就是 Function.bind() 和箭头函数存在的原因。
  • @WladimirPalant 你应该回答我会接受它
  • @Kirk:已经有很多答案了,我不会重复其他人所说的。例如,thefourtheye 和我说的差不多。

标签: javascript arrays closures iteration


【解决方案1】:

Array.forEach 已经将上下文参数作为可选的最后一个参数,

(function() {
    this.myvar = "myvar";

    [1,2,3,4].forEach(function(v) {
        console.log("v:"+v);
        console.log("myvar="+this.myvar);
    }, this);
})();

MDN forEach

此外,上述示例(如果我们不处理有关this 的实例的方法)在不使用bindforEach 的可选上下文参数的情况下也可以正常工作,以下也可以正常工作:

function myFunction() {
    this.myVar = 'myVar';

    [1,2,3].forEach(function() {
        console.log(this.myVar); // logs 'myVar'
    });
}
myFunction();

因为 javascript 在功能范围内,所以匿名函数可以使用 this 访问父函数的范围,并且可以正确记录。 this 只有在处理实例方法时才会真正成为上下文问题。

【讨论】:

  • 啊,是的,我正在尝试使其自动化,所以我不需要将“this”作为第二个参数传入
  • 然后,根据您之前的示例,如果您在我的示例中取出添加的this,由于您调用它的上下文,它仍然会正确记录myvar。请记住,javascript是函数作用域,因此传递给forEach 的匿名函数已经可以访问它的父作用域,由函数内部的this 定义。 this 变量只有在处理处理实例的方法时才会出现问题;但在上述情况下,它只是函数调用和作用域。
【解决方案2】:

答案是否定的,JavaScript 函数无法确定调用者中this 的值。

你可以绑定当前对象传递的函数,像这样

function myFunction() {
    this.myVar = 'myVar';

    [1,2,3].forEach(function() {
        console.log(this.myVar); // logs 'myVar'
    }.bind(this));
}

在 ECMA 脚本 6 中,您可以使用 Arrow function,像这样

[1,2,3].forEach(() => {
    console.log(this.myVar); // logs 'myVar'
});

【讨论】:

  • 啊,这是目前的方式,我希望让它自动化,所以我不需要调用 .bind(this)
  • @Kirk 我包含了一种使用箭头函数的方法,请检查。但是我们需要 ES 6 的实现。
  • typescript 中编写您的代码并自动获取获取粗箭头功能(包括此绑定)。 typescript.codeplex.com.
【解决方案3】:

在传递回调时与this 变量混淆的替代方法是,您始终可以将this 分配给一个新变量,以便子作用域函数可以访问它:

Array.prototype.each = function(fn) {
    return this.forEach(fn, arguments.callee.caller);
}


function myFunction() {
    var me = this;
    me.myVar = 'myVar';

    [1,2,3].each(function() {
        console.log(me.myVar); // logs 'myVar'
    });
}

现在您不必记住将this 作为第二个参数传递

【讨论】:

    【解决方案4】:

    首先必须指出myFunction是一个构造函数。然而,标识符中的第一个字母没有大写。请叫它MyFunction

    如果在没有 new 运算符的情况下调用构造函数,this 将绑定到全局对象,即浏览器中的 window。这使得大小写约定成为我们发现此类事故的唯一方法。

    以下代码行证明了这一点:

    // After the original code...
    myFunction();
    console.log(window.myVar); // logs "myVar"
    

    其次,为了能够在任何数组上应用函数,而不是更改 Array.prototype,请考虑以下几点:

    var utils = {array: {}};  // utils.array is a container for array utilities.
    utils.array.each = function (array, func) {
        var i;
        for (i = 0; i < array.length; i += 1) { func(array[i]); }
    };
    utils.write = function (s) { 
        console.log(s); // Alternatively, document.write(s);
    };
    utils.array.each([1, 2, 3], utils.write); // prints 1 2 and 3 (on separate lines)
    

    请注意,我们没有使用 thisnew。它们使 JavaScript 看起来像 Java,除此之外,它们很少用于有用的目的。

    虽然库可以修改 Object.prototypeArray.prototype,但最终开发人员不应该。

    此外,我们应该(理想情况下)能够执行以下操作:

    • utils.array.each([1, 2, 3], console.log);
    • utils.array.each([1, 2, 3], document.write);

    但大多数浏览器不允许这样做。

    希望这会有所帮助。

    【讨论】:

    • 没有规定必须将构造函数的名称大写。不是每个人都遵循这个约定,这很好。
    • 很遗憾,您是完全正确的! JS 不强制执行任何命名约定。作为程序员,我们有责任做到这一点。没有它,我们更有可能不小心调用没有 new 关键字的构造函数。这样做是灾难性的。 (我的回答证明了这一点。)
    • 好吧,也许吧。但是有些人和团队根本不使用大写约定。没关系。这是个人选择。您可以编写不大写构造函数名称的好代码 - 最重要的是要保持一致。
    • @sgroves 当然。但是,如果您希望其他人阅读您的代码,那么遵循约定不是可取的。事实上,在 C++(或 Python)中,不遵循大写约定不会是灾难性的,但我们仍然遵循它。 (我们中的许多人都是凭直觉来做的。)我们不应该在 JS 中做吗?不仅如此,建议遵循所有Douglas Crockford's JS Conventions...... 如果你愿意幽默我,我们应该尝试遵循LittleJ 约定。
    • 当然。我只是觉得奇怪的是,当它不相关时,这是你提到的第一件事。据我们所知,这可能是一个爱好项目。这家伙可以随意使用他想要的任何名字,而且没有错。 (也很抱歉,我不是 littlej 的粉丝。我实际上喜欢 javascript,以及它是如何工作的......主要是。)
    【解决方案5】:

    如果我正确理解您的要求,那么您正在尝试覆盖“this”。 我觉得this可以帮到你。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-19
      • 2021-02-19
      • 1970-01-01
      相关资源
      最近更新 更多