javaScript中的this是变化的导致在很多地方this都不一样,于是很多小伙伴在开发的时候会很迷惑,详细理解清楚this将有助于我们在今后的学习中更好的理解其他的东西,下面就开始讲解this。
记住下面这几句话:
- 函数预编译过程 this —> window 预编译的时候AO对象里面会有this键,它的值为window
- 全局作用域里 this —> window
- call/apply 可以改变函数运行时this指向
- obj.func(); func()里面的this指向obj)
全局上下文
- 无论是否在严格模式下,在全局执行上下文中(在任何函数体外部)this 都指代全局对象。
函数上下文
- 先来看一句话:在函数内部,this的值取决于函数被调用的方式,但是箭头函数例外,下面会解释。
简单调用:
不在严格模式下面 ,this 的值默认指向全局对象。
不在严格模式下面 ,this 的值默认指向全局对象。
例1:
function f1(){
return this;
}
//在浏览器中:
f1() === window; // true 在浏览器中,全局对象是window
//在Node中:
f1() === global; // true
在严格模式下,this将保持他进入执行上下文时的值,所以下面的this将会默认为undefined。
例2:
function f2(){
"use strict"; // 这里是严格模式
return this;
}
f2() === undefined; // true
之所以是这个样子,是因为在严格模式下,如果 this 没有被执行上下文(execution context)定义,那它将保持为 undefined
在这个例子当中this的确应该是undefined,因为f2是被直接调用的,而不是作为对象的属性或方法调用的(如 window.f2())。
我们还可以使用call()和apply()来调用函数,给他绑定this,具体的用法自己去查。需要注意的是,使用call和apply的时候,如果传递给 this 的值不是一个对象,JavaScript 会尝试使用内部 ToObject 操作将其转换为对象。
bind方法
- ECMAScript 5 引入了 Function.prototype.bind。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,也就是返回了一个新的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。还需要注意的是,如果将this传递给call、bind、或者apply 它将被忽略。
箭头函数
箭头函数也是es6出现的,解决了函数this不正确绑定的问题,和普通的函数不太一样,要区分开来理解。
箭头函数的this与封闭词法上下文的this保持一致 ,即箭头函数内部的this指向被创建时的上下文 ,如果被创建时的上下文的this变化,那么箭头函数内部的this也会跟着变。
下面我们通过例子来理解。
例1:
var foo = () => {
return this
}
var obj = {
foo: foo
}
console.log(foo() === window) // true
console.log(obj.foo() === window) // true
例1中,foo这个箭头函数被创建时所在的上下文是window,当我们通过第一种方式调用时,它会返回的this就是window.当我们使用第二种obj.foo()方法来调用时候,foo不是一个箭头函数,而是一个普通的函数,那么按照前面说的那句话“在函数内部,this的值取决于函数被调用的方式”,来理解,此时,foo的this将会被绑定到obj,但是这里的foo是一个箭头函数,当它创建时,它的this就被绑定到window,所以即使是通过obj.foo()的方式来调用,它的this不会改变为指向obj,它仍然指向window。可以理解为箭头函数绑定到的this优先存在
例2:
var obj = {
bar: function() {
var x = () => { // 这里被创建时的上下文是bar这个function 那么这个function的this是什么,箭头函数的this就是什么
return this
}
return x;
}
};
var fn = obj.bar(); // 通过点调用时bar的this被绑定到obj 箭头函数的this就是obj
console.log(fn() === obj); // true
var fn2 = obj.bar; //获得了引用但是没调用this不会被绑定到obj
console.log(fn2()() == window); //true
在例2中,我们在内部声明的箭头函数被创建时的上下文是bar这个函数上下文,那么这个函数的this是什么,箭头函数的this就是什么。接着我们使用了两种方式来调用这个bar函数。第一种是通过obj.bar()调用,此时bar的this被绑定到obj,那么此时箭头函数的this也指向obj。第二种方式是先使用fn2 = obj.bar获得了引用,但是没调用,所以此时bar的this不会被绑定到obj,然后
使用f2()()调用bar,此时相当于可以这样理解 window.f2()(),那么此时bar的调用者是window,所以它的this将会指向window,因此此时箭头函数内部的this现在也是window。
例3:
var obj = {
foo: () => {
return this
}
};
var fo = obj.foo
console.log(fo() === obj) // true
在例3中,虽然是先获取的引用,没有先把foo的this绑定为obj,但是foo被创建时的上下文就是obj,因此不管是通过obj.foo()直接调用,还是先获取引用再调用,得到的结果都是一样的。这里的fo()任然可以理解为window.fo(),理论上也是foo的this会被绑定到全局,但是内部使用的箭头函数,也可以像例1一样理解为箭头函数绑定到的this优先存在。
原型链中的this:
如果该方法存在于一个对象的原型链上,那么this指向的是调用这个方法的对象
例子:
function test () {
}
test.prototype.sayName = function () {
return this
}
var person = new test()
console.log(person.sayName() === person) //true
作为构造函数:
它的this被绑定到正在构造的新对象。这句话的意思是当你执行 var person = new Person()时候 this指向正在被构造的新对象person
作为一个DOM事件处理函数:
当函数被用作事件处理函数时,它的this指向触发事件的元素
作为一个内联事件处理函数(写在标签内用""包含):
它的this指向当前的DOM元素
其他
函数调用函数,被调用的函数的this指向window,这里说的普通函数,箭头函数会受所创建时的上下文的this影响
例1:使用普通函数
var obj = {
foo: test1,
}
function test1 () {
function test2 () {
return this
}
return test2()
}
console.log(obj.foo() === window) // true
这里虽然test1的this被绑定为obj 但是当test1调用特殊test2时,这个this仍是window
例2:使用箭头函数
var obj = {
foo: test1,
}
function test1 () {
var test2 = () => this
return test2()
}
console.log(obj.foo() === obj) // true
由于箭头函数内部的的this是所创建时的上下文也就是test1,所以这里是true