一个能够确定 this 值的算法

deanyuancn
发布于 2021-5-11 16:32
浏览
0收藏

每个 JavaScript 程序猿,包括我自己,都一直在努力了解 this 关键字在代码中的真正身份。

我设计了一个通用算法,可以帮你在任何情况下确定 this 关键字的值。虽然我尽可能的使算法容易看懂,但还是建议你多看几遍并理解相关术语。

另外还用了几个例子展示怎样用这个算法一步一步的对 this 进行评估,最后你自己亲自试一试。

 

1. this 算法


把算法定义为 ThisValueOfFunction(func, invocationType) ,返回值为在以 invocationtype 方式调用函数 func 时的 this 值:

ThisValueOfFunction(func, invocationType):

1、如果 func 是一个箭头函数,那么

如果 func 是在最外面的作用域 中定义的,那么返回 globalObject
否则

 SuffeFunc 是 Func 的外部函数
返回 ThisValueOfFunction(outerFunc, outerInvocationType)


2、如果 func 是 originFunc 函数的绑定函数,那么

thisArg 是 Func = OriginFunc.bind(thisarg) 的参数
返回 thisArg


3、如果 func 是 someclass 类 中的 constructor() 方法,那么

instance 是 instance = new SomeClass() 的实例
返回 instance


4、如果 func 是一个常规函数,那么

如果 invocationtype 是作为构造函数,那么

newObject 是新构造的对象 newObject = new func()
返回 newObject


如果 invocationtype 是间接调用的,那么

thisArg 是 func.call(thisArg) 或 func.apply(thisArg) 的参数
返回 thisArg


如果 invocationtype 是 方法,那么

object 是在 object.func() 上调用 func 的对象
返回 object


如果 invocationtype 是常规的,那么

如果启用了严格的模式,那么返回 undefined
否则返回 globalObject


1.1 算法中使用的术语


这个算法使用了大量的 JavaScript 术语。如果你不熟悉某些东西,先看下面的解释。

 

  • 箭头函数

箭头函数是使用粗箭头语法 => 定义的函数。 箭头函数示例:

const sum = (number1, number2) => {
  return number1 + number2;
}

 

  • 绑定函数

绑定函数是通过在函数上调用方法 myFunc.bind(thisArg, arg1, ..., argN)创建的函数。 绑定函数的示例:

function originalFunction() {
  // ...
}

const boundFunction = originalFunction.bind({ prop: 'Value' });

 

  • 常规函数

常规函数是用 function 关键字或在对象上定义的简单 JavaScript 函数。 常规函数的示例:

function regularFunction(who) {
  return `Hello, ${who}!`;
}

const object = {
  anotherRegularFunction(who) {
    return `Good bye, ${who}!`
  }
};

 

  • constructor()

constructor() 是 class 内部的一种特殊方法,用于初始化类实例。

class SomeClass() {
  constructor(prop) {
    this.prop = prop;
  }
}

 

  • 最外部的作用域

最外部的作用域是没有外部作用域的最顶级作用域。

// 最外部的作用域
let a = 1;

function someFunction() {
  // someFunction() 的作用域
  // 这里不是最外部的作用域
  let b = 1;
}

 

  • 外部函数
    外部函数在其作用域内包含另一个函数。
// outerFunction() 是 myFunction() 的外部函数
function outerFunction() {
  function myFunction() {
    //...
  }
}

 

  • 全局对象全局对象是在全局作用域内始终存在的对象。 window 是浏览器环境中的全局对象,在 Node 环境中是 global。

 

  1. 调用
    函数的调用只是使用一些参数来调用该函数。
    function sum(number1, number2) {
      return number1 + number2;
    }
    sum(1, 3);           // 调用
    sum.call({}, 3, 4);  // 调用
    sum.apply({}, 5, 9); // 调用
    
    const obj = {
      method() {
        return 'Some method';
      }
    };
    obj.method(); // 调用
    
    class SomeClass {
      constructor(prop) {
        this.prop = prop;
      } 
    }
    const instance = new SomeClass('Value'); // 调用​

 

  • 构造函数调用
    使用 new 关键字调用函数或类时,将发生构造函数调用。
    function MyCat(name) {
      this.name = name;
    }
    const fluffy = new MyCat('Fluffy'); // 构造函数调用
    
    class MyDog {
      constructor(name) {
        this.name = name;
      }
    }
    const rex = new MyDog('Rex'); // 构造函数调用​

 

  • 间接调用
    使用 func.call(thisArg, ...) 或 func.apply(thisArg, ...) 方法调用函数时,会发生间接调用。
    function sum(number1, number2) {
      return number1 + number2;
    }
    
    sum.call({}, 1, 2);  // 间接调用
    sum.apply({}, 3, 5); // 间接调用​

 

  • 方法调用
    当在属性访问器表达式 object.method() 中调用函数时,将发生方法调用。
    const object = {
      greeting(who) {
        return `Hello, ${who}!`
      }
    };
    
    object.greeting('World');    // 方法调用
    object['greeting']('World'); // 方法调用​

 

  • 常规调用
    只用函数参数变量调用 func(...) 时,会发生常规调用。
    function sum(number1, number2) {
      return number1 + number2;
    }
    
    sum(1, 4); // 常规调用​

 

  • 严格模式
    严格模式是对运行 JavaScript 代码有特殊限制的一种特殊模式。 通过在脚本的开头或函数作用域的顶部添加 use strict 指令来启用严格模式。


2.例子


例 1

const myFunc = () => {
  console.log(this); // logs `window`};

myFunc();

 

例 2

const object = {
  method() {
    console.log(this); // logs { method() {...} }  } 
};

object.method();

 

ThisValueOfFunction(object.method, “作为方法调用”)

method() 同时是 object 的属性,是常规函数。与算法的情况 4 匹配。

object.method() 是一种方法调用,因为是属性访问的,送一因此与 4.3 匹配。

然后,根据 4.3,method() 方法中的 this 等于方法的拥有者 (object.method()) — object。

 

例 3

function MyCat(name) {
  this.name = name;

  const getName = () => {
    console.log(this); // logs { name: 'Fluffy', getName() {...} }    return this.name;
  }

  this.getName = getName;
}

const fluffy = new MyCat('Fluffy');
fluffy.getName();

 

ThisValueOfFunction(getName, “作为方法调用”)

getName() 是一个箭头函数,所以符合算法的情况 1;因为 mycat 是 getName()的外部函数,然后与 1.2 匹配。

分支 1.2.2 :this 在 getName() 箭头函数内部的值等于外部函数的值 MyCat。

所以让我们在 MyCat 函数上运行算法 ThisValueOfFunction(MyCat, "做为构造函数") 。

 

ThisValueOfFunction(MyCat, “作为构造函数”)

MyCat 是常规函数,所以跳转到算法的分支 4。

因为 MyCat 做为构造函数调用 new MyCat('Fluffy'),符合分支 4.1。最后根据 4.1.1 和 4.1.2,this 在 MyCat 中等于构造的对象:fluffy。

然后,返回箭头函数后符合 1.2.2,在 getname() 中的 this 等于 mycat 的 this,最终结果为 fluffy。

 

3. 练习


要理解这个算法,最好自己亲自试试。下面是 3 个练习。

 

练习 1

const myRegularFunc = function() {
  console.log(this); // logs ???};

myRegularFunc();

 

如何确定 myRegularFunc() 中的 this 值?写出你的判断步骤。

 

练习 2

class MyCat {
  constructor(name) {
    this.name = name;
    console.log(this); // logs ???  }
}

const myCat = new MyCat('Lucy');

 

如何确定 new MyCat('Lucy') 中的 this 值?写出你的判断步骤。

 

练习3

const object = {
  name: 'Batman',

  getName() {
    const arrow = () => {
      console.log(this); // logs ???      return this.name;
    };

    return arrow();
  };
}

object.getName();

 

如何确定 arrow() 中的 this 值?写出你的判断步骤。

 

 

 

分类
标签
已于2021-5-11 16:32:12修改
收藏
回复
举报
回复
    相关推荐