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
apply
和call
类似,只是传参数的方式有点不一样,apply
接受一个list为参数,比如:apply(null, [1])
。
有人可能会问为什么可以直接在函数上调用apply
和call
,这是因为他们都属于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