庖丁解牛学前端1——从一个面试题说起 全局变量/闭包/值传递/IIFE

参考文章:

  • 破解前端面试(80% 应聘者不及格系列):从 闭包说起 - 知乎
  • 学习Javascript闭包 - 阮一峰
  • 什么是立即执行函数?有什么作用? - 知乎

从一题面试入手:求输出的顺序与时间(用分隔符标识一秒间隔)

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}
console.log(new Date, i);

第一眼看到,想当然:0-1-2-3-4-5。显然是错的,正确答案为5-5555

  • 知识点1:变量作用域。在javascript里,变量作用域分为全局变量局部变量块作用域变量。在出现constlet之前,只有前两种。
    • 定义在函数内部的变量为局部变量,定义在函数外的变量为全局变量。
    • 局部变量与全局变量完全独立,即使相同名称也不会被覆盖。
    • 块作用域变量与使用{...}来划分范围。
var varV = 1;
let letV = 1;
console.log('全局varV=', varV);  // 1
console.log('全局letV=', letV);  // 1
if (true) { // 块作用域内尝试覆盖
  var varV = 2;
  let letV = 2;
  console.log('在代码块内varV=', varV); // 2
  console.log('在代码块内letV=', letV); // 2
}
console.log('全局varV=', varV); // 2
console.log('全局letV=', letV); // 1
function amFunction() { // 函数作用域内尝试覆盖
  var varV = 3;
  let letV = 3;
  console.log('在函数内varV=', varV); // 3
  console.log('在函数内letV=', letV); // 3
}

amFunction();
console.log('全局varV=', varV); // 2
console.log('全局letV=', letV); // 1

追问:如果需要输出5-01234应该怎么改

答案如下:

for (var i = 0; i < 5; i++) {
    (function(j) {  // j = i
        setTimeout(function() {
            console.log(new Date, j);
        }, 1000);
    })(i);
}
console.log(new Date, i);
  • 知识点2:闭包(Closure)。闭包的概念之前一直没有细抠,就是定义在函数里的函数。主要有两个用法,允许从外部访问局部变量以及允许变量常驻内存
var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc : function(){
    var that = this;
    return function(){ return that.name; };
  }
};
alert(object.getNameFunc()()); // My Object

因为getNameFunc将自己内部的函数返回,因而形成闭包,所以在全局域可以读取到getNameFunc的局部变量(这里主要是that),如果将that行注释,则返回’The Window’。

  • 知识点3:值传递/引用传递。明确一点,Javascript里不存在引用传递,所有的参数都是值传递,只是变量分为值类型和引用类型,如果是引用类型(Object, Array, Function, Date等)的变量,当然传递过去的是一个引用

  • 知识点4:立即执行函数(IIFE)。什么是IIFE,IIFE有什么用?

    1. 立即执行函数的本质就是:声明一个匿名函数+立即执行它。
    2. 创建独立的变量作用域,防止内部变量被污染。

你可能感兴趣的:(庖丁解牛学前端)