this對象
this總是返回一個對象,也就是返回屬性或者方法當(dāng)前所在的對象。
可以近似地認(rèn)為,this是所有函數(shù)在運行時的一個隱藏參數(shù),指向該函數(shù)在運行時的環(huán)境對象。
如果一個函數(shù)在調(diào)用時,被某一個對象所擁有,那么該函數(shù)在調(diào)用時,內(nèi)部的this指向該對象。
如果函數(shù)獨立調(diào)用,那么該函數(shù)內(nèi)部的this,則指向undefined。
在非嚴(yán)格模式中,當(dāng)this指向undefined時,它會被自動指向全局對象window。
即函數(shù)在調(diào)用時,this總指向它的擁有者(調(diào)用者),若自己獨立調(diào)用,就指向undefined,非嚴(yán)格模式中獨立調(diào)用指向window。
// 使用嚴(yán)格模式
function foo() {
'use strict';
console.log(this);
}
foo(); // foo()獨立調(diào)用 this指向undefined
window.foo(); // foo()被window調(diào)用,this指向window
this指向在運行時確定,一旦確定,不可更改。
var name = 'Tom';
var obj = {
name: 'Leo'
};
function sayName() {
this = obj; // 試圖在函數(shù)運行時修改this
console.log(this.name);
}
sayName();//報錯,函數(shù)在運調(diào)用時已確定this指向,無法更改
賦值操作會改變this指向。
var name = 'Tom'
function sayName() {
console.log(this.name);
}
var obj = {
name: 'Leo',
say: sayName // 將sayName函數(shù)的引用賦值給say
}
obj.say(); //Leo
var foo = obj.say; // 將sayName函數(shù)的的引用賦值給全局變量foo
foo();
// obj.say()在obj中被調(diào)用,this指向obj
// foo()獨立調(diào)用,非嚴(yán)格模式中,this指向window
// 也可以理解為foo()在window中被調(diào)用,因為在全局環(huán)境中foo()==window.foo()
var name = 'Tom';
var obj = {
name: 'Leo',
foo: {
name: 'Bob',
sayName: function () {
console.log(this.name);
}
}
}
obj.foo.sayName(); // Bob
var say = obj.foo.sayName; //將sayName函數(shù)的引用地址賦值給say
say(); // Tom 函數(shù)sayName獨立執(zhí)行,this指向window
var box = obj.foo; // 將對象foo的引用地址賦值給box
box.sayName(); // Bob 函數(shù)sayName在foo對象中執(zhí)行,this指向foo
this指向函數(shù)運行時的環(huán)境對象,與函數(shù)定義時的位置無關(guān)。
var name = 'Tom';
var obj = {
name: 'Leo',
sayName: function () { // sayName函數(shù)在obj中定義
console.log(this.name);
}
};
obj.sayName(); // Leo,在obj中調(diào)用sayName,指向obj
var foo = obj.sayName; // 將sayName引用地址賦值給foo
foo(); // Tom 獨立調(diào)用sayName,this指向window
var box = {
name: 'Bob',
say:foo // 將sayName引用地址賦值給say
};
box.say(); // Bob 在box中調(diào)用sayName,this指向box
分析一些例子
// demo01
function sayName() {
// 'use strict';
console.log(this.name)
}
function func(fn) {
fn(); // 函數(shù)被傳進來后獨立調(diào)用
}
var name = 'Tom';
var obj = {
name: 'Leo',
say: sayName // 函數(shù)sayName引用地址賦值給say
};
func(obj.say); // Tom
// 將sayName函數(shù)傳入func函數(shù),并調(diào)用sayName
// 函數(shù)sayName獨立調(diào)用,非嚴(yán)格模式時函數(shù)中的this為window
// 使用嚴(yán)格模式時,this為undefined,執(zhí)行this.name會報錯
// demo02
var name = 'Tom';
var obj = {
name: 'Leo',
func: function () {
(function () {
//'use strict'
console.log(this.name);
})();
}
}
obj.func();//Tom
// obj.func()調(diào)用時,內(nèi)部自執(zhí)行函數(shù)自動執(zhí)行,沒有調(diào)用者,this指向window
// 使用嚴(yán)格模式時,this為undefined,執(zhí)行this.name會報錯
JavaScript提供了三種綁定this的方法:call、apply和bind。
call方法,第一個參數(shù)是this的指向(即函數(shù)執(zhí)行時所在的作用域),然后在所指定的作用域中,調(diào)用該方法。
call方法的第一個參數(shù)應(yīng)該是一個對象,如果參數(shù)為空、null、和undefined,則默認(rèn)傳入全局對象。
如果傳入的第一參數(shù)是原始值,那么會自動轉(zhuǎn)換成對應(yīng)的包裝類型,然后再傳入call方法。
call第二個及以后的參數(shù)是函數(shù)調(diào)用時所需的參數(shù)。
apply方法和call方法類似,第一個參數(shù)也是this的指向。唯一區(qū)別是它接受一個數(shù)組作為函數(shù)執(zhí)行時的參數(shù)。
apply/call方法不僅綁定函數(shù)執(zhí)行時所在的對象,還會立即執(zhí)行函數(shù),因此要把綁定的語句寫在函數(shù)體內(nèi)。
bind方法將函數(shù)體內(nèi)的this綁定到某個對象,然后每次返回一個新函數(shù)。
bind方法比apply/call方法更進一步,除了綁定this,還可以綁定原函數(shù)的參數(shù)。
function fn(n, m) {
console.log(this.a + n + m);
}
var obj = {
a: 20
}
fn.call(obj, 100, 10); // 130
fn.apply(obj, [20, 10]); // 50
var arr = [8,3,4,1,5,9];
console.log(Math.max.apply(null, arr)); // 9
// 使用call/apply將arguments轉(zhuǎn)換為數(shù)組, 返回結(jié)果為數(shù)組,arguments自身不會改變
var arg = [].slice.call(arguments);
//將DOM中的nodelist轉(zhuǎn)換為數(shù)組
[].slice.call( document.getElementsByTagName('li') );
// 實現(xiàn)繼承
var Person = function (name) {
this.name = name;
}
var Student = function (age) {
this.age = age;
Person.call(this, name);
}
Student.prototype.message = function () {
console.log('name:'+ this.name+', age:'+this.age);
}
new Student('Leo', '26').message();
靈活使用this。
var name = 'Tom';
var obj = {
name: 'Leo',
sayName: function() {
setTimeout(function() {
console.log(this.name)
}, 500)
}
}
obj.sayName(); // Tom*/
// 定時器中回調(diào)函數(shù)獨立調(diào)用,this指向window
// 要獲取obj中的name
// 方法1,使用一個變量,保存this
var obj = {
name: 'Leo',
sayName: function() {
var _this = this;
setTimeout(function() {
console.log(_this.name)
}, 500)
}
}
obj.sayName() // Leo
// 方法2 使用bind (注意:若使用call/apply會立即執(zhí)行回調(diào)函數(shù))
var obj = {
name: 'Leo',
sayName: function() {
setTimeout(function() {
console.log(this.name)
}.bind(this), 500)
}
}
obj.sayName() // Leo
參考資料:
《JavaScript高級程序設(shè)計》
《JavaScript 標(biāo)準(zhǔn)參考教程》
湯姆大叔-深入理解JavaScript系列