作用域:
在 JavaScript 中, 作用域(scope,或译有效范围)就是变量和函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。
作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少命名冲突。
JavaScript语言中的作用域分为全局作用域和局部作用域。
作用域链:
当我们在局部作用域中,访问一个变量时,系统首先会在当前作用域中寻找该变量,如找到则直接使用。反之,则继续向上一级作用域中寻找该变量,如找到则直接使用,反之,继续向上一级作用域中去寻找…直到全局作用域,如找到则直接使用,如未找到则直接在全局作用域中声明该变量,我们把这种链式查询关系就称之为为作用域链!
函数上下文(this):
当调用函数时,除了显式提供的参数外,this参数也会默认地传递给函数。this参数是面向对象JavaScript编程的一个重要组成部分,代表函数调用相关联的对象。因此,通常称之为函数上下文。
函数执行上下文:
执行上下文是内部的JavaScript概念,JavaScript引擎使用执行上下文来跟踪函数的执行。
既然具有两种类型的代码(全局代码和局部代码),那么就有两种执行上下文:全局执行上下文和函数执行上下文。二者最重要的差别是:全局执行上下文只有一个,当JavaScript程序开始执行时就已经创建了全局上下文;而函数执行上下文是在每次调用函数时,就会创建一个新的。
词法环境:
无论何时创建函数,都会创建一个与之相关联的词法环境,并存储在名为[[Environment]]的内部属性上(也就是说无法直接访问或操作)。两个中括号用于标志内部属性。
每个执行上下文都有一个与之关联的词法环境,词法环境中包含了在上下文中定义的标识符的映射表。
词法环境由环境记录与对外部环境引入记录两个部分组成。
其中环境记录用于存储当前环境中的变量和函数声明的实际位置;外部环境引入记录很好理解,它用于保存自身环境可以访问的其它外部环境,那么说到这个,是不是有点作用域链的意思?
我们在前文提到了全局执行上下文与函数执行上下文,所以这也导致了词法环境分为全局词法环境与函数词法环境两种。
调用栈:
函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A
的内部调用函数B
,那么在A
的调用帧上方,还会形成一个B
的调用帧。等到B
运行结束,将结果返回到A
,B
的调用帧才会消失。如果函数B
内部还调用函数C
,那就还有一个C
的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。
注:上面的“调用记录”和“调用帧”都是指“执行上下文”。
闭包:
闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
原型:
《前端程序员面试笔试宝典》 7.6.2
几乎每一个对象都包含一个原型属性用于关联另一个对象(但也有可能为undefined),关联后就能使用那个对象的属性和方法,这其实就是JavaScript的原型式继承。除了null、Object.prototype(Object的原型属性关联的对象)和用Object.create(null)创建的对象等少数几个没有原型的对象之外,大部分都具有原型,并且是一个继承自Object.prototype的原型。
《JavaScript忍者秘籍》
原型的概念很简单。每个对象(注:有些没有)都含有原型的引用,当查找属性时,若对象本身不具有该属性,则会查找原型上是否有该属性。
原型链:
《前端程序员面试笔试宝典》 7.6.2
对象之间通过原型关联到一起,就好比用一条锁链将一个个对象连接在一起,在与各个对象挂钩后,最终形成了一条原型链。在读取对象的一个属性时,会先在对象中查询自有属性,如果不存在,再沿着原型链向上搜索匹配的继承属性,直至找到或到达原型链顶端,才停止搜索。
new的过程:
- 创建一个新对象。
- 该对象作为this参数传递给构造函数,从而成为构造函数的函数上下文(即this)。
- 这个新对象会被执行[[Prototype]]连接。
- 新构造的对象作为new运算符的返回值。
5.如果构造函数返回一个对象,则该对象将作为整个表达式的值返回,而传入构造函数的this(this仍然是新创建 的对象)将被丢弃。
6.但是,如果构造函数返回的是非对象类型,则忽略返回值,返回新创建的对象。