编程知识 cdmana.com

温故而知新篇之《JavaScript忍者秘籍(第二版)》学习总结(三)——闭包和作用域

前言

这本书的电子版我已经在学习总结第一篇已经放了下载链接了,可以去查看
温故而,知新篇之《JavaScript忍者秘籍(第二版)学习总结(一)——函数篇

你自律用在什么地方,什么地方就会成就你。要记住当你快顶不住的时候,磨难也快顶不住了。

加油吧,兄弟们


先来一个自增函数看看

var addnum= function(){
  var num=0; // 闭包内 参数私有化
  return  function(){
    return num++
  }
}
const Addnum= addnum()
Addnum()
console.log(Addnum()) // 1
console.log(Addnum()) // 2

封装私有变量

function Ninja() {
 var feints = 0;
 this.getFeints = function() {
  return feints;
  };
  this.feint = function() {
   feints++;
  };
}
var ninja1 = new Ninja();
ninja1.feint();

通过执行上下文来跟踪代码

具有两种类型的代码,那么就有两种执行上下文:全局执行上下文和函数执行上下文。二者最重要的差别是:

  • 全局执行上下文只有一个,当JavaScript程序开始执行时就已经创建了全局上下文;
  • 而函数执行上下文是在每次调用函数时,就会创建一个新的。

定义变量的关键字与词法环境

关键字var

var globalNinja = "Yoshi";  //⇽--- 使用关键字var定义全局变量

function reportActivity() {
  var functionActivity = "jumping";  //⇽--- 使用关键字var定义函数内部的局部变量

  for (var i = 1; i < 3; i++) {
     var forMessage = globalNinja + " " + functionActivity;   //⇽--- 使用关键字var在for循环中定义两个变量
     console.log(forMessage === "Yoshi jumping",
         "Yoshi is jumping within the for block");  //⇽--- 在for循环中可以访问块级变量,函数内的局部变量以及全局变量
     console.log(i, "Current loop counter:" + i);
  }

  console.log(i === 3 && forMessage === "Yoshi jumping",
      "Loop variables accessible outside of the loop");  //⇽--- 但是在for循环外部,仍然能访问for循环中定义的变量
  }

reportActivity();
console.log(typeof functionActivity === "undefined"
   && typeof i === "undefined" && typeof forMessage === "undefined",
   "We cannot see function variables outside of a function");  //⇽--- 函数外部无法访问函数内部的局部变量”

这源于通过var声明的变量实际上总是在距离最近的函数内或全局词法环境中注册的,不关注块级作用域。

使用let与const定义具有块级作用域的变量

const GLOBAL_NINJA = "Yoshi";  //⇽--- 使用const定义全局变量,全局静态变量通常用大写表示

function reportActivity() {
 const functionActivity = "jumping";   //⇽--- 使用const定义函数内的局部变量

  for (let i = 1; i < 3; i++) {
     let forMessage = GLOBAL_NINJA + " " + functionActivity;   //⇽--- 使用let在for循环中定义两个变量
     console.log(forMessage === "Yoshi jumping",
         "Yoshi is jumping within the for block");
     console.log(i, "Current loop counter:" + i);   //⇽--- 在for循环中,我们毫无意外地可以访问块级变量、函数变量和全局变量
  }

  console.log(typeof i === "undefined" && typeof forMessage === "undefined",
      "Loop variables not accessible outside the loop");  //⇽--- 现在,在for循环外部无法访问for循环内的变量
}

reportActivity();
console.log(typeof functionActivity === "undefined"
   && typeof i === "undefined" && typeof forMessage === "undefined",
   "We cannot see function variables outside of a function");  //⇽--- 自然地,在函数外部无法访问任何一个函数内部的变量

与var不同的是,let和const更加直接。let和const直接在最近的词法环境中定义变量(可以是在块级作用域内、循环内、函数内或全局环境内)。我们可以使用let和const定义块级别、函数级别、全局级别的变量。

在词法环境中注册标识符

const firstRonin = "Kiyokawa";
check(firstRonin);
function check(ronin) {
 assert(ronin === "Kiyokawa", "The ronin was checked! ");
}

先执行了check函数,后声明。却没有报错,因为什么呢?
JavaScript代码的执行事实上是分两个阶段进行的。

  • 在第一阶段,没有执行代码,但是JavaScript引擎会访问并注册在当前词法环境中所声明的变量和函数。JavaScript在第一阶段完成之后
  • 开始执行第二阶段,具体如何执行取决于变量的类型(let、var、const和函数声明)以及环境类型(全局环境、函数环境或块级作用域)。

JavaScript易用性的一个典型特征,是函数的声明顺序无关紧要。

函数重载

console.log(fun); // function
var fun =3;
function fun(){

}
console.log(fun); // 3

是函数变量提升了,但是我们需要通过词法环境对整个处理过程进行更深入的理解。
JavaScript的这种行为是由标识符注册的结果直接导致的。

  • 在处理过程的第2步中,通过函数声明进行定义的函数在代码执行之前对函数进行创建,并赋值给对应的标识符;
  • 在第3步,处理变量的声明,那些在当前环境中未声明的变量,将被赋值为undefined。
  • 示例中,在第2步——注册函数声明时,由于标识符fun已经存在,并未被赋值为undefined。这就是第1个测试fun是否是函数的断言执行通过的原因。之后,执行赋值语句var fun = 3,将数字3赋值给标识符fun。执行完这个赋值语句之后,fun就不再指向函数了,而是指向数字3。

小结

  • 通过闭包可以访问创建闭包时所处环境中的全部变量。闭包为函数创建时所处的作用域中的函数和变量,创建“安全气泡”。通过这种的方式,即使创建函数时所处的作用域已经消失,但是函数仍然能够获得执行时所需的全部内容。
  • 我们可以使用闭包的这些高级功能:
通过构造函数内的变量以及构造方法来模拟对象的私有属性。
处理回调函数,简化代码。
  • JavaScript引擎通过执行上下文栈(调用栈)跟踪函数的执行。每次调用函数时,都会创建新的函数执行上下文,并推入调用栈顶端。当函数执行完成后,对应的执行上下文将从调用栈中推出。
  • JavaScript引擎通过词法环境跟踪标识符(俗称作用域)。
  • 在JavaScript中,我们可以定义全局级别、函数级别甚至块级别的变量。
  • 可以使用关键字var、let与const定义变量:
关键字var定义距离最近的函数级变量或全局变量。
关键字let与const定义只能赋值一次的变量。
  • 闭包是JavaScript作用域规则的副作用。当函数创建时所在的作用域消失后,仍然能够调用函数。”

摘录来自: [美] John Resig Bear Bibeault Josip Maras. “JavaScript忍者秘籍(第2版)。” iBooks.

版权声明
本文为[哦哈哈]所创,转载请带上原文链接,感谢
https://segmentfault.com/a/1190000037792473

Scroll to Top