关于函数相关的一些概念,如执行环境、变量对象、做用域链、闭包,以前是详细查阅了资料研究了一番的,可现在时间久了,不少东西有生疏了。javascript
记录下本身的大脑思考过程,是个很好的学习过程。前端
1.执行环境。(execution context)java
由可执行的代码建立。分为3种。web
1)全局执行环境。当程序开始执行时即进入全局执行环境。全局执行环境进栈。浏览器
2)函数执行环境(局部执行环境)。每当调用一个函数时,就进入一个局部执行环境,该函数执行环境进栈。在执行return以后,该函数执行环境出栈,把控制权返回给以前的执行环境。闭包
3)eval()函数执行环境。使用js原生eval()函数时也会进入一个执行环境,执行环境进栈,在函数执行完毕后,执行环境出栈。ecmascript
全局执行环境是最外层的一个执行环境。在web浏览器中,全局执行环境被认为是window对象,由于全部的全局变量和全局函数都是做为window对象 的属性和方法建立的。某个执行环境中的全部代码执行完毕后,该环境被销毁,保存在其中的全部变量和函数定义也随之销毁(全局执行环境直到应用程序退出—— 例如关闭网页或者浏览器——时才会被销毁),被垃圾回收机制回收。ide
2.变量对象(variable object)函数
每一个执行环境都有一个与之对应的变量对象。做为执行环境的属性,规定了执行环境中能够操做的变量和函数。虽然咱们的代码没法访问这个对象,可是解析器在处理数据时会在后台使用它。分为两种。
a. 全局执行环境的变量对象是全局变量,也就是全局对象自身--在程序开始时已经建立。这里说的便是window/global对象
b. 函数执行环境的变量对象成为活动对象AO,---在进入函数执行环境时建立学习
咱们刚才说了变量对象决定了该做用域内可进行操做的函数和变量,那咱们具体说下VO包含了哪些东西。
变量对象由3部分组成: 1,该执行环境内的全部函数声明。2.做为形参传递进来的arguments 3.该执行环境内的全部变量声明
3.做用域与做用域链
做用域便是函数和变量可被有效使用的范围。
做用域链是当代码在一个环境中执行时建立的,做用域链的用途就是要保证执行环境中能有效有序的访问全部变量和函数。做用域链的最前端始终都是当前执行的代码所在环境的变量对象,下一个变量对象是来自其父亲环境,再下一个变量对象是其父亲的父亲环境,直到全局执行环境。
标识符解析是沿着做用域链一级一级地搜索标识符的过程。搜索过程始终从做用域链的前端开始,而后逐级地向后回溯,直到找到标识符为止(若是找不到标 识符,一般会致使错误发生)。其实,通俗的理解就是:在本做用域内找不到变量或者函数,则在其父亲的做用域内寻找,再找不到则到父亲的父亲做用域内寻找, 直到在全局的做用域内寻找!
重点看下做用域链是什么:
scope chain:
一个函数执行环境的做用域链是在函数调用时开始建立的
由这个函数执行环境的变量对象AO+scope property组成
函数的scope property属性:
一个函数的scope property属性是在这个函数建立(定义或叫声明)的时候就存在了
无论它是否有被调用(里面放的是父级的变量对象)
因此,一个执行环境的做用域链包括的是本身的AO和全部父级环境的AO。理解了这些就清楚了为什么内部函数能够使用外部函数中定义的变量和函数。
4.执行环境、变量对象、做用于链的关系图。
执行环境为一个对象,VO(AO),this、做用链做为对象的三个属性看待。因此在进入一个执行环境时,跟它相关的这三个属性也随之肯定了。
activeExecutionContext = {
VO: {...}, // or AO
this: thisValue,
Scope: [ // Scope chain
// list of all variable objects
// for identifiers lookup
]
};
5.垃圾回收机制。
6.谈谈闭包。
6-1)什么是闭包。
说法一:闭包就是一个函数能够访问到另外一个函数的变量---感受等于没说。
说法二: mdn认为闭包是一个特殊的对象,包含了两个部分(闭包函数和函数建立时的环境---即变量)---感受这个说法更贴近实际一些。
闭包,整体上是说依赖于做用域链和垃圾回收机制。要理解闭包最好从编译器的角度来理解。
6-2)闭包的应用场景
a.实现封装。
function Person(){ var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } } var john = Person(); console.log(john.getName()); john.setName("john"); console.log(john.getName()); var jack = Person(); console.log(jack.getName()); jack.setName("jack"); console.log(jack.getName());
6-3)闭包的特色
7)建立函数的方式(定义函数的方式)
因为这里主要讲解的是函数相关的重要概念,因此补充下能够经过哪些方式建立函数。
3种方法,
Function构造器
函数声明
函数表达式
8.this指向。
1)做为普通函数调用。
var name = "The Window"; var obj={ name: "My Object", getNameFunc: function () { return function(){ return this.name; } } }; console.log(obj.getNameFunc()());
obj.getNameFunc()等同于函数function(){ return this.name; }。
因此obj.getNameFunc()再也不与obj有任何关系,因此它的调用仅仅是普通函数调用。在js的normal模式下,this指向window。在strict模式下,this为undefined。
参考:
这里的表达仅为我的理解,比较官方的更精确的表述请见参考。
《javascript高级程序设计第3版》
ECMAscript官方文档。
http://dmitrysoshnikov.com/ecmascript/chapter-6-closures/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures