摘要:作為構造函數何為構造函數所謂構造函數就是用來對象的函數,像等都是全局定義的構造函數。正在跑步正在說話正在跑步正在說話如上,如果函數作為構造函數用,那么其中的就代表它即將出來的對象。
前言
總括:詳解JavaScript中的this的一篇總結,不懂this這個難點,很多時候會造成一些困擾,寫出一些bug不知如何收場,所以一起來寫bug吧,不對,一起來寫代碼吧。
原文地址:JavaScript中的this
知乎專欄: 前端進擊者
博主博客地址:Damonare的個人博客
人生得意須盡歡,莫使金樽空對月
正文? JavaScript中的this格外的不一樣,比如Java語言中的this是在代碼的執行階段是不可更改,而JavaScript的this是在調用階段進行綁定。?因為這一性質所以給了this很大的發揮空間。但其在嚴格模式和非嚴格模式下又有些不一樣,在函數的不同調用方式也導致this有些區別。?
What"s this??首先對this的下個定義:this是在執行上下文創建時確定的一個在執行過程中不可更改的變量。
所謂執行上下文,就是JavaScript引擎在執行一段代碼之前將代碼內部會用到的一些變量、函數、this提前聲明然后保存在變量對象中的過程。這個"代碼片段"包括:全局代碼(script標簽內部的代碼)、函數內部代碼、eval內部代碼。而我們所熟知的作用域鏈也會在保存在這里,以一個類數組的形式存儲在對應函數的[[Scopes]]屬性中。
this只在函數調用階段確定,也就是執行上下文創建的階段進行賦值,保存在變量對象中。這個特性也導致了this的多變性:?即當函數在不同的調用方式下都可能會導致this的值不同。
??上面我們說過了在嚴格模式下和非嚴格模式下this表現不同:
var a = 1; function fun() { "use strict"; var a = 2; return this.a; } fun();//?報錯 Cannot read property "a" of undefined
?嚴格模式下,this指向undefined;
var a = 1; function fun() { var a = 2; return this.a; } fun();//1
?非嚴格模式下this指向window;
上面同一段代碼,在不同模式下之所以有不同表現,就是因為this在嚴格模式,非嚴格模式下的不同。
結論:當函數獨立調用的時候,在嚴格模式下它的this指向undefined,在非嚴格模式下,當this指向undefined的時候,自動指向全局對象(瀏覽器中就是window)
多提一句,在全局環境下,this就是指向自己,再看?:
this.a = 1; var b = 1; c = 1; console.log(this === window)//true //這三種都能得到想要的結果,全局上下文的變量對象中存在這三個變量
再多提一句,當this不在函數中用的時候會怎樣?看?:
var a = 1000; var obj = { a: 1, b: this.a + 1 } function fun() { var obj = { a: 1, c: this.a + 2 //嚴格模式下這塊報錯 Cannot read property "a" of undefined } return obj.c; } console.log(fun());//1002 console.log(obj.b);//1001
這種情況下this還是指向了window。那么我們可以多帶帶下個結論:
當obj在全局聲明的時候,obj內部屬性中的this指向全局對象,當obj在一個函數中聲明的時候,嚴格模式下this會指向undefined,非嚴格模式自動轉為指向全局對象。
?好了,剛剛小試牛刀下,知道了嚴格模式和非嚴格模式下this的區別,然而我們日常應用最多的還是在函數中用this,上面也說過了this在函數的不同調用方式還有區別,那么函數的調用方式都有哪些呢?四種:
在全局環境或是普通函數中直接調用
作為對象的方法
使用apply和call
作為構造函數
下面分別就四種情況展開:
直接調用上面的?其實就是直接調用的,不過我決定再寫???:
var a = 1; var obj = { a: 2, b: function () { function fun() { return this.a } console.log(fun()); } } obj.b();//1
fun函數雖然在obj.b方法中定義,但它還是一個普通函數,直接調用在非嚴格模式下指向undefined,又自動指向了全局對象,正如預料,嚴格模式會報錯undefined.a不成立,a未定義。
重要的事情再說一遍:當函數獨立調用的時候,在嚴格模式下它的this指向undefined,在非嚴格模式下,當this指向undefined的時候,自動指向全局對象(瀏覽器中就是window)。?
作為對象的方法var a = 1; var obj = { a: 2, b: function() { return this.a; } } console.log(obj.b())//2
?b所引用的匿名函數作為obj的一個方法調用,這時候this指向調用它的對象。這里也就是obj。那么如果b方法不作為對象方法調用呢?啥意思呢,就是這樣?:
var a = 1; var obj = { a: 2, b: function() { return this.a; } } var t = obj.b; console.log(t());//1
如上,t函數執行結果竟然是全局變量1,為啥呢?這就涉及Javascript的內存空間了,就是說,obj對象的b屬性存儲的是對該匿名函數的一個引用,可以理解為一個指針。當賦值給t的時候,并沒有多帶帶開辟內存空間存儲新的函數,而是讓t存儲了一個指針,該指針指向這個函數。相當于執行了這么一段偽代碼:
var a = 1; function fun() {//此函數存儲在堆中 return this.a; } var obj = { a: 2, b: fun //b指向fun函數 } var t = fun;//變量t指向fun函數 console.log(t());//1
此時的t就是一個指向fun函數的指針,調用t,相當于直接調用fun,套用以上規則,打印出來1自然很好理解了。
使用apply,call關于apply和call是干什么的怎么用本文不涉及,請移駕:apply,call
這是個萬能公式,實際上上面直接調用的代碼,我們可以看成這樣的:
function fun() { return this.a; } fun();//1 //嚴格模式 fun.call(undefined) //非嚴格模式 fun.call(window)
這時候我們就可以解釋下,為啥說在非嚴格模式下,當函數this指向undefined的時候,會自動指向全局對象,如上,在非嚴格模式下,當調用fun.call(undefined)的時候打印出來的依舊是1,就是最好的證據。
為啥說是萬能公式呢?再看函數作為對象的方法調用:
var a = 1; var obj = { a: 2, b: function() { return this.a; } } obj.b() obj.b.call(obj)
如上,是不是很強大,可以理解為其它兩種都是這個方法的語法糖罷了,那么apply和call是不是真的萬能的呢?并不是,ES6的箭頭函數就是特例,因為箭頭函數的this不是在調用時候確定的,這也就是為啥說箭頭函數好用的原因之一,因為它的this固定不會變來變去的了。關于箭頭函數的this我們稍后再說。
作為構造函數何為構造函數?所謂構造函數就是用來new對象的函數,像Function、Object、Array、Date等都是全局定義的構造函數。其實每一個函數都可以new對象,那些批量生產我們需要的對象的函數就叫它構造函數罷了。注意,構造函數首字母記得大寫。
function Fun() { this.name = "Damonre"; this.age = 21; this.sex = "man"; this.run = function () { return this.name + "正在跑步"; } } Fun.prototype = { contructor: Fun, say: function () { return this.name + "正在說話"; } } var f = new Fun(); f.run();//Damonare正在跑步 f.say();//Damonare正在說話
如上,如果函數作為構造函數用,那么其中的this就代表它即將new出來的對象。為啥呢?new做了啥呢?
偽代碼如下:
function Fun() { //new做的事情 var obj = {}; obj.__proto__ = Fun.prototype;//Base為構造函數 obj.name = "Damonare"; ...//一系列賦值以及更多的事 return obj }
也就是說new做了下面這些事:
創建一個臨時對象
給臨時對象綁定原型
給臨時對象對應屬性賦值
將臨時對象return
也就是說new其實就是個語法糖,this之所以指向臨時對象還是沒逃脫上面說的幾種情況。
當然如果直接調用Fun(),如下:
function Fun() { this.name = "Damonre"; this.age = 21; this.sex = "man"; this.run = function () { return this.name + "正在跑步"; } } Fun(); console.log(window)
其實就是直接調用一個函數,this在非嚴格模式下指向window,你可以在window對象找到所有的變量。
另外還有一點,prototype對象的方法的this指向實例對象,因為實例對象的__proto__已經指向了原型函數的prototype。這就涉及原型鏈的知識了,即方法會沿著對象的原型鏈進行查找。
箭頭函數剛剛提到了箭頭函數是一個不可以用call和apply改變this的典型。
我們看下面這個?:
var a = 1; var obj = { a: 2 }; var fun = () => console.log(this.a); fun();//1 fun.call(obj)//1
以上,兩次調用都是1。
那么箭頭函數的this是怎么確定的呢?箭頭函數會捕獲其所在上下文的 this 值,作為自己的 this 值,也就是說箭頭函數的this在詞法層面就完成了綁定。apply,call方法只是傳入參數,卻改不了this。
var a = 1; var obj = { a: 2 }; function fun() { var a = 3; let f = () => console.log(this.a); f(); }; fun();//1 fun.call(obj);//2
如上,fun直接調用,fun的上下文中的this值為window,注意,這個地方有點繞。fun的上下文就是此箭頭函數所在的上下文,因此此時f的this為fun的this也就是window。當fun.call(obj)再次調用的時候,新的上下文創建,fun此時的this為obj,也就是箭頭函數的this值。
再來一個?:
function Fun() { this.name = "Damonare"; } Fun.prototype.say = () => { console.log(this); } var f = new Fun(); f.say();//window
有的同學看到這個?會很懵逼,感覺上this應該指向f這個實例對象啊。不是的,此時的箭頭函數所在的上下文是__proto__所在的上下文也就是Object函數的上下文,而Object的this值就是全局對象。
那么再來一個?:
function Fun() { this.name = "Damonare"; this.say = () => { console.log(this); } } var f = new Fun(); f.say();//Fun的實例對象
如上,this.say所在的上下文,此時箭頭函數所在的上下文就變成了Fun的上下文環境,而因為上面說過當函數作為構造函數調用的時候(也就是new的作用)上下文環境的this指向實例對象。
后記文中定義均為個人總結,不妥之處還請雅正。
轉載請注明出處。
以上。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84256.html
摘要:理解的函數基礎要搞好深入淺出原型使用原型模型,雖然這經常被當作缺點提及,但是只要善于運用,其實基于原型的繼承模型比傳統的類繼承還要強大。中文指南基本操作指南二繼續熟悉的幾對方法,包括,,。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。 怎樣使用 this 因為本人屬于偽前端,因此文中只看懂了 8 成左右,希望能夠給大家帶來幫助....(據說是阿里的前端妹子寫的) this 的值到底...
摘要:和類在開始時遇到類組件,只是需要有關類的基礎。畢竟,中的條件呈現僅再次顯示大多數是而不是特定的任何內容。 在我的研討會期間,更多的材料是關于JavaScript而不是React。其中大部分歸結為JavaScript ES6以及功能和語法,但也包括三元運算符,語言中的簡寫版本,此對象,JavaScript內置函數(map,reduce,filter)或更常識性的概念,如:可組合性,可重用...
摘要:對象在中,除了數字字符串布爾值這幾個簡單類型外,其他的都是對象。那么在函數對象中,這兩個屬性的有什么區別呢表示該函數對象的原型表示使用來執行該函數時這種函數一般成為構造函數,后面會講解,新創建的對象的原型。這時的函數通常稱為構造函數。。 本文原發于我的個人博客,經多次修改后發到sf上。本文仍在不斷修改中,最新版請訪問個人博客。 最近工作一直在用nodejs做開發,有了nodejs,...
摘要:所以相同點是,在全局范圍內,全局變量終究是屬于老大的。只生效一次引入了。只生效一次在箭頭函數中,與封閉詞法環境的保持一致。我通常把這些原始函數叫做構造函數。在里面你可以嵌套函數,也就是你可以在函數里面定義函數。 showImg(https://img-blog.csdnimg.cn/20190522000008399.jpg?x-oss-process=image/watermark,...
摘要:原文許多人被中的關鍵字給困擾住了,我想混亂的根源來自人們理所當然地認為中的應該像中的或中的一樣工作。盡管有點難理解,但它的原理并不神秘。在瀏覽器中,全局對象是對象。運算符創建一個新對象并且設置函數中的指向調用函數的新對象。 原文:Understanding the this keyword in JavaScript 許多人被JavaScript中的this關鍵字給困擾住了,我想混亂的...
閱讀 1210·2019-08-30 15:55
閱讀 959·2019-08-30 15:55
閱讀 2156·2019-08-30 15:44
閱讀 2890·2019-08-29 14:17
閱讀 1136·2019-08-29 12:45
閱讀 3311·2019-08-26 10:48
閱讀 3136·2019-08-23 18:18
閱讀 2605·2019-08-23 16:47