上一次看了第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高级程序设计》三:函数,闭包,作用域的更多相关文章

  1. Javascript高级程序设计——执行环境与作用域

    Javascript中执行环境是定义了变量或函数有权访问的其他数据,决定了各自的行为,每个执行的环境都有一个与之关联的变量对象,环境中定义的所以变量和函数都保存在这个对象中. 全局执行环境是最外围的一 ...

  2. Javascript高级程序设计——this、闭包、函数表达式

    在javascript中函数声明会被提升,而函数表达式不会被提升.当函数执行时,会创建一个执行环境和相应的作用域链,然后利用arguments和其他的命名参数的值来初始化函数的活动对象,作用域链链中所 ...

  3. javascript 高级程序设计 三

    Sorry,前两张介绍的主题还是JavaScript,而第一章介绍了JavaScript和ECMAScript区别,所以前两章介绍的主题应该改为ECMAScript,但是 标题就不改了因为现在人们习惯 ...

  4. JavaScript高级程序设计(三):基本概念:数据类型

    特别注意:ECMAScript是区分大小写的. 一.变量 1.ECMAScript的变量是松散型的.所谓松散型就是可以用来保存任何类型的数据.即每个变量仅仅是一个用于保存值的占位符而已.定义变量时要使 ...

  5. 读书笔记 - javascript 高级程序设计 - 第一章 简介

      第一章 简介   诞生时间 1995 最初用途 客服端验证 第一版标准 注意是标准 1997年 Ecma-262  一个完整的js实现由三部分组成 ECMAScript DOM 文档对象模型 BO ...

  6. JavaScript高级程序设计之函数性能

    setTimeout 比 setInterval 性能更好 // 取代setInterval setTimeout(function self () { // code goes here setTi ...

  7. JavaScript高级程序设计之函数

    函数实际上是对象,每个函数都是Function类型的实例. 函数是引用类型. 函数名实际上是一个指向函数对象的指针,不会与某个函数绑定. // 这种写法更能表达函数的本质 var sum = func ...

  8. 读书笔记-JavaScript高级程序设计(1)

    1.组合继承 (JavaScript 中最常用的继承模式 ) (position: page168) (书中定义了两个变量名 SuperType   SubType  乍一看 感觉不太能区分,我将改为 ...

  9. 读书笔记 - javascript 高级程序设计 - 第二章 在Html中使用JavaScript

    1 <script>的6个属性 async  立即下载当前script标签的外部脚本 但不能影响别的 charset 没用了 defer  文档显示之后再执行脚本,只对外部脚本有效 lan ...

  10. 《JavaScript高级程序设计》 -- 变量、作用域和内存问题(二)

    1.基本类型与引用类型 基本类型:值保存在变量中 (Number.String.Boolean.Undefined.Null).在内存中占据固定大小空间,被保存在栈内存中 引用类型:值是保存在内存中的 ...

随机推荐

  1. Phalcon之 表单(Forms)

    Phalcon中提供了 Phalcon\Forms组件以方便开发人员创建和维护应用中的表单. 以下的样例中展示了主要的用法: <?php use Phalcon\Forms\Form, Phal ...

  2. C++内存管理学习笔记(7)

    /****************************************************************/ /*            学习是合作和分享式的! /* Auth ...

  3. TMG 2010 VPN配置

    微软的ISA 到2006以后就叫TMG了,上周在公司的服务器上安装测试了下,虽然增加了很多功能,但是主要功能上和ISA 2004差不多,最近在部署L2TP VPN,由于防火墙带的远程访问VPN为纯的L ...

  4. poj3140(树的dfs)

    题目链接:http://poj.org/problem?id=3140 题意:给定一棵n棵节点的树,求删去某条边后两个分支的最小差异值. 分析:num[u]表示以u点为根节点的子树的总人数,那么不在该 ...

  5. ThreadSafeClientConnManager的20个例子

    Java Code Examples for org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager The following are ...

  6. Tier和Layer

    在实际开发工作中.我们经常听到"架构设计"和"架构师"这种名词,它并不新奇和神奇,可是却非常少有人对"架构"有全面的了解和认识.更谈不上掌握 ...

  7. Mac OS X在建筑Python科学计算环境

    经验(比如这篇日志:http://blog.csdn.net/waleking/article/details/7578517).他们推荐使用Mac Ports这种软件来管理和安装全部的安装包.依照这 ...

  8. hdu 4107当卡段树

    其核心思想是记录最大的节点值和最低值,假设max<p要么min>=p时间,在节点只变化add值,不要子树遍历:否则,就往子树递归. #include<iostream> #in ...

  9. SIP基本呼叫

    我们首先来看下主要的呼叫流程. INVITEsip:69690067@beijing.chinamobile.com;user=phone SIP/2.0 From:"+8610696900 ...

  10. 使用LabVIEWPC的制备

    使用LabVIEWPC的制备 1.下载.安装LabVIEW 如今,互联网搜索,你可以搜索出一大推LabVIEW下载并安装破解教程.因此,这里没有具体描述的.请自行百度~~.另外.提醒一下,下载LabV ...