javascript之this綁定
基本知識
call-site
- 函數或子程序的
調用站點
並非是它們聲明的地方,而是調用函數或者是通過動態調度調用的位置。
- 函數或子程序的
call-stack
- 調用堆棧是一個方法列表,按調用順序保存所有在運行期被調用的方法
function baz(){
//調用棧是 `baz`
bar(); //`bar`的調用站點
}
function bar(){
//調用棧是 `baz`->`bar`
foo(); //`foo`的調用站點
}
function foo(){
//調用棧是 `baz`->`bar`->`foo`
console.log("foo");
}
baz(); //baz的調用站點
綁定規則
默認綁定
function foo(){ console.log(this.a); } var a=2; foo(); //2 function bar(){ var a=6; foo(); } bar(); //2
當
foo()
被調用時,this.a
決定全局變量a
。因爲在這種情況下,this
與函數調用相關,this
始終指向全局對象如果在
strict mode
的影響下,全局對象將不會是默認綁定,相反this
將會被設置成undefined
function foo(){ "use strict"; console.log(this.a); } var a=2; foo(); //TypeError: this is undefined
隱式綁定
- 如果函數作爲對象的方法調用,this指向的是這個上級對象,即調用方法的對象
function foo(){ console.log(this.a); } var obj1={ a:2, foo:foo }; obj1.foo() //2 var obj2={ a=42, obj1:obj1 } obj2.obj1.foo() //2
- 不管
foo
是在obj
中聲明的還是作爲引用加入的,這個函數都已經屬於obj
對象
隱式失綁
- 當一個隱式綁定失去綁定轉化爲默認綁定時,,this綁定將會根據是否爲
strict mode
變爲全局對象或者是undefined
function foo(){ console.log(this.a); } function doFoo(fn){ //`fn`僅僅是一個`foo`的引用 fn(); //<-- call-site } var obj={ a:2, foo:foo }; var a="oops,global";//`a`同樣也是一個全局對象的一個屬性 doFoo(obj.foo); //"oops,global" setTimeout(obj.foo,100);//"oops,global"
- 當一個隱式綁定失去綁定轉化爲默認綁定時,,this綁定將會根據是否爲
顯式綁定
function foo(){ console.log(this.a); } var obj={ a:2 }; foo.call(obj); //2
通過顯式綁定
foo.call()
,在調用foo
時允許我們強制將this
賦值爲obj
如果你將一個基本的類型作爲
this
的綁定值,那麼這些基本值會轉換成相應的形式(new String(..)
,new Boolean(..)
,new Number(..)
),這通常被叫做裝箱
硬綁定
- 硬綁定是顯式綁定的一種形式,這種綁定易讀並且健壯
function foo(){ console.log(this.a); } var obj={ a:2, foo:foo }; var bar=function(){ foo.call(obj); }; bar(); // 2 setTimeout(bar,100); // 2 //`bar`使得`foo`的`this`始終與`obj`相綁定,並且不能被覆蓋 bar.call(window); //2
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); var b=bar(3); // 2 3 console.log(b); // 5
API調用環境
- 許多庫函數和許多javascript語言裏新增的內嵌函數提供了一個可供選擇的參數,通常叫做’上下文‘,它被用來確保你的回調函數在不使用
bind(..)
的情況下使用一個指定的this
function foo(el){ console.log(el,this.id); } var obj={ id:"awesome" }; //對於`foo`的調用使用`obj`作爲`this` [1,2,3].forEach(foo,obj); // 1 awesome 2 awesome 3 awesome
- 許多庫函數和許多javascript語言裏新增的內嵌函數提供了一個可供選擇的參數,通常叫做’上下文‘,它被用來確保你的回調函數在不使用
new
綁定在傳統的面嚮對象語言中,構造器是類的一個特殊的方法,當類通過
new
運算符來實例化時,構造器通常像這樣被調用something = new MyClass(..);
。javascript也有new
運算符,但與此不同。在javascript中,構造器僅僅是和new
運算符一起調用的函數,它不和類關聯,也不能初始化對象,僅僅是常規的函數。舉個例子,當Number(..)
函數被調用作爲new
運算符的一部分,它作爲一個構造器初始化一個新創造的對象。當一個函數
Foo
和new
運算符一起被觸發,或者說作爲構造器被調用,下列的事情將會被自動完成- 一個繼承自
Foo.prototype
的新對象被創建。 - 使用指定的參數調用構造函數
Foo
,並將this
綁定到新創建的對象。new Foo
等同於new Foo()
,也就是沒有指定參數列表,Foo
不帶任何參數調用的情況。 - 由構造函數返回的對象就是
new
表達式的結果。如果構造函數沒有顯式返回一個對象,則使用步驟1創建的對象。(一般情況下,構造函數不返回值,但是用戶可以選擇主動返回對象,來覆蓋正常的對象創建步驟)
function Foo(a){ this.a=a; } var bar=new Foo(2); console.log(bar.a); // 2
- 一個繼承自
綁定規則優先級
new
綁定- 顯式綁定
- 隱式綁定
- 默認綁定