JavaScript 引擎
将.js文件内容转化为机器懂的语言。只要实现这个功能,就可以被叫做引擎。我们甚至可以自己编写。
词法分析 Parser
符合ECMA标准的JavaScript引擎:
抽象语法树 AST
解释器 Interpreter
即时的,从第一行开始读,解释为ByteCode,运行。
这样的环境中会做很多重复的工作,如不断重复计算循环中的函数
编译器 Compiler
启动慢,先编译为计算机可以理解的low-level编程语言,再执行,相当于做了优化(Optimized)
混合编译器
前两个各取所长,构成的Just In Time Compiler,使得运行速度加快。我们要写让机器更好预测的代码。
JavaScript 运行时
Philip Roberts:到底什么是Event Loop呢?
Call Stack
栈溢出 Stack Overflow
Memory Heap
垃圾回收 Garbage Collection
内存泄漏 Memory Leaks
- addEventListener
- setInternal
执行上下文 Execution Context
每当我们调用函数,JavaScript引擎看到括号 (),都会创建一个新的执行上下文,即都会发生一个创建阶段和一个执行阶段。
- 创建阶段
在这个阶段中,执行上下文会分别创建变量对象,建立作用域链,以及确定this的指向。
- 代码执行阶段
创建完成之后,就会开始执行代码,这个时候,会完成变量赋值,函数引用,以及执行其他代码。
1.词法作用域
lexical scope => availabel data + variables where the function was defined,not where the function is called(dynamic scope => this keyword),to "fix it",use arrow function. 变量对象(variable object) ? 每一个执行上下文都会分配一个变量对象(variable object),变量对象的属性由 变量(variable) 和 函数声明(function declaration) 构成。在函数上下文情况下,参数列表(parameter list)也会被加入到变量对象(variable object)中作为属性。变量对象与当前作用域息息相关。不同作用域的变量对象互不相同,它保存了当前作用域的所有函数和变量。
全局对象
定义在顶层代码中。全局上下文中的变量对象就是全局对象。 可以看出,执行上下文栈(图右上角)的底层永远有个Global Execution Context。
Arguments对象
调用函数时,会为其创建一个Arguments对象,并自动初始化局部变量arguments,指代该Arguments对象。所有作为参数传入的值都会成为Arguments对象的数组元素。
- ES6之后用...args 剩余形参代替可变长度实参列表,区别剩余形参和扩展操作符,它们的作用恰好相反。
变量提升 Hoisting
变量提升发生在上下文创建阶段。JavaScript引擎看到var或者function的时候,会提升他们,并在内存中分配空间。
-
变量:提升了左边的一部分即
var teddy = undefined。如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。 -
函数表达式
var sayhi = function() {
console.log('hi')
}//no hoisting
sayhi();
- 函数声明式: function关键字+命名函数的标识符+()+{}
sayhi();
function sayhi() {
console.log('hi')
}//hoisting
2.作用域链 Scope chain
参考: JavaScript深入之作用域链 , 一道js面试题引发的思考
查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
3.this 关键字
this是动态作用域的。
this :is the object that the function is a property of. ===>>> who called me ? “点运算符左边”是谁
this 原理
由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。JavaScript 允许在函数体内部,引用当前环境的其他变量。
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 单独执行
f() // 1
// obj 环境执行
obj.f() // 2
this作用
- give methods access to their object
- execute same code for multiple object
动态和静态作用域链
obj并没有调用anotherFunc,而是sing函数在内部调用了anotherFunc,也就说嵌套函数不继承词法环境意义上父函数的环境,this使用动态作用域,解决方法:
- 箭头函数
- bind
- self = this
引申 call,apply & bind
-
前两个用来从别的对象中“借用方法”,立即执行,两个区别在于传递参数列表的方法,call用逗号分隔,apply用数组形式传递
-
bind返回一个新的函数,使我们可以稍后调用有特定的上下文或this关键字的函数