理解 JavaScript 中的执行上下文和执行栈

译者序 最近在研究 JavaScript 基础性的东西,但是看到对于执行上下文的解释我发现有两种,一种是执行上下文包含:scope(作用域)、variable object(变量对象)、this value(this 值),另外一个种是包含:lexical environment(词法环境)、variable environment(变量环境)、this value(this 值)。 后面我查阅了不少博客以及 ES3 和 ES5 的规范才了解到,第一种是 ES3 的规范,经典书籍《JavaScript高级程序设计》第三版就是这样解释的,也是网上广为流传的一种,另一种是 ES5 的规范。 然后我接着又去翻了 ES2018 中的,发现又有变化了,已经增加了更多的内容了,考虑到这部分内容颇为复杂,准备后面再进行总结分享,查资料的时候看到这篇讲执行上下文(ES5 )的还不错,所以就翻译出来先分享给大家。 以后看到变量对象、活动对象知道是 ES3 里面的内容,而如果是词法环境、变量环境这种词就是 ES5 以后的内容。 以下是正文: 什么是执行上下文? 简而言之,执行上下文是计算和执行 JavaScript 代码的环境的抽象概念。每当 Javascript 代码在运行的时候,它都是在执行上下文中运行。 执行上下文的类型 JavaScript 中有三种执行上下文类型。 全局执行上下文 — 这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。 函数执行上下文 — 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序(将在后文讨论)执行一系列步骤。 Eval 函数执行上下文 — 执行在 eval 函数内部的代码也会有它属于自己的执行上下文,但由于 JavaScript 开发者并不经常使用 eval,所以在这里我不会讨论它。 执行栈 执行栈,也就是在其它编程语言中所说的“调用栈”,是一种拥有 LIFO(后进先出)的数据结构,被用来存储代码运行时创建的所有执行上下文。 当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行上下文并压入栈的顶部。 引擎会执行处于栈顶的执行上下文的函数。当该函数执行结束时,执行上下文从栈中弹出,控制流程到达当前栈中的下一个上下文。 让我们通过下面的代码示例来理解: let a = 'Hello World!...

April 2, 2020 · 3 min · 448 words · 桃翁

如何对 React 函数式组件进行优化

前言 目的 本文只介绍函数式组件特有的性能优化方式,类组件和函数式组件都有的不介绍,比如 key 的使用。另外本文不详细的介绍 API 的使用,后面也许会写,其实想用好 hooks 还是蛮难的。 面向读者 有过 React 函数式组件的实践,并且对 hooks 有过实践,对 useState、useCallback、useMemo API 至少看过文档,如果你有过对类组件的性能优化经历,那么这篇文章会让你有种熟悉的感觉。 React 性能优化思路 我觉得React 性能优化的理念的主要方向就是这两个: 减少重新 render 的次数。因为在 React 里最重(花时间最长)的一块就是 reconction(简单的可以理解为 diff),如果不 render,就不会 reconction。 减少计算的量。主要是减少重复计算,对于函数式组件来说,每次 render 都会重新从头开始执行函数调用。 在使用类组件的时候,使用的 React 优化 API 主要是:shouldComponentUpdate 和 PureComponent,这两个 API 所提供的解决思路都是为了减少重新 render 的次数,主要是减少父组件更新而子组件也更新的情况,虽然也可以在 state 更新的时候阻止当前组件渲染,如果要这么做的话,证明你这个属性不适合作为 state,而应该作为静态属性或者放在 class 外面作为一个简单的变量 。 但是在函数式组件里面没有声明周期也没有类,那如何来做性能优化呢? React.memo 首先要介绍的就是 React.memo,这个 API 可以说是对标类组件里面的 PureComponent,这是可以减少重新 render 的次数的。 可能产生性能问题的例子 举个例子,首先我们看两段代码: 在根目录有一个 index.js,代码如下,实现的东西大概就是:上面一个 title,中间一个 button(点击 button 修改 title),下面一个木偶组件,传递一个 name 进去。...

November 19, 2019 · 4 min · 774 words · 桃翁

如何让 useEffect 只在依赖变化的时候执行

遇到问题 今天遇到一个 useEffect 的问题,遇到一个问题:在 useEffect 里面发异步请求,然后第二个参数的依赖也是异步请求之后得到的结果,然后就导致最终结果会请求两次 useEffect 里的函数。 const [metaKey, setMetaKey] = useState<string[]>([]) // useEffect1 useEffect(() => { getServiceCoreIndexParam().then((res: IResult) => { setMetaKey(res.data.defaultValue) return res.data }) }, []) // useEffect2 useEffect(() => { getAdvisorIndexTable({ visitdate: props.visitdate, advisorSupervisor: props.advisorSupervisor, comparevisitdate: props.comparevisitdate, metaKeys: metaKey || [] }).then((res: IResult) => { res.success && setTable(res.data) }) }, [props.visitdate, props.advisorSupervisor, metaKey, props.comparevisitdate]) 分析一下这段代码,首先在组件 mount 的时候,useEffect2 会调用一次 getAdvisorIndexTable,当 useEffect1 执行完毕之后 setMetaKey 后,由于 metaKey 发生改变,导致 getAdvisorIndexTable 还会调用一次,这很明显是我们不想看到的结果,因为这只是一个默认请求,然而发了两次请求。...

October 14, 2019 · 2 min · 266 words · 桃翁

新手学习 React 迷惑的点

网上各种言论说 React 上手比 Vue 难,可能难就难不能深刻理解 JSX,或者对 ES6 的一些特性理解得不够深刻,导致觉得有些点难以理解,然后说 React 比较难上手,还反人类啥的,所以我打算写两篇文章来讲新手学习 React 的时候容易迷惑的点写出来,如果你还以其他的对于学习 React 很迷惑的点,可以在留言区里给我留言。 为什么要引入 React 在写 React 的时候,你可能会写类似这样的代码: import React from 'react' function A() { // ...other code return <h1>前端桃园</h1> } 你肯定疑惑过,下面的代码都没有用到 React,为什么要引入 React 呢? 如果你把 import React from ‘react’ 删掉,还会报下面这样的错误: 那么究竟是哪里用到了这个 React,导致我们引入 React 会报错呢,不懂这个原因,那么就是 JSX 没有搞得太明白。 你可以讲上面的代码(忽略导入语句)放到在线 babel 里进行转化一下,发现 babel 会把上面的代码转化成: function A() { // ...other code return React.createElement("h1", null, "前端桃园"); } 因为从本质上讲,JSX 只是为 React.createElement(component, props, ...children) 函数提供的语法糖。...

September 5, 2019 · 5 min · 1019 words · 桃翁

Deep In React 之详谈 React 16 Diff 策略(二)

文章首发于个人博客 这是我 Deep In React 系列的第二篇文章,如果还没有读过的强烈建议你先读第一篇:详谈 React Fiber 架构(1)。 前言 我相信在看这篇文章的读者一般都已经了解过 React 16 以前的 Diff 算法了,这个算法也算是 React 跨时代或者说最有影响力的一点了,使 React 在保持了可维护性的基础上性能大大的提高,但 Diff 过程不仅不是免费的,而且对性能影响很大,有时候更新页面的时候往往 Diff 所花的时间 js 运行时间比 Rendering 和 Painting 花费更多的时间,所以我一直传达的观念是 React 或者说框架的意义是为了提高代码的可维护性,而不是为了提高性能的,现在所做的提升性能的操作,只是在可维护性的基础上对性能的优化。具体可以参考我公众号以前发的这两篇文章: 别再说虚拟 DOM 快了,要被打脸的 深入理解虚拟 DOM,它真的不快 如果你对标题不满意,请把文章看完,至少也得把文章最后的结论好好看下 在上一篇将 React Fiber 架构中,已经说到过,React 现在将整体的数据结构从树改为了链表结构。所以相应的 Diff 算法也得改变,以为以前的 Diff 算法就是基于树的。 老的 Diff 算法提出了三个策略来保证整体界面构建的性能,具体是: Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计。 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。 对于同一层级的一组子节点,它们可以通过唯一 id 进行区分。 基于以上三个前提策略,React 分别对 tree diff、component diff 以及 element diff 进行算法优化。 具体老的算法可以见这篇文章:React 源码剖析系列 - 不可思议的 react diff...

July 30, 2019 · 6 min · 1235 words · 桃翁