摘要:當我們不想再對象內部間接包含引用函數,而像在某個對象上強制調用函數。我們可以用中內置的和的方法來實現,這兩個方法的第一個參數是一個對象,是給準備的,接著再調用函數時將其綁定到。
this是什么
在javascript中,每個執行上下文可以抽象成一組對象
而this是與執行上下文相關的特殊對象,任何對象都可以用作this上下文的值,一個重要的注意事項就是this值是執行上下文的屬性,但不是變量對象的屬性。這樣的話,與變量相反,this值不會參與標識符解析,即在訪問代碼時,他的值直接來自執行上下文,也沒有任何作用域鏈查找,在進入上下文中,this只能確定一次。所以this的值是和其所處的上下文環境有關系。
this全面解析在歷屆this的綁定之前,首先要理解調用位置,調用位置就是函數在代碼中內調用的位置(而不是聲明的位置),例子:
function baz(){ // 當前調用棧是: baz // 因此,當前調用位置是全局作用域 console.log("baz"); bar(); // <-- bar的調用位置 } function bar(){ // 當前調用棧是baz -> bar // 因此調用位置在baz中 console.log("bar"); foo();// <-foo的調用位置 } function foo(){ // 當前調用棧是baz -> bar -> foo // 調用位置在bar中 console.log("foo"); } baz() // <- baz的調用位置
首先要介紹的最常見的函數調用類型:獨立函數調用。可以把這條規則看作是無法應用其他規則時的默認規則
function foo(){ console.log(this.a); } var a = 2; foo(); //2
因為foo()在全局執行上下文中調用,所以this指向全局變量
如果使用嚴格模式,則不能將全局對象用于默認綁定,因此this會綁定到undefined:
function foo(){ "use strict" console.log(this.a); } var a = 2; foo(); //error
雖然this的綁定規則完全取決于調用位置,但是只有foo()運行在非嚴格模式下時。默認綁定才能綁定到全局對象;在嚴格模式下調用foo()則不影響默認綁定
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();
當函數引用有上下文對象時,隱式綁定規則會把函數調用中的this綁定到這個上下文對象,因為調用foo()時this被綁定到obj,因此this.a和obj.a是一樣的
對象屬性引用鏈中只有上一層或者說最后一層在調用位置起作用
function foo(){ console.log(this.a); } var obj2 = { a: 42, foo: foo } var obj1 = { a: 2, obj2: obj2 } obj1.obj2.foo(); //42
被隱式綁定的函數會丟失綁定對象,也就是說他會應用默認綁定,從而把this綁定到全局對象或者undefined
function foo(){ console.log(this.a); } var obj = { a:2, foo: foo }; var bar = obj.foo; //函數別名 var a = "oops, global"; //a是全局對象的屬性。 bar(); //"oops, global"
雖然bar引用了obj.foo這個引用,但實際上他引用的是foo函數的本身。也就是說bar()是一個在全局上下文中調用的函數,因此this指向了全局對象。
這種情形頁出現在參數傳遞中。
function foo(){ console.log(this.a); } function doFoo(fn){ fn(); } var obj = { a: 2, foo: foo }; var a = "oops, global"; //a是全局對象的屬性 doFoo(obj.foo);
參數傳遞其實就是一種隱式賦值,因此我們傳入函數也會被隱式賦值。
在分析隱式綁定時,我們必須在一個對象內部包含一個指向函數的屬性,并通過這個屬性間接引用函數,從而把this間接(隱式)綁定到這個都對象上。
當我們不想再對象內部間接包含引用函數,而像在某個對象上強制調用函數。我們可以用javascript中內置的apply和call的方法來實現,這兩個方法的第一個參數是一個對象,是給this準備的,接著再調用函數時將其綁定到this。因為你可以直接指定this的綁定對象,因此我們稱之為顯式綁定。
function foo(){ console.log(this.a); } var obj = { a: 2 } foo.call(obj); //2
通過foo.call(...)我們可以在調用foo時強制把他的this綁定到obj上。如果你傳入一個原始值(字符串類型,布爾類型或者數字類型)來當作this的綁定對象,這個原始值會被轉換成他的對象形式,也就是“裝箱”
我們通過顯示綁定的變種解決綁定丟失的問題
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
硬綁定的典型應用場景就是創建一個包裹函數,負責接收參數并返回值
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
另一種方式則是創建一個可以重復使用的輔助函數
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的指向,現在來講講他們的區別以及ES5新增的方法bind
apply和call之間最主要的區別在于傳入參形式的不同。他倆的第一個參數都是指定了函數體內的this指向。
而第二個參數apply傳入為一個帶下標的集合,這個集可以為數組,也可以為類數組。apply方法把這個集合中的元素作為參數傳遞給被調用的函數
var func = function(a,b,c){ alert([a, b, c]); //1 2 3 } func.apply(null, [1, 2, 3])
call傳入的參數數量不固定,跟apply相同的是,第一個參數也是函數體內的this指向,從第二個參數開始往后,每個參數依次傳入函數。
var func = function(a, b, c){ alert([a, b, c]); //1 2 3 } func.call(null, 1, 2, 3);
當使用call或者apply的時候,如果我們傳入的第一個參數為null,函數體內的this會指向默認的宿主對象,在瀏覽器是window
大多數的高級瀏覽器已經實現了bind方法用來指定函數內部的this的指向
function foo(){ console.log(this.a); } var obj = { a: 2 } var bar = foo。bind(obj); bar(); //2
bind(..)會返回一個硬編碼的新函數,他會把你指定的參數設置為this的上下文并調用原始函數
我們也可以用apply模仿一個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中,構造函數只是一些使用new操作符時調用的函數,它們并不會屬于某個類,也不會是實例化一個類。
使用new來調用函數,或者說發生構函數調用時,會自動執行下面的操作
1.創建(或者說構造)一個去全新的對象。
2.這個新對象會被執行[[prototype]]連接
3.這個新對象會綁定到函數調用的this
4.如果函數沒有其他返回對象,那么new表達式中的函數調用會自動返回這個新對象。
function foo(a){ this.a = a; } var bar = new foo(2); console.log(bar.a); //2
前面我們說過硬綁定這種方式綁定之后無法修改this值,會降低函數靈活性。
如果可以給默認綁定指定一個全局對象和undefined以外的值,那就可以實現和硬綁定相同過的效果,同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()可以手動講this綁定到obj1上,但如果應用默認綁定,則會將this綁定到obj上
ES6中出現了不同與以上四種規則的特殊函數類型: 箭頭函數。它是根據外層(外層或者全局)作用域來決定的
function foo(){ return (a) => { //this 繼承來自foo() console.log(this.a) }; } var obj1 = { a: 2 } var obj2 = { a: 3 } var bar = foo.call(obj1); bar.call(obj2); //2 不是3!
foo()內部創建的箭頭函數會捕獲調用時foo()的this,由于foo()的this綁定到obj1,bar的this也會綁定到obj1,箭頭函數的綁定無法被更改。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84996.html
摘要:在嚴格模式下調用函數則不影響默認綁定。回調函數丟失綁定是非常常見的。因為直接指定的綁定對象,稱之為顯示綁定。調用時強制把的綁定到上顯示綁定無法解決丟失綁定問題。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第三期,本周的主題是this全面解析,今天是第9天。 本計劃一共28期,每期重點攻克一個面試重...
摘要:在傳統的面向類的語言中,構造函數是類中的一些特殊方法,使用初始化類是會調用類中的構造函數。 在上一節中我們詳細介紹了this的兩種綁定方式,默認綁定和隱式綁定,在這一節我們繼續介紹this的另外兩種綁定方式顯示綁定和new綁定。那么,我們要解決的問題當然就是上一節中我們提到的:this丟失! 顯式綁定 在隱式綁定中,我們必須在一個對象的內部包含一個指向函數的屬性,并通過這個屬性間接引用...
摘要:首次運行代碼時,會創建一個全局執行上下文并到當前的執行棧中。執行上下文的創建執行上下文分兩個階段創建創建階段執行階段創建階段確定的值,也被稱為。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第一期,本周的主題是調用堆棧,,今天是第一天 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進...
摘要:調用棧就是為了到達當前執行位置所調用的所有函數。由于無法控制回調函數的執行方式,因此就沒有辦法控制調用位置得到期望的綁定,下一節我們會介紹如何通過固定來修復這個問題。 在《你不知道的this》中我們排除了對于this的錯誤理解,并且明白了每個函數的this是在調用時綁定的,完全取決于函數的調用位置。在本節中我們主要介紹一下幾個主要內容: 什么是調用位置 綁定規則 this詞法 調用...
閱讀 2722·2021-11-11 17:21
閱讀 613·2021-09-23 11:22
閱讀 3578·2019-08-30 15:55
閱讀 1641·2019-08-29 17:15
閱讀 573·2019-08-29 16:38
閱讀 904·2019-08-26 11:54
閱讀 2504·2019-08-26 11:53
閱讀 2750·2019-08-26 10:31