2021-06-20-this关键字的指向和原理
这篇文章深入探讨了 JavaScript 中 this 关键字的指向原理及其在不同调用环境中的行为。
this 关键字的指向和原理
定义
this是JavaScript中的关键字,它隐式的传递了一个对象引用,可以理解为指针,调用函数的对象,它的指向取决于函数在哪里被调用,且运行时进行绑定
不同调用环境中 this 的指向
浏览器全局环境中
在全局环境中执行this,表示Global对象,在浏览器中表示window对象
console.log(this); //Window
console.log(typeof this); //object
console.log(this === window); //true
new 运算符调用
当通过new运算符来调用函数时,函数被当做为一个构造函数,this 的指向构造函数创建出来的对象
var name = "jalever";
function A() {
console.log(this.name);
}
A(); // jalever
var B = new A();
B(); //undefined (因为B并没有name属性)
作为对象属性,非 window 环境中调用
//定义一个对象obj,添加属性name,添加方法objFun
var obj = {
name: "jalever",
objFun: function () {
console.log(this); // Object {name: "jalever"}
console.log(typeof this); //object
console.log(this === window); //false
console.log(this.name); //jalever
},
};
//调用obj对象的方法
obj.objFun(); //this 绑定到当前对象,也就是obj对象
作为对象属性,window 环境中调用
//定义一个对象obj,添加属性name,添加方法objFun
var obj = {
name: "objname",
objFun: function () {
console.log(this); //Window
console.log(typeof this); //object
console.log(this === window); //true
console.log("My name is " + this.name); // My name is windowname
},
};
var name = "windowname";
var test = obj.objFun;
test();
解析
可以看出函数内部中 this 值不是静态的,是动态的,可以改变的,每次调用一个函数时,它总是在重新求值。函数内部中的 this 值,实际上是由函数被调用的父作用域提供,依赖实际函数的语法。
例如下面这个例子
//定义一个对象obj,添加属性name,添加方法objFun
var obj = {
name: "objname",
objFun: function () {
console.log(this.name);
},
};
var name = "windowname";
var test = obj.objFun;
obj.objFun(); //objname
test(); //windowname
第一种情况时,我们调用obj对象的方法是通过obj.objFun();
,此时,this 绑定到当前对象,也就是obj对象;而在第二种情况中,我们调用方法的方式是var test = obj.objFun;
test();
,此时,test不是一个对象的引用,所以this值代表全局对象
原理
this 的设计,跟内存里面的数据结构有关系
var obj = { foo: 5 };
上面的代码将一个对象赋值给变量obj。JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 }
,然后把这个对象的内存地址赋值给变量obj
也就是说,变量obj是一个地址。后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性
原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。举例来说,上面例子的 foo 属性,实际上是以下面的形式保存的
所以当属性的值是一个函数时
var obj = { foo: function () {} };
这时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给 foo 属性的 value 属性
回到我们之前的那个例子,如下:
//定义一个对象obj,添加属性name,添加方法objFun
var obj = {
name: 'objname',
objFun: function(){
console.log(this。name);
}
};
var name = "windowname"
var test = obj.objFun;
obj.objFun(); //objname
test(); //windowname
它的内存结构如下所示:
所以,当我们使用obj.objFun();
执行函数时,this.name的执行环境为obj环境;但我们使用var test = obj.objFun();
test();
执行时,test已经获取到了objFun函数的存储地址了,所以它的this.name执行环境就为window环境。