JS中this的指向有些复杂,分为较多种的情况。此外,在严格模式和非严格模式之间也会有一些差别。
注:node环境中的全局变量global统一对应浏览器环境中的全局变量window。
全局环境
// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // true
无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。
纯粹的函数调用
严格模式
function test(){
"use strict";
// 这里是严格模式
return this;
}
console.log( test() === undefined);
// true
console.log( window.test() === window);
// true
可以看到严格模式下,因为没有调用test方法的对象,于是this指向undefined,当我们注明了使用window调用的时候,于是指向了window全局对象。
非严格模式
var a="123";
let b="321";
function test(){
console.log(this.a);
console.log(this.b);
}
test();
// 123
// undefined
这里我们看到this指向执行test这个方法的对象,前面是空的,同时也是非严格模式,也就是window对象,于是this.a等于window.a。
var将a变量添加到window这个对象下面。let不会将b变量添加到window下面。
ES6中建议使用let 和const 代替原来的var。
对象中的方法调用
let obj={
a:"123",
test:function(){
console.log(this.a);
}
}
obj.test();
// 123
此时this指向执行test的obj,自然顺利成章的this.a等于obj.a。
方法中内部方法的调用
var a="123";
let obj ={
a:"321",
test: function(){
return function(){
console.log(this.a);
}
}
}
obj.test()();
// 123
方法内部的方法(闭包)中的this统一指向window。
构造方法中的调用
function Person(age){
this.name="123";
this.age=age;
}
let worker=new Person(33);
console.log(worker.name);
// 123
构造函数中的this.name最终会指向实例自己的属性。它的this被绑定到正在构造的新对象。
bind,apply,call中的调用
let obj={
a:"123",
test:function(){
console.log(this.a);
}
}
let g = obj.test.bind({a:"444"});
g();
// 444
let h = g.bind({a:'555'}); // bind只生效一次!
h();
// 444
obj.test.call({a:"222"},1,2,3);
// 222
obj.test.apply({a:"333"},[1,2,3]);
// 333
bind会创建一个与obj.test具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。
上面的g已经指向绑定过了的obj.test,于是想要再次绑定g的this就失效了。
call,apply后的第一个参数决定了this的指向。
call的第23456等等参数是独立的值。
apply的第二个参数是数组。
箭头函数
var a="123";
let obj={
a:"321",
test:()=>{
console.log(this.a);
}
}
obj.test();
//123
let obj={
a:"123",
test:function(){
let testyou= ()=>{
console.log(this.a);
}
testyou();
}
}
obj.test();
//123
obj.test.apply({a:"321"});
//321
注意:如果将this传递给call、bind、或者apply,它将被忽略。不过你仍然可以为调用添加参数,不过第一个参数(thisArg)应该设置为null。
对于箭头函数的this,看了挺多的文章,我个人是这样理解的:
1.从箭头函数那一层逐渐向外看。
2.只要是对象,跳过,继续向外,如果到头了,没有遇到方法,恭喜,那么箭头函数的this指向window。
3.如果外部某一层是方法,停下,思考该方法下的this 指向,箭头函数的this继承于它。
第一个例子:test被定义为箭头函数,它的外层是个对象obj,跳过,到头了,于是指向window
第二个例子:testyou被定义为箭头函数,它的外层是test方法,于是寻找执行这个test方法时内部的this指向,指向obj,于是箭头函数的this指向obj。随后用apply改变test函数的this指向,箭头函数的this所以继承了改变。