前言
为什么要有this
首先你应该先了解执行上下文
的概念,例如下面这个函数,javaScript运行在函数体内部,引用当前上下文的其他变量。
function func() {
console.log(a);
}
问题是:函数的调用可以在任何其他执行上下文中被调用,因此a的指向就不同了,因此我们需要依靠this来优雅地指向当前代码运行时所处的上下文环境
。
优雅的准确的
看下面这段代码,func1()中是不是会误导你认为这个name是全局的name,而func2()就清晰的让你知道,这个name,是局部的name,其实两者都是指向局部的name,而func2()的方式明显更优雅。假如对象的名字很长,很容易就产生误导。
//假设有个对象名字很长,而且有可能会改名
var iAmALongLongLongNameObject={
name:"coffe",
func1(){
//如果光看代码,是不是容易看成调用了全局变量的name?
return iAmALongLongLongNameObject.name;
},
func2(){
//这里光看代码就很准确地知道是调用了局部变量的name!
return this.name;
}
}
this的指向规则
函数是否在new中被调用,指向新创建的对象
如果是的话,this 绑定的是新创建的对象
特殊情况:SetTimeout或SetInterval结合使用
function func(name) {
this.name = name;
this.getName = function() {
return this.name;
};
}
var obj = new func("coffe"); //this会指向obj
console.log(obj.getName()); //>> coffe
函数是否通过call、apply、bind显式指向,指向的是call、apply、bind三个方法的第一个参数指定的对象
如果是的话,this指向的是call、apply、bind三个方法的第一个参数指定的对象。
var obj1 = {
name: "coffe"
};
function func() {
return this.name; //这里的this本来指向window
}
var str = func.call(obj1); //改变了func函数里面this的指向,指向obj1
console.log(str); //>> coffe
var num = 0;
class Obj {
constructor(num){
this.num = num;
}
func(){
console.log(this.num);
}
func1(){
setTimeout(function () {
console.log("setTimeout:"+this.num);
}.bind(this), 1000);//bind
}
func2(){
setInterval(function () {
console.log(this.num);
}.bind(this), 2000);//bind
}
}
var obj = new Obj(1);
obj.func();//>> 1 输出的是obj.num
obj.func1()//>> setTimeout:1 输出的是obj.num
obj.func2()//>> 1 1 1 1 …… 输出的是obj.num
函数是否被当做某个对象的方法而调用(隐式指向)
var obj1 = {
name: "coffe",
func() {
return this.name; //指向obj1
}
};
//这里的obj1.func(),表明func函数被obj1调用,因此func中的this指向obj1
console.log(obj1.func()); //>> coffe
若以上都不是的话,使用默认绑定,绑定到全局对象
function func() {
console.log( this.a ); // this指向全局对象
}
var a = 2;
func(); //>> 2
var num = 0;
class Obj {
constructor(num){
this.num = num;
}
func(){
console.log(this.num);
}
func1(){
setTimeout(function () {
console.log("setTimeout:"+this.num);
}, 1000)
}
func2(){
setInterval(function () {
console.log(this.num);
}, 2000)
}
}
var obj = new Obj(1);
obj.func();//>> 1 输出的是obj.num
obj.func1()//>> setTimeout:0 输出的是window.num
obj.func2()//>> 0 0 0 0 …… 输出的是window.num
箭头函数
箭头函数并不是使用function关键字定义的,而是使用被称为“胖箭头”的操作符 =>
定义的。
箭头函数不遵守this的四种指向规则
,而是根据函数定义时的作用域来决定 this 的指向
。何谓“定义时的作用域”?就是你定义这个箭头函数的时候,该箭头函数在哪个函数里,那么箭头函数体内的this就是它父函数的this。
一旦被确定,就不可更改,一“箭”钟情!
function func() {
// 返回一个箭头函数
return a => {
//this 继承自 func()
console.log(this.a);
};
}
var obj1 = {
a: 2
};
var obj2 = {
a: 3
};
var bar = func.call(obj1);
bar.call(obj2); //>> 2 不是 3 !
// func() 内部创建的箭头函数会捕获调用时 func() 的 this。
// 由于 func() 的 this 绑定到 obj1, bar(引用箭头函数)的 this 也会绑定到 obj1,
// this一旦被确定,就不可更改,所以箭头函数的绑定无法被修改。(new 也不行!)
注意
严格模式
对于默认指向来说,决定this指向对象的并不是调用位置是否处于严格模式
,而是被调用的函数体是否处于严格模式
。如果被调用的函数体处于严格模式并且本身没有该属性
,this会指向undefined,否则this会指向全局对象,记住,处于严格模式下,要么是指向全局对象,要么就是undefined。
var a = "coffe"; //为全局对象window添加一个属性a
function func() {
"use strict";//开启严格模式
return this.a;
}
//严格模式下,this指向undefined
console.log(func()); //>> TypeError
function func() {
console.log(this.a);
}
var a = "1891";
(function() {
"use strict";
func(); //>> 1891
//这里输出 1891 而不是报错,是因为严格模式下,this的指向与func的调用位置无关
})();
var a = "coffe"; //为全局对象window添加一个属性a
function func() {
let a = 1
"use strict";//开启严格模式
return this.a;
}
//严格模式下,如果函数体内部也存在相同的变量,那么该变量指向全局对象
console.log(func()); //>> coffe
SetTimeout或SetInterval结合使用
var num = 0;
class Obj {
constructor(num){
this.num = num;
}
func(){
console.log(this.num);
}
func1(){
setTimeout(function () {
console.log("setTimeout:"+this.num);
}, 1000)
}
func2(){
setInterval(function () {
console.log(this.num);
}, 2000)
}
}
var obj = new Obj(1);
obj.func();//>> 1 输出的是obj.num
obj.func1()//>> setTimeout:0 输出的是window.num
obj.func2()//>> 0 0 0 0 …… 输出的是window.num