Javascript - Kyle Simpson的高级Javascript练习1

时间:2016-08-30 07:13:31

标签: javascript

在Kyle Simpson(@ kyle-simpson)高级JavaScript课程here的练习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. 可以/必须:

    • 声明额外的变量(只要它们不是全局的)
    • 修改(就地)函数声明/初始化
    • 添加/删除语句/表达式(IIFE,返回,参数等)
    • 尽可能少地进行更改
  4. 解决方案(只需更改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'。但是,在J内调用I时 - 为什么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到ž

    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);
    }
    

2 个答案:

答案 0 :(得分:1)

  

该功能以功能参考J的分配在功能本身内更新,以打印J&#39;。

完全。

  

但是,在J内拨打I时 - 为什么J()需要被调用两次?

第一个电话 - 正如您所说 - 仅更新J变量。第二个调用实际上将调用更新的函数,即记录&#39; J&#39;并继续K

  

何时调用每个字母的这些函数?

正如你所说,他们被称为#34;递归&#34;从这些函数内部直到i到达终点。第一个启动链的调用位于上面的J函数中:

fns.K();

(请注意rest[0]K

  

当创建第i个字母的函数时,我们调用第i + 1个字母的函数(通过window[rest[i+1]]();),此时可能未定义

没有。下一个函数的调用是在我们正在创建的函数内部。这些函数只是创建的,它们尚未从循环内调用 - 函数调用就是IIFE的函数调用。循环结束后,将从J调用它们。

答案 1 :(得分:1)

  

然而,当从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)将确保没有超出范围的调用。