Как это работает с параметрами по умолчанию?

Итак... ES6¹ (который был стандартизирован несколько часов назад) предоставляет параметры по умолчанию для функций, аналогичных функциям в PHP, Python и т. д. Я могу делать такие вещи, как:

function foo (bar = 'dum') {
    return bar;
}

foo(1); // 1
foo(); // 'dum'
foo(undefined); // 'dum'

MDN говорит, что значение параметра по умолчанию оценивается во время вызова. Это означает, что каждый раз, когда я вызываю функцию, выражение 'dum' оценивается снова (если только реализация не делает каких-то странных оптимизаций, которые нас не волнуют).

Мой вопрос в том, как this играет в этом роль?

let x = {
  foo (bar = this.foo) {
    return bar;
  }
}

let y = {
  z: x.foo
}

x.foo() === y.z(); // what?

В настоящее время транспайлер babel оценивает его как false, но я этого не понимаю. Если они действительно оцениваются во время вызова, как насчет этого:

let x = 'x from global';

function bar (thing = x) {
  return thing;
}

function foo () {
  let x = 'x from foo';
  return bar();
}

bar() === foo(); // what?

В настоящее время транспайлер babel оценивает³ его как true, но я этого не понимаю. Почему bar не берет x из foo при вызове внутри foo?

1 – Да, я знаю, что это ES2015.
2 – Пример A
3 – Пример Б


person user3459110    schedule 17.06.2015    source источник
comment
Есть ли объявление о принятии правлением окончательного проекта ES6?   -  person    schedule 17.06.2015
comment
Спецификация @squint доступна по адресу ecma-international.org/ecma-262. /6.0/index.html :)   -  person user3459110    schedule 17.06.2015
comment
Ваше сравнение по существу оценивается как x.foo === y.foo, что явно равно false, потому что в обоих случаях вы вызываете функцию foo, но в первом случае this === x, а во втором случае this === y. Вопрос, похоже, действительно в том, почему по сути let x = {foo(){ return this; }}; let y = {z: x.foo}; y.foo() === y. Ответ в том, что y.foo() это то же самое, что и y.foo.call(y). Вот как определяется this.   -  person loganfsmyth    schedule 18.06.2015


Ответы (2)


Мой вопрос в том, как this играет в этом роль? Я не понимаю. Действительно ли они оцениваются во время разговора?

Да, инициализаторы параметров оцениваются во время вызова. Это сложно, но в основном шаги следующие:

  1. В стеке устанавливается новый контекст выполнения. ,
    с новой средой в "область закрытия" вызываемой функции
  2. При необходимости он thisBinding инициализируется
  3. Declarations are instantiated:
    1. Mutable bindings for the parameter names are created
    2. При необходимости объект arguments создается привязкой
    3. Привязки итеративно инициализируются из списка аргументов (включая все деструктуризации и т. д.)
      При этом оцениваются инициализаторы
    4. Если были задействованы какие-либо замыкания, вставляется новая среда.
    5. Изменяемые привязки для переменных, объявленных в теле функции, создаются (если это еще не сделано именами параметров) и инициализируются с помощью undefined
    6. Создаются привязки для переменных let и const в теле функции
    7. Привязки для функций (из объявлений функций в теле) инициализируются с инстанцированными функциями.
  4. Наконец, тело функция оценивается.

Таким образом, инициализаторы параметров имеют доступ к this и arguments вызова, к ранее инициализированным другим параметрам и ко всему, что находится в их «верхней» лексической области видимости. На них не влияют переменные, объявленные в теле функции (хотя на них влияют все остальные параметры, даже если они находятся в их временной мертвой зоне).

что насчет этого:

function bar (thing = x) {}
{
  let x = 'x from foo';
  return bar();
}

Я не понимаю. Почему bar не берет x из foo при вызове внутри foo?

Потому что x — это локальная переменная, к которой у bar нет доступа. Нам так повезло, что они не имеют динамической области видимости! Инициализаторы параметров оцениваются не на месте вызова, а внутри области действия вызываемой функции. В этом случае идентификатор x преобразуется в глобальную переменную x.

person Bergi    schedule 17.06.2015
comment
Если кому-то интересно, у нас было дальнейшее обсуждение этого в чат, если вы хотите прочитать :) - person user3459110; 18.06.2015

Когда они говорят "оценивается во время вызова", я думаю, они имеют в виду вызов по имени. Вот как babel выводит ваш третий пример:

'use strict';

var x = 'x from global';

function bar() {
  var thing = arguments[0] === undefined ? x : arguments[0];

  return thing;
}

function foo() {
  var x = 'x from foo';
  return bar();
}

bar() === foo(); // what?

Поскольку var x наследуется в пределах лексической области действия bar от глобальной области видимости, это область, в которой она используется.

Теперь рассмотрим следующее:

let i = 0;

function id() {
  return i++;
}

function bar (thing = id()) {
  return thing;
}

console.info(bar() === bar()); // false

Который транспилируется в

"use strict";

var i = 0;

function id() {
  return i++;
}

function bar() {
  var thing = arguments[0] === undefined ? id() : arguments[0];

  return thing;
}

console.info(bar() === bar()); // false

Обратите внимание, что здесь id вызывается внутри функции, а не кэшируется и запоминается во время определения функции, следовательно, вызов по имени, а не call- по стоимости.

Таким образом, во втором примере поведение правильно. Здесь нет y.foo, а поскольку this имеет динамическую область действия в Javascript (т. е. зависит от получателя данного вызова функции), когда y.z() ищет this.foo, он будет искать его в y, поэтому y.z() вернет undefined, а x.foo() просто вернет саму функцию foo.

Если вы делаете привязку к получателю, вы можете привязать foo к x при назначении. Тогда он должен работать так, как ожидалось.

Извините, если что-то из этого неясно; дайте мне знать в комментариях, и я был бы рад уточнить! :)

person Travis Kaufman    schedule 17.06.2015
comment
и поскольку в Javascript область действия динамически ограничена, вот в чем заключается мой вопрос. Использование this.something не следует принимать специально, потому что this.something не волшебное, а только this. Я добавлю очищенный пример к вопросу, чтобы прояснить это, спасибо за ответ! - person user3459110; 18.06.2015