1、JavaScript引擎和运行时间
(1)JavaScript引擎:Google的V-8、Node.js……
调用栈(call stack):代码实际执行的地方,使用执行上下文(Execution context)
堆(Heap):非结构化的内存池
(2)代码运行:编译(compilation)或解释(interpretation)
编译:代码→可执行文件→运行
解释:代码→运行(运行速度非常缓慢,JS曾经使用)
→即时编译(Just-in-time,JIT compilation)
JavaScript编译语言的过程:代码→解析→抽象语法树(Abstract Syntax Tree,AST)→编译→机器码→执行(执行中进行优化)
(3)JavaScript Runtime
Web API:为JavaScript提供需要的功能
回调函数(Callback Function):【回调队列→调用栈→回调队列】→事件循环
2、JavaScript代码运行
(1)执行上下文(Execution context):一种JavaScript的执行环境,为顶级代码(top-level code)创建,包含所有必要的信息,函数只在调用的时候执行
①内容:
可变环境(variable environment)(变量、函数、参数对象)
作用域链(scope chain)
this关键词(this keyword)
②执行顺序:执行上下文会堆放在调用栈中,先进后出
(2)运行
(3)函数运行:执行函数,等待回调函数
3、作用域(scope)
(1)分类
①全局作用域(Global scope):在任何函数或代码块以外,变量能在任何地方被调用
②函数作用域(Function scope):局部作用域(local scope),只能在函数内部调用
③块作用域(Block scope):大括号内声明的变量只能在内部调用(只限于let和const,但是用var可以在外部被调用),函数也是块范围的
(2)作用域链(Scope chain):先在自身作用域找,子作用域可以调用上层作用域声明的变量(变量查找),同级作用域无法互相查找
4、可变环境(Variable environment)与提升(Hoisting)
(1)提升(Hoisting):变量可以在声明之前被调用
函数声明:存放在可变环境中,甚至比代码执行还早,所以可以在声明前被调用
var变量:实际值为undefined
let和const变量:不会被提升,不能在声明之前被调用→TDZ(Time dead zone)
函数表达式、箭头:取决于是否由var或let、const创建→两种不同的错误
(2)时间死区:let和const变量存放在此,知道存在,但是未初始化,不能被调用,便于发现错误,让常量变量const不能重新分配值
(3)总之:尽量声明所有的变量和函数在调用之前,让代码更干净易读。
6、this关键字
(1)this:一种特殊的变量,指向所有者(调用函数的对象)
(2)四种调用方式
箭头函数没有this关键字,直接指向全局的this
使用var时实际上会在全局创建变量,因此如果同时使用var声明了一个变量,又在箭头函数中使用this关键字调用一个同名变量,this会直接指向该var变量
(3)Object内函数嵌套this问题:
①在父函数中声明const self = this,在子函数中使用self,这样在调用self时就会提升到上一层函数的this
②子函数采用箭头函数:箭头函数的this直接指向上一层函数的this
(4)arguments:一个类数组对象,可以通过索引来操作数据,也可以获取长度。arguments 代表的是实参。在调用函数时,我们所传递的实参都会在arguments 中保存。
arguments只在函数中使用,箭头函数没有arguments关键字
7、原始类型(Primitives)和对象(Objects)
(1)原始类型储存在调用栈中,对象存储在堆中
原始类型→调用栈→标识符(Identifier)→地址(Address)、值(Value)
对象→对象名存储在调用栈→对象内容存储在堆
(2)复制对象:Object.assign({}, object); →将复制的对象与一个空对象合并→新对象
浅拷贝→只复制第一层,要是对象里有对象,那个对象依然在堆中指向同一个地址
深度克隆→Lodash