Skip to content
字数
1451 字
阅读时间
6 分钟

背景

借助阮一峰老师的话:它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。

在Javascript中,this指向函数执行时的当前对象

这句话看似平常,可是要非常注意三个字:" 运行时 ",这说明 this 关键字只与函数的执行环境有关,而与声明环境没有关系。也就是这个 this 到底代表的是什么对象要等到函数运行时才知道,有点类似函数定义时的参数列表只在函数调用时才传入真正的对象

this 关键字虽然会根据环境变化,但是它始终代表的是调用当前函数的那个对象。

这就引出了 JS 中函数调用的问题。

在 JS 中调用函数的模式可以分为 4 种:

  • 方法调用模式
  • 函数调用模式
  • 构造器调用模式
  • apply 调用模式

方法调用模式

当函数被保存为一个对象的属性时,它就可称为这个对象的方法。当一个方法被调用时,this 被绑定到这个对象上。如果调用表达式包含一个提取属性的动作(. 或 []),那么它被称为方法调用

js
var name = "window";
var obj = {
    name: "kxy",
    sayName: function() {
        console.log(this.name);
    }
};
obj.sayName();  //kxy

sayName 函数作为对象 obj 的方法调用,所以函数体中的 this 就代表 obj 对象

调用的先后顺序

对象的属性调用和函数的方法调用

  • new Foo.getName(),这里等价于 new (Foo.getName()),先执行 Foo.getName(),输出 2,然后 new 一个实例;
  • new Foo().getName(),这里等价于 (new Foo()).getName(), 先 new 一个 Foo 的实例,再执行这个实例的 getName 方法,但是这个实例本身没有这个方法,所以去原型链 protot 上边找,实例.__protot__ === Foo.prototype,所以输出 3;
  • new new Foo().getName(),这里等价于 new (new Foo().getName()),如上述 6,先输出 3,然后 new 一个 new Foo().getName() 的实例

函数调用模式

当一个函数并非一个对象的属性时,那么它就是被当做函数来调用的。在此种模式下,this 被绑定为全局对象,在浏览器环境下就是 window 对象。例如:

js
var name = "window";
function sayName() {
    console.log(this.name);
}
sayName();

sayName 以函数调用模式调用,所以函数体中的 this 代表 window 对象

构造函数模式

如果在一个函数前面加上 new 关键字来调用,那么就会创建一个连接到该函数的 prototype 成员的新对象,同时,this 会被绑定到这个新对象上。这种情况下,这个函数就可以成为此对象的构造函数。

js
function Obj() {
    this.name = "kxy";
}
var person = new Obj();
console.log(person.name);   //kxy

Obj 作为构造函数被调用,函数体内的 this 被绑定为新创建的对象 person

apply 调用模式

在 JS 中,函数也是对象,所有函数对象都有两个方法:apply 和 call,这两个方法可以让我们构建一个参数数组传递给调用函数,也允许我们改变 this 的值

js
var name = "window";
var person = {
    name: "kxy"
};
function sayName() {
    console.log(this.name);
}
sayName();    //window
sayName.apply(person);   //kxy
sayName.apply();    //window

当以函数调用模式调用 sayName 时,this 代表 window;当用 apply 模式调用 sayName,并给它传入的第一个参数为 person 时,this 被绑定到 person 对象上。如果不给 apply 传入任何参数,则 this 代表 window

demo 测验

js
var name = "window";
function showName() {
    console.log(this.name);
}
var person1 = {
    name: "kxy",
    sayName: showName
}
var person2 = {
    name: "Jake",
    sayName: function() {
        var fun = person1.sayName;
        fun();
    }
}
person1.sayName();    //kxy
person2.sayName();    //window

先看第一个执行语句:person1.sayName(); 首先确定这是方法调用模式,对象为 person1,再看 sayName 被赋值为全局函数对象 showName,在 showName 执行时,this 绑定的是 person1,所以结果为 "kxy"。

再看第二个执行语句:person2.sayName(); 这还是方法调用模式,对象为 person2,调用的是它的 sayName 方法。再看 sayName 函数体,发现函数体最终执行的函数是 fun,fun 是用函数调用模式调用的。而 fun 最终也被赋值为 showName 函数,因为 fun 是用函数调用模式调用的,所以这里的 this 绑定为 window,结果为 "window"

总结

函数 调用执行 不同,调用作为对象的方法使用,执行则不强调作为对象的方法来说,一般是 window 对象

  • 全局作用域中,this 指向 window

  • 在普通函数中,谁调用函数,this 指向谁

  • 箭头函数中没有自己的 this,它指向其父级所处的上下文

  • 事件绑定中的 this,指向事件源

javascript、
// 事件源.onclik = function(){ } //this -> 事件源

// 事件源.addEventListener(function(){ }) //this->事件源
var div = document.querySelector('div'); 
div.addEventListener('click',function() {
	console.log(this); //this->div
});

div.onclick = function() {
console.log(this) //this->div
}

定时器中采用 回调函数 作为处理函数,所以回调函数中的 this 指向 window

  • 构造函数中的 this 指向实例化对象

  • obt.fn.call(),这里 call 的参数啥都没写,就表示 null,如果 call 的参数为 undefined 或 null,那么 this 就会指向全局对象

js
 obt.fn();
 obt.fn.call();
 (obt.fn)(); // 加括号改变运算顺序

匿名函数 的 this 是指向全局对象的

IIFE(立即调用函数表达式,别名叫匿名函数自调用)中的this指向 window

转载

JS中的this对象详解

JS中的this指向问题

贡献者

The avatar of contributor named as jiechen jiechen

页面历史

撰写