日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

JS基础篇--函数声明与定义,作用域,函数声明与表达式的区别

發布時間:2023/12/15 javascript 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JS基础篇--函数声明与定义,作用域,函数声明与表达式的区别 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Scoping & Hoisting

例:

var a = 1;function foo() {if (!a) {var a = 2;}alert(a); };foo();

上面這段代碼在運行時會產生什么結果?

盡管對于有經驗的程序員來說這只是小菜一碟,不過我還是順著初學者常見的思路做一番描述:

  • 創建了全局變量 a,定義其值為 1
  • 創建了函數 foo
  • 在 foo 的函數體內,if 語句將不會執行,因為 !a 會將變量 a 轉變成布爾的假值,也就是 false
  • 跳過條件分支,alert 變量 a,最終的結果應該是輸出 1
  • 嗯,看起來無懈可擊的推理啊,但讓人驚訝的是:答案竟然是 2!為什么?

    別著急,我會解釋給你聽。首先我要告訴你這不是什么錯誤,而是 JavaScript 語言解釋器的一個(非官方的)特性,某人(Ben Cherry)把這個特性叫做:Hoisting(目前尚未有標準的翻譯,比較常見的是提升)。

    聲明與定義

    為了理解 Hoisting,我們先來看一個簡單的情況:

    var a = 1;

    你是否想過,上面這句代碼在運行的時候到底發生了什么?
    你是否知道,就這句代碼而言,“聲明變量 a” 和 “定義變量 a”這兩個說法哪一個才是正確的?

    下例叫做 “聲明變量”:

    var a;

    下例叫做 “定義變量”:

    var a = 1;

    聲明:是指你聲稱某樣東西的存在,比如一個變量或一個函數;但你沒有說明這樣東西到底是什么,僅僅是告訴解釋器這樣東西存在而已;
    定義:是指你指明了某樣東西的具體實現,比如一個變量的值是多少,一個函數的函數體是什么,確切的表達了這樣東西的意義。
    總結一下:

    var a; // 這是聲明 a = 1; // 這是定義(賦值) var a = 1; // 合二為一:聲明變量的存在并賦值給它

    重點來了:當你以為你只做了一件事情的時候(var a = 1),實際上解釋器把這件事情分解成了兩個步驟,一個是聲明(var a),另一個是定義(a = 1)。
    這和 Hoisting 有何關系?

    回到最開始的那個令人困惑的例子,我告訴你解釋器是如何分析你的代碼的:

    var a; a = 1;function foo() {var a; // 關鍵在這里if (!a) {a = 2;}alert(a); // 此時的 a 并非函數體外的那個全局變量 }

    如代碼所示,在進入函數體后解釋器聲明了新的變量 a,而無論 if 語句的條件如何,都將為新的變量 a 賦值為 2。你若不相信可以在函數體外面 alert(a),然后再執行 foo() 對比一下結果就知道了。

    Scoping(作用域)

    有人可能會問了:“為什么不是在 if 語句內聲明變量 a?”

    因為 JavaScript 沒有塊級作用域(Block Scoping),只有函數作用域(Function Scoping),所以說不是看見一對花括號{} 就代表產生了新的作用域,和 C 不一樣!

    當解析器讀到 if 語句的時候,它發現此處有一個變量聲明和賦值,于是解析器會將其聲明提升至當前作用域的頂部(這是默認行為,并且無法更改),這個行為就叫做 Hoisting。

    OK,大家都懂了,你懂了嗎……

    懂了不代表就會用了,就拿最開始的例子來說,如果我就是想要 alert(a) 出那個 1 可咋整呢?

    創建新的作用域

    alert(a) 在執行的時候,會去尋找變量 a 的位置,它從當前作用域開始向上(或者說向外)一直查找到頂層作用域為止,若是找不到就報 undefined。

    因為在 alert(a) 的同級作用域里,我們再次聲明了本地變量 a,所以它報 2;所以我們可以把本地變量 a 的聲明向下(或者說向內)移動,這樣 alert(a) 就找不到它了。

    記住:JavaScript 只有函數作用域!

    var a = 1;function foo() {if (!a) {(function() { // 這是上一篇說到過的 IIFE,它會創建一個新的函數作用域var a = 2; // 并且該作用域在 foo() 的內部,所以 alert 訪問不到}()); // 不過這個作用域可以訪問上層作用域哦,這就叫:“閉包”};alert(a); };foo();

    你或許在無數的 JavaScript 書籍和文章里讀到過:“請始終保持作用域內所有變量的聲明放置在作用域的頂部”,現在你應該明白為什么有此一說了吧?因為這樣可以避免 Hoisting 特性給你帶來的困擾(我不是很情愿這么說,因為 Hoisting 本身并沒有什么錯),也可以很明確的告訴所有閱讀代碼的人(包括你自己)在當前作用域內有哪些變量可以訪問。但是,變量聲明的提升并非 Hoisting 的全部。在 JavaScript 中,有四種方式可以讓命名進入到作用域中(按優先級):

  • 語言定義的命名:比如 this 或者 arguments,它們在所有作用域內都有效且優先級最高,所以在任何地方你都不能把變量命名為 this 之類的,這樣是沒有意義的
  • 形式參數:函數定義時聲明的形式參數會作為變量被 hoisting至該函數的作用域內。所以形式參數是本地的,不是外部的或者全局的。當然你可以在執行函數的時候把外部變量傳進來,但是傳進來之后就是本地的了
  • 函數聲明:函數體內部還可以聲明函數,不過它們也都是本地的了
  • 變量聲明:這個優先級其實還是最低的,不過它們也都是最常用的
  • 另外,還記得之前我們討論過 聲明 和 定義 的區別吧?當時我并沒有說為什么要理解這個區別,不過現在是時候了,記住:

    Hosting 只提升了命名,沒有提升定義

    這一點和我們接下來要講到的東西息息相關,請看:

    函數聲明與函數表達式的差別

    先看兩個例子:

    function test() {foo();function foo() {alert("我是會出現的啦……");} }test(); function test() {foo();var foo = function() {alert("我不會出現的哦……");} }test();

    同學,在了解了 Scoping & Hoisting 之后,你知道怎么解釋這一切了吧?

    在第一個例子里,函數 foo 是一個聲明,既然是聲明就會被提升(我特意包裹了一個外層作用域,因為全局作用域需要你的想象,不是那么直觀,但是道理是一樣的),所以在執行 foo() 之前,作用域就知道函數 foo 的存在了。這叫做函數聲明(Function Declaration),函數聲明會連通命名和函數體一起被提升至作用域頂部。

    然而在第二個例子里,被提升的僅僅是變量名 foo,至于它的定義依然停留在原處。因此在執行 foo() 之前,作用域只知道 foo 的命名,不知道它到底是什么,所以執行會報錯(通常會是:undefined is not a function)。這叫做函數表達式(Function Expression),函數表達式只有命名會被提升,定義的函數體則不會。

    尾記:Ben Cherry 的原文解釋的更加詳細,只不過是英文而已。我這篇是借花獻佛,主要是更淺顯的解釋給初學者聽,若要看更多的示例,請移步原作,謝謝。

    轉載地址:http://segmentfault.com/a/119...

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的JS基础篇--函数声明与定义,作用域,函数声明与表达式的区别的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。