读书时间《JavaScript高级程序设计》三:函数,闭包,作用域
上一次看了第6章,面向对象。这里接着看第7章。
第7章:函数表达式
定义函数有两种方式:函数声明、函数表达式
//函数声明
function functionName(arg0,arg1,arg2){
//code...
}
//函数表达式
var functionName = function(arg0,arg1,arg2){
//code...
};
函数声明有个重要的特征是函数申明提升。就是在执行代码前会先读取函数声明,意味着可以把函数声明放在调用它的语句后面。
//函数声明提升
sayHi(); //Hello
function sayHi(){
alert("Hello");
}
函数表达式看起来好像是常规的变量赋值语句。即创建一个函数并将它复制给变量 functionName。这种情况下创建的函数叫做匿名函数。创建的匿名函数能赋值给变量,也能作为其他函数的值返回。
函数表达式在调用前必须先赋值。
//函数表达式没有函数提升
sayHi(); //Error
var sayHi = function(){
alert("Hello");
};
1. 递归
递归函数是在一个函数通过名字调用自身的情况下构成的。
//递归函数: 名称
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
} var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //error! //递归函数: arguments.callee
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
} var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //24
使用 arguments.callee 替代函数名,确保无论怎样调用函数都不会出问题。
在严格模式下("use strict"),不能访问 arguments.callee。 可以使用函数表达式解决问题。
"use strict"
//递归函数: 表达式
var factorial = function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
}
2. 闭包
闭包是指有权访问另一个函数作用域中的变量的函数。 创建闭包的常用方式,就是在一个函数内部创建另一个函数。(创建的内部函数可以访问外部函数中声明的所有局部变量、参数和其他内部函数,当该内部函数在外部函数外被调用,就生成了闭包)。
当一个函数调用时会创建一个执行环境及相应的作用域链,并把作用域链赋值给一个特殊的内部属性( [[scope]] )。然后使用 this、arguments和其他明明参数的值来初始化函数的活动对象。在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,......一直到作用域链的终点全局执行环境。
在函数执行过程中,为读写变量的值,需要在作用域链中查找变量。
function compare(value1,value2){
if(value1 < value2){
return -1;
}else if(value1 > value2){
return 1;
}else{
return 0;
}
}
var result = compare(5,10);
先定义了compare() 函数, 然后再全局作用域中调用了它。调用compare()创建了包含 this、arguments、value1、value2的活动对象。全局执行环境的变量对象(this、result、compare)在compare()执行环境的作用域链中处于第二位。

在作用域链中,闭包只能取得包含函数中任何变量的最后一个值。闭包保存的是整个变量对象,不是某个特殊的变量。
function createFunctions(){
var result = new Array();
for (var i = 0; i < 5; i++){
result[i] = function(){
return i;
};
}
return result;
}
var arr = createFunctions();
console.log(arr[0]()) //5
console.log(arr[1]()) //5
console.log(arr[2]()) //5
console.log(arr[3]()) //5
console.log(arr[4]()) //5
function createFunctions2(){
var result = new Array();
for (var i = 0; i < 5; i++){
result[i] = function(num){
return function(){
return num;
}
}(i);
}
return result;
}
var arr2 = createFunctions2();
console.log(arr2[0]()) //0
console.log(arr2[1]()) //1
console.log(arr2[2]()) //2
console.log(arr2[3]()) //3
console.log(arr2[4]()) //4
内存泄漏
补充:
执行环境:每调用一个函数时(执行函数时),会为该函数创建一个封闭的局部的运行环境,即该函数的执行环境。函数总是在自己的执行环境中执行,如读写局部变量、函数参数、运行内部逻辑。创建执行环境的过程包含了创建函数的作用域,函数也是在自己的作用域下执行的。从另一个角度说,每个函数执行环境都有一个作用域链,子函数的作用域链包括它的父函数的作用域链。
作用域、作用域链、调用对象:函数作用域分为词法作用域和动态作用域。词法作用域是函数定义时的作用域,即静态作用域。当一个函数定义时,他的词法作用域就确定了,词法作用域说明的是在函数结构的嵌套关系下,函数作用的范围。这个时候也就形成了该函数的作用域链。作用域链就是把这些具有嵌套层级关系的作用域串联起来。函数的内部[[scope]]属性指向了该作用域链。动态作用域是函数调用执行时的作用域。当一个函数被调用时,首先将函数内部[[scope]]属性指向了函数的作用域链,然后会创建一个调用对象,并用该调用对象记录函数参数和函数的局部变量,将其置于作用域链顶部。动态作用域就是通过把该调用对象加到作用域链的顶部来创建的,此时的[[scope]]除了具有定义时的作用域链,还具有了调用时创建的调用对象。换句话说,执行环境下的作用域等于该函数定义时就确定的作用域链加上该函数刚刚创建的调用对象,从而也形成了新的作用域链。所以说是动态的作用域,并且作用域链也随之发生了变化。再看这里的作用域,其实是一个对象链,这些对象就是函数调用时创建的调用对象,以及他上面一层层的调用对象直到最上层的全局对象。
3. 块级作用域
在JavaScript中,没有块级作用域概念。可以用匿名函数模仿块级作用域。
var functionName = function(){
//块级作用域
}
(function(){
//块级作用域
})();
(function(){
//块级作用域
}());
4. 私有变量
在JavaScript中,没有私有成员的概念。任何在函数中定义的变量,都可以作为私有变量(函数的参数、局部变量、内部函数)。
// add函数中有3个私有变量:num1 num2 num。
function add(num1,num2){
var num = num1 + num2;
return num;
}
这章介绍了递归函数、闭包、作用域等。要好好的理解闭包、作用域链、执行环境这些概念。
读书时间《JavaScript高级程序设计》三:函数,闭包,作用域的更多相关文章
- Javascript高级程序设计——执行环境与作用域
Javascript中执行环境是定义了变量或函数有权访问的其他数据,决定了各自的行为,每个执行的环境都有一个与之关联的变量对象,环境中定义的所以变量和函数都保存在这个对象中. 全局执行环境是最外围的一 ...
- Javascript高级程序设计——this、闭包、函数表达式
在javascript中函数声明会被提升,而函数表达式不会被提升.当函数执行时,会创建一个执行环境和相应的作用域链,然后利用arguments和其他的命名参数的值来初始化函数的活动对象,作用域链链中所 ...
- javascript 高级程序设计 三
Sorry,前两张介绍的主题还是JavaScript,而第一章介绍了JavaScript和ECMAScript区别,所以前两章介绍的主题应该改为ECMAScript,但是 标题就不改了因为现在人们习惯 ...
- JavaScript高级程序设计(三):基本概念:数据类型
特别注意:ECMAScript是区分大小写的. 一.变量 1.ECMAScript的变量是松散型的.所谓松散型就是可以用来保存任何类型的数据.即每个变量仅仅是一个用于保存值的占位符而已.定义变量时要使 ...
- 读书笔记 - javascript 高级程序设计 - 第一章 简介
第一章 简介 诞生时间 1995 最初用途 客服端验证 第一版标准 注意是标准 1997年 Ecma-262 一个完整的js实现由三部分组成 ECMAScript DOM 文档对象模型 BO ...
- JavaScript高级程序设计之函数性能
setTimeout 比 setInterval 性能更好 // 取代setInterval setTimeout(function self () { // code goes here setTi ...
- JavaScript高级程序设计之函数
函数实际上是对象,每个函数都是Function类型的实例. 函数是引用类型. 函数名实际上是一个指向函数对象的指针,不会与某个函数绑定. // 这种写法更能表达函数的本质 var sum = func ...
- 读书笔记-JavaScript高级程序设计(1)
1.组合继承 (JavaScript 中最常用的继承模式 ) (position: page168) (书中定义了两个变量名 SuperType SubType 乍一看 感觉不太能区分,我将改为 ...
- 读书笔记 - javascript 高级程序设计 - 第二章 在Html中使用JavaScript
1 <script>的6个属性 async 立即下载当前script标签的外部脚本 但不能影响别的 charset 没用了 defer 文档显示之后再执行脚本,只对外部脚本有效 lan ...
- 《JavaScript高级程序设计》 -- 变量、作用域和内存问题(二)
1.基本类型与引用类型 基本类型:值保存在变量中 (Number.String.Boolean.Undefined.Null).在内存中占据固定大小空间,被保存在栈内存中 引用类型:值是保存在内存中的 ...
随机推荐
- 【mysql】关于子查询的一个例子
假设表my_tbl包含三个字段a,b,c:现在需要查询表中列a的每个不同值下的列b为最小值的记录量. 比如表记录为: a b c 1 3 'cd' 2 3 'nhd' 1 5 'bg' ...
- C++ Preprosessor import
#import Attributes Provides links to attributes used with the #import directive. Microsoft Specific ...
- linux yum命令
1 安装yum install 全部安装yum install package1 安装指定的安装包package1yum groupinsall group1 安装程序组group1 2 更新和升级y ...
- c++多态的案例分析
近期在研究c++中多态的应用 ,当中遇到些许的疑问与问题,可是终于的结果是不容置疑的,以下记录下我的学习过程,以纪念本个知识点. 首先,是从一个案例開始的,题目大意是这种: 设定一个多边形的公共类,然 ...
- SWT的TableVierer的使用一
1,简单显示,表格的式样见注释中的内容 import org.eclipse.jface.viewers.TableViewer;import org.eclipse.swt.SWT;import o ...
- PCB板蛇形走线有什么作用
PCB板蛇形走线有什么作用 PCB上的不论什么一条走线在通过高频信号的情况下都会对该信号造成时延时,蛇形走线的主要作用是补偿"同一组相关"信号线中延时较小的部分,这些部分一般是没 ...
- 浅谈TCP优化(转)
很多人常常对TCP优化有一种雾里看花的感觉,实际上只要理解了TCP的运行方式就能掀开它的神秘面纱.Ilya Grigorik 在「High Performance Browser Networking ...
- (23)unity4.6学习Ugui中国文档-------非官方Demo1
大家好,我是广东太阳. 转载请注明出处:http://write.blog.csdn.net/postedit/38922399 更全的内容请看我的游戏蛮牛地址:http://www.unitym ...
- Windows Phone开发(4):框架和页
原文:Windows Phone开发(4):框架和页 在开如之前,我想更正一个小问题,之前我在第一篇文章中说,Visual Studio Express for Windows Phone的中文版没有 ...
- python战斗2:看到一个页面编码
编程环境:虚拟linux (windows 下 cygwin) 识别网页编码. usage: python coding http://www.***.com 測试结果: watermark/2/te ...