介绍下javascript中的最重要几个概念执行环境、做用域、以及原型链。理解了这两个概念才能写出好的javascript代码,特别是在理解闭包、写插件或者使用使用框架快速上手。
javascript
一、执行环境(摘抄于javascript高级程序设计):
html
执行环境(execution context,为简单起见,有时也称为“环境”)是 JavaScript 中最为重要的一个概念。执行环境定义了变量或函数是否有权访问的其余数据,决定了它们各自的行为。每一个执行环境都有一个与之关联的变量对象(variable object) ,环境中定义的全部变量和函数都保存在这个对象中。虽然咱们编写的代码没法访问这个对象,但解析器在处理数据时会在后台使用它。前端
全局执行环境是最外围的一个执行环境。根据 ECMAScript 实现所在的宿主环境不一样,表示执行环境的对象也不同。在 Web 浏览器中,全局执行环境被认为是 window 对象(第 7 章将详细讨论) ,所以全部全局变量和函数都是做为 window 对象的属性和方法建立的。某个执行环境中的全部代码执行完毕后,该环境被销毁,保存在其中的全部变量和函数定义也随之销毁(全局执行环境直到应用程序退出——例如关闭网页或浏览器——时才会被销毁) 。java
每一个函数都有本身的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行以后,栈将其环境弹出,把控制权返回给以前的执行环境。ECMAScript 程序中的执行流正是由这个方便的机制控制着。 浏览器
二、做用域(摘抄于javascript高级程序设计):闭包
当代码在一个环境中执行时,会建立变量对象的一个做用域链(scope chain) 。做用域链的用途,是保证对执行环境有权访问的全部变量和函数的有序访问。做用域链的前端,始终都是当前执行的代码所在环境的变量对象。若是这个环境是函数,则将其活动对象(activation object)做为变量对象。活动对象在最开始时只包含一个变量,即 arguments 对象(这个对象在全局环境中是不存在的) 。做用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是做用域链中的最后一个对象。框架
ps:在C中,每次执行一个函数,都会在内存的栈从新中划定一个区域,加载实参、本地变量、返回地址等信息。这片新的内存栈就是执行环境,而执行链就是传递的指针(不是具体变量、并且每一个函数都是传递指针)。全部本栈帧能够范围本身的变量和前面栈帧的变量,直到全局变量(window对象)。而前面的栈帧不能访问后面的栈帧变量。函数
三、案例:
测试
<!DOCTYPE html> <html> <head> <script> var one="One"; function firstFunction(){ var two="Two"; console.log("Two:--"+one); console.log("Two:--"+two); //console.log("Two:--"+three); function sencondeFunction(){ var three="three"; console.log("three:--"+one); console.log("three:--"+two); console.log("three:--"+three); } sencondeFunction(); } firstFunction(); </script> </head> </html>
正常
this
把注释部分打开(前一个栈帧、访问后一个栈帧。会出现错误)
2、原型链
摘抄于《javascript高级程序设计》的概念:ECMAScript 中描述了原型链的概念,并将原型链做为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。简单回顾一下构造函数、原型和实例的关系:每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,假如咱们让原型对象等于另外一个类型的实例,结果会怎么样呢?显然,此时的原型对象将包含一个指向另外一个原型的指针,相应地,另外一个原型中也包含着一个指向另外一个构造函数的指针。假如另外一个原型又是另外一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念
一、首先一张图:(摘抄于知乎)
二、开始瞎掰
a、咱们的测试代码
<!DOCTYPE html> <html> <head> <script> function firstFunction(){ this.two="two"; this.age="age"; } firstFunction.prototype.a="a"; var first=new firstFunction(); console.log(first); console.log(firstFunction.prototype); </script> </head> </html>
b、浏览器告诉咱们真像
联系上面的代码,咱们先画一张图
__proto__:属性能够看做是原型链。
constructor:看做是java的字节码文件,只是用户定义的class对象,尚未在内存中实现。
prototype:则是class在堆中的实现,才具备使用价值。
接着将firstFunction.prototype中的constructor继续展开(后面的主语):
从这个图咱们看见,prototype原型的__proto__是Object对象。而后,constructor的__proto__是function()对象,而后咱们继续做画:
在图上添加2条红线后,咱们还须要展开 __ptoto__:function(),以下图
从这咱们发现function的__proto__也是Object,再把咱们的画补齐:
后面的__proto__就展开也没有__proto__、constructor、prototype了,就不上图了。这里咱们发现这图还有些变扭。根据《javascript高级程序设计》中描述:有prototype必然有对应的constructor,有constructor比然有对应的prototype。补全
全部的function的__proto__都是function.prototype。而全部prototype的__proto__都是object.prototype。最后object.prototype的__proto__指向的null