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,
  • 从值->值

    • symbol
      既没有办法转换成别的类型,也没有办法从别的类型转换过来
    • Boolean
      除了 undefined、null、0、0n 、""、Nan返回 false 之外,其他都转换为true
  • 隐式转换 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. 再执行 -> 查找自定义的变量和函数。

查找变量

单个执行上下文中如何查找变量:

 1: 如果是同名的函数,编译阶段选择后者,覆盖前者。
 2: 如果变量和函数同名,编译阶段 函数提升优先级>变量提升

非块级作用域查找变量 -> 作用域链

查找: 当前作用域->上级作用域->全局作用域
- 作用域链: 通过作用域查找变量的链条,由词法作用域决定. 
  词法作用域由函数声明的位置来决定,和函数调用没有关系.(静态作用域)  

块级作用域中的变量查找 -> 作用域链和词法环境角度

查找: 当前上下文的词法环境-> 上级上下文的词法环境

闭包

当调用一个外部函数返回一个内部函数后,即使外部函数执行结束,但内部函数引用的外部函数的变量仍然保存在内存中,这些变量的集合就是闭包
原理: 预扫描内部函数;把内部函数引用的外部变量保存到堆的闭包对象中。

  • 闭包是怎么回收的
    作为全局变量而一直存在-> 用于会一直使用的闭包,不回收 会造成内存泄漏。
    局部变量-> 用于使用频率不高且占内存较大的闭包,父函数销毁后垃圾回收机制会自动回收

=this

  • this 是和执行上下文绑定的

  • 指向
    1.当函数作为对象的方法调用时,函数中的 this 就是该对象;
    2. 当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window;

  1. 嵌套函数中的 this 不会继承外层函数的 this 值。
  2. 箭头函数的this 就是它外层函数的 this。
posted @ 2021-12-14 23:54  忘川酒  阅读(37)  评论(0编辑  收藏  举报