一、this的指向
1.this是Javascript語言的一個關(guān)鍵字。
它代表函數(shù)運(yùn)行時潭袱,自動生成的一個內(nèi)部對象挪略,只能在函數(shù)內(nèi)部使用历帚。
function test(){
this.x = 1;
}
2.理解這句話:
this的指向在函數(shù)定義的時候是確定不了的滔岳,只有函數(shù)執(zhí)行的時候才能確定this到底指向誰,實際上this的最終指向的是那個調(diào)用它的對象挽牢。
- 情況1:如果一個函數(shù)中有this谱煤,但是它沒有被上一級的對象所調(diào)用,那么this指向的就是window(嚴(yán)格模式下禽拔,this指向不是window刘离,是undefined)
function a(){
var user = "via";
console.log(this.user); //undefined
console.log(this); //Window
}
a();
- 情況2:函數(shù)有被上一級的對象所調(diào)用, 則this指向的就是上一級的對象睹栖。
var o = {
user:"via",
fn:function(){
console.log(this.user); //via
}
}
window.o.fn();
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //12
}
}
}
o.b.fn();
- 情況3:函數(shù)中包含多個對象硫惕,盡管這個函數(shù)是被最外層的對象所調(diào)用,this指向的也只是它上一級的對象野来。
var o = {
a:10,
b:{
// a:12,
fn:function(){
console.log(this.a); //undefined
}
}
}
o.b.fn();
- 特殊:
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
//函數(shù)fn是被對象b所引用恼除,但是在將fn賦值給變量j的時候并沒有執(zhí)行
//所以最終指向的是window
- 當(dāng)this碰到return
如果返回值是一個對象,那么this指向的就是那個返回的對象曼氛,如果返回值不是一個對象那么this還是指向函數(shù)的實例豁辉。
function fn()
{
this.user = 'via';
return {};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = 'via';
return 1;
}
var a = new fn;
console.log(a.user); //via
特殊:雖然null也是對象,但是在這里this還是指向那個函數(shù)的實例
function fn()
{
this.user = 'via';
return null;
}
var a = new fn;
console.log(a.user); //via
二舀患、call徽级、apply、bind改變this指向
- 直接看代碼
var a = {
user:"via",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b(); //undefined
a.fn(); //via
b.call(a);//通過在call方法构舟,給第一個參數(shù)添加要把b添加到哪個環(huán)境中灰追,this就會指向那個對象。
//call還可以添加多個參數(shù)
b.apply(a);//apply也可以有多個參數(shù)狗超,不同的是第二個參數(shù)必須是一個數(shù)組
- 如果call和apply的第一個參數(shù)寫的是null弹澎,那么this指向的是window對象
var a = {
user:"via",
fn:function(){
console.log(this); //Window
}
}
var b = a.fn;
b.call(null);//window
b.apply(null);//window
- bind方法返回的是一個修改過后的函數(shù)。
var a = {
user:"via",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b.bind(a);
//firefox:bound fn()
length: 0
name: "bound fn"
__proto__: function ()
//chrome:? (){
console.log(this.user);
}
var a = {
user:"via",
fn:function(){
console.log(this.user); //via
}
}
var b = a.fn;
var c = b.bind(a);
c();//執(zhí)行函數(shù)
- 同樣bind也可以有多個參數(shù)努咐,并且參數(shù)可以執(zhí)行的時候再次添加苦蒿,但是要注意的是,參數(shù)是按照形參的順序進(jìn)行的
var a = {
user:"via",
fn:function(e,d,f){
console.log(this.user); //via
console.log(e,d,f); //10 1 2
}
}
var b = a.fn;
var c = b.bind(a,10);
c(1,2);
二渗稍、其他指向
- 構(gòu)造函數(shù)
通過這個函數(shù)生成一個新對象(object)佩迟。這時,this就指這個新對象竿屹。
function Fn(){
this.num = 1;
}
var a = new Fn();
console.log(a.num); //1
//a創(chuàng)建了一個Fn的實例并沒有執(zhí)行
//調(diào)用這個函數(shù)Fn的是對象a报强,那么this指向的自然是對象a
- 存在于一個對象的原型鏈上,那么this指向的是調(diào)用這個方法的對象
var o = {
f : function(){
return this.a + this.b;
}
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
- 作為一個內(nèi)聯(lián)事件處理函數(shù)
this指向監(jiān)聽器所在的DOM元素:
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
- 作為一個DOM事件處理函數(shù)
this指向觸發(fā)事件的元素(一些瀏覽器在使用非addEventListener的函數(shù)動態(tài)添加監(jiān)聽函數(shù)時不遵守這個約定)拱燃。
// 被調(diào)用時秉溉,將關(guān)聯(lián)的元素變成藍(lán)色
function bluify(e){
console.log(this === e.currentTarget); // 總是 true
// 當(dāng) currentTarget 和 target 是同一個對象是為 true
console.log(this === e.target);
this.style.backgroundColor = '#A5D9F3';
}
- getter或setter的函數(shù)都會把 this 綁定到正在設(shè)置或獲取屬性的對象。
function sum() {
return this.a + this.b + this.c;
}
var o = {
a: 1,
b: 2,
c: 3,
get average() {
return (this.a + this.b + this.c) / 3;
}
};
Object.defineProperty(o, 'sum', {
get: sum, enumerable: true, configurable: true});
console.log(o.average, o.sum); // 2, 6
三、在匿名函數(shù),定時器中的函數(shù)setTimeout/setInterval中
function Person() {
this.age = 0;
setTimeout((function() {
console.log(this);
}), 1000);
}
var p = new Person();//window
- 匿名函數(shù),定時器中的函數(shù),由于沒有默認(rèn)的宿主對象,所以默認(rèn)this指向window
- 用一個 變量提前把正確的 this引用保存 起來, that = this 或者 ** _this = this** 來保存我們需要的this指針
function Person() {
var that=this ;
that.age = 0;
setTimeout((function() {
console.log(that);
}).bind(this), 3000);
}
var p = new Person();//1秒后返回構(gòu)造函數(shù)新生成的對象 Person{...}
- 或用func.bind(this)給回調(diào)函數(shù)直接綁定宿主對象, bind綁定宿主對象后依然返回這個函數(shù)
function Person() {
this.age = 0;
setTimeout((function() {
console.log(this);
}).bind(this), 1000);
}
var p = new Person();//1秒后返回構(gòu)造函數(shù)新生成的對象 Person{...}
四召嘶、箭頭函數(shù)
- 不綁定this
箭頭功能不會創(chuàng)建自己的this父晶;它使用封閉執(zhí)行上下文的this值。 - 在箭頭函數(shù)中弄跌,this是根據(jù)當(dāng)前的詞法作用域來決定的甲喝,就是說,箭頭函數(shù)會繼承外層函數(shù)調(diào)用的this綁定(無論this綁定到什么)铛只。在全局作用域中埠胖,它會綁定到全局對象上。
(默認(rèn)指向在定義它時所處的對象(宿主對象),而不是執(zhí)行時的對象, 定義它的時候,可能環(huán)境是window)
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
注意:如果將thisArg傳遞給call淳玩、bind押袍、或者apply,它將被忽略(thisArg即傳入三個函數(shù)中的第一個參數(shù))凯肋。仍可以為調(diào)用添加參數(shù)谊惭,不過第一個參數(shù)應(yīng)該設(shè)置為null。
- 與普通函數(shù)對比
var obj={
num:3,
fn:function(){
setTimeout(function(){
console.log(this.num);
//this出現(xiàn)在全局函數(shù)setTImeout()中的匿名函數(shù)里
//并沒有某個對象進(jìn)行顯示調(diào)用侮东,所以this指向window對象
});
}
}
obj.fn();//undefined
var obj1={
num:4,
fn:function(){
setTimeout(() => {
console.log(this.num); //this指向函數(shù)的宿主對象了
});
}
}
obj1.fn();//4
- 多層嵌套的箭頭函數(shù)
var obj1={
num:4,
fn:function(){
var f=() => { //object圈盔,也就是指obj1
console.log(this);
setTimeout(() => {
console.log(this);//object,也就是指obj1
});
}
f();
}
}
obj1.fn();
改動一處箭頭函數(shù)
var obj1={
num:4,
fn:function(){
var f=function(){
console.log(this);
//函數(shù)f定義后并沒有對象調(diào)用悄雅,this直接綁定到最外層的window對象
setTimeout(() => {
console.log(this);
//外層this綁定到了window,內(nèi)層也相當(dāng)于定義在window層(全局環(huán)境)
});
}
f();
}
}
obj1.fn();
var obj1={
num:4,
fn:function(){
var f=() => {
console.log(this);
//object,f()定義在obj1對象中驱敲,this就指向obj1,這是箭頭函數(shù)this指向的關(guān)鍵
setTimeout(function() {
console.log(this);
//window,非箭頭函數(shù)的情況下還是要看宿主對象是誰
//如果沒有被對象調(diào)用宽闲,函數(shù)體中的this就綁定的window上
});
}
f();
}
}
obj1.fn();
- 1.箭頭函數(shù)的this綁定看的是this所在的函數(shù)定義在哪個對象下众眨,綁定到哪個對象則this就指向哪個對象
- 2.如果有對象嵌套的情況,則this綁定到最近的一層對象上