JavaScript中的this关键字,是JavaScript中的混乱的来源之一。

this关键字介绍

在很多面向对象语言中,this关键字都是作为一个引用,让对象的方法能够引用到对象本身。但是在JavaScript中,this的使用却是有点混乱。

首先,在全局作用域中,this代表全局对象。比如在浏览器中执行console.log(this);,你得到window对象。

再看看下面的例子:

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

f1() // 得到window对象

但是启动了use strict之后:

function f2() {
  'use strict';
  return this;
}

f2() // 输出undefined

this这个关键字正儿八经的用法是作为函数调用时的属性,用来指向函数的属主。简单的说,如果f是对象o的方法,那么调用f的时候,它的this应该指向o。上面例子中f1和f2都是全局函数,你既可以说他们是全局对象的方法,也可以说他们不属于任何对象(认为全局对象很特殊,应该被排除在外)。JavaScript一开始是支持第一种观点(以f1为例子),但是加了use strict之后,就表示转向支持第二种观点了。

访问this对象的属性

this关键字的意义在于可以让函数访问this对象的其他属性,比如:

var obj = {
  a : 15,
  f : function () {
    console.log(this.a + 1);
  }
}

obj.f() // 输出16

操弄this关键字

作为一门脚本语言,JavaScript可以让你操弄this关键字,让一个函数自己都不知道自己亲娘是谁。首先我们可以通过apply()或者call()来调用一个函数的时候,给这个被调用的函数设一个其他的this值:

function f3() {
  'use strict'
  console.log(this, arguments)
}
f3.call(null); // 输出null
f3.call(1); // 输出1,需要特别注意的是Javascript会通过Number()这个构造函数把字面值1转化为Number对象,再作为f2的this
f3.call(null, 1) // 输出null,以及arguments里面包含一个参数,就是传进去的1

applycall类似,只是传参数的方式有点不一样,apply接受一个list为参数,比如:apply(null, [1])

有人可能会问为什么可以直接在函数上调用applycall,这是因为他们都属于Function.prototype的方法。而Function.prototype又是函数对象的prototype,一个函数对象可以直接调用其prototype的方法。

一个典型的需要操弄this关键字的场景,就是触发DOM事件的时候,把相应的回调函数的this指向发生该事件的DOM元素。

锁定this关键字

Function.prototype提供了一个bind的方法,可以帮助你锁定this:

f4 = f3.bind("hi")
f4() // 输出 hi
f4.call("newhi") // 还是输出 hi

arrow函数

ES6提供了一种快捷定义函数的方式,叫做arrow函数

var f5 = (() => console.log(this))
f5() // 输出global对象
f5.call("123") // 依然输出global对象

arrow函数的this值在arrow函数定义时就被绑定死了,就不能被修改了。

创建新对象

在JavaScript里面,你可以通过new关键字创建一个新的对象,这个新的对象会有自己的this属性。

function Foo() {
  if (!new.target) throw 'Foo() must be called with new';

  this.msg = "Hello, world!"
  this.hi = function() {
    console.log(this.msg)
  }
}

f = new Foo()
f.hi() // 输出"Hello, world!",也就是this.msg的值

参考链接

本文的大部分内容参考了MDN的this