疯狂的技术宅

以前出于工作目的,编写和翻译了大量的技术文章,以前端为主,删掉了过时的、毫无营养的内容,留下的都是精华。


  • 首页

  • 分类

  • 标签

  • 归档

  • 关于本站

  • 回到主站

  • 搜索

一个能够确定 this 值的算法

时间: 2021-03-19 分类: 前端技术   字数: 1807 字 阅读: 4分钟
标签: #JavaScript# #this# #算法#
  • 本文译自:https://dmitripavlutin.com/javascript-this-algorithm/
  • 译者:疯狂的技术宅

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

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

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

1. this 算法

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

ThisValueOfFunction(func, invocationType):

  1. 如果 func 是一个箭头函数,那么
    1. 如果 func 是在最外面的作用域 中定义的,那么返回 globalObject
    2. 否则
      1. SuffeFunc 是 Func 的外部函数
      2. 返回 ThisValueOfFunction(outerFunc, outerInvocationType)
  2. 如果 func 是 originFunc 函数的绑定函数,那么
    1. thisArg 是 Func = OriginFunc.bind(thisarg) 的参数
    2. 返回 thisArg
  3. 如果 func 是 someclass 类 中的 constructor() 方法,那么
    1. instance 是 instance = new SomeClass() 的实例
    2. 返回 instance
  4. 如果 func 是一个常规函数,那么
    1. 如果 invocationtype 是作为构造函数,那么
      1. newObject 是新构造的对象 newObject = new func()
      2. 返回 newObject
    2. 如果 invocationtype 是间接调用的,那么
      1. thisArg 是 func.call(thisArg) 或 func.apply(thisArg) 的参数
      2. 返回 thisArg
    3. 如果 invocationtype 是 方法,那么
      1. object 是在 object.func() 上调用 func 的对象
      2. 返回 object
    4. 如果 invocationtype 是常规的,那么
      1. 如果启用了严格的模式,那么返回 undefined
      2. 否则返回 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。

  • 调用 函数的调用只是使用一些参数来调用该函数。

    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();

ThisValueOfFunction(myFunc, “常规的”)

myfunc 是箭头函数:从而在算法中匹配情况 1。同时 myFunc 在最外面的作用域内定义,匹配情况 1.1。

算法 1.1 中返回 globalObject 意思是 myFunc 中的 this 值为全局对象 window(在浏览器环境中)。

例 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 值?写出你的判断步骤。

标签: #JavaScript# #this# #算法#

标题:一个能够确定 this 值的算法

链接:https://fe-tech.viewnode.com/post/202103/19/

作者:疯狂的技术宅

声明: 本博客文章除特别声明外,均采用 CC BY-NC-ND 4.0 国际许可协议( 知识共享署名-非商业性使用-禁止演绎 4.0),转载请注明出处!

在JavaScript中实现队列
ES2018 中 4 个有用的功能
  • 文章目录
  • 站点概览
疯狂的技术宅

疯狂的技术宅

退休程序员,硬件发烧友,人工智能爱好者。写写代码喝喝茶,晒晒太阳带带娃。

457 日志
8 分类
583 标签
GitHub
友情链接
  • viewnode
  • mofish
标签云
  • Javascript 172
  • Node.Js 62
  • Vue 36
  • Typescript 28
  • 实战项目 28
  • 面试 21
  • React 20
  • Css 17
  • 面试题 16
  • 教程 13
  • Promise 12
  • Chrome 9
  • Debug 9
  • 调试 9
  • 资源 9
  • Deno 8
  • Dom 8
  • 杂谈 8
  • 正则表达式 8
  • 测试 8
  • 1. this 算法
    • 1.1 算法中使用的术语
  • 2.例子
    • 例 1
    • 例 2
    • 例 3
  • 3. 练习
    • 练习 1
    • 练习 2
    • 练习3
© 2018 - 2022 疯狂的技术宅 All Rights Reserved
Powered by - Hugo v0.99.0 / Theme by - NexT
Storage by 俺的服务器 / 冀ICP备2022010157号
0%