this
代指了程序当前执行的上下文对象。
this
是 ECMAScript 中的关键字,但也是一个引用类型的变量,其值就是上下文对象所在“堆”中的引用地址。
this
只会与函数/方法有关,因为它们是可执行的代码。
而上下文对象可以简单粗暴的理解为当前代码执行所处的对象。例如下面代码的执行就是发生在 window
对象中,所以通过 this
关键字,就可以拿到 window
对象的属性与方法。
var _name = "world";
function sayHello(name) {
return `Hello ${this._name}`;
}
sayHello();
而下面代码的执行则是放生在对象 obj
中的。
var _name = "world";
var obj = {
_name: "obj",
getObjName() {
return this.name;
},
getTopName() {
return this._name;
},
};
obj.getObjName(); //obj
obj.getTopName(); // undefined
我们约定:
- 全局对象所调用的函数就称之为“函数”,例如
sayHello()
=>window.sayHello()
。 - 对象所调用的函数则称之为“方法”。
相信聪明的你一定发现了,this
对象与作用域肯定存在一定的联系,但是为什么 obj.getTopName()
的值是 undefined
呢?而不是遵循局部作用域在解析标识符时会继续沿着作用域链(scope chain)查找呢?
this
会被保存在作用域的“活动对象(AO)”中,在全局作用域中它被称之为”变量对象(VO)”。
由于函数不属于某个对象的成员属性,所以其活动对象中的 this
默认指向的是 window
对象(当然这在严格模式下会有不同),因而就会造成函数每次搜索 this
的时候,只会搜索到当前活动对象为止,即永远也不会访问外部函数或方法作用域中的 this
。这也是为什么 obj.getTopName()
的值是 undefined
了。
- 对象方法中的
this
引用的是这个对象。 - 函数中的
this
引用的是window
对象,因为它属于全局调用。 - 箭头函数的
this
是其所处最近一层上下文对象的引用。
严格模式下,函数的 this
指向的是 undefined
。
"use strict";
function useStrictThis() {
console.log(this);
}
useStrictThis(); //undefined
使用 new
运算符调用一个构造函数时,构造函数中的 this
指向的是其创建的实例对象。
function Person(name) {
this.name = name;
this.age = 27;
}
Person.prototype.sayName = function () {
return this.name;
};
Person.prototype.sayAge = function () {
return this.age;
};
//----------------------------
var p1 = new Person("jack");
p1.sayName(); //jack
p1.sayAge(); //27
至于为什么构造函数的实例对象还能获取到其构造函数原型对象上的方法或属性,这就是基于原型继承的特点造成的了。
new
运算符实际上是一个语法糖,它主要封装了创建对象、关联对象原型、调用构造函数实例化对象以及最终返回该对象的功能。
function New(Constrouct) {
var obj = Object.create(null);
obj.__proto__ = Constrouct.prototype;
Constrouct.call(obj);
return obj;
}
我们已经掌握了函数与方法的定义区分,并且也知道了在非严格模式下函数中的 this
默认指向的是 window
对象,而严格模式下则保持值为undefined
。
var _name = "world";
function greet() {
var _name = "kim";
function say() {
console.log(this._name); //world
}
say();
}
至于对象方法中的 this
永远指向的是最后一个点运算符 .
所属的对象。
var family = {
name: "We are a family",
father: "jack",
mather: "alis",
son: {
name: "kim",
say() {
return `my name is ${this.name}`;
},
},
say() {
return this.name;
},
};
family.say(); //We are a family
family.son.say(); //my name is kim
箭头函数没有 this
,或者说其 this
引用的是当前箭头函数所处作用域中的 this
,也就是外部环境的上下文。
var obj = {
greet() {
var say = () => {
console.log(this);
};
say();
},
say: () => {
console.log(this);
},
};
obj.greet(); //obj
obj.say(); //window
需要注意的是 js 是基于词法作用域的。
function Animal(name){
this.name = name;
}
Animal.prototype.eat = ()=>{
console.log(this); //window
};
Animal.prototype.say=function(){
console.log(this); Animal{}
};
var sheep = new Animal('sheep');
sheep.eat();
sheep.say();
永远记着:
- 箭头函数没有
this
。 - 箭头函数的
this
引用的是外部作用域环境的this
。 call
、apply
、bind
等方法无法对箭头函数产生作用。
apply,call,bind
等方法可以修改函数/方法执行时 this
的引用。从而人为的指定函数/方法执行时的上下文。
var _name = "world";
var conf = {
_name: "config",
};
function greet() {
return `Hello,${this._name}`;
}
greet(); //Hello world
greet.call(conf); //Hello config
- 判断函数的调用方式,是函数调用,还是方法调用。
- 如果是方法调用,那么点
.
运算符左边的对象就是this
的引用,否则没有,继续第三步。 - 函数或方法是否通过
call
、apply
、bind
等方法调用的,如果是,它们会强制修改this
的引用,如果不是继续进行下一步。 - 该函数或方法是否充当了构造函数通过
new
运算符调用?如果是this
的引用就是该构造函数的实例对象,如果不是继续下一步。 - 如果是严格模式下,则
this
就是undefined
,否则默认指向window
对象。
就算在严格模式
use strict
定时器setTimeout
与setInterval
中的this
都是指向 window 对象。
"use strict";
setTimeout(() => {
console.log(this); //window
});