Javascript -高级Javascript练习1由Kyle Simpson编写

Javascript - Exercise 1 of Advanced Javascript by Kyle Simpson

本文关键字:Javascript Kyle Simpson 编写 高级 练习      更新时间:2023-09-26

在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)
  1. 修复代码,使其在控制台中打印字母A-Z

  2. 不能:

    • 任何全局变量
    • 删除或合并任何函数声明
    • 创建任何新函数(除了iife——提示!)
    • 重新安排申报顺序
  3. /必须:

    • 声明额外的变量(只要它们不是全局的)
    • 修改(原位)函数声明/初始化
    • 添加/删除语句/表达式(生命、返回、参数等)
    • 尽可能少的修改

解决方案(只需要更改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)将确保没有越界调用。