js进阶-唯一不变的是变化本身
-
动态类型系统
-
类型转换
-
从值->引用,调用 Object(x) 函数。
- 值类型与对象类型转换,就是将该值存入私有槽,或者从私有槽中把相应的值取出来的过程
obj=Object(x); 等效于 obj.[[PrimitiveValue]]=x;
- 原始值primitive value与一般值value
- 原始值:能放入私有槽(即有相应的包装类)的值 + 特殊值undefined和null
- 值类型与对象类型转换,就是将该值存入私有槽,或者从私有槽中把相应的值取出来的过程
-
从引用->值,调用x.valueOf()方法/调用4种值类型的包装类函数,Number(x)。
转换结果要尽量地趋近于 string、number 和 boolean 三者之一。
任何对象都会有继承自原型的两个方法-toString()和valueOf()。- 当预期是“number”时,valueOf()优先调用;否则以toString()为优先。仍然得不到基本类型值时,
还会顺序调用另一方法。 - []和{}在转换为值类型时会先调用valueOf然后调用toString。
- {}运算=> {}在前->数字类型的代码块,{}在后->前有数字类型就Nan,无数字类型就[object Object]
- []+{},前者转换'',后者转换为[object Object],字符串连接,所以结果为'[object Object]'
- {}+[],前者为代码块(看作数字类型),后者转换为0,所以结果为0
- {}+{},后者+操作符将转换为NaN,
- 当预期是“number”时,valueOf()优先调用;否则以toString()为优先。仍然得不到基本类型值时,
-
从值->值
- symbol
既没有办法转换成别的类型,也没有办法从别的类型转换过来 - Boolean
除了 undefined、null、0、0n 、""、Nan返回 false 之外,其他都转换为true
- symbol
-
隐式转换 VS 显式转换
隐式转换的结果总是倾向于“string/number”两种值类型。
如果一个运算无法确定类型,那么在类型转换前,它的运算数将被预设为 number。 如a+b- '+'不确定类型就优先Number(),两侧有字符串就优先进行字符串连接;
- '=='有基础类型则转为基础类型()
-
显式转换
只决定了“转换的预期”,而它内部的转换过程,仍需要“隐式转换过程”来参与的。 -
环境
-
环境可以细分为四种,并由两个类别的基础环境组件(声明环境和对象环境)构成。
概念:本质是为JavaScript中的“可执行的语法块”创建了一个名字表的映射。 -
两个类别:声明环境就是名字表-“名字 -> 数据”的对照表;
对象环境是JS的一个对象,可以看成是对照表的一个结果/实现。
对象环境只为全局环境的global对象/with(obj)...语句中的obj创建,其他情况下创建的都必然是“声明环境 -
四种环境:全局环境-是一个复合环境,由“对象环境 + 声明环境”组成;
其他3种环境(函数、模块和 Eval环境),都是一个单独的声明环境。 -
执行上下文
- 执行环境/闭包链:静态语法被称为作用域;在动态环境中它们被称为上下文
- 执行上下文栈/调用栈call stack:管理执行上下文,是JS引擎追踪函数执行的一个机制
- 栈溢出
当分配的调用栈空间被占满时,js引擎就会报错 - 执行上下文的两个环境
- 词法环境
每一个执行上下文(执行点+执行参考)都需要关联到一个对照表就称为“词法环境。可以是六种任一 - 变量环境
包含传统风格的“var 声明和函数声明”,与词法环境共用一个对照表
- 词法环境
-
eval()
- 做什么?用于应对“动态执行”的环境。解析字符串 x,包括对一些特殊字面量如8进制的语法解析。
- 在哪里执行? 当前上下文
- Eval环境:变量环境与词法环境的指向不同-变量环境指向全局的变量环境,词法环境指向它自有的词法环境。
- 运行中的eval(...)
直接调用 eval(x) ;(eval)(x)
间接调用 (0, eval)(x) 将eval()调用起来/执行到eval()函数的方式- 所有的“间接调用”的代码总是执行在“全局环境”中。
- 所有的“间接调用”的代码将默认执行在“非严格模式”中,突破严格模式限制
- 严格模式
“严格模式”是可执行对象的一个属性,并不是执行环境的属性。
eval通过“词法环境与变量环境分离”绕过了“严格模式”的影响,
严格模式不是环境的性质,间接调用使用全局时无法进入严格模式而已 - (0, eval)(x)
分组表达式内部还有一个连续运算,它计算每一个表达式,并返回最后一个表达式的值
-
-
动态函数的实现原理
函数的类化 new Function('x=1')();- 四种可以动态创建的函数:一般函数(是显式声明的)、生成器函数、异步生成器函数和异步函数
- 特点:所有的“动态函数”的父级作用域将指向全局,执行在非严格模式中,
与“间接调用 eval()”的唯一差异,仅在于“多封装了一层函数”。 - 意义:牺牲环境的一致性,换取性能。
JavaScript代码的执行流程
执行环境==闭包链:静态语法被称为作用域或域;在动态环境中它们被称为上下文
- 先编译 -> 进行变量提升函数提升-> 执行上下文(保存变量和函数的一个执行环境) + 可执行代码
- 再执行 -> 查找自定义的变量和函数。
查找变量
单个执行上下文中如何查找变量:
1: 如果是同名的函数,编译阶段选择后者,覆盖前者。
2: 如果变量和函数同名,编译阶段 函数提升优先级>变量提升
非块级作用域查找变量 -> 作用域链
查找: 当前作用域->上级作用域->全局作用域
- 作用域链: 通过作用域查找变量的链条,由词法作用域决定.
词法作用域由函数声明的位置来决定,和函数调用没有关系.(静态作用域)
块级作用域中的变量查找 -> 作用域链和词法环境角度
查找: 当前上下文的词法环境-> 上级上下文的词法环境
闭包
当调用一个外部函数返回一个内部函数后,即使外部函数执行结束,但内部函数引用的外部函数的变量仍然保存在内存中,这些变量的集合就是闭包
原理: 预扫描内部函数;把内部函数引用的外部变量保存到堆的闭包对象中。
- 闭包是怎么回收的
作为全局变量而一直存在-> 用于会一直使用的闭包,不回收 会造成内存泄漏。
局部变量-> 用于使用频率不高且占内存较大的闭包,父函数销毁后垃圾回收机制会自动回收
=this
-
this 是和执行上下文绑定的
-
指向
1.当函数作为对象的方法调用时,函数中的 this 就是该对象;
2. 当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window;
- 嵌套函数中的 this 不会继承外层函数的 this 值。
- 箭头函数的this 就是它外层函数的 this。