上一次看了第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. hdu2062(递推)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2062 详细分析:http://mamicode.com/info-detail-95273.html ...

  2. Java的重载和重写差别(面试常见)

    今天在看C#的基础知识,同是面向对象的语言.看到重载和重写.我突然想了半天.有点模糊了,立即度娘一番.回忆起自己在北京实习的项目,实际上,开发中经经常使用到重载和重写,自己不去总结罢了.今天找了一份比 ...

  3. MySQL 全角转换为半角

    ​序言:       用户注冊时候,录入了全角手机号码,所以导致短信系统依据手机字段发送短信失败.如今问题来了,怎样把全角手机号码变成半角手机号码? 1.手机号码全角转换成半角先查询出来全角半角都存在 ...

  4. Java自学资料——线程

    [转]传智播客成都java培训中心学员笔记. 线程: static int MAX_PRIORITY 线程能够具有的最高优先级. static int MIN_PRIORITY 线程能够具有的最低优先 ...

  5. UVA12304 2D Geometry 110 in 1! 计算几何

    计算几何: 堆几何模版就能够了. . .. Description Problem E 2D Geometry 110 in 1! This is a collection of 110 (in bi ...

  6. u-boot 的bootcmd 和bootargs详解,烧写分析

    下面链接这篇文章也非常重要,介绍DM3X的一系列烧写步骤和设置方法 http://www.61ic.com/Article/DaVinci/TMS320DM3x/201204/41827.html U ...

  7. hdu4336压缩率大方的状态DP

    Card Collector Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  8. Jsoup 抓取和数据页 认识HTTP头

    推荐一本书:黑客攻防技术宝典.Web实战篇  :       顺便留下一个疑问:能否通过jsoup大量并发訪问web或者小型域名server,使其瘫痪?其有用jsoup熟悉的朋友能够用它解析url来干 ...

  9. SQL SERVER IN参数化处理

    方法一. CREATE TABLE [dbo].[Users] ( Id INTEGER IDENTITY(1, 1) PRIMARY KEY , Name NVARCHAR(50) NOT NULL ...

  10. PL/SQL“ ORA-14551: 无法在查询中执行 DML 操作”解决

    环境 Oracle 11.2.0 + SQL Plus 问题 根据以下要求编写函数:将scott.emp表中工资低于平均工资的职工工资加上200,并返回修改了工资的总人数.PL/SQL中有更新的操作, ...