原文地址javascript
一般所說的:若是是全局環境中,this指向全局對象,若是是對象的方法,這this指向這個對象。html
例子1:java
var foo = { bar: function() { console.log(this) } } foo.bar(); (foo.bar)(); (foo.bar = foo.bar)(); (false || foo.bar)(); (foo.bar, foo.bar)();
例子1前二者爲foo,後面都是全局對象。後三者並無指向foo。因此咱們上面的一般說法不精確。git
在全局環境中,this指向全局對象。而在普通函數調用中,this是由激活上下文的調用者提供,即調用這個函數的父做用域,以及函數調用的語法形式,決定了this的值,這是一個動態可變的值。github
例子2:編程
var foo = { bar: function() { console.log(this) console.log(this === foo) } } foo.bar() // foo, true var fn = foo.bar console.log(fn === foo.bar) // true fn() // global, false
例子2中,第一次調用指向foo,把foo.bar
賦值給fn以後,this沒有指向foo。是什麼致使this指向的變化呢?數組
this
是執行上下文的一個屬性:緩存
activeExecutionContext = { VO: {...}, this: thisValue }
在普通函數調用中,this是由激活上下文的調用者提供,即調用這個函數的父做用域,函數調用的語法形式,決定了this的值,這是一個動態可變的值。閉包
爲何會引發這個差別呢?
由於引用類型的不一樣處理,是否會獲取真實的值,所致使的。app
引用類型存在形式:
1 標識符(變量名,函數名,函數參數名,全局對象屬性名)
2 屬性訪問器(foo.bar(); foo['bar']()
, 點標記法;能夠動態設置屬性名的方括號[]
)
爲了從引用類型中獲取真實的值,存在相似getValue
的方法。而函數上下文的規則是,函數上下文中this由調用者提供,並由調用形式決定。若是調用的圓括號左側是一個引用類型,this爲這個引用類型,若是是非引用類型,這爲null,但爲null無心義,被隱式轉化爲全局對象。
this是一個指針,便於代碼的更爲簡潔地複用。
// 無this function upper(context) { return context.name.toUpperCase() } function speak(context) { var greeting = "Hello, I'm " + upper(context) console.log(greeting) } var me = { name: 'm' } var you = { name: 'y' } speak(me) // 利用this function upper() { return this.name.toUpperCase() } function speak() { var greeting = "Hello, I'm " + upper.call(this) console.log(greeting) } speak.call(me)
這裏this能夠簡化上下文對象的傳遞。其餘OPP語言中this關鍵字和OPP密切相關,通常是引用剛建立的對象,但在ECMAScript中,this只限於引用建立過的對象,this的指向和函數調用形式有關,不必定引用類型調用就指向引用類型。
1 構造函數中的this
function C() { console.log(this) this.x = 10 } var a = new C() console.log(a.x);
new操做符會調用函數的內部的Construct方法,建立對象,以後調用函數的Call方法,把新建立對象做爲this值。
2 調用函數時call與apply設置this的值
var b = 10 function a(c) { console.log(this.b) console.log(c) } a(20) a.call({b: 20}, 30) a.apply({b: 20}, [40])
call,apply,bind皆爲動態的改變this指針的方法。其中call和apply是當Object沒有某個方法,可是其它對象有,能夠藉助call和apply改變this的指向,調用其它對象的方法。bind爲綁定this爲某個對象。
典型的應用:
將類數組元素轉化爲數組:Array.prototype.slice.apply(document.getElementsByTagName('*'))
檢查類型:
function isArray(obj) { return Object.prototpye.toString.call(obj) === '[object Array]' }
箭頭函數則與前三者不一樣。
If kind is Arrow, set the [[ThisModel]]
internal slot of F to lexical.If the
value is "lexical", this is an ArrowFunction and does not have a local this
If thisModel is lexical, return NormalCompletion(undefined).
箭頭函數沒有本身的this綁定,同時在函數執行時綁定this會被直接忽略。其中this老是指向定義時所在的對象,而不是運行時所在的對象。即箭頭函數的this值是lexical
scope 的this值。這一特性使得箭頭函數在React中的render函數中使用起來很方便。
function foo() { setTimeout(() => { console.log('id: ', this.id) }, 100) } var id = 0 foo.call({id: 42}) // 容易誤解的地方 // {id: 42} // 是箭頭函數定義所在的對象仍是運行時所在的對象。因爲箭頭函數位於foo函數內部,只有foo函數運行以後他纔會生成,因此foo運行時所在的對象,即箭頭函數定義所在的對象。
var f = () => 5; // 近似等價於 var f = function() {return 5;}.bind(this);
綜上,call,apply,bind使得JavaScript具備動態改變this的特性,而箭頭函數使得JavaScript具備固定this的指向的特性。一動一靜,相得益彰。
::
this.x = 0 let module = { x: 1, getX: function() { console.log(this.x) } } module.getX() let get = module.getX get() // 0 let boundGetX = get.bind(module) boundGetX() // 1 let ES7boundGetx = module::get ES7boundGetx() // 1
class P { foo() { console.log('P.foo') } } class C extends P { foo() { super.foo() } } var c1 = new C() c1.foo() // P.foo var D = { foo: function() { console.log('D.foo') } } var E = { foo: C.prototype.foo } Object.setPrototypeOf(E, D) E.foo() // P.foo
可見super的綁定是靜態綁定,建立時即完成綁定。因此E委託了D,但並不能調用到D.foo()
,相似於箭頭的函數的this綁定。
鏈式調用的實現;
function Constructor() { this.art = 0 } Constructor.prototype.fn_0 = function() { console.log('0') return this; } Constructor.prototype.fn_1 = function() { console.log('1') return this; } new Constructor().fn_0().fn_1()
調用的方法返回this便可。
end()
的實現
function end() { return this.prevObject || this.constructor(null) } // 設置preObject的函數 function pushStack( ele ) { // Build a new jQuery macthed element set var ret = jQuery.merge( this.constructor(), elems); ret.prevObject = this // ret.pervObject 設置爲當前jQuery對象引用 ret.context = this.context return ret; }
pushStack函數在不少涉及DOM操做的函數都有調用,用於緩存了當前的this。因爲只存儲當前,因此這裏只須要一個preObject便可,無需放在一個數組裏。
this是JavaScript特性之一,具備腳本語言的動態特性,帶來不少便捷,同時因爲super和箭頭函數的特性,使得this具備了靜態的特性,在這兩種狀況下,this是固定且沒法改變的。其利與弊都是this的靈活,雙刃劍。因此纔有了ES2015中super和箭頭函數的固定this的特性。
this可被從新賦值麼?(不能,this是保留字)
1 call參數爲null時,this的指向
function a() { console.log(this) } a.call(null)
2 調用形式對this的影響
var foo = { bar: function() { console.log(this) } } foo.bar(); (foo.bar)(); (foo.bar = foo.bar)(); (false || foo.bar)(); (foo.bar, foo.bar)();
《你所不知道的JavaScript(上卷)》