摘要:當(dāng)我們不想再對(duì)象內(nèi)部間接包含引用函數(shù),而像在某個(gè)對(duì)象上強(qiáng)制調(diào)用函數(shù)。我們可以用中內(nèi)置的和的方法來(lái)實(shí)現(xiàn),這兩個(gè)方法的第一個(gè)參數(shù)是一個(gè)對(duì)象,是給準(zhǔn)備的,接著再調(diào)用函數(shù)時(shí)將其綁定到。
this是什么
在javascript中,每個(gè)執(zhí)行上下文可以抽象成一組對(duì)象
而this是與執(zhí)行上下文相關(guān)的特殊對(duì)象,任何對(duì)象都可以用作this上下文的值,一個(gè)重要的注意事項(xiàng)就是this值是執(zhí)行上下文的屬性,但不是變量對(duì)象的屬性。這樣的話,與變量相反,this值不會(huì)參與標(biāo)識(shí)符解析,即在訪問(wèn)代碼時(shí),他的值直接來(lái)自執(zhí)行上下文,也沒(méi)有任何作用域鏈查找,在進(jìn)入上下文中,this只能確定一次。所以this的值是和其所處的上下文環(huán)境有關(guān)系。
this全面解析在歷屆this的綁定之前,首先要理解調(diào)用位置,調(diào)用位置就是函數(shù)在代碼中內(nèi)調(diào)用的位置(而不是聲明的位置),例子:
function baz(){ // 當(dāng)前調(diào)用棧是: baz // 因此,當(dāng)前調(diào)用位置是全局作用域 console.log("baz"); bar(); // <-- bar的調(diào)用位置 } function bar(){ // 當(dāng)前調(diào)用棧是baz -> bar // 因此調(diào)用位置在baz中 console.log("bar"); foo();// <-foo的調(diào)用位置 } function foo(){ // 當(dāng)前調(diào)用棧是baz -> bar -> foo // 調(diào)用位置在bar中 console.log("foo"); } baz() // <- baz的調(diào)用位置
首先要介紹的最常見(jiàn)的函數(shù)調(diào)用類(lèi)型:獨(dú)立函數(shù)調(diào)用??梢园堰@條規(guī)則看作是無(wú)法應(yīng)用其他規(guī)則時(shí)的默認(rèn)規(guī)則
function foo(){ console.log(this.a); } var a = 2; foo(); //2
因?yàn)?b>foo()在全局執(zhí)行上下文中調(diào)用,所以this指向全局變量
如果使用嚴(yán)格模式,則不能將全局對(duì)象用于默認(rèn)綁定,因此this會(huì)綁定到undefined:
function foo(){ "use strict" console.log(this.a); } var a = 2; foo(); //error
雖然this的綁定規(guī)則完全取決于調(diào)用位置,但是只有foo()運(yùn)行在非嚴(yán)格模式下時(shí)。默認(rèn)綁定才能綁定到全局對(duì)象;在嚴(yán)格模式下調(diào)用foo()則不影響默認(rèn)綁定
function foo(){ console.log(this.a); } var a = 2; (function(){ foo(); //2 })()
function foo(){ console.log(this.a); } var obj = { a: 42, foo: foo }; obj.foo();
當(dāng)函數(shù)引用有上下文對(duì)象時(shí),隱式綁定規(guī)則會(huì)把函數(shù)調(diào)用中的this綁定到這個(gè)上下文對(duì)象,因?yàn)檎{(diào)用foo()時(shí)this被綁定到obj,因此this.a和obj.a是一樣的
對(duì)象屬性引用鏈中只有上一層或者說(shuō)最后一層在調(diào)用位置起作用
function foo(){ console.log(this.a); } var obj2 = { a: 42, foo: foo } var obj1 = { a: 2, obj2: obj2 } obj1.obj2.foo(); //42
被隱式綁定的函數(shù)會(huì)丟失綁定對(duì)象,也就是說(shuō)他會(huì)應(yīng)用默認(rèn)綁定,從而把this綁定到全局對(duì)象或者undefined
function foo(){ console.log(this.a); } var obj = { a:2, foo: foo }; var bar = obj.foo; //函數(shù)別名 var a = "oops, global"; //a是全局對(duì)象的屬性。 bar(); //"oops, global"
雖然bar引用了obj.foo這個(gè)引用,但實(shí)際上他引用的是foo函數(shù)的本身。也就是說(shuō)bar()是一個(gè)在全局上下文中調(diào)用的函數(shù),因此this指向了全局對(duì)象。
這種情形頁(yè)出現(xiàn)在參數(shù)傳遞中。
function foo(){ console.log(this.a); } function doFoo(fn){ fn(); } var obj = { a: 2, foo: foo }; var a = "oops, global"; //a是全局對(duì)象的屬性 doFoo(obj.foo);
參數(shù)傳遞其實(shí)就是一種隱式賦值,因此我們傳入函數(shù)也會(huì)被隱式賦值。
在分析隱式綁定時(shí),我們必須在一個(gè)對(duì)象內(nèi)部包含一個(gè)指向函數(shù)的屬性,并通過(guò)這個(gè)屬性間接引用函數(shù),從而把this間接(隱式)綁定到這個(gè)都對(duì)象上。
當(dāng)我們不想再對(duì)象內(nèi)部間接包含引用函數(shù),而像在某個(gè)對(duì)象上強(qiáng)制調(diào)用函數(shù)。我們可以用javascript中內(nèi)置的apply和call的方法來(lái)實(shí)現(xiàn),這兩個(gè)方法的第一個(gè)參數(shù)是一個(gè)對(duì)象,是給this準(zhǔn)備的,接著再調(diào)用函數(shù)時(shí)將其綁定到this。因?yàn)槟憧梢灾苯又付?b>this的綁定對(duì)象,因此我們稱(chēng)之為顯式綁定。
function foo(){ console.log(this.a); } var obj = { a: 2 } foo.call(obj); //2
通過(guò)foo.call(...)我們可以在調(diào)用foo時(shí)強(qiáng)制把他的this綁定到obj上。如果你傳入一個(gè)原始值(字符串類(lèi)型,布爾類(lèi)型或者數(shù)字類(lèi)型)來(lái)當(dāng)作this的綁定對(duì)象,這個(gè)原始值會(huì)被轉(zhuǎn)換成他的對(duì)象形式,也就是“裝箱”
我們通過(guò)顯示綁定的變種解決綁定丟失的問(wèn)題
function foo(){ console.log(this.a); } var obj = { a: 2 } var bar = function(){ foo.call(obj); }; bar(); //2 setTimeout(bar, 100); //2 //硬綁定的bar不可能在修改他的this bar.call(window); //2
硬綁定的典型應(yīng)用場(chǎng)景就是創(chuàng)建一個(gè)包裹函數(shù),負(fù)責(zé)接收參數(shù)并返回值
function foo(something){ console.log(this.a, something); return this.a + something; } var obj = { a: 2 } var bar = function(){ return foo.apply(obj, arguments); } var b = bar(3); //2. 3 console.log(b); //5
另一種方式則是創(chuàng)建一個(gè)可以重復(fù)使用的輔助函數(shù)
function foo(something){ console.log(this.a, something); return this.a + something; } var obj = { a: 2 } function bind(fn, obj){ return function(){ return fn.apply(obj, arguments); } } var bar = bind(foo, obj); var b = bar(3); //2 3 console.log(b); //5
之前介紹了apply和call可以改變this的指向,現(xiàn)在來(lái)講講他們的區(qū)別以及ES5新增的方法bind
apply和call之間最主要的區(qū)別在于傳入?yún)⑿问降牟煌?。他倆的第一個(gè)參數(shù)都是指定了函數(shù)體內(nèi)的this指向。
而第二個(gè)參數(shù)apply傳入為一個(gè)帶下標(biāo)的集合,這個(gè)集可以為數(shù)組,也可以為類(lèi)數(shù)組。apply方法把這個(gè)集合中的元素作為參數(shù)傳遞給被調(diào)用的函數(shù)
var func = function(a,b,c){ alert([a, b, c]); //1 2 3 } func.apply(null, [1, 2, 3])
call傳入的參數(shù)數(shù)量不固定,跟apply相同的是,第一個(gè)參數(shù)也是函數(shù)體內(nèi)的this指向,從第二個(gè)參數(shù)開(kāi)始往后,每個(gè)參數(shù)依次傳入函數(shù)。
var func = function(a, b, c){ alert([a, b, c]); //1 2 3 } func.call(null, 1, 2, 3);
當(dāng)使用call或者apply的時(shí)候,如果我們傳入的第一個(gè)參數(shù)為null,函數(shù)體內(nèi)的this會(huì)指向默認(rèn)的宿主對(duì)象,在瀏覽器是window
大多數(shù)的高級(jí)瀏覽器已經(jīng)實(shí)現(xiàn)了bind方法用來(lái)指定函數(shù)內(nèi)部的this的指向
function foo(){ console.log(this.a); } var obj = { a: 2 } var bar = foo。bind(obj); bar(); //2
bind(..)會(huì)返回一個(gè)硬編碼的新函數(shù),他會(huì)把你指定的參數(shù)設(shè)置為this的上下文并調(diào)用原始函數(shù)
我們也可以用apply模仿一個(gè)bind
Function.prototype.bind = function(){ var self = this; var context = Array.prototype.shift().call(arguments); var args = Array.prototype.slice().call(arguments); return function(){ this.apply(context, Array.prototype.concat.call(args, Array.prototype.shift().call(arguments);)) } } var obj = { name: "foo" }; var func = function(a, b, c, d){ console.log(this.name); console.log([a, b, c, d]) // }.bind(obj, 1, 2); func(3,4);
在javascript中,構(gòu)造函數(shù)只是一些使用new操作符時(shí)調(diào)用的函數(shù),它們并不會(huì)屬于某個(gè)類(lèi),也不會(huì)是實(shí)例化一個(gè)類(lèi)。
使用new來(lái)調(diào)用函數(shù),或者說(shuō)發(fā)生構(gòu)函數(shù)調(diào)用時(shí),會(huì)自動(dòng)執(zhí)行下面的操作
1.創(chuàng)建(或者說(shuō)構(gòu)造)一個(gè)去全新的對(duì)象。
2.這個(gè)新對(duì)象會(huì)被執(zhí)行[[prototype]]連接
3.這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的this
4.如果函數(shù)沒(méi)有其他返回對(duì)象,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象。
function foo(a){ this.a = a; } var bar = new foo(2); console.log(bar.a); //2
前面我們說(shuō)過(guò)硬綁定這種方式綁定之后無(wú)法修改this值,會(huì)降低函數(shù)靈活性。
如果可以給默認(rèn)綁定指定一個(gè)全局對(duì)象和undefined以外的值,那就可以實(shí)現(xiàn)和硬綁定相同過(guò)的效果,同hi是保留隱式綁定或者顯示綁定修改this的能力
Function.prototype.softbind = function(){ var self = this; var context = [].shift.call(arguments); var args = [].slice.call(arguments); var bound = function(){ return self.apply((!this|| this === (window || global))?obj:this, [].concat.call(args, [].slice.call(arguments))); } bound.prototype =Object.create(self); return bound; } function foo(){ console.log(this.name); } var obj = { name: "obj" } var obj1 = { name: "obj1" } var fooobj = foo.softbind(obj, 1); fooobj(); //name: obj obj1.foo = foo.softbind(obj); obj1.foo(); //name: obj1 setTimeout(obj1.foo, 10); //name: obj
可以看到,軟綁定版本的foo()可以手動(dòng)講this綁定到obj1上,但如果應(yīng)用默認(rèn)綁定,則會(huì)將this綁定到obj上
ES6中出現(xiàn)了不同與以上四種規(guī)則的特殊函數(shù)類(lèi)型: 箭頭函數(shù)。它是根據(jù)外層(外層或者全局)作用域來(lái)決定的
function foo(){ return (a) => { //this 繼承來(lái)自foo() console.log(this.a) }; } var obj1 = { a: 2 } var obj2 = { a: 3 } var bar = foo.call(obj1); bar.call(obj2); //2 不是3!
foo()內(nèi)部創(chuàng)建的箭頭函數(shù)會(huì)捕獲調(diào)用時(shí)foo()的this,由于foo()的this綁定到obj1,bar的this也會(huì)綁定到obj1,箭頭函數(shù)的綁定無(wú)法被更改。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/84996.html
摘要:在嚴(yán)格模式下調(diào)用函數(shù)則不影響默認(rèn)綁定。回調(diào)函數(shù)丟失綁定是非常常見(jiàn)的。因?yàn)橹苯又付ǖ慕壎▽?duì)象,稱(chēng)之為顯示綁定。調(diào)用時(shí)強(qiáng)制把的綁定到上顯示綁定無(wú)法解決丟失綁定問(wèn)題。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第三期,本周的主題是this全面解析,今天是第9天。 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重...
摘要:在傳統(tǒng)的面向類(lèi)的語(yǔ)言中,構(gòu)造函數(shù)是類(lèi)中的一些特殊方法,使用初始化類(lèi)是會(huì)調(diào)用類(lèi)中的構(gòu)造函數(shù)。 在上一節(jié)中我們?cè)敿?xì)介紹了this的兩種綁定方式,默認(rèn)綁定和隱式綁定,在這一節(jié)我們繼續(xù)介紹this的另外兩種綁定方式顯示綁定和new綁定。那么,我們要解決的問(wèn)題當(dāng)然就是上一節(jié)中我們提到的:this丟失! 顯式綁定 在隱式綁定中,我們必須在一個(gè)對(duì)象的內(nèi)部包含一個(gè)指向函數(shù)的屬性,并通過(guò)這個(gè)屬性間接引用...
摘要:首次運(yùn)行代碼時(shí),會(huì)創(chuàng)建一個(gè)全局執(zhí)行上下文并到當(dāng)前的執(zhí)行棧中。執(zhí)行上下文的創(chuàng)建執(zhí)行上下文分兩個(gè)階段創(chuàng)建創(chuàng)建階段執(zhí)行階段創(chuàng)建階段確定的值,也被稱(chēng)為。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開(kāi)始前端進(jìn)階的第一期,本周的主題是調(diào)用堆棧,,今天是第一天 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)...
摘要:在嚴(yán)格模式下,對(duì)象的函數(shù)中的指向調(diào)用函數(shù)的對(duì)象實(shí)例顯式綁定,,通過(guò)可以把的綁定到上。間接引用最容易在賦值時(shí)發(fā)生返回目標(biāo)函數(shù)的引用詞法之前介紹的種綁定規(guī)則可以包含所有正常的函數(shù),但是中介紹了一種無(wú)法使用這些規(guī)則的特殊函數(shù)類(lèi)型箭頭函數(shù)。 this到底指向什么? this關(guān)鍵詞是javaScript中最復(fù)雜的機(jī)制之一,一般有兩個(gè)誤區(qū):1.this指向函數(shù)自身;2.this指向函數(shù)的作用域; ...
摘要:調(diào)用棧就是為了到達(dá)當(dāng)前執(zhí)行位置所調(diào)用的所有函數(shù)。由于無(wú)法控制回調(diào)函數(shù)的執(zhí)行方式,因此就沒(méi)有辦法控制調(diào)用位置得到期望的綁定,下一節(jié)我們會(huì)介紹如何通過(guò)固定來(lái)修復(fù)這個(gè)問(wèn)題。 在《你不知道的this》中我們排除了對(duì)于this的錯(cuò)誤理解,并且明白了每個(gè)函數(shù)的this是在調(diào)用時(shí)綁定的,完全取決于函數(shù)的調(diào)用位置。在本節(jié)中我們主要介紹一下幾個(gè)主要內(nèi)容: 什么是調(diào)用位置 綁定規(guī)則 this詞法 調(diào)用...
閱讀 2736·2021-11-11 17:21
閱讀 627·2021-09-23 11:22
閱讀 3591·2019-08-30 15:55
閱讀 1651·2019-08-29 17:15
閱讀 583·2019-08-29 16:38
閱讀 921·2019-08-26 11:54
閱讀 2517·2019-08-26 11:53
閱讀 2764·2019-08-26 10:31