2021-06-20-this关键字的指向和原理

这篇文章深入探讨了 JavaScript 中 this 关键字的指向原理及其在不同调用环境中的行为。


this 关键字的指向和原理

定义

thisJavaScript中的关键字,它隐式的传递了一个对象引用,可以理解为指针,调用函数的对象,它的指向取决于函数在哪里被调用,且运行时进行绑定

不同调用环境中 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 };

上面的代码将一个对象赋值给变量objJavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量obj

也就是说,变量obj是一个地址。后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性

原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。举例来说,上面例子的 foo 属性,实际上是以下面的形式保存的

image-1.webp

所以当属性的值是一个函数时

var obj = { foo: function () {} };

这时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给 foo 属性的 value 属性

image-2.webp

回到我们之前的那个例子,如下:

//定义一个对象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

它的内存结构如下所示:

image-3.webp

所以,当我们使用obj.objFun();执行函数时,this.name的执行环境为obj环境;但我们使用var test = obj.objFun(); test(); 执行时,test已经获取到了objFun函数的存储地址了,所以它的this.name执行环境就为window环境。

this 的使用场景

image-4.webp

Reference

掌握 JavaScript 中的 this,call,apply 的原理

JavaScript 的 this 原理