前言


javascript中的this是啥东西?为啥我们经常被他搞得晕头转向不知所以?他是恶魔?是天使 ?是怪胎?让我们一起来揭开它那神秘的面纱。


this - 想说爱你不容易_函数调用


他是个啥


首先​​this​​​是Javascript语言的关键字之一,指函数​​运行​​时的当前对象。那既然和函数运行有关,js中函数有哪些调用模式呢?


  1. 纯粹的函数调用
  2. 对象的方法调用
  3. 构造函数调用
  4. apply、call调用

我擦,有木有一千只草泥马在心里蹦腾不息,人家是要弄懂this,你这又是整的哪一出

​​

我们慢慢来,一步步从这些调用模式中探究this这个神奇的远古神兽

纯粹的函数调用


函数调用 即 ​​functionName ()​​​ 模式,这也是我们使用的最多的一种方式,其属于全局调用,浏览中默认情况下函数内部的this指向​​window​​,当然是在非严格模式下。


this.name = 'qianlong';

function showName () {
console.log(this.name);
console.log(this === window);
}

showName()

// qianlong
// true

对象的方法调用


当一个函数作为对象的某个属性方法被调用的时候


var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
}
};

obj.showName();
// qianlong

可以看出this指向是obj这个对象,其实本质上讲函数调用形式内部this就是指向调用它的那个对象

上面的例子相当于

window.showName()

这也是为什么可以读取到全局定义的name属性的原因。

​再来​

var showName = function () {
console.log(this.name);
},
obj = {
name: 'qianlong',
showName: showName
};

obj.showName();

这个时候输出的是什么呢

结果是不变的,在js中,一切都是对象,而这里也只是将,obj的showName属性指向,showNmae函数的引用地址。

​继续​

当我们把​​showName​​方法赋值给了一个变量,又会有什么事情发生呢?

var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
}
};

var tempShowName = obj.showName;

tempShowName()

// undefined

为什么不是期望的那样输出 qianlong呢。obj的showName方法是一个对象,当把它赋值给了tempShowName变量,此时便和obj没有什么关系了,而这个时候的调用和下面是等价的。

window.tempShowName()

window上此事并没有​​name​​​属性,自然输出是​​undefined​​。

构造函数调用


当使用 ​​new​​ 去调用一个构造函数的时候,内部的this,指向的是实例化出来的对象。


var Person = function (name, sex) {
this.name = name;
this.sex = sex;
console.log(this);
};

var p1 = new Person('qianlong', 'boy');

// Person {name: 'qianlong', sex: 'boy'};

构造函数也是函数,所以当你用普通调用方式调用时

var Person = function (name, sex) {
this.name = name;
this.sex = sex;
console.log(this);
};

Person('qianlong', 'boy');

// 这个时候相当于给window对象添加了name和sex两个属性。

window.name // 'qianlong'
window.sex // 'boy'

apply、call调用


使用call和apply方式去调用一个函数的时候,内部的this指向的是传进来的第一个参数,当第一个参数是​​undefined​​​或者​​null​​​的时候,依旧指向​​window​


关于call和apply欢迎查看另一篇文章

js中call、apply、bind那些事

var showName = function () {
console.log(this);
};

showName() // window
showName.call(undefined) // window
showName.call(null) // window
showName.call({name: 'qianlong'}) // {name: 'qianlong'}

箭头函数


在 ES6 的新规范中,加入了箭头函数,它和普通函数最不一样的一点就是 this 的指向,普通函数中的this,是运行时候决定的,而箭头函数却是定义时候就决定了。


var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
},
showNameLater: function () {
setTimeout(() => {
console.log(this.name);
}, 1000)
}
};

obj.showNameLater();

// qianlong
var obj = {
name: 'qianlong',
showName: () => {
console.log(this.name);
}
};

obj.showName();
// undefined

一些坑

  1. setTimeout
var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
},
showNameLater: function () {
setTimeout(this.showName, 1000);
}
};

obj.showNameLater();

// undefined

这里在执行setTimeout这个函数的时候传了obj的showName函数作为第一个参数,其效果与

var showName = obj.showName

是相同的。而setTimeout内部其实也是执行了传进去这个函数而已,即。

showName();

还记得这种调用方式和​​window.showName()​​是类似的效果吗?这个时候输入为undefined也就好理解了。

那么怎么解决这个问题呢,毕竟我们期望的效果是输出​​qianlong​​。

var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
},
showNameLater: function () {
var self = this;
setTimeout(function () {
self.showName();
}, 1000);
}
};

obj.showNameLater();

​或者​

var obj = {
name: 'qianlong',
showName: function () {
console.log(this.name);
},
showNameLater: function () {
setTimeout(this.showName.bind(this), 1000);
}
};

obj.showNameLater();
  1. setTimeout


尼玛坑爹啊,居然还是因为你。


'use strict';

function show() {
console.log(this);
}

show(); // undefined

setTimeout(show, 1); // window

在严格模式下面,函数调用的时候没有指定this的情况下,内部this的表现为​​undefined​​,但是setTimeout却不同,其内部默认还是指向window。

  1. 为构造函数指定this
var Person = function (name, sex) {
this.name = name;
this.sex = sex;
};

var p1 = new Person.call({});

// Uncaught TypeError: Person.call is not a constructor

​这里报错了,原因是我们去 new 了 Person.call 函数 ,这里的函数不是一个构造函数;​

当然解决方式也是有的。

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

var p1 = new (Person.bind({}))('qianlong', 'sex');

// Person {name: "qianlong", sex: "sex"}
  1. 为箭头函数指定this
var show = (str) => {
console.log(str);
console.log(this);
};

show('qianlong');
// qianlong
// window

show.call({name: 'qianlong'}, 'qianlong');
// qianlong
// window

可以看到使用call来手动改变箭头函数中的this的时候,无法成功。 箭头函数中的 this 在定义它的时候已经决定了(执行定义它的作用域中的 this),与如何调用以及在哪里调用它无关,包括 (call, apply, bind) 等操作都无法改变它的 this。

结语


文章可能有些疏漏与错误之处,欢迎各位指正。