上一次看了第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. 一步一步学android之事件篇——单选按钮监听事件

    在平常使用软件的时候,我们经常会碰见一些选择题,例如选择性别的时候,在男和女之间选,前面说过这个情况要用RadioGroup组件,那么点击了之后我们该怎么获取到选择的那个值呢,这就是今天要说的OnCh ...

  2. cocos2dx使用tolua关于字符串处理的一个问题

    正在使用cocos2dx的tolua binding在此过程中发现的一个问题.假设一回或输入是std::string当我们不同意包括二进制数据,和std::string我同意,这样一来就导致了不正确的 ...

  3. Java 兑换ObjectC代码

    现在非常多app发展,server我们使用Java发展,实时数据交换,与Android非常easy实现.是否xml依然是json,它可以很容易地转换成一个对象.这可以是硬IOS该程序猿.它们应根据数据 ...

  4. As long as Binbin loves Sangsang

    题目连接 题意: 给定一个无向图,每一个边有两个属性.长度和一个字母'L','O','V'.'E'中的一个.从1点開始到达n点,每次必须依照L -> O -> V -> E -> ...

  5. Windows phone 8 学习笔记(3) 通信

    原文:Windows phone 8 学习笔记(3) 通信 Windows phone 8 可利用的数据通信方式比较广泛,在硬件支持的前提下,我们可以利用WiFi.蓝牙.临近感应等多种方式.数据交互一 ...

  6. html网页特殊符号代码

    HTML特殊字符编码大全:往网页中输入特殊字符,需在html代码中加入以&开头的字母组合或以&#开头的数字.下面就是以字母或数字表示的特殊符号大全.                   ...

  7. Codeforces Round#297 div2

    B: 题意:给定一个字符串,然后给定m个数字 对于每个数字ai的含义是,将ai到n-ai+1的字符串给翻转一遍. 要求输出m次翻转之后的字符串. 想法就是判断第i个位置的字符是翻转了奇数次,还是偶数次 ...

  8. Apache介绍

    怎样使用Apache许可证         若用户须要应用Apache许可证,请将下面演示样例使用适当的注视方法包括在作品源文件里,将括号"[]"中的字段以用户自身的区分信息来替换 ...

  9. Java并发学习之中的一个——线程的创建

    本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.与每一个Java语言中的元素一样,线程是对象.在Java中,我们有两种方式创建线程: a.通过直接继承thread类,然后覆盖run方法. b ...

  10. Python学习路径8——Python对象2

    1.标准型运营商 1.1对象值对照 比较运算符用于如果相同类型的对象是相等.所有的内建类型的是在比较操作中支持,返回布尔比较操作值True 或 False. <span style=" ...