JavaScript之自我總結篇

来源:http://www.cnblogs.com/giggle/archive/2016/03/31/5338551.html
-Advertisement-
Play Games

最近在看湯姆大叔的"深入理解JavaScript系列",寫得真的不錯,對於我而言特別是12章到19章,因為大叔研究的點,就主要是從底層來研究JavaScript為什麼會出現鐘種特有的語言現象,所以學習了大叔的文章後,自己對JavaScript的認知也更明白了,以前好多地方是知其然而不知其所以然,你要 ...


最近在看湯姆大叔的"深入理解JavaScript系列",寫得真的不錯,對於我而言特別是12章到19章,因為大叔研究的點,就主要是從底層來研究JavaScript為什麼會出現鐘種特有的語言現象,所以學習了大叔的文章後,自己對JavaScript的認知也更明白了,以前好多地方是知其然而不知其所以然,你要問我JavaScript為什麼會出現這些現象,我也只能說這是它語言本身的特性嘛。

以下是我初次看了大叔Javascript系列(12到19章)一時不能理解的點,但經過細細品味後才豁然開朗。

所以,自我總結如下:

註:總結中摻雜了個人的觀點以及理解層度,所以有什麼錯誤的地方,還請不吝指教。 

一、深入理解JavaScript系列(12)之變數對象: 

在大叔這章中,大叔提到了一個概念就是‘變數對象(varibale object)’

‘變數對象’,是與執行上下文有關的,因為JavaScript在執行表達式時,總得知道相應的變數存儲在哪吧?不然怎麼獲取或改變對應的變數值呢?

所以引入了一個‘變數對象’的概念。

都說了是對象嘛,就是以鍵值對的方式,存儲到變數對象中咯。

1、 在進入執行上下文時,‘變數對象’VO

(1)會將函數的所有形參(如果我們是在函數執行上下文),以形參名和其對應的值作為變數對象的屬性,如果形參沒有對應的值,就是undefined咯。

(2)會將所有函數聲明,以函數名和對應的函數對象作為變數對象的屬性。如果,變數對象中已經存在了相同名稱的屬性,就完全替代。

(3)會將所有變數聲明,以變數名和其對應值(undefined)作為變數對象的屬性。如果變數名稱與上述(1)、(2)中的形參名或函數名撞車了,則變數聲明不會去影響        存在的這類屬性。

且,在上面提到,變數對象與執行上下文有關,那麼它們究竟什麼關係呢?

分兩種情況:

一種情況就是在進入任何執行上下文之前就創建的對象,此乃全局對象(Global object)global;

另一種就是,我們都知道在JavaScript中作用域是以函數function為基準的,所以函數的變數對象其實就是執行上下文對象;

具體見以下兩幅圖:

 

                   圖一

 

                   圖二

註:函數中的變數對象是不能訪問的。

那為什麼全局中的變數對象能訪問呢?

因為可以通過this或者window,因為window是全局變數global的一個屬性,且引用了global。這也就是為什麼在全局變數中訪問標識符時,用this或者直接訪問標識符時,會比用window快的原因。

2、 在執行代碼時

變數對象VO,已經在進入上下文時,做了預處理。So,接下來就是根據具體的代碼改變對應的值了。

二、深入理解JavaScript系列(13)之This:

說到this,簡單點嘛就是由調用者決定的,誰調用的就是誰。

如下:

function fn(){
    console.log(this);
};
var foo = {
    bar: fn
};
//輸出的this指向foo
foo.bar();

但JavaScript底層到底是個怎樣的處理機制呢?

在大叔的這章中,引入了一個‘引用類型(Reference type)’的概念。

引用類型(Reference type),使用偽代碼可以將其表示為擁有兩個屬性的對象:

----base,即擁有屬性的那個對象;

----propertyName,即屬性名,從而可以獲取相應的值。

如下:

且,要返回引用類型的值,只存在兩種情況:

1、  處理一個標識符時;

2、  處理屬性訪問器( . 或 [ ] )時.

註意:只有兩種情況哦,要從引用類型中得到一個屬性值嘛,還需要一步,就是底層調用GetValue方法,從‘引用類型’對象中得到對應屬性值的值。

咦,講了這麼多和this有什麼相關?

直接摘至大叔:

 

好了,如果理解了上面的流程,下麵的幾個例子中this也就OK啦。

'use strict';
var foo = {
    bar: function(){
        console.log(this);
    }
};
foo.bar();//this為foo
(foo.bar)();//this為foo (foo.bar = foo.bar)();//this為undefined (false || foo.bar)();//this為undefined (foo.bar, foo.bar)();//this為undefined
三、深入理解JavaScript系列(14)之作用域鏈:

作用域鏈是上下文所有‘變數對象(varibale object)’的列表,提到‘變數對象’,so此鏈用來變數查詢。且函數上下文的作用域鏈在函數調用時創建,包含活動對象和這個函數內部的[[scope]]屬性,而這個[[scope]]屬性是所有父變數對象的層級鏈,在函數創建時存在其中,且不會改變,即,函數一旦創建了,無論你調或不調用,[[scope]]已存儲在函數對象中了。

當函數被調用時,進入執行上下文activeExecutionContext,其中包含Scope屬性,即作用域鏈。

作用域鏈(Scope)在上下文中具體見下:

activeExecutionContext = {
    VO:{...},//or AO
    this: thisValue,
    Scope/*Scope chain*/: [
        AO + [[Scope]]
    ]
}

Scope又包含變數對象和函數的 [[scope]]屬性。

當我們解析一個標識符(標識符是變數名,函數名,函數參數名和全局對象中未識別的屬性名)時,解析過程將沿著這條鏈(Scope)去查找,查到就返回它的值,如果在Scope這條鏈中,沒有找到相應的值,就會沿著全局對象這條原型鏈去查找,因為函數活動對象沒有原型。

例子如下:

'use strict';
function foo(){
    function bar(){
        console.log(x);
    };
bar(); }; Object.prototype.x
= 10; this.__proto__.x = 200; foo();//x為200

另外:通過函構造函數創建的函數的[[scope]]屬性總是唯一的全局對象.

四、深入理解Javascript系列(15)之函數:
函數聲明與函數表達式的區別:

函數聲明,在‘變數對象’章中已經知道,在進入執行上下文時,會將所有的函數聲明以鍵值對的形式存儲到變數對象VO中。

但,

函數表達式不會添加到變數對象VO中,且在代碼執行階段創建,用完後立刻銷毀。命名函數表達式也一樣哦,因為它是表達式嘛。

例如:

<!DOCTYPE html>
    <head>
        <title>JavaScript</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            'use strict';
            (function foo(x){
                console.log(x);
            }(1));
            foo(2);//此時會報錯:foo is not defined
        </script>
    </body>
</html>

執行以上代碼,chrome效果圖如下:

和函數表達式不一樣麽,用完即刻銷毀。那命名函數表達式有什麼用呢?

命名函數表達式,可以通過名稱遞歸自己嘛。

但,剛纔不是說命名函數表達式和函數表達式都不會添加到變數對象VO中嗎?那它怎麼通過名稱自己調用自己的?

當解釋器在代碼執行階段,遇到命名的函數表達式,解釋器將創建一個輔助的特定對象,並添加到當前作用域鏈的最頂端。然後創建函數表達式,然後將命名函數表達式的名字添加到這個輔助的特定對象中,且值為該函數引用,當命名函數表達式在其自身調用時,它就在這個特定的對象中找到自己。最後,當命名函數表達式執行完成後,從父作用域鏈中移除那個輔助的特定對象。

具體演算法見下:

五、深入理解JavaScript系列(16)之閉包:

因為作用域鏈,從而使得所有的函數都是閉包。

但,有一類函數比較特殊,那就是通過Function構造器創建的函數,因為其[[Scope]]只包含全局對象。

ECMAScript中,閉包指的是:

1、從理論角度:所有的函數。

    因為它們都在創建的時候,就將上層上下文的數據保存起來了。哪怕是最簡單的全局變數也是如此,因為函數中訪問的全局變數就相當於是訪問自由變數,這個時候使用最外層     的作用域。

2、從實踐角度:以下函數才算閉包:

   (1)、即時創建它的上下文已經銷毀,它任然存在(比如,內部函數從父函數中返回);

   (2)、在代碼中引用了自由變數。

給出一段經典的代碼:

var data = [];
for( var k = 0; k < 3; k++){
    data[k] = function(){
        alert(k);
    };
};
data[0]();// 3,而不是0
data[1]();// 3,而不是1
data[2]();// 3,而不是2

常用的解決方法是,通過閉包,如下:

var data = [];
for( var k = 0; k < 3; k++){
    data[k] = (function _helper(x){
        return function(){
            alert(x);
        };
    })(k);//傳入"k"值
};
//現在結果正確了
data[0]();// 0
data[1]();// 1
data[2]();// 2

除了閉包,我們還可以怎麼解決呢?

如下:

var data = [];
for( var k = 0; k < 3; k++){
    (data[k] = function(){
        alert(arguments.callee.x);
    }).x = k;//將k作為函數的一個屬性
};
//結果也是對的
data[0]();// 0
data[1]();// 1
data[2]();// 2

但是arguments.callee在ECMAScript5中的嚴格模式下是不能用的,所以我們可以用命名函數表達式來做。

如下:

var data = [];
for( var k = 0; k < 3; k++){
    (data[k] = function foo(){
        alert(foo.x);
    }).x = k;//將k作為函數的一個屬性
};
//結果也是對的
data[0]();// 0
data[1]();// 1
data[2]();// 2
六、其他:

ECMAScript中將對象作為參數傳遞的策略——按共用傳遞:修改參數的屬性將會影響到外部,而重新賦值將不會影響到外部對象。

其實不僅是參數傳遞,其他都是這樣的策略(對象都是賦予地址值)。

如下:

var foo = {};
//b其實是添加到function對象中的,而不是foo.a中的,因為foo.a只是引用了function,即得到了它的地址而已
(foo.a = function(){

}).b = 10;
//得到10
console.log(foo.a.b);
//將foo.a賦值予變數c
var c = foo.a;
//改變foo.a的值,如一個空對象
foo.a = {};
//輸出c.b,還是得到10
console.log(c.b);

數組也是一個對象,所以如果我將數組中的元素指向帶有this的函數,那麼其指向的是數組對象。

//聲明變數arr,並賦值為數組
var arr = [];
//給arr數組的第一二個元素賦值
arr[0] = function(){
    console.log(this);
    //因為this指向的是arr對象,所以this['1']或this[1],就相當於arr[1]
    this['1']();
};
arr[1] = function(){
    console.log("I'm 1 ");
};
//this指向的是arr對象
arr[0]();

 好了,時間也不早了,晚安~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 基本介面:Collection 介面: List 實現類:ArrayList LinkedList 介面:Set(唯一,無序) 實現類:HashSet(哈希) TreeSetMap介面:Map 實現類:HashMap ArrayList類特點:長度可變,連續空間,任意類型,類型相同適用情況:適合多次 ...
  • 作為後端應用的開發者,我們經常開發、調試、測試完我們的應用併發布到生產環境,用戶就可以直接訪問到我們的應用了。但對於互聯網應用,在你的應用和用戶之間還隔著一層低調的或厚或薄的負載均衡層軟體,它們不顯山不露水默默的發揮著重要的作用,以至於我們經常忽略了它們的存在。因為負載均衡層通常不在一般開發人員的問 ...
  • 你是如何指定一個頁面的編碼的呢?你知道瀏覽器是怎麼識別編碼的嗎? 首先,一個很簡單的例子,用遇簡的HTML頁面來看看各瀏覽器下有什麼不同: 最簡HTML,<head>和<body>都沒有內容,伺服器也不給出具體的編碼聲明,直接從本地打開,各個瀏覽器下查看頁面的編碼: 從表格中可以看出,對於沒有使用任 ...
  • <input type="checkbox">: 1 2 3 4 5 2012歐洲杯"死亡之組"小組出線的國家隊是:<br> <input type="checkbox" name="nation" value="Germany">德國 <input type="checkbox" name="na ...
  • 一:結構 註意,1 必須是三張以上圖片,2 最外層carousel-wrap必須要有一個寬高 二:CSS 三:JS 基於JQ, 四:測試地址 http://game.feiliu.com/zk/new/plugin/default.html 五:demo源碼下載 http://files.cnblo ...
  • 前端開發命名規則很重要,但是網上的命名規則一大篇,下麵是我總結的一些常用的短語,希望能幫到大家,肯定不全,歡迎大家補充。 1.如果是模塊,可以這樣首碼: 彈出:pop 公共:global(縮寫:gb) 標題:title,tit 提示:hint 菜單:menu 信息:info 預覽:pvw 導航:na ...
  • 本文主要對GET與POST基本區別進行彙總並掌握,如有錯誤與遺漏之處,請指出。 文章出處:http://www.cnblogs.com/useryangtao/ 1. HTTP HTTP(即超文本傳輸協議)是現代網路中最常見和常用的協議之一,設計它的目的是保證客戶機和伺服器之間的通信。 HTTP 的 ...
  • 一、基礎雜記 1. document.write() 1 <script type="text/javascript"> 2 document.write('<h2>我愛你</h2>'); 3 </script> 2. 變數必須聲明才能使用: 使用var聲明:局部變數; 沒有使用var聲明:全局變數 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...