this指向 - Node環境

1.全局上下文中 thisnode

/*
1.全局上下文中的 this

node環境下:
    
    嚴格模式下: {} {} 報錯

    非嚴格模式下:{} {} {}
 */

'use strict'; // 嚴格模式

// demo 1:
console.log(this);  // {}

// demo 2:
var a = this;
console.log(a); // {}
var b = 1;
console.log(global.b); // undefined

// demo 3:
b = this; // 注意:嚴格模式下,變量必須得聲明;非嚴格模式,能夠省略聲明符
console.log(b); // {}
c = 1;
console.log(global.c); // 1

/*
分析:

    node環境下:

    node環境下 的上下文組成 與 瀏覽器環境下 有些不一樣;

    擴展瞭解一下,Node.js的全局對象 global:

    定義:Node.js中的全局對象是 global, 全部全局變量(除了 global 自己之外)都是global對象的屬性;

    在 Node.js 中,咱們能夠直接訪問到 global屬性,而不須要在應用中包含它;

    也就是說,global 是全局對象,可是它仍是 global.global的屬性值,這是遞歸的過程;

    這跟瀏覽器下的 window對象 是一個邏輯,window對象仍是 window.window 的屬性值;

    全局對象 與 全局變量的關係:

    global的根本做用是做爲全局變量的宿主,按照 ECMAScript的定義,知足如下條件的變量爲全局變量:

        1.在最外層定義的變量

        2.全局對象的屬性

        3.隱式定義的變量(未定義直接賦值的變量)

    當你定義一個全局變量時,這個變量同時也會成爲全局對象的屬性,反之亦然。須要注意的是,在 Node.js中你不可能在最外層定義變量,

    由於全部用戶代碼都是屬於當前模塊的,而模塊自己不是最外層上下文。

    注意:永遠使用 var 定義變量以免引入全局變量,由於全局變量會污染命名空間,提升代碼的耦合風險。

    因此,以上三個demo 獲取 this 的方式:

    demo 1: 直接獲取 this, 實際上 demo 1 建立執行上下文的過程是在 當前模塊中進行的,因此說這個模塊的上下文環境纔是,demo 1 代碼執行時的執行上下文,

        也就是說 環境對象是當前模塊對象,而這個模塊對象是個 新對象{},而結果輸出 {}, 也就符合「this 指向其執行上下文的環境對象」這一結論了;

    demo 2: 根據定義,因爲用var 聲明的變量,實際上並非在最外層上下文中聲明的變量,是在當前模塊聲明的變量(global.b 值爲 undefined,已經證實這點了),

        可是,整個代碼的執行過程仍是在 當前模塊的上下文中進行的,同上 輸出 {},也就符合「this 指向其執行上下文的環境對象」這一結論;

    demo 3: 能夠看出 demo 3 在非嚴格模式下隱式定義了一個變量,而且給它賦了值,根據全局變量的定義,這樣隱式定義變量的方式,

        其實是在全局對象上定義一個屬性,也稱爲全局變量,因此 經過 global對象能夠獲取到(global.c 的值爲 1,已證實這點);

        可是要弄清楚,在這個全局變量的 定義過程,仍是在 當前模塊進行的,因此 此時 this指向依然是 當前模塊的執行上下文對象 {};

        因此,「this 指向其執行上下文的環境對象」 這一結論依然成立;

總結:

    Node環境下:

        全局的執行上下文 進行的過程是在 當前模塊下進行的,因此全局執行上下文的環境對象是當前模塊{},因此全局中 this 指向當前模塊對象{};
 */

 

2.函數上下文的 this瀏覽器

/*
    2.函數上下文中的 this

    Node環境下:

        嚴格模式下: this 指向被指定的環境對象,即便指定的是 null 或者 undefined;

        非嚴格模式下:this 指向被指定的環境對象,若是指定的是 null 或者 undefined,會轉爲全局對象 global對象; 
     */

"use strict"

// demo 1:
function aa(){
    console.log(this);
}

aa(); // 做爲單獨函數執行(沒有指定 環境對象):strict-->"undefined"    nostrict-->"global"


// demo 2:
var obj = {
    a: 1,
    b: function(){
        console.log(this);
    }
}

obj.b(); // 做爲對象方法執行: strict-->obj    nostrict-->obj

// demo 3:
aa.call(obj); // 做爲單獨函數執行(指定 環境對象爲 obj): strict-->obj    nostrict-->obj

// demo 4:
aa.call(null); // 做爲單獨函數執行(指定 環境對象爲 null):strict-->null    nostrict-->global

// demo 5:
aa.call(undefined); //做爲單獨函數執行(指定 環境對象爲 undefined):strict-->undefined    nostrict-->global 

/*
總結:函數上下文中的 this

Node環境 嚴格模式下:

    函數做爲單獨函數 執行:

        1.函數執行上下文的環境對象 是指定的,指定什麼就是什麼;this指向這個指定的環境對象;即便指定的是 null 或者 undefined;

    函數做爲對象方法 執行:

        2.這個函數執行上下文的環境對象就是這個擁有它的對象;this指向這個擁有它的對象;


Node環境 非嚴格模式下:

    函數做爲單獨函數 執行:

        1.函數執行上下文的環境對象 是指定的,指定什麼就是什麼;this指向這個指定的環境對象,若是指定的是 null 或者 undefined,會轉爲全局對象 global對象; 

    函數做爲對象方法 執行:

        2.這個函數執行上下文的環境對象就是這個擁有它的對象;this指向這個擁有它的對象;
 */

 

3.對象屬性中的 thisapp

/*
    3.對象屬性中的 this

    注意:這裏指的是以下的形式,不要把它和 對象方法中的 this搞混;對象方法中的 this,要規劃到 函數上下文的 this中; 

    Node環境下:
        
        嚴格模式下:{}

        非嚴格模式下: {}
 */
'use strict'
var obj = {
    a: 1,
    b: 2,
    c: this,
    sayHi: function(){
        console.log('Hi');
    }
}

console.log(obj.c);    // {}

/*
    分析:

        其實,這樣的對象字面量的形式,可能看起來會有些困惑,咱們能夠變形來分析;由於對象字面量的形式,其實是由以下的形式簡化而來的寫法;

        var obj = new Object();
        obj.a =1;
        obj.b = 2;
        obj.c = this;
        obj.sayHi = function(){
            console.log('Hi');
        }
        
        這樣看來就清晰不少了,上邊這段代碼執行的時候,不就是把全局執行上下文的環境對象賦給 obj.c 屬性嗎,關於 Node中全局上下文的環境對象 爲 一個新對象{},咱們已經介紹過了;

        並且結果,也正符合咱們此時所得出的結果;

        因此,這樣做爲對象中的 this,能夠規到全局執行上下文中的 this 一類中,this 指向 全局執行上下文的環境對象{};
*/
/*
    一個例子,可能沒有什麼說服力,咱們再來個嵌套形式的 來證明咱們的結論, 以下:
 */
var o1 = {
    a: 1,
    b: this,
    o2: {
        a: 1,
        b: this
    }
}

console.log(o1.o2.b); // {}
/*
    結果依然是 {}, 其實 如上的形式,能夠變形爲:

    var o1 = new Object();
    o1.a = 1,
    o1.b = this;
    o1.o2 = new Object();
    o1.o2.a = 1;
    o1.o2.b = this;
    
    上面這段代碼 在執行時,它的執行上下文的環境對象依然是 全局上下文的環境對象;因此說 this依然指向 {};

 */

/*
    歸納:對象屬性中的 this指向爲 全局執行上下文的環境對象{};
*/

 

4.構造函數 和 原型方法中的 this函數

/*
    4.構造函數 和 原型方法中的 this

    瀏覽器環境下:
        
        嚴格模式下:以構造函數名命名的新對象

        非嚴格模式下: 以構造函數名命名的新對象
 */
     "use strict"
     
     function Person(){

        console.log(this);    // Person {}

        this.name = 'jack';

        console.log(this);    // Person {name: "jack"}
    }

    Person.prototype.sayThis = function(){
        console.log(this);
    }

    Person.prototype.sayThis();    // {sayThis: ƒ}

    new Person();    // Person {} --> // Person {name: "jack"}
    
    /*
    分析 1:
     
        構造函數與普通函數的最重要的不一樣之處,就是構造函數可經過 new操做符,創造實例;

        那麼在利用構造函數創造實例的過程到底發生了什麼呢? 其實呢,是要經歷如下幾個過程的:

            1.創造一個 新對象,做爲執行上下文的環境對象;(注意:這裏爲何說成是新對象,而不說成是空對象呢,由於 function默認是有 prototype屬性存在的,它指向原型對象)

            2.構造函數開始執行,它的執行上下文環境對象就爲這個新對象,也就是說 this指向這個新對象;

            3.利用 this來給這個新對象賦值;

            4.返回這個被賦值以後的 新對象;
        
        經過上面 new Person() 執行後輸出的結果來看,確實是這樣的一個過程;沒有沒給 this賦值前輸出的是 Person{}, 賦值後,輸出的 Person{name:'jack'};

        因此 歸納:

            構造函數中的執行上下文的環境對象爲,以構造函數名命名的新對象;

    分析 2:

        至於原型方法中 this, 其實,在咱們瞭解了 「函數上下文的 this」 以後,應該很清楚了,它指向給它指定的環境對象,也就是肯定了的 構造函數的原型對象;

        因此,Person.prototype.sayThis() 執行後,輸出的結果是 Person構造函數的原型對象 --> Person.prototype 對象;
     */

 

5.應用 call、apply、bind 方法後的 thisthis

    /*
    5.應用 call、apply、bind 方法後的 this

    瞭解:call、apply、bind 這三個方法是 Function對象纔有的方法;它們的做用,主要是指定函數中 this中的指向,只是用法稍有不一樣;
    
    瀏覽器環境下:
        
        嚴格模式下:this指向 這三個方法所指定的這個值,不管是什麼,即便是 null、undefined, this 也指向它們;

        非嚴格模式下:this指向 這三個方法所指定的這個值,null 和 undefined 值會被轉換爲全局對象 window;

     */
    
    "use strict"
    
    // demo 1:
    var o1 = {
        a: 11,
        b: 12,
        sayA: function(){
            console.log(this.a);
        }
    }

    var o2 = {
        a: 21,
        b: 22
    }

    o1.sayA.call(o2);    // 21

    // demo 2:
    function sayB(){
        console.log(this.b);
    }

    sayB.call(o2);    // 22
    sayB.apply(o2);    // 22

    var bSayB = sayB.bind(o2);
    bSayB();        // 22
    
    /*
    其實這塊不該該單提出來一個做總結分析的,徹底能夠規劃到「函數上下文的 this」中去,只是在咱們平時 coding的時候, 

    這三個方法是常常要用到的 因此單拿出來,以做記憶吧;

     */    

 

 原創:轉載註明出處,謝謝 :)spa

相關文章
相關標籤/搜索