
前言
本來this應該放在《上下文環(huán)境和作用域》中來講,結果發(fā)現(xiàn)自己整理的時候,例子越來越多,所以單獨寫一篇用于加深理解,以后工作或項目中發(fā)現(xiàn)一些this相關問題可以進一步完善文章的例子。
this
概念
在函數(shù)中this到底取何值,是在函數(shù)真正被調用執(zhí)行的時候確定的,函數(shù)定義的時候確定不了
因為this的取值是執(zhí)行上下文環(huán)境的一部分,每次調用函數(shù),都會產生一個新的執(zhí)行上下文環(huán)境。
構造函數(shù)
所謂構造函數(shù)就是用來new對象的函數(shù)。其實嚴格來說,所有的函數(shù)都可以new一個對象,但是有些函數(shù)的定義是為了new一個對象,而有些函數(shù)則不是。另外注意,構造函數(shù)的函數(shù)名第一個字母大寫(規(guī)則約定)。例如:Object、Array、Function等。
function Foo() {
this.name = "yzh";
this.year = 1996;
console.log(this); //Object { name: "yzh", year: 1998 }
}
var f1 = new Foo();
console.log(f1.name); //yzh
console.log(f1.year); //1996
以上代碼中,如果函數(shù)作為構造函數(shù)用,那么其中的this就代表它即將new出來的對象。
注意,以上僅限newFoo()的情況,即Foo函數(shù)作為構造函數(shù)的情況。如果直接調用Foo函數(shù),而不是new Foo(),情況就大不一樣了。
function Foo() {
this.name = "yzh";
this.year = 1996;
console.log(this); //Object { name: "yzh", year: 1998 }
}
Foo(); //window
這種情況下this是window。
函數(shù)作為對象的一個屬性
如果函數(shù)作為對象的一個屬性時,并且作為對象的一個屬性被調用時,函數(shù)中的this指向該對象。
var obj = {
x: 10,
fn: function() {
console.log(this); //Object { x: 10, fn: fn() }
console.log(this.x); //10
}
};
obj.fn();
以上代碼中,fn不僅作為一個對象的一個屬性,而且的確是作為對象的一個屬性被調用。結果this就是obj對象。
如果fn函數(shù)不作為obj的一個屬性被調用,會是什么結果呢?
var obj = {
x: 10,
fn: function() {
console.log(this); //window
console.log(this.x); //undefined
}
};
var f1 = obj.fn;
f1();
如上代碼,如果fn函數(shù)被賦值到了另一個變量中,并沒有作為obj的一個屬性被調用,那么this的值就是window,this.x為undefined。
函數(shù)用call或者apply調用
為了防止看不懂這塊先理解下基礎并重要的東西
函數(shù)內部屬性
在函數(shù)內部,有兩個特殊的對象:arguments和this.
主要介紹下arguments,它是類數(shù)組對象,包含傳入函數(shù)的所有參數(shù),這個對象還有一個叫callee的屬性,該屬性為一個指針,指向擁有這個arguments對象的函數(shù)。
這個例子主要用于消除函數(shù)的執(zhí)行與同名函數(shù)的緊密耦合現(xiàn)象。代碼如下:
function factorial(num) {
if (num <= 1) {
return 1;
} else {
// return num * factorial(num - 1);
//1.一般的遞歸算法運用
return num * arguments.callee(num - 1)
//2.更好的做法
}
}
var trueFactorial = factorial;
factorial = function() {
return 0;
};
alert(trueFactorial(5)); //120
alert(factorial(5)); //0
函數(shù)屬性
每個函數(shù)都包含兩個屬性:length和prototype.
length表示函數(shù)希望接受的命名參數(shù)的個數(shù)
function sayName(name){
alert(name);
}
function sum(num1, num2){
return num1 + num2;
}
function sayHi(){
alert("hi");
}
alert(sayName.length); //1
alert(sum.length); //2
alert(sayHi.length); //0
prototype屬性這里不再重復介紹。
函數(shù)方法
每個函數(shù)都包含兩個非繼承而來的方法:apply()和call(),這兩個方法的用途都是在特定的作用域中調用函數(shù),實際上等于設置函數(shù)體內的this對象值。
apply()方法接受兩個參數(shù):一個是在其中運行函數(shù)的作用域,另一個是參數(shù)數(shù)組。
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
return sum.apply(this, arguments);
}
function callSum2(num1, num2){
return sum.apply(this, [num1, num2]);
}
alert(callSum1(10,10)); //20
alert(callSum2(10,10)); //20
callsSum1()執(zhí)行sum()函數(shù)時傳入this作為this值(在全局作用域中調用,所以傳入的對象為window對象)下面的例子同理。
call()方法與apply()方法差不多,區(qū)別在于接受參數(shù)的方式不同。簡單來說就是:傳遞給函數(shù)的參數(shù)必須逐個列舉出來。(如果是無參函數(shù)就不用傳參數(shù))
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
alert(callSum(10,10)); //20
傳遞參數(shù)并非兩個函數(shù)的真正用武之地,真正強大的地方是擴充函數(shù)賴以運行的作用域
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue
如下代碼如果不用函數(shù)的方法來實現(xiàn)。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //red
o.sayColor = sayColor;
o.sayColor(); //blue
如果要輸出o對象里的color屬性值,必須把sayColor函數(shù)賦給對象o并調用 o.sayColor()時,this引用的對象為o
回歸要講的例子
當一個函數(shù)被call和apply調用時,this的值就取傳入的對象的值。
var obj = {
x: 10
};
var fn = function() {
console.log(this); //Object { x: 10 }
console.log(this.x); //10
};
fn.call(obj);
全局 & 調用普通函數(shù)
在全局環(huán)境下,this永遠是window,這個應該沒有非議。
console.log(this === window); //true
普通函數(shù)在調用時,其中的this也都是window。
var x = 10;
var fn = function() {
console.log(this); //window
console.log(this.x); //10
}
fn();
注意以下的情況
var obj = {
x: 10,
fn: function() {
function f() {
console.log(this); //Window
console.log(this.x); //undefined
}
f();
}
};
obj.fn();
函數(shù)f雖然是在obj.fn內部定義的,但是它仍然是一個普通的函數(shù),this仍然指向window。
總結:this指向調用該函數(shù)的對象
構造函數(shù)的prototype中,this代表著什么
function Fn() {
this.name = "yzh";
this.year = 1996;
}
Fn.prototype.getName = function() {
console.log(this.name);
}
var f1 = new Fn();
f1.getName(); //yzh
在Fn.prototype.getName函數(shù)中,this指向的是f1對象。因此可以通過this.name獲取f1.name的值
bind()
bind()方法創(chuàng)建一個新的函數(shù), 當被調用時,它的this關鍵字被設置為提供的值 ,在調用新函數(shù)時,提供任何一個給定的參數(shù)序列。
這個函數(shù)其實也很重要,只是當時紅寶書也沒提及很多。
語法
fun.bind(thisArg[, arg1[, arg2[, ...]]])
參數(shù)
thisArg
當綁定函數(shù)被調用時,該參數(shù)會作為原函數(shù)運行時的 this 指向。當使用new 操作符調用綁定函數(shù)時,該參數(shù)無效。
arg1, arg2, ...
當綁定函數(shù)被調用時,這些參數(shù)將置于實參之前傳遞給被綁定的方法。
返回值
返回由指定的this值和初始化參數(shù)改造的原函數(shù)拷貝
描述
bind() 函數(shù)會創(chuàng)建一個新函數(shù)(稱為綁定函數(shù)),新函數(shù)與被調函數(shù)(綁定函數(shù)的目標函數(shù))具有相同的函數(shù)體(在 ECMAScript 5 規(guī)范中內置的call屬性)。當目標函數(shù)被調用時 this 值綁定到 bind() 的第一個參數(shù),該參數(shù)不能被重寫。綁定函數(shù)被調用時,bind() 也接受預設的參數(shù)提供給原函數(shù)。一個綁定函數(shù)也能使用new操作符創(chuàng)建對象:這種行為就像把原函數(shù)當成構造器。提供的 this 值被忽略,同時調用時的參數(shù)被提供給模擬函數(shù)。
示例
創(chuàng)建綁定函數(shù)
this.x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 返回 81
var retrieveX = module.getX;
retrieveX(); // 返回 9, 在這種情況下,"this"指向全局作用域
// 創(chuàng)建一個新函數(shù),將"this"綁定到module對象
// 新手可能會被全局的x變量和module里的屬性x所迷惑
var boundGetX = retrieveX.bind(module);
boundGetX(); // 返回 81
End
歡迎拍磚,有更多的好的例子,歡迎提出,文中有誤的地方也歡迎指正。