Методы Nodejs с "thisArg" и "use strict"; вопрос

У меня есть node v0.10.28, установленный вместе с V8 v3.14.5.9 на Fedora 19. Проблема, с которой я сталкиваюсь, связана с методами, которые имеют необязательный аргумент thisArg, например Array.prototype.forEach.

Если я выполню следующий код на Chromium v33 или 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 передается без изменений в качестве этого значения. Это изменение из версии 3, где undefined или null thisArg заменяется глобальным объектом, а ToObject применяется ко всем другим значениям и этот результат передается как это значение. Несмотря на то, что thisArg передается без изменений, функции нестрогого режима все еще выполняют эти преобразования при входе в функцию.

и, например, sec-array.prototype.foreach

Если указан параметр thisArg, он будет использоваться в качестве этого значения для каждого вызова callbackfn. Если он не указан, вместо него используется 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.

В node оба приведенных выше фрагмента возвращают

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

Может ли кто-нибудь подтвердить, является ли это проблемой в моей среде node, или если это проблема с node?

Обновление: просто для подтверждения, в обоих случаях на node typeof this возвращается object.

Ответ 1

Проблема существует с node v0.10.28 (последняя стабильная), установленная вместе с V8 v3.14.5.9 (и более ранними версиями), но проблема не в самой node, а в V8, у которой есть ошибка.

Отчет об ошибке можно найти в проблеме 2273, опубликованной 5 августа 2012 года.

Функция строгого режима должна получать не принудительное значение '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();
})();

Исправлена ​​ошибка с исходным кодом V8 в версия r14149 от 5 апреля 2013 г.

Таким образом, проблема была длительной и затрагивала все среды, которые были основаны на двигателе V8.

Я смог подтвердить, что Chrome v27 по-прежнему был затронут этой проблемой, и он запускал V8 v 3.16 и может подтвердить, что Chrome v34 с V8 v3.24.35.33 больше не затрагивается. Итак, где-то между этими 2 исправление для V8 прошло в основном.

Предложение от @cookiemonster. Решением может быть использование более поздней версии node (из их нестабильного репо), но я не могу подтвердить это.

Мне не удалось найти отчет об этой проблеме в node списке проблем.

Единственное другое решение - проверить эту ошибку (приведенный выше код) и самостоятельно подделать затронутые методы. Я тестировал это решение, и он работает, вот прокладка, с которой я тестировал. (взято из проекта es5-shim)

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);
        }
    }
};

Проблема была решена с помощью