1. 程式人生 > >javascript 執行環境,作用域鏈和閉包

javascript 執行環境,作用域鏈和閉包

strong str AI UNC 形參 場景 我們 引用 pos

首先看下這條語句:

(function($) {…})(jQuery);

1.原理:

function(arg){…}
這就定義了一個匿名函數,參數為arg

而調用函數時,是在函數後面寫上括號和實參的,由於操作符的優先級,函數本身也需要用括號,即:
(function(arg){…})(param)
這就相當於定義了一個參數為arg的匿名函數,並且將param作為參數來調用這個匿名函數

而(function($){…})(jQuery)則是一樣的,之所以只在形參使用$,是為了不與其他庫沖突,所以實參用jQuery
相當於

funtion output(s){…};

output(jQuery);

或者

var fn=function(s){…};fn(jQuery);

2.作用:

這種寫法的最大好處是形成閉包。在(function($) {…})(jQuery)在內部定義的函數和變量只能在此範圍內有效。

形成私有函數、私有變量的概念。

幾個概念:

1、執行環境(execution context):

每個函數都有自己的執行環境,當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行後,棧將其環境彈出,把控制權返回給之前的執行環境。

2、作用域鏈(scope chain):

函數的內部環境可以通過作用域鏈訪問到所有的外部環境,但是外部環境卻不可以訪問外部環境,這就是作用域

ES5中只有全局作用域和函數作用域,沒有塊級作用域。

(但在ES6中多了一個let,他可以保證外層塊不受內層塊的影響。即內層塊形成了一個塊級作用域,這是let的一個特點。

它不簡單,因為在許多的函數嵌套的情景下,只有對它理解深刻,才能更好的去分析。)

3、閉包 :指有權訪問另一個函數作用域中的變量的函數

創建閉包的常見方式,就是在一個函數內部創建另一個函數(通常是匿名函數)。

在一個函數a內部定義的另一個函數b,當b在a之外被執行時,就會形成閉包。同時b函數仍然可以訪問到a函數中的局部變量與函數。

弊端:閉包會攜帶包含他的函數的作用域,會比其他函數占用更多的內存。所以請慎用閉包

function fn(){
  var array=[];
  for(var i=0;i<10;i++){
     array[i]=function(){
        return i;
}
}
return array;
}
fn();//[ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ, ƒ]

我們的本意是得到這個數組中每個函數都能返回自己的索引值,可是得到的是每個函數卻都返回了10.

閉包保存的是定義它的那個函數內部的局部變量丶參數和其他內部函數,也就是說保存的是這個函數執行上下文中的整個VO,而不是一個變量。上面代碼中的函數作用域鏈中都保存著fn的活動對象,他們引用的都是一個i,當fn返回時,i的值是10,所以每個函數都引用保存i那個變量的同一個變量。我們如果想得到原先想得到的那個結果,可以加上另一個匿名函數改變他的父作用域(其實應該是創建它的作用域),將它包裹起來。

function fn(){
  var array=[];
  for(var i=0;i<10;i++){
     array[i]=function(num){
        return function(){
           return num;
};
}(i);
}
return array;
}

這個匿名函數有一個參數num,同時是返回值。在調用每個匿名函數時,傳入了變量i。由於參數是按值傳遞的,所以i就會復制給num,而這個匿名函數的內部又創建了一個訪問num的閉包,返回後能夠訪問到該匿名函數中的VO(包括參數),於是每個函數返回的都是num的一個副本,所以可以得到不同的值。

其實,說了這麽多,我們只要熟悉閉包的兩個應用場景,就能比較好的理解閉包的意義。

一.作為函數的返回值.。作為函數返回值被執行後仍然可以訪問定義它的那個函數環境的VO。

function f(){
   var a=1;
   return function(){
      console.log(a);
}  
}

var g=f();
g();//1;

二.作為一個函數的參數。作為函數返回值被當做另一個函數的參數傳入時,仍然是訪問定義它的那個函數環境的VO

function f(){
   var a=1;
   return function(){
      console.log(a);
}  
}
var g=f();
g();//1;


function F(fn){
var a=2;
fn();
}
F(g);//1

上面兩個小例子也正好說明了閉包可以訪問定義它的那個函數作用域下的內部變量和內部函數。其實是整個VO,所以還包含參數。

關於this對象

我們知道,this對象是在運行時基於函數的執行環境綁定的,全局函數中,this=window;

當某個函數被作為某個對象的方法調用時,this=對象

匿名函數的執行環境具有全局性,因此其this對象通常指向window

所以在閉包中使用this對象或arguments對象,必須將該對象的引用保存到閉包能夠訪問到的另一個變量中。

javascript 執行環境,作用域鏈和閉包