系列文章:javascript
this 機制在 javascript 中是動態綁定,或稱爲運行期綁定的。這就致使 JS 中的 this 關鍵字會有多重含義,因此會給咱們形成一誤解。學習 this 的第一步是明白this既不指向函數自身也不指向函數的詞法做用域。java
人們容易把 this 理解成指向函數自身,看下面的代碼bash
function foo(num) {
console.log("foo: " + num
this.count++;
}
// 這裏爲 foo 添加了一個屬性 count,初始化爲 0
foo.count = 0;
for(var i = 0; i < 10; i++) {
if (i > 5) {
foo(i);
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
console.log(foo.count); // 0
// foo 確實被調用了4次,可是 foo.count 仍是 0,
// 是由於函數內部的 this 並非指向那個函數對象,
// 因此雖然屬性名相同,可是根對象卻不相同
複製代碼
this指向函數的做用域,這個問題比較複雜,由於有時它是正確的,有時它的錯誤的。但能夠明確的是:this在任何狀況下,都不指向函數的詞法做用域。閉包
function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log(this.a);
}
foo();
複製代碼
結果是 undefined ,由於這裏經過 this.bar() 來引用 bar 函數,這裏能調用成功是由於:app
⚠️注意:這裏有人會認爲結果是 Reference Error 報錯,其實和 RHS(Javascript進階1--做用域和閉包 內有解釋)混淆了。函數
javascript中,this 是在運行時進行綁定的,它的上下文取決於函數調用時的各類條件。oop
先根據把綁定的優先級拋出結論,按照如下順序進行判斷:post
函數是否在 new 中調用(new 綁定)?若是是的話,this 綁定的是新建立的對象。學習
var bar = new Foo()
ui
函數是否經過 call、apply(顯式綁定)或者硬綁定調用?若是是的話, this 綁定的是指定的對象。
var bar = foo.call(obj2)
函數是否在某個上下文對象中調用(隱式綁定)?若是是的話,this 綁定的是那個上下文對象。
var bar = obj1.foo()
若是都不是,使用默認綁定,嚴格模式下,綁定到 undefined,不然綁定到全局對象。
var bar = foo()
在傳統的面向類的語言中,構造函數是類的一些特殊方法,使用 new 初始化類時,會調用類中的構造函數。
首先,咱們須要澄清的一個誤解是:在JS中,是沒有構造函數的,在普通的函數調用前面加 new 關鍵字以後,就會把這個函數調用變成一個「構造函數調用」。實際上,new 會劫持全部普通函數並用構造函數的形式來調用它。
那麼在JS中,使用 new 來調用函數,var obj = new Foo()
,會執行如下操做:
首先建立一個全新的空對象。
var obj = {}
將空對象的原型 [[prototype]] 賦值爲構造函數的原型.
obj.__proto__ = Foo.prototype
更改 this 的指向。
Foo.call(obj)
若 return 有值,而且值是對象,則直接返回此對象,不然,返回新建立的對象 obj。
如今看一段簡單的代碼檢測一下學習成果
function foo1(a) {
this.a = a;
}
function foo2(name) {
this.name = name;
return {
w:1
};
}
foo2.prototype.getName = function() {
return this.name;
};
var bar = new foo1(2);
console.log(bar.a) // 2
var a = new foo2('hh');
a.getName();
// Uncaught TypeError: a.getName is not a function
// 這是由於,foo2 有返回值,而且是一個對象,因此a值是 {w: 1}
複製代碼
若是想調用函數時,強制把函數的 this 綁定到 obj 上,可使用 call(...) 和 apply(...) 方法,它們的第一個參數一個對象,是給 this 準備的,這稱之爲顯式綁定。
function foo() {
console.log(this.a)
}
var obj = {
a: 2
};
foo.call(obj) //2
複製代碼
⚠️注意:若是傳入了一個原始值(布爾、數字、字符串等類型)來看成 this 的綁定對象,這個原始值會被轉換成它的對象形式(也就是 new Boolean(...)、new Number(...)、new String(...))。
硬綁定就是顯式綁定的一個變種,用於咱們將 this 被永久綁定到 obj 的 foo 函數,這樣咱們就沒必要每次調用 foo 都在尾巴上加上 call 那麼麻煩。
function foo() {
console.log(this.a)
}
var obj = {
a: 2
};
var bindFunc = foo.bind(obj)
bindFunc(); //2
複製代碼
call/apply 與 bind 的區別是:call、apply將當即執行該函數,bind 不執行函數,只返回一個可供執行的函數。
當一個函數被一個對象調用時,會把函數調用中的 this 綁定到這個上下文對象中。
fucntion foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
obj.foo() //2
複製代碼
⚠️注意:當咱們使用隱式綁定規則時,要注意下面兩點:
function foo() {
console.log(this.a);
}
function doFoo(fn) {
fn();
}
var obj = {
a: 2,
foo: foo
};
var a = 'oops, global';
// obj.foo 引用 foo 函數自己,應用默認綁定
doFoo(obj.foo); // oops, global
var bar = obj.foo;
// bar 引用 foo 函數自己,應用默認綁定
bar(); // oops, global
複製代碼
當直接使用不帶任何修飾的函數引用進行函數調用時,只能使用默認綁定,沒法應用其餘規則。在嚴格模式下,this 會綁定到 undefined,在非嚴格模式下, this會綁定到全局對象。
若是把 null 或者 undefined 做爲 this 的綁定對象傳入 call、apply或者 bind,會應用默認綁定規則。
箭頭函數根據外層(函數或全局)做用域來決定 this。
function foo() {
return (a) => {
// 繼承自 foo()
console.log(this.a)
};
}
var obj1 = {
a: 2
};
var obj2 = {
a: 3
};
var bar = foo.call(obj1);
bar.call(obj2) // 2
複製代碼
⚠️注意:箭頭函數的綁定沒法被修改
練習題1:
function Foo() {
getName = function() {
console.log(1);
};
return this;
}
// 靜態方法賦值
Foo.getName = function() {
console.log(2);
};
Foo.prototype.getName = function() {
console.log(3);
};
var getName = function() {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName(); //2
// 變量聲明提高,賦值語句留在原地,因此結果是4
getName(); //4
// 在Foo函數中,重寫了全局做用域下的 getName 函數
Foo().getName(); //1
getName(); //1
// 等價於new (Foo.getName)(), 運算符優先級:成員訪問 > new(不帶括號) > 函數調用
new Foo.getName(); //2
// 等價於 (new Foo()).getName(),運算符優先級:成員訪問、new(帶括號)的優先級同樣,因此從左到右執行。
// 使用 new 實例 Foo 後生成的實例上,只有原型方法,沒有靜態方法
new Foo().getName();
複製代碼
練習題2:
var title = 'world';
var a = {
title: 'hello',
alias: this.title,
show() {
console.log(this.title, this.alias)
},
ashow: () => {
console.log(this.title, this.alias)
}
}
a.show(); // hello world
var b = a.show;
b(); // world undefined
a.ashow(); // world undefined
複製代碼