Подтвердить что ты не робот

Использование `super` внутри функции стрелки внутри функции стрелки внутри метода

Я пытаюсь выяснить, наблюдается ли какое-то поведение, которое я наблюдаю в Node v4.1.1 (V8 v4.5.103.33) относительно super и функций стрелок, и если это так (или действительно, если нет), где он находится в спецификации, в котором говорится, что он должен (или не должен) работать в различных случаях, которые у меня есть.

Вкратце: использование super в функции стрелки (inner) внутри другой функции стрелки (outer) внутри метода работает, если outer не имеет аргументов или переменных inner, даже если inner ссылается на аргументы или переменные method. Я хочу знать , что говорит спецификатор: должно ли оно работать все время, даже если V8 не работает? Никогда? Только в конкретных случаях, когда V8 в настоящее время позволяет ему работать, а не там, где это не так?

Здесь MCVE:

"use strict";

class Parent {
    show(msg) {
        console.log(`Parent#show: ${msg}`);
    }
}

class Child extends Parent {
    method(arg) {
        let outer = (x) => {
            console.log(`outer: x = ${x}`);
            let inner = () => {
                super.show(`arg = ${arg}, x = ${x}`);
            };
            inner();
        };
        outer(42);
    }
}

new Child().method("arg");

Сбой:

$ node test.js
/path/test.js:13
                super.show(`arg = ${arg}, x = ${x}`);
                ^^^^^

SyntaxError: 'super' keyword unexpected here
    at outer (/path/test.js:16:13)
    at Child.method (/path/test.js:18:9)
    at Object. (/path/test.js:22:13)
    at Module._compile (module.js:434:26)
    at Object.Module._extensions..js (module.js:452:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Function.Module.runMain (module.js:475:10)
    at startup (node.js:117:18)
    at node.js:951:3

Если вы удалите ссылку на x, которая находится в inner:

            let inner = () => {
                super.show(`arg = ${arg}`); // <== removed x from this
            };

он работает и выводит:

outer: x = 42
Parent#show: arg = arg

Чтобы доказать себе, что дело "работает" не в том, что функции были оптимизированы, я вернул их из метода и назвал их. Вот этот немного более сложный случай (обратите внимание на комментарии); эта версия работает:

"use strict";

class Parent2 {
    show(msg) {
        console.log(`Parent2#show: ${msg}`);
    }
}

class Child2 extends Parent2 {
    method(arg) {
        let flag = Math.random() < 0.5;
        console.log(`method called with ${arg}, flag is ${flag}`);
        let x = "A";                 // **A**
        let outer2 = (/*x*/) => {    // **B**
            //let x = "C";           // **C**
            let inner2 = () => {
                super.show(`${x}: ${arg} (${flag})`);
            };
            return inner2;
        };
        return outer2;
    }
}

let o = new Child2().method("arg");
console.log(`type of outer2: ${typeof o}`);
let i = o();
console.log(`type of inner2: ${typeof i}`);
i("B");

Вывод:

method called with arg, flag is false
type of outer2: function
type of inner2: function
Parent2#show: A: arg (false)

Но если мы прокомментируем строку с меткой A и раскомментируем либо B, либо C, она терпит неудачу, как это делает MCVE.

Дополнительные примечания:

  • Я должен подчеркнуть, что вам нужно, чтобы функции стрелок были вложены. outer не имеет проблем с доступом к super. Я не хочу загромождать вопрос другим большим блоком кода, но если вы добавите super.show(`outer: arg = ${arg}, x = ${x}`); в начало outer, он будет работать нормально.

  • Как вы можете видеть, inner использует как аргумент, так и переменную из method (ну, MCVE просто использует arg), и это прекрасно, но как только inner пытается использовать аргумент или переменная от outer, все взорвется.

  • Babel и Traceur с удовольствием передают случай, когда V8 не будет работать (здесь и здесь), но это может быть просто им что-то не так, что V8 получает право (или, конечно, наоборот).

  • Он не относится к строкам шаблона; версия pre-MCVE этого не использовала их (и использовала promises, вот как мы закончили со стрелками внутри стрелок).

Просто чтобы подчеркнуть, вопрос в том, что здесь указано здесь, и где в спецификации указано.

Моя кишка говорит мне, что это всего лишь ошибка V8 – — это первые дни для этого, в конце концов, честный 'нах. Но в любом случае, я просто пытаюсь понять, каково должно быть поведение, что говорит спецификация. Я пытался следить за его различными и разными разделами, говоря о super и "базовых объектах" и т.д., И, честно говоря, я просто не понимаю.

4b9b3361

Ответ 1

Похоже, что это действительно ошибка в V8 (теперь она fixed). Обратите внимание, что если нет вложенной функции стрелок, она отлично работает.

Итак, если мы рассмотрим литеральный текст спецификации, чтобы узнать, является ли это ошибкой, начните с самого ключевого слова super:

12.3.5.3 Семантика времени выполнения: MakeSuperPropertyReference (свойствоKey, строгое)

Абстрактная операция MakeSuperPropertyReference с аргументами propertyKey и strict выполняет следующие шаги:

  • Пусть env будет GetThisEnvironment().
  • Если env.HasSuperBinding() является ложным, вызовите исключение ReferenceError.
  • Пусть actualThis будет env.GetThisBinding().
  • ReturnIfAbrupt (actualThis).
  • Пусть baseValue будет env.GetSuperBase().
  • Пусть bv - RequireObjectCoercible (baseValue).
  • ReturnIfAbrupt (БВ).
  • Возвращает значение типа Reference, которое является Super Reference, базовым значением которого является bv, ссылочным именем которого является свойствоKey, чье thisValue является actualThis и чей строгий ссылочный флаг является строгим.

Пусть игнорирует большинство словесных материалов и беспокоится о GetThisEnvironment():

8.3.2 GetThisEnvironment()

Абстрактная операция GetThisEnvironment находит запись окружения, которая в настоящее время связывает ключевое слово this. GetThisEnvironment выполняет следующие шаги:

  • Пусть lex - это исполняемые контексты выполнения LexicalEnvironment.
  • Повторите
    а. Пусть envRec будет lexs EnvironmentRecord.
    б. Пусть существует envRec.HasThisBinding().
    с. Если существует true, верните envRec.
    д. Пусть external - значение ссылки внешней среды lexs.
    е. Пусть lex является внешним.

ПРИМЕЧАНИЕ. Цикл на шаге 2 всегда заканчивается, потому что список окружений всегда заканчивается глобальной средой, которая имеет эту привязку.

Теперь, когда мы знаем, что функции стрелок не имеют привязок к this, он должен пропустить запись об окружающей среде текущей функции и функции, которая немедленно ее помещает.

Это остановится после достижения "обычных" функций и продолжит поиск ссылки на объект super, как и ожидалось, в соответствии со спецификацией.

Аллен Вирфс-Брок, редактор проекта спецификации ECMAScript, похоже, подтверждает, что это было сделано в ответ на список рассылки es-discuss несколько лет назад:

super лексически ограничен, как и this для ближайшей закрывающей функции, которая его определяет. Все формы определения функций, кроме функций стрелок, вводят новые привязки this/super, поэтому мы можем просто [сказать], что this/super связывается в соответствии с ближайшим заключением определения функции, отличной от стрелки.