Использование `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
    на внешнем (/path/test.js:16:13)
    в Child.method (/path/test.js:18:9)
    на объекте. (/Path/test.js:22:13)
    в Module._compile (module.js:434:26)
    в Object.Module._extensions..js (module.js:452:10)
    в Module.load (module.js:355:32)
    в Function.Module._load (module.js:310:12)
    в Function.Module.runMain (module.js:475:10)
    при запуске (node.js:117:18)
    в node.js:951:3

Если вы удалите ссылку на x это в inner:

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

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

внешний: х = 42
Родительское шоу: 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");

Выход:

метод вызывается с аргументом arg, флаг равен false
тип external2: функция
тип inner2: функция
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 работает правильно (или, конечно, наоборот).

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

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

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

1 ответ

Решение

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

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

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

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

  1. Пусть env будет GetThisEnvironment().
  2. Если env.HasSuperBinding() имеет значение false, генерировать исключение ReferenceError.
  3. Пусть validThis будет env.GetThisBinding ().
  4. ReturnIfAbrupt (actualThis).
  5. Пусть baseValue будет env.GetSuperBase().
  6. Пусть bv будет RequireObjectCoercible (baseValue).
  7. ReturnIfAbrupt (БВ).
  8. Возвратите значение типа Reference, которое является Super Reference, базовым значением которого является bv, чьим ссылочным именем является propertyKey, thisValue - validThis, а флаг строгой ссылки является строгим.

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

8.3.2 GetThisEnvironment ()

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

  1. Пусть lex будет LexicalEnvironment запущенного контекста выполнения.
  2. Повторение
    а. Пусть envRec будет окружающей средой lex.
    б. Пусть существует будет envRec.HasThisBinding().
    с. Если существует, true, вернуть envRec.
    д. Пусть external будет значением ссылки на внешнюю среду lex.
    е. Пусть lex будет внешним.

П р и м е ч а н и е - Цикл на шаге 2 всегда завершается, потому что список сред всегда заканчивается глобальной средой, которая имеет эту привязку.

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

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

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

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

Другие вопросы по тегам