一、简介
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