diff --git a/深入探索 JavaScript/JavaScript 执行.md b/深入探索 JavaScript/JavaScript 执行.md index c6c5c39..48edbc9 100644 --- a/深入探索 JavaScript/JavaScript 执行.md +++ b/深入探索 JavaScript/JavaScript 执行.md @@ -47,6 +47,48 @@ o.showThis(); // o 如果将例子中的 showThis 函数改写为类中的方法,那么直接调用 showThis 方法的 this 的结果也会不同,这是因为 JavaScript 的 Class 被设计成了默认在严格模式下(use strict)执行,而严格模式下 this 指向会发生一些改变。 +this 是一个复杂的机制,JavaScript 标准定义了 \[\[ thisMode \]\] 私有属性,这个属性有以下三种取值: +- lexical - 表示从上下文中找 this,对应箭头函数。 +- global - 表示当 this 为 underfined 时,取全局对象,对应普通函数。 +- strict - 当严格模式时使用,this 严格按照调用时传入的值,可能为 underfined 或 null + +实际上 this 也可以看作是在调用时被确认的词法环境,普通函数被调用时会将 this 指向调用者压入执行上下文栈,但箭头函数不会创建新的词法环境,因此箭头函数中的 this 将从词法环境中查找并继承(复用)其定义时外部函数的 this。 + +JavaScript 提供了可以操作 this 的内置函数,如 Function.prototype.call 和 Function.prototype.apply,它们可以指定函数调用时传入的 this,除此之外还有 Function.prototype.bind,它可以返回一个更改了 this 的函数,它们甚至可以应用在箭头函数中,但是它并不会修改 this,仅仅实现了传参的能力。 + +Kyle Simpson 关于 this 的总结: +1. 如果由new调用 - 绑定到新创建的对象 +2. 如果由 call、apply 或 bind调用 - 绑定到指定的对象 +3. 如果由上下文对象调用 - 绑定到那个上下文对象 +4. 其他调用情况 - 严格模式下会绑定到 undefined,非严格模式绑定到全局对象 +5. 以上情况对于箭头函数都不适用,它会继承外层函数的 this 绑定 + +## 上下文栈 + +>JavaScript 引擎并非一行一行分析执行代码,而是一段一段的分析执行,当执行一段代码的时候会进行一些准备工作 + 函数能够引用定义时的变量,函数也能记住定义时的 this,因此,函数内部必然有一个机制来保存这些信息,这个用来保存定义时上下文的机制就是私有属性 \[\[Environment\]\]。 -在函数定义时,会保存定义时的上下文为函数的 \[\[Environment\]\],而在函数执行时,会创建一条新的执行环境记录,也就是函数执行时的上下文被设置为函数的 \[\[Environment\]\],这个动作就是切换上下文,着一层一层形成的上下文就是函数的 **“作用域链”**。 \ No newline at end of file +在函数执行时,会创建一条执行环境记录,也就是函数定义时的上下文设置为函数的 \[\[Environment\]\],这个动作就是切换上下文,无论函数以何种形式被调用,变量都会依照定义时的环境被查找出来。 + +JavaScript 用一个栈来管理执行上下文,当函数调用时,会入栈一个新的执行上下文,函数调用结束之后,执行上下文被出栈 + +this 的值存放在私有属性 \[\[ThisBindingStatus\]\] 中 + +函数创建新的执行上下文中的词法环境记录时,会根据 \[\[thisMode\]\] 来标记新纪录的 \[\[ThisBindingStatus\]\] 私有属性。代码执行遇到 this 时,会逐层检查当前词法环境记录中的 \[\[ThisBindingStatus\]\],当找到有 this 的环境记录时获取 this 的值。 + +## Completion Record / 完成标记 + +>Completion Record 是 JavaScript 中的一个规范类型,用于描述异常、跳出等语句执行过程 + +Completion Record 是语言实现者才需要关心的内容,但是我们可以从中看出一些 JavaScript 更加底层的实现逻辑,它有着三个字段分别是: +- \[\[type\]\] - 表示完成的类型,有 break continue return throw 和 normal 几种类型 +- \[\[value\]\] - 表示语句的返回值,如果语句没有,则是 empty +- \[\[target\]\] - 表示语句的目标,通常是一个 JavaScript 标签 + +JavaScript 正是依靠语句的 Completion Record 类型,方才可以在语句的复杂嵌套结构中,实现各种控制,普通语句执行后,会得到 \[\[type\]\] 为 normal 的 Completion Record,JavaScript 引擎遇到这样的 Completion Record,会继续执行下一条语句。 + +这些语句中,只有表达式语句会产生 \[\[value\]\],当然,从引擎控制的角度,这个 value 并没有什么用处,Chrome 控制台显示的正是语句的 Completion Record 的 \[\[value\]\] + +参考文章: +- http://www.ecma-international.org/ecma-262/#sec-ecmascript-specification-types \ No newline at end of file diff --git a/深入探索 JavaScript/JavaScript 语法.md b/深入探索 JavaScript/JavaScript 语法.md new file mode 100644 index 0000000..eeac0dc --- /dev/null +++ b/深入探索 JavaScript/JavaScript 语法.md @@ -0,0 +1,57 @@ +## import 声明 + +import 声明有两种用法,一个是直接 import 一个模块,另一个是带 from 的 import,它能引入模块里的一些信息。 + +```javascript +import "mod"; //引入一个模块 +import v from "mod"; //把模块默认的导出值放入变量v +``` + +直接 import 一个模块,只是保证了这个模块代码被执行,引用它的模块是无法获得它的任何信息的,而带 from 的 import 意思是引入模块中的一部分信息,可以把它们变成本地的变量。 + +独立的 `export` 导入相当于是一个引用,导出的变量仍然指向同一个地址(无关引用类型和值类型)。 + +而 `export default` 导出的则是一个值,但是对于引用类型而言,也是会被修改的 + +## var 声明的预处理 + +>var 在预处理阶段,不关心赋值的部分,只管在当前作用域声明这个变量 + +```javascript +var a = 1; +function foo() { + console.log(a); // undefined + + // var 会穿透一切语言结构 + if(false) { + var a = 2; + } +} + +foo(); +``` + +## function 声明 + +>function 声明表现跟 var 相似,不同之处在于,function 声明不但在作用域中加入变量,还会给它赋值。 + +看一个与之不同的例子: + +```javascript +// foo 会被提升,但不会被赋值 +console.log(foo); // undefined + +if(true) { + function foo(){ } +} +``` + +## class 声明 + +class 声明也是存在预处理的,但是它的行为会更加的符合直觉,更倾向于抛出错误 + +## 指令序言机制 + +"use strict"是 JavaScript 标准中规定的唯一一种指令序言,但是设计指令序言的目的是,留给 JavaScript 的引擎和实现者一些统一的表达方式,在静态扫描时指定 JavaScript 代码的一些特性。 + +JavaScript 的指令序言是只有一个字符串直接量的表达式语句,它只能出现在脚本、模块和函数体的最前面。 \ No newline at end of file diff --git a/深入探索 JavaScript/碎片记录.md b/深入探索 JavaScript/碎片记录.md index e69de29..1cc5e24 100644 --- a/深入探索 JavaScript/碎片记录.md +++ b/深入探索 JavaScript/碎片记录.md @@ -0,0 +1,14 @@ +- `12.toString()` 会报错,这是因为 JavaScript 允许10进制 Number 省略小数点前或者后,`12.toString()` 中的 `12.` 会被当作省略了小数点后的数字,如果想要这一句正常工作,需要在中间加个空格:`12 .toString()` 或是 `12..toString()` +- 模板支持添加处理函数的写法,这时模板的各段会被拆开,传递给函数当参数: + +```javascript +function f(){ + console.log(arguments); +} + +var a = "world" +f`Hello ${a}!`; // [["Hello", "!"], world] +``` + +- JavaScript switch 语句继承自 Java,Java 中的 switch 语句继承自 C 和 C++,原本 switch 语句是跳转的变形,所以我们如果要用它来实现分支,必须要加上 break。 +- 在 C 时代,switch 生成的汇编代码性能是略优于 if else 的,但是对 JavaScript 来说,则无本质区别。 \ No newline at end of file