Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

阅读《你不知道的JavaScript》笔记 #5

Open
mamba-1024 opened this issue Aug 31, 2018 · 4 comments
Open

阅读《你不知道的JavaScript》笔记 #5

mamba-1024 opened this issue Aug 31, 2018 · 4 comments

Comments

@mamba-1024
Copy link
Owner

mamba-1024 commented Aug 31, 2018

理解JavaScript的环境

  • 引擎
    从头到尾负责整个JavaScript程序的编译及执行过程
  • 编译器
    引擎的好朋友之一,负责语法分析及代码生成等脏活累活
  • 作用域
    引擎的另一位好朋友,负责收集并维护由所有声明的变量组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
@mamba-1024
Copy link
Owner Author

mamba-1024 commented Aug 31, 2018

词法化 (也叫单词化)

大部分标准语言编译器的第一个工作阶段叫做 词法化
词法化的过程会对源代码中的字符进行检查,如果是有状态的解析过程,还会赋予单词语义。

了解了词法化后,下面我们就该说说正题了

词法作用域

词法作用域就是定义在词法阶段的作用域。词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的。
词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。
所以词法作用域又叫静态作用域
因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。

var value = 1;
function foo() {
    console.log(value);
}
function bar() {
    var value = 2;
    foo();
}
bar(); // 1

@mamba-1024
Copy link
Owner Author

mamba-1024 commented Sep 4, 2018

JavaScript闭包

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用 域之外执行。
直接上一个示例代码:

function foo() { 
  var a = 2;
  function bar() {
    console.log( a );
  }
  return bar; 
}
var baz = foo();  // 拿到该函数所在词法作用域中的嵌套函数(可以访问外层函数中的变量)
baz(); // 2 —— 这就是闭包的效果

我们将 bar 所引用的函数对象本身当作返回值,然后赋值给变量 baz 并调用 baz(),实际上只是通过不同的标识符引用调用了内部的函数 bar()。
通常foo() 执行后, foo() 的整个内部作用域都被销毁,引擎的垃圾回收机制会回收不再使用的内存空间。
而闭包的神奇之处就在于阻止这个事情的发生。实际上内部作用域依然存在,没有被回收,因为bar()函数还在使用。
拜 bar() 所声明的位置所赐,它拥有涵盖 foo() 内部作用域的闭包,使得该作用域能够一 直存活,以供 bar() 在之后任何时间进行引用。
bar() 依然持有对该作用域的引用,而这个引用就叫作闭包。

function foo() { 
  var a = 2;
  function baz() {
    console.log( a ); // 2
  }
  bar( baz );  // 1.把内部函数baz传递给bar
}
function bar(fn) {
  fn(); //  2.当调用的时候还可以访问a,这就是闭包!
}
var fn;
function foo() {
  var a = 2;
  function baz() {
    console.log( a );
  }
  fn = baz; // 将 baz 分配给全局变量
}
function bar() {
  fn(); //  这就是闭包!
}
foo();
bar(); // 2

无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包。

@mamba-1024
Copy link
Owner Author

mamba-1024 commented Sep 4, 2018

常见的闭包

  • 定时器函数setTimeout
function wait(message) {
    setTimeout( function timer() {
         console.log( message );
    }, 1000 ); 
}
wait( "Hello, closure!" );
  • 循环与闭包
for (var i=1; i<=5; i++) { 
  setTimeout( function timer() {
      console.log( i );
   }, i*1000 );
}
console.log => 5次6

在上面代码中,i是定义在全局作用域中的一个变量,定时器的回调函数会在所有的循环执行完毕后才执行的,此时内存中的变量 i 的值等于6,
稍加改进,我们让定时器函数立即执行

for (var i=1; i<=5; i++) { 
  (function(j) {
    setTimeout( function timer() { 
      console.log( j );
      }, j*1000 );
   })( i );
}

@mamba-1024
Copy link
Owner Author

mamba-1024 commented Sep 4, 2018

利用闭包实现的模块化

function foo() {
  var something = "cool";
  var another = [1, 2, 3];
  function doSomething() {
    console.log( something );
  }
  function doAnother() {
    console.log( another.join( " ! " ) );
  } 
  return {
    doSomething: doSomething,
    doAnother: doAnother
  };
}
var coolModule = foo(); 
coolModule.doSomething(); // cool
coolModule.doAnother(); // 1 ! 2 ! 3

利用闭包,我们可以把函数内部的方法暴露出来,可以在其他地方引用。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant