this指向

136 阅读3分钟

在执行上下文那篇文章中我们还留了一个Thisbinding没展开细节,全局的this指向没什么好说的,但是函数的this指向情况是有很多种的,但是this一定是指向一个对象的。

函数中的this指向取决于函数的调用方式,也就意味着this指向不是创建时确定的,不是静态的。是在调用时决定,是动态的。

函数调用

作为普通函数调用,包括立即执行函数。

  • 在非strict mode下,普通函数调用中this为全局对象
  • 在strict mode下,普通函数调用中this为undefined

方法调用

作为对象的方法调用时,this指向该对象。

var a = 2;
var someObj = {
    a:1,
    print:function(){
        console.log(this.a)
    }
}

var outPrint = someObj.print;

someObj.print(); // 1
outPrint();// 2

注意以下代码中,用实例化对象调用方法和原型直接调用方法的区别。

function Car(){
    this.brand = 'Benz';
}

Car.prototype = {
    brand: 'Mazda',
    intro: function(){
        console.log(this.brand);
    }
}

var car = new Car();

car.intro(); // 'Benz'
Car.prototype.intro(); // 'Mazda'

构造函数调用

new方式调用的函数运行上下文中,ThisBinding = newObject,因此在new方式调用的函数中,this指向新创建的对象,具体可以看原型链那篇文章。

间接调用

call()方法和apply()方法都可以指定this的值,区别在于apply采用数组形式传参。

以下代码将构造函数Car的this指向改成实例对象newCar。

function Car(brand, color){
    this.brand = brand;
    this.color = color;
}

var newCar = {};
Car.call(newCar, 'Benz', 'red');

console.log(newCar); // {brand: 'Benz', color: 'red'}
// 用伪代码可以表示为
// function Car(brand, color){
//     newCar.brand = brand;
//     newCar.color = color;
// }

以下代码是较为复杂的例子。

function Person(name, age){
    this.name = name;
    this.age = age;
}

function Programmer(name, age){
    Person.apply(this, [name, age]);
    this.work = '程序员';
}

var p = new Programmer('张三', 18);
console.log(p);
// Programmer {name: '张三', age: 18, work: '程序员'}

bind()调用

Function.prototype.bind()方法是创建一个和原函数一样的函数,但是它的this指向bind()函数的第一个参数。无论新函数如何被调用,this都会指向bind()的第一个参数。

function f() {
  return this.a;
}

var g = f.bind({a: 'azerty'});
console.log(g()); // azerty

箭头函数调用

箭头函数是ES6的新内容,箭头函数的this是静态的,保存的是它被创建时所在的执行上下文的this。在global下创建箭头函数,它的this指向global对象。在function里创建,它的this指向function当前执行上下文的this的值。

这个和我们前面提到的函数都不一样,前面提到的函数都是和调用时的运行上下文相关,箭头函数的this却和创建时的运行上下文相关。

以下代码中可以看出定时器中的箭头函数的this指向,就是回调函数function的this指向,都是div。

let oBox1 = document.getElementsByClassName('box1')[0];

oBox1.onclick = function(){
    console.log(this); // <div class="box1"></div>
    setTimeout(() => {
        console.log(this); // <div class="box1"></div>
        this.style.background = 'pink';
    },1000);
}

this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this

以下代码中当箭头函数作为事件回调函数时,它的this指向,就是init函数的this指向,即handler对象。

var handler = {
    id: '123456',
    init: function() {
        document.addEventListener('click', event => {
            console.log(this);             
        }, false);
    }
};
handler.init(); // {id: '123456', init: ƒ}

注意对箭头函数使用call()、apply()和bind()来绑定this是无效,没有效果。

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

var obj = {func: foo};
console.log(obj.func() === globalObject); // true

console.log(foo.call(obj) === globalObject); // true

foo = foo.bind(obj);
console.log(foo() === globalObject); // true