javascript 执行环境,作用域、作用域链、闭包
1、执行环境
执行环境是JavaScript中国最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了他们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写代码无法访问这个对象,但解析器在处理数据时会在后台使用它。
全局执行环境时最外围的一个执行环境。根据ECMAScript事先实现所在的宿主环境不同,表示执行环境的对象也不一样。在Web浏览器中,全局执行环境被认为是window对象,一次所有全局变量和函数都是最为window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境知道应用程序退出,例如关闭网页或浏览器,才会被销毁)。
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
2、作用域、作用域链
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。
标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直到找到标识符为止(如果找不到标识符,返回 undefined)。
var obj = {
a:"hel"
}
console.log(obj.b); // undefined
var newValue = oldValue; //Error: oldValue is not defined
3、闭包
闭包是指有权访问另一个函数作用域中变量的函数。创建闭包的常见方式是,在一个函数中创建另一个函数。
function createComparisonFunction(propertyName){
return function (object1,object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName]; if(value1 < value2){
return -1;
}else if(value1 > value2){
return 1;
}else {
return 0;
}
};
} var compareNames = createComparisonFunction("name"); var result = compareNames({name: "Tom"},{name:"Jerry"}); //接触对匿名函数的应用(以便释放内存)
compareNames = null;
上面的例子中,createComparisonFunction()函数返回一个内部函数(匿名函数),此函数访问了外部函数的变量propertyName。当在其他地方调用这个返回的匿名函数,仍让可以访问变量propertyName。因为内部函数的作用链中包含了createComparisonFunction()函数的作用域,即此内部函数是一个闭包。
下图展示了调用compareNames()的过程中作用域之间的关系。
在函数中定义的内部函数会将它的外部函数的活动对象添加到它的作用域链中。在上面例子中匿名函数从createComparisonFunction()返回之后,它的作用域链就包含createComparisonFunction()函数的活动对象和全局变量对象。所以返回的函数就可以访问createComparisonFunction()中所定义的所有变量。一般来讲,函数执行完之后,它的活动对象会被销毁,但闭包不同。createComparisonFunction()函数执行完后,它的活动对象不会立即销毁,因为匿名函数的作用域链仍然引用这个活动对象,除非匿名函数被销毁。
还有一个例子:
function createFucntions(){
var result = new Array(); for (var i=0; i<10 ;i++){
result[i] = function(){
return i;
};
}
return result;
}
var functions = createFucntions();
functions.forEach(function(item,i){
console.log(item()); //都是 10
})
返回一个函数数组。每个函数都保存着createFucntions()的活动对象,都引用同一个变量 i,当createFucntions()执行完之后,变量 i 的值是10。可做如下修改,就满足要求了:
function createFucntions(){
var result = new Array(); for (var i=0; i<10 ;i++){
result[i] = function(num){
return function(){
return num;
}
}(i);
}
return result;
}
闭包因因包含其他函数的作用域,所以会比其他函数占用更多内存。
4、模仿块级作用域(私有作用域)
JavaScript中没有块级作用域的概念,所以在块语句中定义的变量,实际上是包含在函数中的。例如:
function out(cout){
for(var i=1; i<=cout; i++){
console.log(i); //1,2,3,4,5
}
var i; // var 重新声明变量,会忽略这个声明
console.log(i); // 6,正常使用 i
}
out(5);
为了模仿块级作用域可以使用立即调用匿名函数的方式:
(function(){
// 块级作用域
})();
所以上面的例子可以如下修改:
function out(cout){
(function(){
for(var i=1; i<=cout; i++){
console.log(i); //1,2,3,4,5
}
})();
console.log(i); // Error: i is not defined
}
out(5);
匿名函数的中定义的变量在执行结束后会被销毁。这种方式可以限制向全局作用域中添加过多的变量和函数,避免命名冲突。
5、私有变量
在JavaScript中所有对象属性都是公有的,没有私有成员的概念,但有私有变量的概念。任何在函数中定义的变量都可以认为是私有变量,因为不能再函数外部访问这些变量。私有变量包括函数的参数,局部变量,及内部函数。
1)构造函数中的私有变量
function Person(name){ function sayHello(){
console.log("Hello "+name);
} this.getName = function(){
return name;
}
this.setName = function(value){
name = value;
}
this.publicMethod = function(){
sayHello();
}
} var p1 = new Person("Tom");
console.log(p1.getName()); // Tom
p1.publicMethod(); // Hello Tom
p1.setName("Tom1");
console.log(p1.getName()); //Tom1
p1.publicMethod(); // Hello Tom1 var p2 = new Person("Jerry");
console.log(p2.getName()); //Jerry
在Person 构造函数外部,不能访问 name ,getName() 和 setName() 因为闭包可以访问 name 属性。sayHello() 方法类似。
因为每次新建实例,都会重新创建这些公有方法,所以私有变量在每个实例中都不同。
2)静态私有变量
静态变量的特点是所有实例都共享。
(function(){
var name = ""; function sayHello(){
console.log("Hello "+name);
} // 未使用 var 声明的变量,会成为一个全局变量
Person = function(value){
name = value;
}; Person.prototype.getName = function(){
return name;
}; Person.prototype.setName = function(value){
name = value;
};
Person.prototype.publicMethod = function(){
sayHello();
} })(); var p1 = new Person("Tom");
console.log(p1.getName()); // Tom
p1.publicMethod(); // Hello Tom
p1.setName("Tom1");
console.log(p1.getName()); //Tom1
p1.publicMethod(); // Hello Tom1 var p2 = new Person("Jerry");
console.log(p1.getName()); //Jerry
console.log(p2.getName()); //Jerry
p1.publicMethod(); // Hello Jerry
p2.publicMethod(); // Hello Jerry
上述例子中,将公有方法定义在原型上,所以所有实例都使用同一个函数,进而所有实例对于 name 变量都是共享的。
javascript 执行环境,作用域、作用域链、闭包的更多相关文章
- JavaScript 执行环境、作用域、内存管理及垃圾回收机制
前言 JavaScript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存. [原理]找出那些不再继续使用的变量,然后释放其占用的内存.为此,垃圾收集器会按照固定的时间间隔( ...
- javaScript执行环境、作用域链与闭包
一.执行环境 执行环境定义了变量和函数有权访问的其他数据,决定了他们各自的行为:每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中.虽然我们编写的代码无法访问这个对象 ...
- 深度剖析Javascript执行环境、作用域链
一.执行环境 执行环境(也叫做执行上下文,Execution Context)是Javascript中最为重要的一个概念.执行环境定义了变量或函数有权访问其他数据,决定了它们各自的行为.每个执行环境都 ...
- JavaScript执行环境和作用域(链)的那些事
执行环境 什么是执行环境 提起作用域,我们不得不说说什么是执行环境.执行环境定义了变量或函数有权访问的其他数据,并决定其各自的行为.每一个执行环境都有一个对应的变量对象,这个对象的作用就是保存在环境中 ...
- JavaScript 执行环境以及作用域链
执行环境(execution context,为简单起见,有时也称为"环境")是 JavaScript 中最为重要的一个概念.执行环境定义了变量或函数有权访问的其他数据,决定了它们 ...
- Javascript执行环境、作用域链
执行环境 可以把执行环境想象为一个圆圈,里面包含了一些变量.函数. 执行环境定义了变量或函数的有权访问的其他数据,决定了它们各自的行为.还有一个顶部执行环境.在浏览器中,顶部执行环境既为window, ...
- 【原】javascript执行环境及作用域
最近在重读<javascript高级程序设计3>,觉得应该写一些博客记录一下学习的一些知识,不然都忘光啦.今天要总结的是js执行环境和作用域. 首先来说一下执行环境 一.执行环境 书上概念 ...
- javascript 执行环境,作用域链和闭包
首先看下这条语句: (function($) {…})(jQuery): 1.原理: function(arg){…}这就定义了一个匿名函数,参数为arg 而调用函数时,是在函数后面写上括号和实参的, ...
- javascript执行环境以及作用域链的理解
在javascript脚步语言中执行环境有两种: 全局环境: 局部环境: 我们可以拿一个田径跑道来打比方,全局环境就可以理解为是最外面跑道,它包含着内部所有的东西,有人在跑步,有人在跳远,这些用着不同 ...
随机推荐
- Java课程作业之动手动脑(三)
1.以下代码为何无法通过编译?哪儿出错了? 在Foo类中已经有了一个Foo的含参构造方法,所以在定义Foo类对象时不能使用new Foo()方法.在Foo类中再写一个无参构造方法,就能编译了. 如果类 ...
- bayes公式 - 再从零开始理解
bayes公式与机器学习 - 再从零开始理解 从本科时候(大约9年前)刚接触Bayes公式,只知道P(A|B)×P(B) = P(AB) = P(B|A)×P(A) 到硕士期间,机器学习课上对P(B| ...
- 关于微信支付接口,curl错误代码58
微信支付接口,curl错误代码58 之前的微信付款到用户零钱都是好好的,今天运营来找我, 我想了了下,就是进行了网站搬家 看了下 微信支付相关的证书配置文件 知道了,在这个 要改下证书的路径 WxPa ...
- [转] Android中的设计模式-备忘录模式
转自Android中的设计模式-备忘录模式 定义 备忘录设计模式的定义就是把对象的状态记录和管理委托给外界处理,用以维持自己的封闭性. 比较官方的定义 备忘录模式(Memento Pattern)又叫 ...
- Android自定义View学习(三)
属性动画(上) 参考:HenCoder 自定义绘制的第 1-6 期:属性动画 Property Animation(上手篇) Interpolator 其实就是速度设置器,设置动画运行的速度. 属性动 ...
- delphi 动态绑定代码都某个控件
delphi 动态绑定代码都某个控件 http://docwiki.embarcadero.com/CodeExamples/Berlin/en/Rtti.TRttiType_(Delphi)Butt ...
- ubuntu交换Caps 和 ESC
https://askubuntu.com/questions/363346/how-to-permanently-switch-caps-lock-and-esc This will allow y ...
- select as table
select order_time, max(sum_price) from (SELECT order, sum(price) as sum_price FROM orders group by o ...
- datasnap服务器支持的参数类型
可作为参数的类型TDBXWideStringValueTDBXAnsiStringValueTDBXInt16ValueTDBXInt32ValueTDBXInt64ValueTDBXSingleVa ...
- RESET MASTER和RESET SLAVE使用场景和说明,以及清除主从同步关系
mysql主从复制中,需要将从库提升为主库,需要取消其从库角色,这可通过执行RESET SLAVE ALL清除从库的同步复制信息.包括连接信息和二进制文件名.位置.从库上执行这个命令后,使用show ...