this
按照慣例,先看一段代碼
看一段代碼
var name = "222";
var a = {
name: "111",
say: function () {
console.log(this.name);
},
};
var fun = a.say;
fun();
a.say();
var b = {
name: "333",
say: function (fn) {
fun();
},
};
b.say(a.say);
b.say = a.say;
b.say();
// 題目來(lái)源于網(wǎng)絡(luò) 并非自創(chuàng)
你能一眼看出輸出答案嗎纫谅?如果可以那么我相信 this 概念你已經(jīng)理解的很透徹了偎漫,如果你不懂輸出內(nèi)容,請(qǐng)接著向下看方篮,相信你會(huì)有所收獲
什么是 this
MDN 上是這樣說(shuō)的:在絕大多數(shù)情況下名秀,函數(shù)的調(diào)用方式?jīng)Q定了 this 的值
ECMAScript 規(guī)范中這樣寫:his 關(guān)鍵字執(zhí)行為當(dāng)前執(zhí)行環(huán)境的 ThisBinding。
按照自己的理解翻譯一下:在 JavaScript 中藕溅,this 的指向是調(diào)用時(shí)決定的匕得,而不是創(chuàng)建時(shí)決定的,這就會(huì)導(dǎo)致 this 的指向會(huì)讓人迷惑蜈垮,簡(jiǎn)單來(lái)說(shuō)耗跛,this 具有運(yùn)行期綁定的特性芒率。
調(diào)用位置
首先需要理解調(diào)用位置裹虫,調(diào)用位置就是函數(shù)在代碼中被調(diào)用的位置,而不是聲明的位置
function baz() {
console.log("baz");
bar();
}
function bar() {
console.log("bar");
foo();
}
function foo() {
console.log("foo");
}
baz();
- 對(duì)于 foo():調(diào)用位置是在 bar()中头镊。
- 對(duì)于 bar():調(diào)用位置是在 baz()中惠猿。
- 而對(duì)于 baz():調(diào)用位置是全局作用域中羔砾。
可以看出,調(diào)用位置應(yīng)該是當(dāng)前正在執(zhí)行的函數(shù)的前一個(gè)調(diào)用中
事件綁定
不論是 DOM0 還是 DOM2 事件綁定,給元素 e 的某個(gè)事件行為綁定方法姜凄,當(dāng)前事件觸發(fā)方法執(zhí)行政溃,方法種的 this 是當(dāng)前元素 e 本身
特殊情況:
- IE6-8 基于 attachEvent 實(shí)現(xiàn) DOM2 事件綁定,事件觸發(fā)方法執(zhí)行态秧,方法中的 this 不是元素本身董虱,大部分情況是 window
- 如果使用 call/apply/bind 強(qiáng)制改變 this 指向,我們以改變的為主
全局上下文 this
很好理解申鱼,全局上下文的 this 實(shí)際上就是 window
console.log(window === this); // true
var a = 1;
this.b = 2;
window.c = 3;
console.log(a + b + c); // 6
函數(shù)上下文 this
-
普通函數(shù)
普通函數(shù)執(zhí)行愤诱,看函數(shù)前面是否有點(diǎn)。有點(diǎn) 捐友,點(diǎn)前面是誰(shuí) this 就是誰(shuí)淫半,沒(méi)有點(diǎn) this 就是 window (嚴(yán)格模式是 undefined)fn() -> this: window/undefined
obj.fn() -> this:obj
xxx.proto.fn() -> this:xxx.proto自執(zhí)行函數(shù):this 一般是 window/undefined
回調(diào)函數(shù)中的 this 一般也是 window/undefined
在函數(shù)內(nèi)部,this 的值取決于函數(shù)被調(diào)用的方式匣砖。
直接調(diào)用
this 指向全局變量科吭,如果當(dāng)前函數(shù)處于全局變量種
function foo() {
return this;
}
console.log(foo() === window); // true
call()、apply()
this 指向綁定的對(duì)象
var person = {
name: "axuebin",
age: 25,
};
function say(job) {
console.log(this.name + ":" + this.age + " " + job);
}
say.call(person, "FE"); // axuebin:25
say.apply(person, ["FE"]); // axuebin:25
可以看到猴鲫,定義了一個(gè) say 函數(shù)是用來(lái)輸出 name对人、age 和 job,其中本身沒(méi)有 name 和 age 屬性变隔,我們將這個(gè)函數(shù)綁定到 person 這個(gè)對(duì)象上规伐,輸出了本屬于 person 的屬性,說(shuō)明此時(shí) this 是指向?qū)ο?person 的匣缘。
bind()
this 將永久地被綁定到了 bind 的第一個(gè)參數(shù)猖闪。
作為對(duì)象的一個(gè)方法
this 指向調(diào)用函數(shù)的對(duì)象
var person = {
name: "axuebin",
getName: function () {
return this.name;
},
};
console.log(person.getName()); // axuebin
這里有一個(gè)需要注意的地方。肌厨。培慌。
var name = "xb";
var person = {
name: "axuebin",
getName: function () {
return this.name;
},
};
var getName = person.getName;
// getName 在全局環(huán)境中,此時(shí)this指向window 所以輸出 xb
console.log(getName()); // xb
發(fā)現(xiàn) this 又指向全局變量了柑爸,這是為什么呢吵护?
還是那句話,this 的指向得看函數(shù)調(diào)用時(shí)表鳍。
作為構(gòu)造函數(shù)
通過(guò)構(gòu)造函數(shù)創(chuàng)建一個(gè)對(duì)象其實(shí)執(zhí)行這樣幾個(gè)步驟:
- 創(chuàng)建新對(duì)象
- 將 this 指向這個(gè)對(duì)象
- 給對(duì)象賦值(屬性馅而、方法)
- 返回 this
- 所以 this 就是指向創(chuàng)建的這個(gè)對(duì)象上。
function Person(name) {
this.name = name;
this.age = 25;
this.say = function () {
console.log(this.name + ":" + this.age);
};
}
var person = new Person("axuebin");
console.log(person.name); // axuebin
箭頭函數(shù)
所有的箭頭函數(shù)都沒(méi)有 this,都是指向外層的當(dāng)前函數(shù)所在的上下文的 this
題解分析
看過(guò)上面的幾種 this 場(chǎng)景譬圣,對(duì)于開始的題目是不是豁然開朗了呢瓮恭,來(lái)分析一下
var name = "222";
var a = {
name: "111",
say: function () {
console.log(this.name);
},
};
var fun = a.say;
fun(); // 此時(shí)fun處于全局環(huán)境中,也就是全局環(huán)境調(diào)用這個(gè)方法厘熟,誰(shuí)調(diào)用this指向誰(shuí)屯蹦,所以打印 222
// fun() === fun.call(window) fun相當(dāng)于是語(yǔ)法糖
a.say(); // 此時(shí)函數(shù)處于a對(duì)象中维哈,所以 this指向 a對(duì)象 所以打印 111
// a.say() === a.say.call(a)
var b = {
name: "333",
say: function (fn) {
fn(); // 此時(shí)fn執(zhí)行沒(méi)有其他的this 綁定,
// fn() === fn.call(window)
// a.say() === fn() === fn.call(window) === a.say.call(window)
},
};
b.say(a.say); // 打印 222
b.say = a.say;
// b.say = a.say 相當(dāng)于
// var b = {
// name: '333',
// say:function(){
// console.log(this.name)
// }
// }
b.say(); // 打印 333
ps: 有些內(nèi)容來(lái)源網(wǎng)絡(luò)登澜,忘記
最后結(jié)果: 222 111 222 333
看到這里阔挠,你明白 this 了嗎,不明白做點(diǎn)題目吧
this 相關(guān)面試題
第一題
let obj = {
fn:(function(){
return function(){
console.log(this)
}
})()
}
obj.fn();
let fn = obj.fn;
fn();
第二題
var fullName = 'language';
var obj = {
fullName: 'javascript',
prop: {
getFullName: function () {
return this.fullName;
}
}
};
console.log(obj.prop.getFullName());
var test = obj.prop.getFullName;
console.log(test());
第三題
var name = 'window';
var Tom = {
name: "Tom",
show: function () {
console.log(this.name);
},
wait: function () {
var fun = this.show;
fun();
}
};
Tom.wait();
第四題
window.val = 1
var json = {
val: 10,
dbl: function () {
this.val *= 2;
}
}
json.dbl();
var dbl = json.dbl;
dbl();
json.dbl.call(window);
alert(window.val + json.val);
第五題
(function () {
var val = 1;
var json = {
val: 10,
dbl: function () {
val *= 2;
}
};
json.dbl();
alert(json.val + val);
})();
答案
// 第一題
obj.fn(); // this指向的是obj ,所以輸出 {fn:function(){}}
let fn = obj.fn;
fn();// 此時(shí)的fn 屬于全局脑蠕,相當(dāng)于把函數(shù)賦值給全局變量购撼,所以 this 是window
// 第二題
console.log(obj.prop.getFullName());
//=>this:obj.prop =>obj.prop.fullName =>undefined
var test = obj.prop.getFullName;
console.log(test());
//=>this:window =>window.fullName =>'language' */
// 第三題
var Tom = {
name: "Tom",
show: function () {
// this->window
console.log(this.name); //=>'window'
},
wait: function () {
// this->Tom
var fun = this.show;
fun();
// 此時(shí)fn執(zhí)行沒(méi)有其他的this 綁定,
// fn() === fn.call(window)
}
};
// 第四題
json.dbl();
//->this:json ->json.val=json.val*2 ->json.val=20
var dbl = json.dbl;
dbl();
//->this:window ->window.val=window.val*2 ->window.val=2
json.dbl.call(window);
//->this:window ->window.val=window.val*2 ->window.val=4
alert(window.val + json.val); //=>"24"
// 第五題
(function () {
var val = 1; //->2
var json = {
val: 10,
dbl: function () {
// this->json
val *= 2; //val=val*2 此處的val是變量 json.val是對(duì)象的屬性
}
};
json.dbl();
alert(json.val + val); //=>"12"
})();