this,英语语法中是一个指示代词,表示指代某些事物。于是在JavaScript中,它其实也是指代了某种东西,一般来说,它指代的都是某个特定的对象。那它指代了什么对象呢?
简单来说,this指代什么,要看包含this的那个函数是如何执行的, 也就是说,this的值是取决于其所包含函数的调用方式的。我们知道函数有4种调用的方式,让我们来具体看看不同方式下,this的值是什么?
注意: this 是JavaScript中的一个关键字,不是一个变量。
直接调用
这是最常见,也最易懂的函数执行方式。这种情况下,默认的this对象就是指向了全局对象。
var a = 'arron'
function a() {
console.log(this.name); // 输出:arron 。这里this的值为全局对象(浏览器中一般就是 Window 对象)
console.log(this); // 输出Window对象
}
a();
不过也要注意以下情况。即当以 “严格模式” 执行的时候,this对象会被设置为 undefined
function a() {
"use strict"
console.log(this); // 输出 undefined
}
a();
那对于嵌套函数呢?
function a() {
function b() {
console.log(this); // 输出Window对象
}
b();
}
a();
其实不管嵌套多少层,只要是直接调用函数,this 都会是指向全局对象的(严格模式除外)
看完后面几种函数的调用方式,你就会对这种调用方式有个区分。
作为某个对象的方法进行调用
函数作为某个对象方法的时候,相当于这个函数绑定到了这个对象,这个对象呢就是这个函数所依赖的一个上下文。这个时候 this 就是指向这个对象的了。
var obj = {
name: 'koler',
say: function () {
console.log(this); // this的值为整个 obj 对象
// 拓展一下
function b() {
console.log(this);
/* 注意这里的this又会是window了,因为函数b它不是obj对象的直接属性(不像say),而是存在于另一个函数作用域中的函数,因此函数b的上下文不再是某个具体的对象,这个时候默认就会设置为全局对象。 */
// 也就是说只有this所在函数直接是作为某个对象的属性存在的时候,它才指代这个对象。
// 其实这里你看成是函数的直接调用(也即第一种情况)也是可以的
}
b();
}
}
注意的一点就是一定要函数是作为某个对象的方法进行调用才会指向这个对象。辨别清楚哪些是方法,哪些不是,就把这种情况和第一种情况区分开来了
我们再拓展一下下,如果是这种情况 parent.child.say()
这种方式进行调用,say方法中的this会是怎样的呢?指向parent还是child呢?
var parent = {
... // parent的其他属性
name: 'Papa',
child: {
... // child的其他属性
name: 'son',
say: function () {
console.log(this.name); // 输出 son 。表示这里的this是指代的中间的 child,而非parent
console.log(this); // 输出 parent.child 指向的对象
}
},
say: function () {
console.log(this.name); // 输出Papa。这个this指向parent
console.log(this); // 输出 parent 对象
}
}
parent.child.say();
parent.say();
Ok,通过实例我们知道了,某个方法你是作为哪个对象的直接子属性,那你的this就是指向这个对象的,与其父对象或与之关联的其他对象无关。
构造函数调用
因为构造函数就是创造出一个新的对象,所以就把这里的this指代新构建出来的这个对象
function Person(name, age) {
this.name = name; // 这里通过this给新建对象实例一个name属性,然后赋予其由构造参数中传递进来的值
this.age = age; // 与上相同
}
var person = new Person('lory', 22);
console.log(person.name); // 输出:lory。这里就可以直接在新建的对象上得到name属性的值
console.log(person.age);
构造函数中的this很直接的指向了新构造出来的对象。简单直接,无需多说。
使用call或apply,以及bind 方法(在ES6中出现的) 指定this所指向的对象
我们知道函数其实也是一种对象,对于我们直接声明的一个函数,它其实是Function对象的一个实例。任何一个函数都具有call和apply方法,其第一个参数都是想要this所指向的对象,其它参数是函数对应的参数值(不过传参方式有点小的区别)。
而 bind() 方法也是每个函数都具备的,其第一个参数也是想要this所指的对象,其它参数是需要绑定的函数的参数,不过其绑定对象后会返回绑定后的函数,而非执行,这是区别所在。
var name = 'lory';
function Person(name, age) {
console.log(this.name + '/' + name + '/' + age);
}
Person(); // 输出lory 。 这里对象直接调用,因此这里的 this 相当于window。也就是window.name (因为全局环境中的变量都会成为全局变量的属性)
Person.call({'name': 'gitai'}, 'yita', 20);
// 输出:gitai/yita/20
// 因为这里用call绑定了一个对象字面量,其中有name属性,所以在输出this.name的时候,就会引用这个对象中的值
Person.apply({'name': 'moder'}, ['yita', 20]);
// 输出: moder/yita/20
// 原因和上面的原因是一样的。
/*
这里占个位置说一下call和apply的区别:
相同点:call和apply第一个参数都是需要this指定的对象
不同点:
call方法除了第一个参数外,其他的参数和函数所需要的参数一一对应的,比如一个Person函数需要name和age两个参数,那在调用call的时候可以这样传入参数 Person.call({}, 'yita', 20);
apply方法除了第一个参数外,第二个参数可以是函数所需要的参数所组成的一个数组。比如还是Person函数,调用apply可以这样调用 Person.apply({}, ['yita', 20]);
*/
下面再看看我们的bind是如何确定this的
var imLili = Person.bind({name: 'Lili'}); // 这里绑定了this的值,返回一个函数引用
imLili(); // 输出 Lili
这个bind其实很简单,和call类似,就是在绑定this的值后不是执行这个函数,而是返回一个新的函数,这个新的函数的this值就是我们所绑定的,所以这个新函数的this就完全确定下来了。而原函数实际不会有影响。
对于bind需要注意的就是它是在ES6中出现的,所以要是你的执行环境不支持,改用call代替吧,反正方法那么多,找一个呗,对吧。