Закончите говорить: 90% вопросов интервьюеры не будут задавать, потому что они не всегда понимают.
Давайте посмотрим на тему, о которой мы сегодня поговорим:
// 问题:foo.x的值是什么?bar.x?
var foo = { n: 1 };
var bar = foo;
foo.x = foo = { n: 2 };
console.log(foo.x) // ?
console.log(bar.x) // ?
// 问题:下面两题的结果是?
(function(x, f = () => x) {
var x;
var y = x;
x = 2;
return [x, y, f()]; // ?
})(1)
(function(x, f = () => x) {
var y = x;
x = 2;
return [x, y, f()]; // ?
})(1)
Эти два вопроса недавно были замечены в дискуссионной группе, и они до сих пор вызвали очень бурное обсуждение, когда были опубликованы. Но в итоге все посчитали, что такая тема бессмысленна, и я бы не стал и не рекомендовал бы ее, когда реально работал над проектом (ps: Если я найду, что кто-то так пишет в проекте, я брошу ее прямо с третьего этажа), но с точки зрения обучения, это на самом деле еще можно исследовать.
Первый вопрос:
// 问题:foo.x的值是什么?bar.x?
var foo = { n: 1 };
var bar = foo;
foo.x = foo = { n: 2 };
console.log(foo.x) // ?
console.log(bar.x) // ?
Как только я увидел этот вопрос, моей первой реакцией было:
foo.x = {n: 2};
bar.x = {n: 2};
В то время мой внутренний монолог был таким: Так просто!!Что тут спрашивать о таком вопросе!
Однако результат таков:
bar.x = {n: 2};
foo.x = undefined;
Почему!!!!!!?????? Я выразился очень подавленно
Затем я решил провести небольшое исследование, и я подытожу его ниже~
На самом деле есть две вещи, которые нужно понять по этому поводу:
- Для присваивания объекта передаются все ссылки, и все они являются вызовами по ссылке.
- Для операторов присваивания сначала всегда вычисляется левое, затем правое, а затем PutValue.
Вы можете обратиться кСтандарт ECMAScriptНиже рассмотрим реализацию приведенного выше кода.
1. Первая и вторая строки кода очень просты, то есть присваиваем объект ({n: 2}) foo, а затем присваиваем объект bar through foo. В настоящее время и bar, и foo хранят ссылки на объект {n: 2}.
и foo, и bar указывают на один и тот же адрес памяти
2. Далее сосредоточьтесь наfoo.x = foo = { n: 2 }
. мы следуем[ Для операторов присваивания всегда сначала оценивайте левое, затем правое, а затем PutValue. ]для разбора этой строки кода.
Первым шагом является оценка Foo.x сначала, foo указывает на объект {n: 2} (в дальнейшем сопущенном как: objectf), Objectf не имеет атрибута X, а затем добавить атрибут X на Objectf, результатом оценки Lvalue - это ссылка на свойство X только что добавлена (некоторые адреса памяти X).
Значение добавленного атрибута x является адресом памяти.
На втором этапе оценивается значение r, и оно равно foo = {n : 2}. Рекурсивно вниз, сначала оцените lvalue, получите ссылку foo, foo или ObjectF, затем оцените rvalue {a: 2}, получите ObjectE, затем PutValue изменит указатель foo на ObjectE, выражение присваивания foo = {n: 2} возвращает ссылку на ObjectE.
В настоящее время foo и ObjectF были развязаны и перенаправлены на ObjectE.В ObjectE нет атрибута x, поэтому foo.x в настоящее время не определен.
На третьем шаге PutValue указывает lvalue на ObjectE, что означает, что адрес памяти X на первом шаге хранит ссылку на ObjectE.
На этом весь процесс назначения завершен.
второй вопрос:
// 问题:下面两题的结果是?
(function(x, f = () => x) {
var x;
var y = x;
x = 2;
return [x, y, f()]; // [2, 1, 1]
})(1)
(function(x, f = () => x) {
var y = x;
x = 2;
return [x, y, f()]; // [2, 2, 1]
})(1)
По этому вопросу вторая функция, я думаю, у всех не будет сомнений. Следует сосредоточиться на первом.
Чтобы это понять, нужно понять две вещи:
1. Тело функции и тело функции — это два разных пространства имен или областей видимости.Область вне функции не может получить доступ к переменным внутри функции. Формальные параметры функции (x, f) и тело функции {} — это две разные области видимости.
(function(a, f = () => x) {
var x = 2;
return [ a, f()];
})(1) // Uncaught ReferenceError: x is not defined
2.Параметры по умолчанию в функциях могут использоваться для более поздних параметров по умолчанию (уже встречающиеся параметры могут использоваться для более поздних параметров по умолчанию).
как понять[Параметры по умолчанию в функции могут использоваться для последующих параметров по умолчанию (параметры, которые встречаются, могут использоваться для последующих параметров по умолчанию)], см. следующий пример:
function singularAutoPlural(singular, plural = singular+"s", rallyingCry = plural + " ATTACK!!!") {
return [singular, plural, rallyingCry ];
}
//["Gecko","Geckos", "Geckos ATTACK!!!"]
singularAutoPlural("Gecko");
//["Fox","Foxes", "Foxes ATTACK!!!"]
singularAutoPlural("Fox","Foxes");
//["Deer", "Deer", "Deer ... change."]
singularAutoPlural("Deer", "Deer", "Deer peaceably and respectfully
petition the government for positive change.")
Демо изMDN
Поймите это, тогда давайте объясним эту тему напрямую~
(function(x, f = () => x) { // 首先这里给参数 f 默认赋值了一个匿名函数,根据我们之前说的第二个知识点这里的 x 就是形参 x。由于作用域的关系 函数f 是不能访问到函数内的 x 的。
var x; // !!! 注意,这里进行了变量声明,会分配新的内存地址。但是因为只进行了声明而没有赋值,所以在作用域链还会找到 形参x
var y = x; // 这里 y 的值取的还是形参 x 的值
x = 2; // 这里 对上面的 var x 进行赋值而形参x 的值是不受影响的(console.log(arguments[0])试一下, 所以 f() 返回是1),此时作用域链上会先找到函数内声明的 x。
return [x, y, f()]; // [2, 1, 1]
})(1)
(function(x, f = () => x) {
var y = x; // 这里只声明了y, x 还是形参x
x = 2; // 这里改变了形参x的值,所以 f() 返回是 2
return [x, y, f()]; // [2, 1, 2]
})(1)
В этом вопросе есть еще ямка.Взял бабель и перекрутил и результат был
"use strict";
(function (x) {
var f = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {
return x;
};
var x;
var y = x;
x = 2;
return [x, y, f()]; // !!! 这里结果是 [2, 1, 2]
})(1);
Старайтесь не писать такой волшебный код!
Если есть какие-либо недоразумения, пожалуйста, поправьте меня!