摘要:原文鏈接參考深入理解原型和閉包完結王福朋博客園中的作用域詳解博客園
前言
王福朋老師的 JavaScript原型和閉包系列 文章看了不下三遍了,最為一個初學者,每次看的時候都會有一種 "大徹大悟" 的感覺,而看完之后卻總是一臉懵逼。原型與閉包 可以說是 JavaScirpt 中理解起來最難的部分了,當然,我也只是了解到了一些皮毛,對于 JavaScript OOP 更是缺乏經驗。這里我想總結一下 Javascript 中的 this 關鍵字,王福朋老師的在文章里也花了大量的篇幅來講解 this 關鍵字的使用,可以說 this 關鍵字也是值得重視的。
我們都知道,每一個 "代碼段" 都會執行在某一個 上下文環境 當中,而在每一個代碼執行之前,都會做一項 "準備工作",也就是生成相應的 上下文環境,所以每一個 上下文環境 都可能會不一樣。
上下文環境 是什么?我們可以去看王福朋老師的文章(鏈接在文末),講解的很清楚了,這里不贅述了。
"代碼段" 可以分為三種:
全局代碼
函數體
eval 代碼
與之對應的 上下文環境 就有:
全局上下文
函數上下文
(elav 就不討論了,不推薦使用)
當然,這和 this 又有什么關系呢?this 的值就是在為代碼段做 "準備工作" 時賦值的,可以說 this 就是 上下文環境 的一部分,而每一個不同的 上下文環境 可能會有不一樣的 this值。
每次在尋找一個問題的解決方案或總結一個問題的時候,我總會去嘗試將這個問題進行合適的分類,而從不同的方面去思考問題。
所以,這里我大膽的將 this 關鍵字的使用分為兩種情況:
全局上下文的 this
函數上下文的 this
(你也可以選擇其他的方式分類。當然,這也不重要了)
全局上下文中的 this在全局執行上下文中(在任何函數體外部),this 都指向全局對象:
// 在瀏覽器中, 全局對象是 window console.log(this === window) // true var a = "Zavier Tang" console.log(a) // "Zavier Tang" console.log(window.a) // "Zavier Tang" console.log(this.a) // "Zavier Tang" this.b = 18 console.log(b) // 18 console.log(window.b) // 18 console.log(this.b) // 18 // 在 node 環境中,this 指向global console.log(this === global) // true函數上下文中的 this
在函數內部,this 的值取決與函數被調用的方式。
this 的值在函數定義的時候是確定不了的,只有函數調用的時候才能確定 this 的指向。實際上 this 的最終指向的是那個調用它的對象。(也不一定正確)
1. 全局函數對于全局的方法調用,this 指向 window 對象(node下為 global ):
var foo = function () { return this } // 在瀏覽器中 foo() === window // true // 在 node 中 foo() === global //true
但值得注意的是,以上代碼是在 非嚴格模式 下。然而,在 嚴格模式 下,this 的值將保持它進入執行上下文的值:
var foo = function () { "use strict" return this } f2() // undefined
即在嚴格模式下,如果 this 沒有被執行上下文定義,那它為 undefined。
在生成 上下文環境 時:
若方法被 window(或 global )對象調用,即執行 window.foo(),那 this 將會被定義為 window(或 global );
若被普通對象調用,即執行 obj.foo(),那 this 將會被定義為 obj 對象;(在后面會討論)
但若未被對象調用,即直接執行 foo(),在非嚴格模式下,this 的值默認指向全局對象 window(或 global ),在嚴格模式下,this 將保持為 undefined。
通過 this 調用全局變量:
var a = "global this" var foo = function () { console.log(this.a) } foo() // "global this"
var a = "global this" var foo = function () { this.a = "rename global this" // 修改全局變量 a console.log(this.a) } foo() // "rename global this"
所以,對于全局的方法調用,this 指向的是全局對象 window (或global ),即調用方法的對象。(注意嚴格模式的不同)
2. 作為對象的方法當函數作為對象的方法調用時,它的 this 值是調用該函數的對象。也就是說,函數的 this 值是在函數被調用時確定的,在定義函數時確定不了(箭頭函數除外)。
var obj = { name: "Zavier Tang", foo: function () { console.log(this) console.log(this.name) } } obj.foo() // Object {name: "Zavier Tang", foo: function} // "Zavier Tang" //foo函數不是作為obj的方法調用 var fn = obj.foo // 這里foo函數并沒有執行 fn() // Window {...} // undefined
this 的值同時也只受最靠近的成員引用的影響:
//接上面代碼 var o = { name: "Zavier Tang in object o", fn: fn, obj: obj } o.fn() // Object {name: "Zavier Tang in object o", fn: fn, obj: obj} // "Zavier Tang in object o" o.obj.foo() // Object {name: "Zavier Tang", foo: function} // "Zavier Tang"
在原型鏈中,this 的值為當前對象:
var Foo = function () { this.name = "Zavier Tang" this.age = 20 } Foo.prototype.getInfo = function () { console.log(this.name) console.log(this.age) } var tang = new Foo() tang.getInfo() // "Zavier Tang" // 20
雖然這里調用的是一個繼承方法,但 this 所指向的依然是 tang 對象。
參考:《Object-Oriented JavaScript》(Second Edition)
3. 作為構造函數如果函數作為構造函數,那函數當中的 this 便是構造函數即將 new 出來的對象:
var Foo = function () { this.name = "Zavier Tang", this.age = 20, this.year = 1998, console.log(this) } var tang = new Foo() console.log(tang.name) // "Zavier Tang" console.log(tang.age) // 20 console.log(tang.year) // 1998
當 Foo 不作為構造函數調用時,this 的指向便是前面討論的,指向全局變量:
// 接上面代碼 Foo() // window {...}4. 函數調用 apply、call、 bind 時
當一個函數在其主體中使用 this 關鍵字時,可以通過使用函數繼承自Function.prototype 的 call 或 apply 方法將 this 值綁定到調用中的特定對象。即 this 的值就取傳入對象的值:
var obj1 = { name: "Zavier1" } var obj2 = { name: "Zavier2" } var foo = function () { console.log(this) console.log(this.name) } foo.apply(obj1) // Ojbect {name: "Zavier1"} //"Zavier1" foo.call(obj1) // Ojbect {name: "Zavier1"} //"Zavier1" foo.apply(obj2) // Ojbect {name: "Zavier2"} //"Zavier2" foo.call(obj2) // Ojbect {name: "Zavier2"} //"Zavier2"
與 apply、call 不同,使用 bind 會創建一個與 foo 具有相同函數體和作用域的函數。但是,特別要注意的是,在這個新函數中,this 將永久地被綁定到了 bind 的第一個參數,無論之后如何調用。
var foo = function () { console.log(this.name) } var obj1 = { name: "Zavier1" } var obj2 = { name: "Zavier2" } var g = foo.bind(obj1) g() // "Zavier1" var h = g.bind(ojb2) // bind只生效一次! h() // "Zavier1" var o = { name: "Zavier Tang", f:f, g:g, h:h } o.f() // "Zavier Tang" o.g() // "Zavier1" o.h() // "Zavier1"5. 箭頭函數
箭頭函數是 ES6 語法的新特性,在箭頭函數中,this 的值與創建箭頭函數的上下文的 this 一致。
在全局代碼中,this 的值為全局對象:
var foo = (() => this) //在瀏覽器中 foo() === window // true // 在node中 foo() === global // true
其實箭頭函數并沒有自己的 this。所以,調用 this 時便和調用普通變量一樣在作用域鏈中查找,獲取到的即是創建此箭頭函數的上下文中的 this。
當箭頭函數在創建其的上下文外部被調用時,箭頭函數便是一個閉包,this 的值同樣與原上下文環境中的 this 的值一致。由于箭頭函數本身是不存在 this,通過 call 、 apply 或 bind 修改 this 的指向是無法實現的。
作為對象的方法:
var foo = (() => this) var obj = { foo: foo } // 作為對象的方法調用 obj.foo() === window // true // 用apply來設置this foo.apply(obj) === window // true // 用bind來設置this foo = foo.bind(obj) foo() === window // true
箭頭函數 foo 的 this 被設置為創建時的上下文(在上面代碼中,也就是全局對象)的 this 值,而且無法通過其他調用方式設定 foo 的 this 值。
與普通函數對比,箭頭函數的 this 值是在函數創建創建確定的,而且無法通過調用方式重新設置 this 值。普通函數中的 this 值是在調用的時候確定的,可通過不同的調用方式設定 this 值。
總結this 關鍵字的值取決于其所處的位置(上下文環境):
在全局環境中,this 的值指向全局對象( window 或 global )。
在函數內部,this 的取值取決于其所在函數的調用方式,也就是說 this 的值是在函數被調用的時候確定的,在創建函數時無法確定。當然,箭頭函數是個例外,箭頭函數本身不存在 this,而在箭頭函數中使用 this 獲取到的便是創建其的上下文中的 this。同時,使用函數的繼承方法 call 、 apply 和 bind 會修改 this 的指向。但值得注意的是,使用 bind 方法會使 this 的值永久的綁定到給定的對象,無法再通過調用 call 和 apply 方法修改 this 的值,箭頭函數調用 call 、 apply 或 bind 方法無法修改 this。
原文鏈接
參考:
深入理解javascript原型和閉包(完結) - 王福朋 - 博客園
this - JavaScript | MDN
javascript中的this作用域詳解 - coder_Jenny - 博客園
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97896.html
摘要:在中,當使用關鍵字調用函數構造函數時,函數構造函數中也有這個概念,但是它不是惟一的規則,而且常常可以引用來自不同執行上下文的不同對象。因此,我們使用調用函數,可以看到這是對象,并且的屬性是正常的。 一直以來,javascript里邊的this都是一個很難理解的東西,之前看的最多的就是阮一峰老師關于this的理解: http://www.ruanyifeng.com/blo... htt...
摘要:然而,異步函數不會立即被推入調用堆棧,而是會被推入任務隊列,并在調用堆棧為空后執行。將事件從任務隊列傳輸到調用堆棧稱為事件循環。我們調用接受和或返回另一個函數稱為高階函數的函數。 為了保證可讀性,本文采用意譯而非直譯 想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你! 1.如何理解 JS 中的this關鍵字? JS 初學者總是對 this 關鍵字感到困惑,因為與其他現...
摘要:理解的函數基礎要搞好深入淺出原型使用原型模型,雖然這經常被當作缺點提及,但是只要善于運用,其實基于原型的繼承模型比傳統的類繼承還要強大。中文指南基本操作指南二繼續熟悉的幾對方法,包括,,。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。 怎樣使用 this 因為本人屬于偽前端,因此文中只看懂了 8 成左右,希望能夠給大家帶來幫助....(據說是阿里的前端妹子寫的) this 的值到底...
摘要:是完全的面向對象語言,它們通過類的形式組織函數和變量,使之不能脫離對象存在。而在基于原型的面向對象方式中,對象則是依靠構造器利用原型構造出來的。 JavaScript 函數式腳本語言特性以及其看似隨意的編寫風格,導致長期以來人們對這一門語言的誤解,即認為 JavaScript 不是一門面向對象的語言,或者只是部分具備一些面向對象的特征。本文將回歸面向對象本意,從對語言感悟的角度闡述為什...
摘要:當談到語言與其他編程語言相比時,你可能會聽到一些令人困惑東西,其中之一是工廠函數和構造函數。好的,讓我們用構造函數做同樣的實驗。當我們使用工廠函數創建對象時,它的指向,而當從構造函數創建對象時,它指向它的構造函數原型對象。 showImg(https://segmentfault.com/img/bVbr58T?w=1600&h=900); 當談到JavaScript語言與其他編程語言...
閱讀 2882·2021-11-22 09:34
閱讀 1212·2021-11-19 09:40
閱讀 3335·2021-10-14 09:43
閱讀 3566·2021-09-23 11:22
閱讀 1601·2021-08-31 09:39
閱讀 880·2019-08-30 15:55
閱讀 1414·2019-08-30 15:54
閱讀 854·2019-08-30 15:53