【问题标题】:Nodejs methods with 'thisArg' and 'use strict'; issue带有 'thisArg' 和 'use strict' 的 Nodejs 方法;问题
【发布时间】:2014-06-22 11:29:01
【问题描述】:

我在 Fedora 19 上安装了节点 v0.10.28 和 V8 v3.14.5.9。我遇到的问题是具有thisArg 可选参数的方法,例如Array.prototype.forEach。

如果我在 Chromium v​​33 或 Firefox v28 上执行以下代码 - jsFiddle

var y = [1, 2, 3];

y.forEach(function (element) {
    console.log(this);
}, 'hej');

我得到一个输出

String {0: "h", 1: "e", 2: "j", length: 3}
String {0: "h", 1: "e", 2: "j", length: 3}
String {0: "h", 1: "e", 2: "j", length: 3}

然后是相同的代码,但处于严格模式 - jsFiddle

var y = [1, 2, 3];

y.forEach(function (element) {
    'use strict';
    console.log(this);
}, 'hej');

我得到一个输出

hej
hej
hej

这些是我根据 ECMA5 规范 sec-function.prototype.call 所期望的结果。

thisArg 值作为 this 值不加修改地传递。这是对第 3 版的更改,其中 undefined 或 null thisArg 被替换为全局对象,而 ToObject 应用于所有其他值,并且该结果作为 this 值传递。即使 thisArg 未经修改地传递,非严格模式函数在进入函数时仍会执行这些转换。

例如sec-array.prototype.foreach

如果提供了 thisArg 参数,它将被用作每次调用 callbackfn 的 this 值。如果未提供,则使用 undefined 代替。

及相关伪代码

Let funcResult be the result of calling the [[Call]] internal method of callbackfn with T as thisArgument and a List containing kValue, k, and O as argumentsList.

然而,在节点上,上面的 sn-ps 都返回

{ '0': 'h', '1': 'e', '2': 'j' }
{ '0': 'h', '1': 'e', '2': 'j' }
{ '0': 'h', '1': 'e', '2': 'j' }

谁能确认这是我的节点环境的问题,还是节点的问题?

更新:只是为了确认,在节点typeof this 上的两种情况下都返回object。

【问题讨论】:

  • console.log(typeof this);
  • @cookiemonster 在这两种情况下,它都显示为object,而不是(严格)string 和(非严格)object,这不是我所期望的。
  • 是的,刚刚做了同样的测试。 this instanceof String 是 true。奇怪的。我无法想象 Node 本身与它有任何关系,除非 V8 中有某种方法可以完全禁用某些严格模式功能,即使在嵌套函数中也是如此。
  • 其他严格模式功能似乎工作正常。
  • 查看这个 V8 错误报告:code.google.com/p/google-caja/issues/detail?id=1501 似乎这是去年修复的。我的 Node 版本必须有旧的 V8。我正在运行 Node 0.10.24,因此它与您的版本非常接近,这似乎是最新的。

标签: javascript node.js strict


【解决方案1】:

与 V8 v3.14.5.9(及更早版本)一起安装的 node v0.10.28(最新稳定版)确实存在问题,但问题不在于 node 本身,而是 V8 存在错误。

可以在 2012 年 8 月 5 日发布的 issue 2273 中找到错误报告。

严格模式函数应该接收非强制的“this”值。也就是说,“this”可以是 undefined/null 而不是全局对象,也可以是原始值而不是装箱值。

调用函数是否处于严格模式无关紧要。但是,即使要调用的函数处于严格模式,诸如 'Array.prototype.forEach' 之类的内置函数也会错误地执行强制转换。

测试用例:

(function() {
  var logger = function() {
    "use strict";
    console.log(this);
  };

  var strictCaller = function() {
    "use strict";
    logger.call("foo");
  };

  var nonStrictCaller = function() {
    logger.call("foo");
  };

  var forEachCaller = function() {
    [123].forEach(logger, "foo");
  };


  // call from strict function: logs primitive value
  strictCaller();

  // call from non-strict function: logs primitive value
  nonStrictCaller();

  // call through forEach: logs *boxed* value (WRONG)
  forEachCaller();
})();

错误修复已于 2013 年 4 月 5 日提交给 revision r14149 中的 V8 源代码

所以这个问题长期存在并影响了所有基于 V8 引擎的环境。

我能够确认 Chrome v27 仍然受到此问题的影响并且它正在运行 V8 v 3.16,并且可以确认带有 V8 v3.24.35.33 的 Chrome v34 不再受到影响。所以在这两者之间的某个地方,V8 的修复成为了主流。

@cookiemonster 的建议一个解决方案可能是使用更高版本的节点(来自他们的不稳定回购),但我无法确认这一点。

我无法在node issues list 中找到有关此问题的任何报告。

唯一的其他解决方案是测试这个错误(上面给出的代码)并自己填充受影响的方法。我已经测试了这个解决方案并且它有效,这是我测试过的垫片。 (取自es5-shim project)

Array.prototype.forEach = function forEach(fun /*, thisp*/ ) {
    'use strict';
    var object = Object(this),
        thisp = arguments[1],
        i = -1,
        length = object.length >>> 0;

    // If no callback function or if callback is not a callable function
    if (Object.prototype.toString.call(fun) !== '[object Function]') {
        throw new TypeError(); // TODO message
    }

    while (++i < length) {
        if (i in object) {
            // Invoke the callback function with call, passing arguments:
            // context, property value, property key, thisArg object
            // context
            fun.call(thisp, object[i], i, object);
        }
    }
};

该问题已被处理:

  1. idiomatic.js
  2. es5-shim
  3. nodejs

【讨论】:

  • +1 我想还需要修补 .map() 和其他 Array.prototype 方法。
  • 我相信,所有具有thisArg 可选参数的Array.prototype.* 方法。我测试了some,这也受到了影响。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-22
  • 1970-01-01
  • 2011-10-23
  • 1970-01-01
  • 2012-09-18
  • 2020-12-07
  • 2012-02-15
相关资源
最近更新 更多