Javascript - 3

Javascript - 3 背后的运行原理

  • High level

  • Garbage-collected

js引擎内部的算法,为了不被不必要的东西堵塞,会从计算机内存中 自动删除旧的、未使用的对象

  • 解释型的 / 即时编译的语言(为了更快做出的调整) interpreted or just-in-time compiled

通过 解释器(Interpreter) 逐行翻译并执行代码的编程语言,它不需要预先将代码全部编译成机器码,而是直接运行源代码,边翻译边执行

  • 多范式

程序化的,面向对象的(OOP),函数式编程(FP)

命令式,声明式

  • 基于原型,面向对象的

除了原始值,js中的东西都是继承自某个原型对象的对象

Object 对象继承自 Object.prototype;Array 对象继承自 Array.prototype;Function 对象继承自 Function.prototype

  • first-class functions (函数式编程)

函数被当作变量,可以把函数传给函数,也可以让函数返回函数

  • Dynamic

  • 单线程

Do one thing at a time 只有一个主线程执行代码,同一时间只能执行一个任务

  • 非阻塞 事件循环 并发模型 Non-blocking event loop concurrency model

避免主线程阻塞

在后台执行需要长时间运行的任务,一旦完成,放回主线程

  • 跨平台

只要目标平台安装了对应的解释器,同一份代码即可运行

  • 非独立

JS 引擎

执行js代码的程序

每个浏览器都有自己的js引擎

包含

  • 调用栈 Call Stack

代码实际 执行 的地方,执行上下文 execution context 堆叠在一起,为了跟踪程序在执行中所处的位置(栈顶就是当前代码执行的地方,当它执行完成就会从栈顶删除,执行会回到之前的执行上下文)

  • 内存堆 Memory Heap

非结构化内存池,存储对象

作用

  1. Parsing 解析:code -> AST

将每行代码分成对语言有意义的部分(const, function),然后把这些部分以结构化的方式存入树中

  1. Compilation 编译:AST -> 机器码

  2. Execution 执行

  3. Optimization 优化

为了尽快开始执行,在开始,创建一个未优化的机器码版本;在后台优化这段机器码,并在运行的程序执行期间重新编译

优化可以做很多次,在每一次优化后,未优化的代码被替换

解析,编译,优化发生在引擎内部的 无法从代码访问的 特殊线程中

JS Runtime

浏览器

Node.js

JS 执行

创建 全局执行上下文 for the top-level code(顶级代码:不在任何函数中的代码)

在最开始,只有在函数外面的代码才会被执行

执行上下文

js总是在执行上下文中执行

  • 在执行其中一段js时的环境

  • 存储所有必要信息的盒子

在任何js项目中,只有一个全局执行上下文

每个函数,在被调用时,有自己的执行上下文

执行上下文的构成

  1. 可变环境 variable environment

所有的变量(let, const, var declarations) 函数声明(functions) 和 传递的参数(arguements object) 都被存储

  1. 作用域链 scope chain

对于 函数外变量 的引用

  1. this 关键字

相反,箭头函数可以使用 来自 最近的常规父级函数的 arguements object 和 this 关键字

call stack追踪执行顺序

  1. 编译完成,顶级代码开始执行,创建全局执行上下文
  2. 全局执行上下文放入stack
  3. 函数创建自己的执行上下文,放入stack
  4. 执行完毕,弹出执行上下文

执行完毕,程序会保持在这个状态,直到真正结束(关闭浏览器选项卡或关闭浏览器窗口)

Scope Chain

Scoping: 程序的 变量 如何被 (js引擎) 组织和访问

lexical scoping 词法作用域: scoping 完全 由函数和代码块的位置控制

写在一个函数中的函数,可以访问其父函数的变量

Scope: space or environment in which a certain variable is declared

全局作用域global scope,函数作用域,块作用域

var 是函数作用域的

Scope of variables 变量的作用域: 该变量可以被访问的代码区域

变量查找

(js引擎 create Scope Chain behind the scenes)如果一个scope需要使用某个变量,但在当前scope找不到,它将在scope chain中查找,看是否能在parent scope中找到变量

变量,函数,arguements object 都是一样的

scope chain 只能向上工作,不能横向工作

Scope Chain VS Call Stack

Hoisting 提升

提升: 使变量在实际声明它们之前 可以被访问

var 会创建一个属性在全局窗口对象上

const let - 该变量会被提升到当前作用域的顶部,但在声明语句之前,变量会处于"暂时性死区" TDZ 中

WHY TDZ

  1. 更容易避免和捕获错误
  2. 使 const 变量真正地工作

WHY HOISTING

  1. 使用函数在实际声明它之前

this 关键字

const jonas = {
    firstName: 'Jonas',
    year: 1991,
    calcAge: function(){
        console.log(2024 - this.year);
        
        // Solution 1
        // const self = this;
        // const isMillenial = function(){
        //     console.log(self);
        // }
        
        // Solution 2
        const isMillenial = () =>{
            console.log(this);
        }
    }
}

arguments 关键字

箭头函数没有 arguments 关键字

const addExpr = function(a,b){
    console.log(arguments);
    return a + b;
}
addExpr(2, 5);
addExpr(2, 5, 8, 12); // 也是合法的,只是后面传进去的两个值没有名字

原始值 VS 对象

原始值 - 原始类型 - 存储在 Call Stack(在它们声明的执行上下文中)

对象 - 引用类型 - 存储在 Heap

原始值的存储

  1. 创建 具有变量名称 的 唯一标识符

  2. 分配一块 有一个特定地址 的 内存值被存储在 这块 内存中

  3. 唯一标识符指向地址

  4. 内存中的值是不可变的,变量值改变,会分配一块新的内存,存入新的值,唯一标识符指向这个新的地址

对象的存储

  1. 对象的唯一标识符 不直接指向 存储对象的地址,而是指向一个存储对象地址的内存地址

对象可能太大无法存储在stack中,于是存储在heap中(一个几乎无限的内存池),stack只存储一个引用以便找到它

  1. 当在复制一个对象的时候,只是在创建一个指向完全相同对象的新变量

复制对象

// Copy objects
const jessica = {
    firstName: 'Jessica',
    lastName: 'Williams',
    age: 27,
};

const jessicaCopy = Object.assign({}, jessica2); // 创建一个全新的对象
jessicaCopy.lastName = 'Davis';
console.log(jessica.lastName); // Williams
console.log(jessicaCopy.lastName); // Davis

浅拷贝:只会拷贝第一级的属性(如果有在对象中的对象,那 Object.assign() 就没用了)

深度克隆:复制所有内容(使用外部库)

posted @ 2025-04-01 19:33  wajiez  阅读(8)  评论(0)    收藏  举报