Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

你不知道的JavaScript-上卷 #1

Open
abinnq opened this issue Nov 17, 2019 · 0 comments
Open

你不知道的JavaScript-上卷 #1

abinnq opened this issue Nov 17, 2019 · 0 comments

Comments

@abinnq
Copy link
Owner

abinnq commented Nov 17, 2019

你不知道的JavaScript-上卷

目录

第一部分 作用域和闭包

第一章 作用域是什么

作用域: 能够存储变量当中的值, 并且能在之后对这个值进行访问或修改

1.1 编译原理

通常将JavaScript称为'动态'语言,或者'解释执行'执行语言, 但其实是 编译语言

  1. 分词/词法分析:
    将字符组成的字符串,分解成编程语言有意义的代码块。 这些代码块叫做词法单元 (token)
  2. 解析/语法分析:
    将词法单元流(数组), 转换成一个由元素逐级嵌套所组成的程序语法结构的树。 这个树被称为抽象语法树(AST)
  3. 代码生成
    将AST转换成可执行代码的过程, 被称为代码的生成。

何时编译
与其他语言不同, JavaScript的编译过程不是发生在构建之前,
JavaScript: 编译发生在代码执行前的几微秒,甚至更短。

过程演示

var a = 2;
// 1.词法分析: 分解成词法单元 `var`、`a`、`=`、`2`
// 2.语法分析: 创建顶级节点 VariableDeclaration, 
//   子节点 Identifier 值为a, 以及一个Literal的子节点 value为2
// 3.代码生成: 用来创建一个叫做a的变量(包括分配内存), 并将一个值存在a中

1.2 理解作用域

  • 引擎: 从头到尾负责JavaScript的编译及执行过程
  • 编译器: 词法分析、语法分析及代码生成
  • 作用域: 收集维护所有声明的表示符(变量), 确定当前执行代码对这些标识符的访问权限

变量的赋值

  1. 编译器在作用域中声明变量(已声明则忽略)
  2. 运行时引擎在作用域中查找变量
  3. 找到变量进行赋值

引擎查找变量规则

  • LHS: 找到变量的容器本身, 为其赋值。a = 2
  • RHS: 找到源值。console.log(a)

1.3 作用域嵌套

作用域链
引擎在当前的执行作用域查找变量, 如果找不到就向上一级查找,
当查找到最外层的全局作用域, 无论找到与否都会停止查找;

1.4 异常

  • RHS: 查询整个作用域链都找不到其值, 引擎将会抛出ReferenceError的异常错误

  • LHS: 查询整个作用域链都找不到,将在全局作用域创建一个该名称变量(非严格模式下), 严格模式下仍然会报错

  • ReferenceError: 和作用域相关的异常错误

  • TypeError: 作用域成功了, 但是对结果的操作非法

1.5 小结

  • 作用域: 是一套规则, 用于确定何处如何查找变量(标识符)
  • LHS: 查找的变量是对其赋值, 赋值操作;
  • RHS: 查找的变量是为了获取值;

= 操作符, 和调用函数传入的参数, 都会导致关联作用域的赋值操作

JavaScript引擎执行代码前,会先对代码进行编译, var a = 2; 被分解成两个步骤

  1. var a 在其作用域中声明新变量, 在代码执行前
  2. a = 2 进行查询(LHS查询)变量a, 并为其赋值2

小测试

function foo(a) {
  var b = a;
  return a + b;
}
var c = foo(2);
// LHS查询 三处: c =.. 、 a = 2(隐式变量分配)、b=a
// RHS查询 四处: foo(2)、 = a 、 a +、 +b

第二章 词法作用域

词法阶段:
词法作用域是由书写代码函时数声明的位置来决定,
作用域查找会在找到第一个匹配的标识符时停止(遮蔽效应)

欺骗词法

  • eval: 对一个或者多个代码字符串进行演算, 并修改已存在的作用域(运行时)
  • with: 对一个对象的引用当作作用域, 其属性当作标识符。从而创建一个新的词法作用域(运行时)

引擎无法在编译时 对作用域查找进行优化, 故两个都会导致代码变慢

词法作用域:
意味着作用域是由写代码时,函数声明的位置决定的
编译的词法分析阶段, 基本能够知道全部标识符在哪里及如何声明, 从而能预测执行过程中如何进行查找

第三章 函数作用域和块作用域

隐藏内部实现

  • 把变量和函数包裹在一个函数的作用域中, 用这个函数来隐藏他们
  • 最小授权或最小暴露原则

规避冲突:
隐藏作用域中的变量和函数所带来的另一个好处, 避免同名标识符之间的冲突

规避冲突方法:

  • 全局命名空间: 多数库常用暴露一个对象Jquery $
  • 模块管理

函数作用域

匿名函数: 回调参数

setTimeout(function() {
  console.log('I waited');
}, 0);
// 1. 匿名函数在栈追踪不会显示出有意义的函数名, 使得调试困难
// 2. 没有函数名,当函数需要引用自身时, 需要使用`arguments.callee`
// 3. 匿名函数省略了代码的可读性/可理解性很重要的函数名

函数声明和函数表达式

  • 函数声明: function 是声明中的第一个词,就是函数声明。(不可以省略函数名)
  • 函数表达式: function 不是函数声明中的第一个词, 就是函数表达式
    区别是他们的名称标识符将会绑定在何处

立即执行函数表达式
立即执行函数表达式 IIFE: (function IIFE(){console.log(1)})()使用()将函数包裹起来, 后面的()代表调用

块作用域
定义: 变量和函数不仅属于所处的作用域, 也可以属于某个代码块{...}

  • with 关键字
  • try/catch 中的catch会创建一个块作用域, 仅在catch中有效
  • let
  • const 其值是固定的常量

第四章 提升

包括变量和函数在内的所有声明都会在代码被执行前首先被处理。

提升

  • 引擎会在解释JavaScript代码前进行编译, 编译阶段找到所有的声明, 并用合适的作用域将其关联起来
  • var a = 2; 被解析成两个阶段: var a(编译阶段), a = 2;(执行阶段), 这个过程叫做提升;
  • 普通块内部的函数声明通常会提升到所在作用域的最顶部
  • 函数首先被提升, 接下来才是变量
  • 声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会被提升

所有的声明(变量和函数)都会被移动到各自所在作用域的最顶端,这个过程叫提升

第五章 作用域闭包

闭包

  • 函数是在当前词法作用域外执行
  • 函数不在定义词法中执行, 但是依然保持对该词法作用域的引用,叫做闭包
  • 闭包阻止了引擎的垃圾回收机制, 及释放不再引用的内存

模块

  1. 必须有外部的封闭函数, 该函数至少被调用一次(每次调用都会被创建一个新的模块实例)
  2. 封闭函数内部必须返回至少一个内部函数(形成闭包)

现代模块机制
调用了函数定义的包装函数, 并将返回值作为该模块的API

ES6模块
将文件作为独立的模块来处理, 每个模块都可以导入其他模块或者特定的API成员, 同时也可以导出自己的的API成员。

第二部分 this和对象原型

@abinnq abinnq changed the title 你不知道的JavaScript上 你不知道的JavaScript-上卷 Nov 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant