一、简介
this
是 JavaScript 中的一个关键字,当一个函数被调用时,除了传入函数的显式参数以外,名为this
的隐式参数也被传入函数。
this
参数指向了一个自动生成的内部对象,这个内部对象被成为函数上下文
二、This的使用场景
1. 全局&调用普通函数
在全局环境中,this 永远指向 window。
console.log(this === window); //true
普通函数在调用时候 (注意不是构造函数,前面不加new),其中的this也是指向window。
但是在严格模式下调用的话,会报错:
var x = 20;
function test(){
console.log(this); // undefined
console.log(this.x); // Uncaught TypeError: Cannot read property 'x' of undefined
}
test();
2. 构造函数
所谓的构造函数就是由一个函数 new
出来的对象,一般构造函数的函数名首字母大写,例如像 Object
,Function
,Array
这些都属于构造函数。
function Test(){
console.log(this); // undefined
console.log(this.x); // Uncaught TypeError: Cannot read property 'x' of undefined
}
var test = new Test();
console.log(test.x); //10
在上述代码的执行中,如果函数作为构造函数使用,那么其中的 this
就代表它即将 new
出来的对象。
但是如果直接调用Test
函数,而不是new Test()
,那就是情况一一样,变成普通函数,指向window
。
function Test(){
this.x = 10;
console.log(this); //Window
}
var test = Test();
console.log(test.x); //undefined
3. 对象方法
如果函数作为对象的方法时,方法中的 this
指向该对象:
var obj = {
x: 10,
test: function () {
console.log(this); //Object
console.log(this.x); //10
}
};
obj.test();
注意:若是在对象方法中定义函数,那么情况就不同了。
var obj = {
x: 10,
test: function () {
function f(){
console.log(this); //Window
console.log(this.x); //undefined
}
f();
}
}
obj.test();
在上述代码中,需要注意的是:
函数f是在 obj.test 内部定义的,但它仍然属于一个普通函数, this 仍指向 window 。
在这种情况下,如果想要调用上层作用域中的变量 obj.x
,可以使用self
缓存外部this
变量。
var obj = {
x: 10,
test: function () {
var self = this;
function f(){
console.log(self); //{x: 10}
console.log(self.x); //10
}
f();
}
}
obj.test();
如果test
函数不作为对象方法被调用,而是在外部赋值给一个全局变量,并没有作为obj
的一个属性使用,那么此时的this
的值是指向window
:
var obj = {
x: 10,
test: function () {
console.log(this); //Window
console.log(this.x); //undefined
}
};
var fn = obj.test;
fn();
4. 构造函数 prototype 属性
function Test(){
this.x = 10;
}
Test.prototype.getX = function () {
console.log(this); //Foo {x: 10, getX: function}
console.log(this.x); //10
}
var test = new Test();
test.getX();
在上述代码中, 在Test.prototype.getX
函数中,this
指向的test
对象。 不仅仅如此,在整个原型链中,this
代表的也是当前对象的值。
5. 函数用 call、apply或者 bind 调用
当一个函数使用call
、apply
或者bind
调用时,this
的取值就是传入的对象的值,即obj
。
var obj = {
x: 10
}
function test(){
console.log(this); //{x: 10}
console.log(this.x); //10
}
test.call(obj);
test.apply(obj);
test.bind(obj)();
6. DOM event this
在一个 HTML DOM
事件处理程序里,this
始终指向这个处理程序所绑定的 HTML DOM
节点:
function Listener(){
document.getElementById('test').addEventListener('click', this.handleClick); //这里的 this 指向 Listener 这个对象。不是强调的是这里的 this
}
Listener.prototype.handleClick = function (event) {
console.log(this); //<div id="foo"></div>
}
var listener = new Listener();
document.getElementById('test').click();
这其实等同于使用函数传参,使handleClick
运行时上下文改变了,如下:
var obj = {
x: 10,
fn: function() {
console.log(this); //Window
console.log(this.x); //undefined
}
};
function test(fn) {
fn();
}
test(obj.fn);
同样的,也可以通过bind
切换上下文的绑定:
function Listener(){
document.getElementById('test').addEventListener('click',this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
console.log(this); //Listener {}
}
var listener = new Listener();
document.getElementById('test').click();
其实以上6种情况,可以总结成一句话: this 指向调用该方法的对象
。
7. 箭头函数中的this
当使用箭头函数的时候,情况就有所不同了:
箭头函数内部的 this 是词法作用域,由上下文确定
。
var obj = {
x: 10,
test: function() {
var fn = () => {
return () => {
return () => {
console.log(this); //Object {x: 10}
console.log(this.x); //10
}
}
}
fn()()();
}
}
obj.test();
现在,函数完全修复了this
的指向,this总是指向词法作用域,也就是外层调用者obj。 如果使用箭头函数,以前的这种hack
写法,就不再需要了:
var self = this;
由于this
在箭头函数中已经按照词法作用域绑定了,所以,用call()或者 apply()调用箭头函数时,无法对this
进行绑定,即传入的第一个参数被忽略:
var obj = {
x: 10,
test: function() {
var fn = () => {
return () => {
return () => {
console.log(this); // Object {x: 10}
console.log(this.x); //10
}
}
}
fn.bind({x: 14})()()();
fn.call({x: 14})()();
}
}
obj.test();
8. 补充说明
this
为保留字,你不能重写this
。
function test(){
var this = {}; //Uncaught SyntaxError: Unexpected token this
}
- 宿主对象
一门语言在运行的时候,需要一个环境,称为'宿主环境', 对于JavaScript,宿主环境最常见的是web浏览器,浏览器提供了一个JavaScript
运行的环境,在这个环境里面,需要提供一些接口,好让JavaScript
引擎能够和宿主环境对接。JavaScript 引擎 才是真正执行JavaScript
代码的地方,常见的引擎有V8(目前最快 JavaScript 引擎、Google 生产)、JavaScript core
。 在浏览器或者服务端(node.js) 都有自己的js引擎,在浏览器中,全局对象为window
,而在node.js
中,全局对象为 global