天天看點

ES6 let和const指令及知識補充ES6 let和const指令

let和const指令及引發的知識補充

  • ES6 let和const指令
    • 1、let指令
      • 基本用法
      • 不存在變量提升
      • 暫時性死區
    • 2、塊級作用域
      • 沒有塊級作用域帶來的不合理場景
      • ES6塊級作用域
    • 3、const
      • 基本用法
      • 本質
      • ES6 聲明變量的六種方法
    • 4、頂層對象的屬性
    • 5、globalThis 對象
    • 6、知識補充
      • 閉包
        • 什麼是閉包?
        • 閉包的特性
        • 閉包的作用
        • 閉包的坑
      • 立即執行函數
        • 立即執行函數的寫法
        • 立即執行函數的作用:
        • 與立即執行函數相關的面試題:
        • 立即執行函數使用的場景
        • 立即執行函數的參數
        • 立即執行函數的傳回值
        • 總結立即執行函數有哪些作用?

ES6 let和const指令

1、let指令

基本用法

ES6 新增了

let

指令,用來聲明變量。它的用法類似于var,但是所聲明的變量,隻在let指令所在的代碼塊内有效。
{
	let a = 10;
	var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
           
for(let i = 0;i < 10; i++){
//...
}
console.log(i);// ReferenceError: i is not defined
           
下面的代碼如果使用var,最後輸出的是10。
var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10
           
上面代碼中,變量

i

var

指令聲明的,在全局範圍内都有效,是以全局隻有一個變量

i

。每一次循環,變量

i

的值都會發生改變,而循環内被賦給數組

a

的函數内部的

console.log(i)

,裡面的i指向的就是全局的

i

。也就是說,所有數組

a

的成員裡面的

i

,指向的都是同一個

i

,導緻運作時輸出的是最後一輪的

i

的值,也就是10。

上面的代碼塊涉及到閉包,見下方閉包。

如果使用let,聲明的變量僅在塊級作用域内有效,最後輸出的是 6。
var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6
           
上面代碼中,變量

i

let

聲明的,目前的i隻在本輪循環有效,是以每一次循環的i其實都是一個新的變量,是以最後輸出的是6。你可能會問,如果每一輪循環的變量i都是重新聲明的,那它怎麼知道上一輪循環的值,進而計算出本輪循環的值?這是因為

JavaScript 引擎内部會記住上一輪循環的值,初始化本輪的變量i時,就在上一輪循環的基礎上進行計算。

2019/09/10了解: 每個i的作用域都隻在目前循環内生效,比如

i=0

隻在

a[i] = function(){
	console.log(i);
	};(i=0)
           

這個範圍内生效,當

i=1

時又是一個新的作用域,不能作用于i=0這個作用域内,是以

a[0]()

的值為0,同理可得

a[6]()

的值為6.

另外,for循環還有一個特别之處,就是設定循環變量的那部分是一個父作用域,而循環體内部是一個單獨的子作用域。
for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc
           
上面代碼正确運作,輸出了 3 次

abc

。這表明函數内部的變量

i

與循環變量

i

不在同一個作用域,有各自單獨的作用域。

不存在變量提升

var

指令會發生變量提升現象,即變量可以在聲明之前使用,值為

undefined

。這種現象多多少少是有些奇怪的,按照一般的邏輯,變量應該在聲明語句之後才可以使用。

為了糾正這種現象,let指令改變了文法行為,它所聲明的變量一定要在聲明後使用,否則報錯。

// var 的情況
console.log(foo); // 輸出undefined
var foo = 2;

// let 的情況
console.log(bar); // 報錯ReferenceError
let bar = 2;
           
上面代碼中,變量

foo

var

指令聲明,會發生變量提升,即腳本開始運作時,變量

foo

已經存在了,但是沒有值,是以會輸出

undefined

。變量

bar

let

指令聲明,不會發生變量提升。這表示在聲明它之前,變量

bar

是不存在的,這時如果用到它,就會抛出一個錯誤。

簡單了解變量提升就是在變量定義之前使用變量,浏覽器不報錯而顯示

undefined

。let不存在變量提升,隻要在定義前使用變量就會報錯。

暫時性死區

隻要塊級作用域記憶體在

let

指令,它所聲明的變量就“綁定”(binding)這個區域,不再受外部的影響。
var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}
           
上面代碼中,存在全局變量

tmp

,但是塊級作用域内

let

又聲明了一個局部變量

tmp

,導緻後者綁定這個塊級作用域,是以在

let

聲明變量前,對

tmp

指派會報錯。

ES6明确規定,如果區塊中存在

let

const

指令,這個區塊對這些指令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。

總之,在代碼塊内,使用

let

指令聲明變量之前,該變量都是不可用的。這在文法上,稱為“暫時性死區”(temporal dead zone,簡稱TDZ)。

還有一些暫時性死區的細節說明可見阮一峰ECMAScript6入門:let暫時性死區說明

2、塊級作用域

沒有塊級作用域帶來的不合理場景

ES5 隻有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景。

第一種場景,内層變量可能會覆寫外層變量。

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined
           
上面代碼的原意是,

if

代碼塊的外部使用外層的

tmp

變量,内部使用内層的

tmp

變量。但是,函數f執行後,輸出結果為

undefined

,原因在于變量提升,導緻内層的

tmp

變量覆寫了外層的

tmp

變量。

第二種場景,用來計數的循環變量洩露為全局變量。

var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5
           
上面代碼中,變量

i

隻用來控制循環,但是循環結束後,它并沒有消失,洩露成了全局變量。

ES6塊級作用域

let

實際上為 JavaScript 新增了塊級作用域。
function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}
           
上面的函數有兩個代碼塊,都聲明了變量

n

,運作後輸出 5。這表示外層代碼塊不受内層代碼塊的影響。如果兩次都使用

var

定義變量

n

,最後輸出的值才是 10。

ES6 允許塊級作用域的任意嵌套。

{{{{
  {let insane = 'Hello World'}
  console.log(insane); // 報錯
}}}};
           

上面代碼使用了一個五層的塊級作用域,每一層都是一個單獨的作用域。第四層作用域無法讀取第五層作用域的内部變量。

内層作用域可以定義外層作用域的同名變量。

{{{{
  let insane = 'Hello World';
  {let insane = 'Hello World'}
}}}};
           
塊級作用域的出現,實際上使得獲得廣泛應用的匿名立即執行函數表達式(匿名 IIFE)不再必要了。
// IIFE 寫法
(function () {
  var tmp = ...;
  ...
}());

// 塊級作用域寫法
{
  let tmp = ...;
  ...
}
           

什麼是立即執行函數

3、const

基本用法

const

聲明一個隻讀的常量。一旦聲明,常量的值就不能改變。
const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.
           
上面代碼表明改變常量的值會報錯。

const

聲明的變量不得改變值,這意味着,

const

一旦聲明變量,就必須立即初始化,不能留到以後指派。
const foo;
// SyntaxError: Missing initializer in const declaration
           
上面代碼表示,對于const來說,隻聲明不指派,就會報錯。

const

的作用域與

let

指令相同:隻在聲明所在的塊級作用域内有效。
if (true) {
  const MAX = 5;
}

MAX // Uncaught ReferenceError: MAX is not defined
           

const

指令聲明的常量也是不提升,同樣存在暫時性死區,隻能在聲明的位置後面使用。
if (true) {
  console.log(MAX); // ReferenceError
  const MAX = 5;
}
           

上面代碼在常量MAX聲明之前就調用,結果報錯。

const聲明的常量,也與let一樣不可重複聲明。

var message = "Hello!";
let age = 25;

// 以下兩行都會報錯
const message = "Goodbye!";
const age = 30;
           

本質

const

實際上保證的,并不是變量的值不得改動,而是變量指向的那個記憶體位址所儲存的資料不得改動。對于簡單類型的資料(數值、字元串、布爾值),值就儲存在變量指向的那個記憶體位址,是以等同于常量。但對于複合類型的資料(主要是對象和數組),變量指向的記憶體位址,儲存的隻是一個指向實際資料的指針,

const

隻能保證這個指針是固定的(即總是指向另一個固定的位址),至于它指向的資料結構是不是可變的,就完全不能控制了。是以,将一個對象聲明為常量必須非常小心。
const foo = {};

// 為 foo 添加一個屬性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一個對象,就會報錯
foo = {}; // TypeError: "foo" is read-only
           
上面代碼中,常量

foo

儲存的是一個位址,這個位址指向一個對象。不可變的隻是這個位址,即不能把

foo

指向另一個位址,但對象本身是可變的,是以依然可以為其添加新屬性。

下面是另一個例子。

const a = [];
a.push('Hello'); // 可執行
a.length = 0;    // 可執行
a = ['Dave'];    // 報錯
           
上面代碼中,常量a是一個數組,這個數組本身是可寫的,但是如果将另一個數組指派給

a

,就會報錯。

如果真的想将對象當機,應該使用

Object.freeze

方法。
const foo = Object.freeze({});

// 正常模式時,下面一行不起作用;
// 嚴格模式時,該行會報錯
foo.prop = 123;
           
上面代碼中,常量

foo

指向一個當機的對象,是以添加新屬性不起作用,嚴格模式時還會報錯。

除了将對象本身當機,對象的屬性也應該當機。下面是一個将對象徹底當機的函數。

var constantize = (obj) => {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, i) => {
    if ( typeof obj[key] === 'object' ) {
      constantize( obj[key] );
    }
  });
};
           

ES6 聲明變量的六種方法

ES5 隻有兩種聲明變量的方法:

var

指令和

function

指令。ES6 除了添加

let

const

指令,後面章節還會提到,另外兩種聲明變量的方法:

import

指令和

class

指令。是以,ES6 一共有 6 種聲明變量的方法。

4、頂層對象的屬性

頂層對象,在浏覽器環境指的是window對象,在 Node 指的是global對象。ES5 之中,頂層對象的屬性與全局變量是等價的。
window.a = 1;
a // 1

a = 2;
window.a // 2
           
上面代碼中,頂層對象的屬性指派與全局變量的指派,是同一件事。
頂層對象的屬性與全局變量挂鈎,被認為是 JavaScript語言最大的設計敗筆之一。這樣的設計帶來了幾個很大的問題,首先是沒法在編譯時就報出變量未聲明的錯誤,隻有運作時才能知道(因為全局變量可能是頂層對象的屬性創造的,而屬性的創造是動态的);其次,程式員很容易不知不覺地就建立了全局變量(比如打字出錯);最後,頂層對象的屬性是到處可以讀寫的,這非常不利于子產品化程式設計。另一方面,window對象有實體含義,指的是浏覽器的視窗對象,頂層對象是一個有實體含義的對象,也是不合适的。
ES6為了改變這一點,一方面規定,為了保持相容性,

var

指令和

function

指令聲明的全局變量,依舊是頂層對象的屬性;另一方面規定,

let

指令、

const

指令、

class

指令聲明的全局變量,不屬于頂層對象的屬性。也就是說,從

ES6 開始,全局變量将逐漸與頂層對象的屬性脫鈎。

var a = 1;
// 如果在 Node 的 REPL 環境,可以寫成 global.a
// 或者采用通用方法,寫成 this.a
window.a // 1

let b = 1;
window.b // undefined
           
上面代碼中,全局變量a由var指令聲明,是以它是頂層對象的屬性;全局變量b由let指令聲明,是以它不是頂層對象的屬性,傳回undefined。

5、globalThis 對象

JavaScript語言存在一個頂層對象,它提供全局環境(即全局作用域),所有代碼都是在這個環境中運作。但是,頂層對象在各種實作裡面是不統一的。
  • 浏覽器裡面,頂層對象是window,但 Node 和 Web Worker 沒有window。
  • 浏覽器和 Web Worker裡面,self也指向頂層對象,但是 Node 沒有self。
  • Node 裡面,頂層對象是global,但其他環境都不支援。
同一段代碼為了能夠在各種環境,都能取到頂層對象,現在一般是使用

this

變量,但是有局限性。
  • 全局環境中,

    this

    會傳回頂層對象。但是,Node 子產品和 ES6 子產品中,

    this

    傳回的是目前子產品。
  • 函數裡面的

    this

    ,如果函數不是作為對象的方法運作,而是單純作為函數運作,

    this

    會指向頂層對象。但是,嚴格模式下,這時

    this

    會傳回

    undefined

  • 不管是嚴格模式,還是普通模式,

    new Function('return this')()

    ,總是會傳回全局對象。但是,如果浏覽器用了 CSP(Content Security Policy,内容安全政策),那麼

    eval

    new Function

    這些方法都可能無法使用。
綜上所述,很難找到一種方法,可以在所有情況下,都取到頂層對象。下面是兩種勉強可以使用的方法。
// 方法一
(typeof window !== 'undefined'
   ? window
   : (typeof process === 'object' &&
      typeof require === 'function' &&
      typeof global === 'object')
     ? global
     : this);

// 方法二
var getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};
           
現在有一個提案,在語言标準的層面,引入

globalThis

作為頂層對象。也就是說,任何環境下,

globalThis

都是存在的,都可以從它拿到頂層對象,指向全局環境下的this。

墊片庫

global-this

模拟了這個提案,可以在所有環境拿到

globalThis

6、知識補充

閉包

什麼是閉包?

閉包的官方概念:閉包是指有權通路另一個函數作用域中的變量的函數。

菜鳥教程對閉包的了解:閉包是一種保護私有變量的機制,在函數執行時形成私有的作用域,保護裡面的私有變量不受外界幹擾。直覺的說就是形成一個不銷毀的棧環境。

百度經驗簡單明了的閉包了解:閉包就是能夠讀取其他函數内部變量的函數。由于在javascript中,隻有函數内部的子函數才能讀取局部變量,是以閉包可以了解成定義在一個函數内部的函數。在本質上,閉包是将函數内部和函數外部連接配接起來的橋梁。

我的了解:閉包在javaScript中的含義是能通路另一個作用域中變量的函數,表現形式是所有函數内部定義的函數,因為隻要是函數内部定義的函數,就可以通路他父作用域内的變量,但是函數内部定義的函數雖可以通路父作用域内的值,但是不會将其傳回,是以現在使用閉包通常是在函數内部定義函數,子函數将父函數的某個值傳回,使得在全局範圍或者祖範圍内可以得到原本無法擷取的父函數的值。

舉個例子:

function nv(){
	var n=10;
	function subNv(){return n;}
	return subNv;
}
var subNv=nv();
subNv();//10
           

當有個函數

nv

的内部有個局部變量

n

,這個變量無法在外面直接得到時,我們通過在這個函數裡面在定義一個函數

subNv

,并且用這個定義的函數去得到這個局部變量,最後我們在傳回這個子函數

subNv

,就能通過傳回的這個函數得到局部變量n。這裡的

subNv

就是閉包。

事實上,每個函數都可以被了解成是一個閉包,因為每個函數都在其所在域中維護了某種私有聯系。但在大多數時候,該作用域在函數體執行完之後就自行銷毀了,除非遇到閉包,導緻作用域被保持。(出自《Javascript面向對象程式設計指南》,暫時未了解)

閉包的特性

csdn中的文章介紹了閉包的三個特性:

①函數嵌套函數 ②函數内部可以引用函數外部的參數和變量 ③參數和變量不會被垃圾回收機制回收

閉包的作用

閉包最常見的作用是在父函數中定義子函數,子函數傳回父函數的某個變量,使其在父函數外也可以通路父函數的變量擷取值。閉包最常見的使用時getter和setter。如下代碼段所示:

var getValue,setValue;
	(function(){
		var secret = 0;
		getValue = function(){
			return secret;
		};
		setValue = function(v){
			secret = v;
		};
	}());
           

在代碼段中定義了一個立即執行函數,在其中定義了全局函數getValue()和setValue(),這兩個函數有權通路父作用域也就是立即執行函數中的secret,是以這兩個函數是閉包。getValue()傳回secret的值,setValue設定secret的值,以此來確定局部變量secret的不可直接通路性。

閉包的坑

循環中的閉包是閉包中最常見的坑,參考以下代碼:

function F(){
		var arr = [],i;
		for(i = 0;i < 3; i++){
			arr[i] = function(){
				return i;
			};
		}
		return arr;
	}
	var arr = F();
	arr[0]();//3
	arr[1]();//3
	arr[2]();//3
           

為什麼輸出都是3呢?因為

arr[i] = function(){return i;};

嵌套在函數F()内,是以循環的3個

arr[i] = function(){return i;};

是三個閉包,他們的

i

取的是父元件中的

i

,又由于建立函數

var arr = F();

時,子函數不會被立即執行,是以這三個閉包并不會記錄當時

i

的值,隻會将它們指向一個共同的局部變量

i

。當通過

arr[0]()

取值時,這時的

i

在F()函數中已經是固定值3了,是以三個閉包傳回的值都是3。

怎麼避免這種情況的發生呢?在ES6中的let出來之前,使用立即執行函數,換一種閉包形式,代碼如下:

function F(){
		var arr = [],i;
		for(i = 0;i < 3; i++){
			arr[i] = (function(x){
				return function(){
					return x;
				};
			};)(i)
		}
		return arr;
	}
	var arr = F();
	arr[0]();//0
	arr[1]();//1
	arr[2]();//2	
           

使用立即執行函數,給立即執行函數傳入目前的

i

,這樣全局變量

i

就被指派給了局部變量

x

,

x

被保護在立即執行函數内,也隻作用于立即執行函數内,是以每次調用

arr[i]()

就可以得到不同的值。

在let出來之後,隻需将for循環内的

i

使用let聲明就可以:

function F(){
		var arr = [];
		for(let i = 0;i < 3; i++){
			arr[i] = function(){
				return i;
			};
		}
		return arr;
	}
	var arr = F();
	arr[0]();//3
	arr[1]();//3
	arr[2]();//3	
           

因為let隻作用于目前作用域内,當

i=0

時,i的作用域為for循環中的判斷條件,

arr[i] = function(){return i;};

是一個閉包,取到了父函數for中

i

的值。這個

i

隻作用于目前作用域,等價于

{
	let i=0;
	arr[i] = function(){
		return i;
	} 
}
{
	let i=1;
	arr[i] = function(){
		return i;
	} 
}
{
	let i=2;
	arr[i] = function(){
		return i;
	} 
}
           

立即執行函數

立即執行函數的寫法

有時,我們定義函數之後,立即調用該函數,這時不能在函數的定義後面直接加圓括号,這會産生文法錯誤。産生文法錯誤的原因是,

function

這個關鍵字,既可以當做語句,也可以當做表達式,比如下邊:

//語句
function fn() {};

//表達式
var fn = function (){};
           

為了避免解析上的歧義,JS引擎規定,如果

function

出現在行首,一律解析成語句。是以JS引擎看到行首是

function

關鍵字以後,認為這一段都是函數定義,不應該以原括号結尾,是以就報錯了。

解決方法就是不要讓

function

出現在行首,讓JS引擎将其了解為一個表達式,最簡單的處理就是将其放在一個圓括号裡,比如下邊:

(function(){
//code
}())

(function (){
//code
})()
           

上邊的兩種寫法,都是以圓括号開頭,引擎會意味後面跟的是表達式,而不是一個函數定義語句,是以就避免了錯誤,這就叫做"立即調用的函數表達式"。

立即執行函數,還有一些其他的寫法(加一些小東西,不讓解析成語句就可以),比如下邊:

(function () {alert("我是匿名函數")}())   //用括号把整個表達式包起來
(function () {alert("我是匿名函數")})()  //用括号把函數包起來
!function () {alert("我是匿名函數")}()  //求反,我們不在意值是多少,隻想通過文法檢查
+function () {alert("我是匿名函數")}() 
-function () {alert("我是匿名函數")}() 
~function () {alert("我是匿名函數")}() 
void function () {alert("我是匿名函數")}() 
new function () {alert("我是匿名函數")}() 
           

立即執行函數的作用:

  1. 不必為函數命名,避免了污染全局變量
  2. 立即執行函數内部形成了一個單獨的作用域,可以封裝一些外部無法讀取的私有變量
  3. 封裝變量

總而言之:立即執行函數會形成一個單獨的作用域,我們可以封裝一些臨時變量或者局部變量,避免污染全局變量

與立即執行函數相關的面試題:

<body>
    <ul id="list">
        <li>公司簡介</li>
        <li>聯系我們</li>
        <li>營銷網絡</li>
    </ul>
    <script>
       var list = document.getElementById("list");
      var li = list.children;
      for(var i = 0 ;i<li.length;i++){
        li[i].onclick=function(){
          alert(i);  // 結果總是3.而不是0,1,2
        }
      }
     </script>  
</body>
           

為什麼

alert

總是3? 通過

for

循環給每個元素綁定

alert

事件,循環結束,

i

值為3.

onclick

指向一個匿名函數,當事件發生,顯示彈框,

i

是最後的

i

,而不是綁定時的值。原因是在函數内定義函數,會形成閉包,内部函數可以通路包含函數内的變量(這裡是

i

),但是内部函數通路的外部函數執行結束後的

i

值,而不是綁定那一刻的值,因為那一刻,事件還未發生。

那麼怎麼解決這個問題呢,可以用立即執行函數,給每個

li

建立一個獨立的作用域,在立即執行函數執行的時候,

i

的值從0到2,對應三個立即執行函數,這3個立即執行函數裡邊的

j

分别是0,1,2是以就能正常輸出了,看下邊例子:

<body>
    <ul id="list">
        <li>公司簡介</li>
        <li>聯系我們</li>
        <li>營銷網絡</li>
    </ul>
    <script>
       var list = document.getElementById("list");
      var li = list.children;
      for(var i = 0 ;i<li.length;i++){
         (function(j){
            li[j].onclick = function(){
              alert(j);
          })(i); 把實參i指派給形參j
        }
      }
     </script>  
</body>
           

也可以使用ES6的塊級作用域解決整個問題

<body>
    <ul id="list">
        <li>公司簡介</li>
        <li>聯系我們</li>
        <li>營銷網絡</li>
    </ul>
    <script>
       var list = document.getElementById("list");
       var li = list.children;
       for(let i = 0 ;i<li.length;i++){
         li[i].onclick=function(){
           alert(i);  // 結果是0,1,2
         }
       }
     </script>  
</body>
           

立即執行函數使用的場景

  1. 你的代碼在頁面加載完成之後,不得不執行一些設定工作,比如時間處理器,建立對象等等。
  2. 所有的這些工作隻需要執行一次,比如隻需要顯示一個時間。
  3. 但是這些代碼也需要一些臨時的變量,但是初始化過程結束之後,就再也不會被用到,如果将這些變量作為全局變量,不是一個好的注意,我們可以用立即執行函數——去将我們所有的代碼包裹在它的局部作用域中,不會讓任何變量洩露成全局變量,看如下代碼:
    ES6 let和const指令及知識補充ES6 let和const指令
    比如上面的代碼,如果沒有被包裹在立即執行函數中,那麼臨時變量todaydom,days,today,year,month,date,day,msg都将成為全局變量(初始化代碼遺留的産物)。用立即執行函數之後,這些變量都不會在全局變量中存在,以後也不會其他地方使用,有效的避免了污染全局變量。

立即執行函數的參數

(function(j){
//代碼中可以使用j
})(i)
           

如果立即執行函數中需要全局變量,全局變量會被作為一個參數傳遞給立即執行函數(上例中的i就是一個全局變量,i代表的是實參,j是i在立即執行函數中的形參)。

立即執行函數的傳回值

像其他函數一樣,立即執行函數也可以有傳回值。除了可以傳回基本類型值以外,立即執行函數也能傳回任何類型的值,比如對象,函數。

ES6 let和const指令及知識補充ES6 let和const指令

上例中立即執行函數的傳回值被指派給了一個變量result,這個函數簡單的傳回了res的值,這個值事先被計算并被存儲在立即執行函數的閉包中。

在五中,如果在以後的代碼中我需要msg這個值,我也可以傳回一個包含msg的對象,友善在以後代碼中使用(這樣五中的一些臨時變量也沒有暴露在外面)。

總結立即執行函數有哪些作用?

1、改變變量的作用域(建立一個獨立的作用域)

<body>
    <ul id="list">
        <li>公司簡介</li>
        <li>聯系我們</li>
        <li>營銷網絡</li>
    </ul>
    <script>
       var list = document.getElementById("list");
      var li = list.children;
      for(var i = 0 ;i<li.length;i++){
         (function(j){
            li[j].onclick = function(){
              alert(j);
          })(i); 把實參i指派給形參j
        }
      }
     </script>  
</body>
           

改變變量i的作用域,把全局變量i以參數的形式傳遞到立即執行函數中,在立即執行函數中定義變量i的形參變量j,變量j就是在立即執行函數的作用域中。(給每個li建立了一個作用域塊,點選的時候尋找自由變量j,在立即執行塊中找到)

2、封裝臨時變量

ES6 let和const指令及知識補充ES6 let和const指令

在上面的代碼中,可以封裝臨時變量,避免全局變量的污染。也可以傳回一個在全局中需要的變量(用return)。