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