this關鍵字是javascript中最複雜的機制之一。它是一個很特別的關鍵字,被自動定義在全部函數的做用域中。this既不指向函數自己也不指向函數的語法做用域。
this是在函數被調用時發生的綁定,this的綁定和函數聲明的位置沒有任何關係,它指向什麼徹底取決於函數在哪裏被調用。
javascript
調用位置:是函數在代碼中被調用的位置,而不是聲明的位置。
* 默認綁定 * 隱式綁定 * 顯式綁定 * new綁定 優先級排序:new綁定 > 顯式綁定 > 隱式綁定 > 默認綁定
沒法應用其它規則時的默認規則
若是使用嚴格模式(strict mode),則不能將全局對象用於默認綁定,所以this會綁定到undefined。
最經常使用的函數調用類型:獨立函數調用。java
function foo(){ console.log(this.a) } var a = 2; foo() //2 >函數調用時應用了this的默認綁定,所以this指向全局對象 'tips: 聲明在全局做用域中的變量就是全局對象的同名屬性。' //嚴格模式 example 1 function (){ "use strict"; console.log(this.a) } var a = 2; foo(); //TypeError:this is undefined //嚴格模式 example 2 function foo(){ console.log(this.a) } var a = 2; (function(){ "use strict"; foo(); //2 })();
隱式綁定規則會把函數調用中的this綁定到這個上下文對象。
當函數引用上下文對象時,對象屬性引用鏈中只有上一層或者說最後一層在調用位置中起做用。es6
//example 1: function foo(){ console.log(this.a) } var obj = { a: 2, foo: foo } obj.foo();//2 //example 2: var obj2 = { a: 42, foo: foo } var obj1 = { a: 2, obj2: obj2 } obj1.obj2.foo();//42
直接指定this的綁定對象稱爲顯式綁定
常見的顯式綁定方法有 call()
和 apply()
編程
call和apply的使用方法: function.call(thisArg, arg1, arg2, ...) //thisArg 在 f unction 函數運行時使用的 this 值 //arg1, arg2, ... 指定的參數列表。 func.apply(thisArg, [argsArray]) //thisArg 在 f unc 函數運行時使用的this值, //若是這個函數處於非嚴格模式下,則指定爲 null 或 undefined 時會自動替換爲指向全局對象,原始值會被包裝。 //argsArray 一個數組或者類數組對象,其中的數組元素將做爲單獨的參數傳給'func'函數, //若是該參數的值爲 null 或 undefined,則表示不須要傳入任何參數。 兩者區別 call()方法接受的是一個參數列表,而apply()方法接受的是一個包含多個參數的數組。
function foo(){ console.log(this.a) } var obj={ a: 2 } foo.call(obj); //2 調用foo時強制把它的this綁定到obj上
裝箱
:若是你傳入了一個原始值(字符串類型、布爾類型或數字類型)來看成this的綁定對象,這個原始值會被轉換成它的對象形式(也就是new String(...)、new Boolean(...)或new Number(...))segmentfault
1>硬綁定 //硬綁定--顯式的強制綁定 function foo(){ console.log(this.a) } var obj = { a:2 } var bar = function(){ foo.call(obj) } bar(); //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 } //簡單的輔助綁定函數 function bind(fn,obj){ return function(){ return fn.apply(obj,arguments) } } var obj = { a:2 } var bar = bind(foo,obj); var b = bar(3) //2 3 console.log(b) //5 //因爲硬綁定是一種很是經常使用的模式,因此ES5提供了內置的方法Function.prototype.bind function foo(something){ console.log(this.a,something) return this.a+something } var obj={ a:2 } var bar = foo.bind(obj); //bind(...)會返回一個硬編碼的新函數,它會把你指定的參數設置爲this的上下文並調用原始函數 var b=bar(3); //2 3 console.log(b); //5 bind(...)的功能之一就是能夠把除了第一個參數(第一個參數用於綁定this)以外的其餘參數都傳給下一層的函數 2>API調用的「上下文」 第三方庫的許多函數,以及Javascript語言和宿主環境中許多新的內置函數,都提供了一個可選的參數,一般被稱爲「上下文」(context),其做用和bind(...)同樣,確保你的回調函數使用指定的this。
在Javascript中,構造函數只是一些使用new操做符時被調用的函數
。它們並不會屬於某個類,也不會實例化一個類。它們只是被new操做符調用的普通函數。實際上並不存在所謂的「構造函數」,只有對於函數的「構造調用」。
數組
function foo(a){ this.a = a } var bar = new foo(2) console.log(bar); //2
使用new調用函數,或者說發生構造函數調用時,會自動執行下面的操做:
一、建立(或者說構造)一個全新的對象
二、這個新對象會被執行[[Prototype]]鏈接
三、這個新對象會綁定到函數調用的this
四、若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象安全
一、若是你把null或者undefined做爲this的綁定對象傳入call、apply或者bind,這些值在調用時會被忽略,實際應用的是默認綁定規則。
一種安全的作法是傳入一個特殊對象,把this綁定到這個對象不會對你的程序產生任何反作用。
在Javascript中建立一個空對象最簡單的方法都是Object。create(null)。Object。create(null)和{}很像,可是並不會建立Object.prototype這個委託,因此它比{}更空。app
二、有可能建立一個函數的「間接引用」,在這中狀況下,調用這個函數會應用默認綁定規則。編程語言
function foo(){ console.log(this.a) } var a =2 var o = { a: 3, foo: foo } var p = { a: 4 } 0.foo(); //3 (p.foo = o.foo)(); //2 >>p.foo = o.foo的返回是目標函數的引用,所以調用位置是foo()而不是p.foo()或者o.foo()
三、軟綁定 Function.prototype.softBind函數
if(!Function.prototype.softBind){ Function.prototype.softBind = function(obj){ var fn = this; //捕獲全部的 curried 參數 var curried = [].slice.call(arguments,1); var bound = function(){ return fn.apply( (!this || this === (window || global)) ? obj : this, curried.concat.apply(curried,arguments) ) } bound.prototype = Object.create(fn.prototype) return bound } }
以前介紹的4條規則能夠包含全部正常的函數,可是在es6中介紹了一種沒法使用這些規則的特殊函數:箭頭函數。
即Currying的音譯。Currying是編譯原理層面實現多參函數的一個技術。
Currying 爲實現多參函數提供了一個遞歸降解的實現思路——把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數並且返回結果的新函數
,在某些編程語言中(如 Haskell),是經過 Currying 技術支持多參函數這一語言特性的。
先找到這個函數的直接調用位置,而後順序應用下面這4條規則判斷this的綁定對象:
文章摘取來源:《你不知道的JavaScript上卷》