在Kyle Simpson (@kyle-simpson)高级JavaScript课程的练习1中,其目标是扩展对作用域,提升等的理解,有一些事情我发现很难遵循。
<标题>练习文件ex1.html
<!DOCTYPE html>
<html>
<head>
<title>Exercise 1</title>
</head>
<body>
<h1>Exercise 1</h1>
<script src="ex1.js"></script>
</body>
</html>
ex1.js
A();
function C() {
console.log("OOPS!");
}
function E(f) {
console.log("E");
f();
var f = F;
}
var A = function() {
console.log("A");
B();
};
var C;
function G() {
console.log("G");
H();
var H = function() {
console.log("H");
I();
};
}
var D = d;
function d() {
console.log("D");
E();
}
function I() {
console.log("I");
J();
J();
}
B = function() {
console.log("B");
C();
};
var F = function() {
console.log("F");
G();
};
var rest = "KLMNOPQRSTUVWXYZ".split("");
for (var i=0; i<rest.length; i++) {
(function(i){
// define the current function
window[rest[i]] = function() {
console.log(rest[i]);
if (i < (rest.length-1)) {
// TODO: call the next function
}
};
})(i);
}
var J = function() {
J = function() {
console.log("J");
K();
};
};
C = function() {
console.log("C");
D();
};
指令(README.md)修复代码,使其在控制台中打印字母A-Z
不能:
- 有任何全局变量
- 删除或合并任何函数声明
- 创建任何新函数(除了iife——提示!)
- 重新安排申报顺序
/必须:
- 声明额外的变量(只要它们不是全局的)
- 修改(原位)函数声明/初始化
- 添加/删除语句/表达式(生命、返回、参数等)
- 尽可能少的修改
解决方案(只需要更改ex1.js)
ex1-fixed.js
(function(global){
function C() {
console.log("OOPS!");
}
function E(f) {
console.log("E");
f();
var f = F;
}
var A = function() {
console.log("A");
B();
};
var C;
function G() {
console.log("G");
H();
function H() {
console.log("H");
I();
}
}
var D = d;
function d() {
console.log("D");
E(F);
}
function I() {
console.log("I");
J();
J();
}
B = function() {
console.log("B");
C();
};
var F = function() {
console.log("F");
G();
};
var rest = "KLMNOPQRSTUVWXYZ".split(""), fns = {};
for (var i=0; i<rest.length; i++) {
(function(i){
// define the current function
fns[rest[i]] = function() {
console.log(rest[i]);
if (i < (rest.length-1)) {
fns[rest[i+1]]();
}
};
})(i);
}
var J = function() {
J = function() {
console.log("J");
fns.K();
};
};
function C() {
console.log("C");
D();
}
return A;
})(window)();
我很好地遵循解决方案,直到I
打印出来。
关于I
打印后部分代码的问题
* * 1。在解决方案(和执行代码)中,作者对函数I
使用了下面的函数声明,对J
使用了函数表达式。这个函数是这样的,函数引用J
的赋值在函数内部被更新为输出'J'。然而,当从I
内部调用J
时-为什么J()
需要调用两次?
var J = function() {
J = function() {
console.log("J");
K();
};
};
function I() {
console.log("I");
J();
J();
}
2。在练习代码中,作者希望修复以下代码行以打印字母L到Z。
对于每个字母,运行一个匿名函数以创建一个字母的函数名(对于字母'K'到'Z'并粘在窗口对象上)-我不理解的部分是
1)当每个字母调用这些函数时-我通过window[rest[i+1]]();
行看到调用-但是我们是如何到达这里的?
2)当创建第i个字母的函数时,我们正在调用第i+第1个字母的函数(通过window[rest[i+1]]();
),这在当时可能是未定义的-但是代码工作并打印出字母L到Z
var rest = "KLMNOPQRSTUVWXYZ".split("");
for (var i=0; i<rest.length; i++) {
(function(i){
// define the current function
window[rest[i]] = function() {
console.log(rest[i]);
if (i < (rest.length-1)) {
// TODO: call the next function
window[rest[i+1]]();
}
};
})(i);
}
标题>
函数是这样的:函数引用
J
的赋值在函数内部被更新为输出'J'。
。
然而,当从
I
内部调用J
时-为什么J()
需要调用两次?
第一个调用-正如您所说-只更新J
变量。第二个调用实际上将调用更新后的函数,即记录'J'并继续使用K
的函数。
每个字母的这些函数何时被调用?
就像你说的,它们在这些函数内部被"递归地"调用,直到i
到达末尾。第一次调用(启动链)在上面的J
函数内部:
fns.K();
(注意rest[0]
是K
)
创建第i个字母的函数时,调用第i+第1个字母的函数(通过
window[rest[i+1]]();
),该函数在该点可能未定义
。下一个函数的调用在我们正在创建的函数内部。这些函数只是被创建的,它们还没有从循环中被调用——这里的函数调用是IIFE的函数调用。它们将在循环结束后从J
调用。
然而,当从I内部调用J时-为什么J()需要调用两次?
在下面给出的代码示例中:
var J = function() {
J = function() {
console.log("J");
K();
};
};
function I() {
console.log("I");
J(); // The first call refines the value of J
J(); // This now calls the inner J instead
}
在I
函数中,第一个J
调用所做的基本上是重新定义函数J
是什么。在第一次调用之后,J
函数从
function () {
J = function() { // Here inside the call is where J is being redefined
console.log("J");
K();
};
}
:
function () {
console.log("J");
K();
}
这就是为什么需要第二次调用。请注意,对J
的所有其他函数调用都可以正常工作,并且打印J
并调用K()
,因此除了用于学习目的之外,它不是很实用。
什么时候调用每个字母的这些函数-我通过
window[rest[i+1]]();
行看到调用-但是我们是如何到达这里的?
在这种情况下,您提供的代码实际上没有做任何事情。这只是创建函数,而不是运行它们中的任何一个,因此为什么window[rest[i+1]]
将在后面定义,以及条件i < (rest.length-1)
将确保没有越界调用。