10 执行上下文视角的 this

Posted by CodingWithAlice on April 11, 2021

10 执行上下文视角的 this

作用域链和this是两套不同的体系,唯一的关系是在全局上下文中的this指向window对象,作用域链的最底端包含了window对象 ## this是什么 如下图所示,`this`是和执行上下文绑定的,每个执行上下文中都有一个`this`,按照执行上下文的分类,`this`也可以分为三类: - 全局执行上下文中的`this`
  • 函数执行上下文中的this

  • eval中的this(略过)

    image-20210411141927332

全局执行上下文中的this

全局执行上下文中的 this 指向 window 对象

### 函数上下文中的this 在默认情况下调用一个函数,其执行上下文中的 this 也是指向 window 对象的

有三种方法改变this指向:

#### 1、call/apply/bind函数 通过call/apply/bind函数 显式改变 this指向

call是立即执行传递this的;bind不是立即执行的,返回的是函数的副本;

bind 的定义:bind 会创建一个新函数,当调用该函数时,bind 会调用原函数,并将其 this 关键词设置为给定的值

let bar = {
  myName : " 极客邦 ",
  test1 : 1
}
function foo(name, age){
  this.myName = " 极客时间 " + name + age;
}
foo.call(bar, "name", "18");
foo.apply(bar, ["name", "17"]);
foo.bind(bar)("name", "16"); // 新函数,立即执行 

#### 2、通过对象调用方法 - 指向对象本身

在全局环境中调用一个函数,函数内部的 this 指向的是全局变量 window。
通过一个对象来调用其内部的一个方法,该方法的执行上下文中的 this 指向对象本身。

通过下方代码可以验证:通过 myObj 对象调用 showThis 方法 –> 最终输出的this值是指向myObj

var myObj = {
  name : " 极客时间 ", 
  showThis: function(){
    console.log(this) // this -> myObj
  }
}
myObj.showThis()

原因:JS执行引擎在执行 myObj.showThis()时,实际上是将其转化成 call 函数执行,代码如:

myObj.showThis.call(myObj);

#### 3、通过构造函数设置 通过 new 关键字构建了一个新对象,并且构造函数中的 this 其实就是新对象本身

function CreateObj(){
  this.name = " 极客时间 "
}
var myObj = new CreateObj()

我们复习一下,JS引擎在new一个构造函数的时候,做了四件事:

  • 首先创建了一个新对象 tempObj
  • 接着将空对象的 __proto__ 指向构造函数的原型
  • 然后改变this指向,执行构造函数中的代码(为这个新函数添加属性) —> 调用 CreateObj.call 方法;立即执行 CreateObj 函数,此时的 CreateObj 函数执行上下文中的 this 指向了 tempObj 对象;
  • 最后返回新对象: tempObj 对象

用代码来表示就是:

var tempObj = new Object();
tempObj.__proto__ = CreateObj.prototype; // 设置原型链
CreateObj.call(tempObj); // 改变 this 指向并立即执行
return tempObj;

自己实现一个 new 操作符

function objectFactory() {
    var obj = new Object()// 创建一个新的空对象
    Constructor = [].shift.call(arguments);
    obj.__proto__ = Constructor.prototype; // 将这个空对象的__proto__指向构造函数的原型
    var ret = Constructor.apply(obj, arguments); // 将this指向空对象
    return typeof ret === 'object' ? ret : obj; // 根据构造函数是否有返回值,给出返回对象
};

this 的缺陷

this 没有作用域的限制

1. 嵌套函数中的 this 不会从外层函数中继承

var myObj = {
  name : " 极客时间 ", 
  showThis: function(){
    console.log(this) // showThis 中的 this -> myObj
    function bar(){
        console.log(this)
    } // bar 中的 this -> window
    bar()
  }
}
myObj.showThis()

解决方案1:把 this 保存为一个 self 变量,再利用变量的作用域机制传递给嵌套函数

本质:把 this 体系转换为了作用域的体系

解决方案2:箭头函数 -> 不会创建自身的执行上下文,取决于外部函数

—> 箭头函数中的this指向定义时当前周围的作用域(词法环境)

2. 普通函数中的 this 默认指向全局对象 window

这样会打破数据的边界,造成一些误操作

  • 解决方案1

    如果要让函数执行上下文中的 this 指向某个对象,最好的方式是通过 call 方法来 显示调用

  • 解决方案2

    通过设置 JavaScript 的严格模式来解决。在严格模式下,默认执行一个函数,其函数的执行上下文中的 this 值是 undefined