но все же я не могу понять, почему он показывает 1 вместо неопределенного.
Это не только вы.Это глубокая, темная часть спецификации.: -)
Ключ в том, что есть два x
s.Да, действительно.Это параметр x
, а есть переменная x
.
Список параметров, содержащий выражений (например, f
's значение по умолчанию) имеет собственную область действия , отдельную от области действия тела функции.Но до того, как списки параметров, возможно, имели выражения, наличие var x
в функции с параметром x
не имело никакого эффекта (x
все еще оставалось параметром со значением параметра).Таким образом, чтобы сохранить это, когда есть список параметров с выражениями в нем, создается отдельная переменная, и значение параметра копируется в переменную в начале тела функции.Что является причиной этого , казалось бы, странного (нет, не только, казалось бы) странного поведения.(Если вы любите углубляться в спецификацию, это копирование с шага 28 из FunctionDeclarationInstantiation .)
Поскольку значение по умолчанию f
, () => x
, равносозданный в области списка параметров, он ссылается на параметр x
, а не на переменную.
Итак, первое решение, [2, 1, 1]
, является правильным, поскольку:
2
было присвоено переменной x
в теле функции.Таким образом, в конце функции переменная x
равна 2
. 1
была назначена для y
из переменной x
до того, как x
получила значение 2
,поэтому в конце функции y
равен 1
. - Значение параметра *
x
никогда не менялось, поэтому f()
приводит к 1
приконец функции
Это , как если бы код был написан так (вместо этого я удалил ненужные скобки и добавил пропущенные точки с запятой):
console.log(function(param_x, f = () => param_x) {
var var_x = param_x;
var y = var_x;
var_x = 2;
return [var_x, y, f()];
}(1));
... Я удалил var x из тела функции и обнаружил, что ответ изменился на # 3 ...
# 3 - [2, 1, 2]
.Это правильно, потому что когда вы удаляете var x
из функции, остается только один x
, параметр (наследуемый телом функции из списка параметров).Таким образом, присвоение 2
x
изменяет значение параметра, которое возвращает f
.
Взяв более ранний пример с param_x
и var_x
, вот как это выглядит, если вы удалите var x;
из него:
console.log(function(param_x, f = () => param_x) {
var y = param_x;
param_x = 2;
return [param_x, y, f()];
}(1));
Вот аннотированное описание исходного кода (с удалением лишних скобок и добавлением пропущенных точек с запятой):
// /---- the parameter "x"
// v vvvvvvvvvvv--- the parameter "f" with a default value
console.log(function(x, f = () => x) {
var x; // <=== the *variable* x, which gets its initial value from the
// parameter x
var y = x; // <=== sets y to 1 (x's current value)
x = 2; // <=== changes the *variable* x's value to 2
// +---------- 2, because this is the *variable* x
// | +------- 1, because this is the variable y
// | | +--- 1, because f is () => x, but that x is the *parameter* x,
// | | | whose value is still 1
// v v vvv
return [x, y, f()];
}(1));
Последнее замечание относительно вашего заголовка:
объявление переменной дважды в IIFE
Переменная объявляется только один раз.Другая вещь - это параметр, а не переменная.Различие редко важно ... это один из тех редких моментов.: -)