乐趣区

关于闭包:温故而知新篇之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.

退出移动版