恢復內容開始 一、this是什麼東東? this是指包含它的函數作為方法被調用時所屬的對象。這句話理解起來跟卵一樣看不懂,但是如果你把它拆分開來變成這三句話後就好理解一點了。 1.包含它的函數 2.作為方法被調用時 3.所屬的對象 二、this的綁定規則 1、預設綁定 2、隱式綁定 先看兩段例子 下 ...
---恢復內容開始---
一、this是什麼東東?
this是指包含它的函數作為方法被調用時所屬的對象。這句話理解起來跟卵一樣看不懂,但是如果你把它拆分開來變成這三句話後就好理解一點了。
1.包含它的函數
2.作為方法被調用時
3.所屬的對象
二、this的綁定規則
1、預設綁定
var x = 0; function num(){ this.x = 1; } console.log(this.x);//0 num(); console.log(this.x);//1
//當沒有使用let或者var時,聲明的變數是全局變數,指向window,
//在這一形態下,其函數內部的this是與全局作用域時一樣,直接指向window,執行完num()後,更改了x的值,所以其形態 依然等價於window。
function foo(){
console.log(this.a) } var a = 2; foo(); //2 console.log(this.document === document); // true // 在瀏覽器中,全局對象為 window 對象: console.log(this === window); // true this.a = 3; console.log(window.a); // 3 this指向全局對象。在非嚴格模式中,當funcion被不帶任何修飾的函數直接引用進行調用的,只能使用預設綁定,無法應用其他規則
2、隱式綁定
先看兩段例子
function foo(){ console.log(this.a) } var obj = { a:2, foo:foo } obj.foo() //2
//隱式綁定————調用位置使用obj上下文來引用函數,可以說函數被調用時obj對象“擁有”或者“包含”它,它就指向誰。
function foo(){ console.log(this.a) } var obj2 = { a:42, foo:foo } var obj1 = { a:2, obj2:obj2,
foo:foo }
obj1.foo() //2 obj1.obj2.foo() //42 //此時可以控制台查看obj1,obj2對象里究竟包含了什麼
//當函數引用有上下文對象時,隱式綁定規則會把函數調用中的this綁定到這個上下文對象
//對象屬性引用鏈中只有最後一層在調用位置中起作用
下麵思考這一段會輸出什麼呢?
function foo(){ console.log(this.a) } var obj = { a:2, foo:foo } var bar = obj.foo; //這裡bar將引用foo函數本身,所以不帶有函數對象的上下文,this會直接指向window
bar() //?
//為什麼沒有隱式綁定?這種情況稱為隱式丟失。
//因為bar=obj.foo 而obj.foo指向foo 也就是bar = function foo(){console.log(this.a)} foo中的this指向window,
//在window中並沒有對a進行定義,so,結果是undefined
接下來再看一段會是什麼結果呢?(參數傳遞時的隱式賦值)
function foo(){ console.log(this.a) } function doback(fn){ fn() } var obj = { a:2, foo:foo } var a = 'global'; doback(obj.foo) //? 顯然答案是global,但是為什麼呢?請繼續往下看!
//隱式丟失--被隱式綁定的函數會丟失綁定對象然後應用預設綁定。
//最後函數執行 doback(obj.foo)時,會進行參數傳遞,也就是 fn = obj.foo,就和上一個例子一樣了。既this指向的是window。
3、顯示綁定
function foo(){ console.log(this.a) } var obj = { a:2 }
foo.call(obj) //2
//顯式綁定--第一個參數是一個對象,接著在調用函數時將其綁定到this,通過foo.call(obj),我們可以在調用foo時強制把他的this綁定到obj上
function foo(){ console.log(this.a) } var obj = { a:2 } var a = '3333333'; var bar = function(){ foo.call(obj) }
bar() // 2
bar.call(window) //2
// 硬綁定後bar無論怎麼調用,都不會影響foo函數的this綁定
// 通過創建函數bar(),併在內部調用foo.call(obj),強制把foo的this綁定到obj上。硬綁定的bar之後無論如何調用函數,都只會在obj上調用foo。
我們來看一下他的應用:
function foo(num) { console.log( this.a, num); return this.a + num; } var obj = { a: 2 }; var bar = function() { return foo.call( obj, ...arguments ); // 將obj對象硬編碼進去
//return foo.apply( obj, arguments ); // 也可以使用apply將obj對象硬編碼進去
}; var b = bar( 3 ); // 2 3 console.log( b ); // 5
function fn1(){ console.log(1); } function fn2(){ console.log(2); } fn1.call(fn2); //輸出 1 fn1.call.call(fn2); //輸出 2 這個暫時沒有搞懂,但是東鴿子大師中有講解,感興趣的小伙伴可以看看。
4、new綁定
我們不去深入瞭解構造函數,但要知道new來調用函數,或者說發生構造函數調用時,執行了哪些
當代碼 new foo(...) 執行時:
(1) 創建一個新對象,它繼承自foo.prototype.
(2) 將構造函數的作用域賦給新對象(因此 this 就指向了這個新對象);new foo 等同於 new foo(),只能用在不傳遞任何參數的情況。
(3) 執行構造函數中的代碼(為這個新對象添加屬性) ;
(4) 返回新對象, 那麼這個對象會取代整個new出來的結果。如果構造函數沒有返回對象,那麼new出來的結果為步驟1創建的對象。
function foo(a){ this.a = a; } var bar = new foo(2); //創建一個新對象bar,它繼承了foo.prototype. 也就是bar這個對象有a這個屬性,且傳進去的是2,使用new來調用foo(..)時,會構造一個新對象,並把它綁定到foo(..)調用中的this上
console.log(bar.a) //2
三、下麵做一個小練習看看你學會了嗎?
例一:
function foo(a){ console.log(this.a) } var obj1 = { a:2, foo:foo } var obj2 = { a:3, foo:foo } obj1.foo() //? obj2.foo() //? obj1.foo.call(obj2) //? obj2.foo.call(obj1) //?
答案:
obj1.foo() //2 隱式綁定
obj2.foo() //3 隱式綁定
obj1.foo.call(obj2) //3 顯式綁定
obj2.foo.call(obj1) //2 顯式綁定
通過答案得出:顯示綁定 > 隱式綁定
顯示綁定優先順序更高,所以在判斷時 應當 優先考慮 是否 存在 顯示綁定
例二:
function foo(someting){ this.a = someting } var obj1 = { foo:foo } var obj2 = {}
obj1.foo(2)
var bar = new obj1.foo(4)
console.log(obj1.a)//? console.log(bar.a)//?
答案:
console.log(obj1.a) //2
console.log(bar.a) //4
通過答案得出: new綁定 > 隱式綁定
顯示綁定優先順序更高,所以在判斷時 應當 優先考慮 是否 存在 new綁定
需要註意的是:new和call/apply無法一起使用,因此無法通過new foo.call(obj)來直接測試
function foo(someting){ this.a = someting } var obj1 = {} var bar = foo.bind(obj1) //不知道bind()方法的同學可以直接點擊此處查看最騷的就是你同學貢獻的詳解。 bar(2) console.log(obj1.a)//? var baz = new bar(3) console.log(obj1.a)//? console.log(baz.a)//?
答案:2 2 3
通過答案得出:new綁定 > 顯示綁定
new修改了硬綁定調用bar(..)中的this,因為使用了new綁定,得到了一個名字為baz的新對象,並且baz.a的值為3.
所以 new綁定 > 顯示綁定 > 隱式綁定 > 預設綁定
在此特別鳴謝同事Jason大哥的share!!!
---恢復內容結束---