函数表达式的基本语法形式

var functionName = function(arguments){
//函数体
}

递归建议

我们通过例子来一步步说明,递归的最佳实现方式。下面是普通递归调用的例子:

// 阶乘的递归函数
var factorial = function(num){
if(num <= 1){
return 1;
}else {
return num * factorial(num-1);
}
} console.log(factorial(3)); //6

我们来看一下这种情况:

// 赋给一个变量
var anotherFactorial = factorial;
factorial = null; // 调用递归函数
console.log(anotherFactorial(3));

运行结果:

TypeError: factorial is not a function

这里提示错误,说factorial不是一个函数,因为我们已经把factorial设置为null,而在执行 anotherFactorial(3) 时,是通过factorial(num-1) 来递归调用的,所以就报错,因为已经把factorial设置为null。

解决的策略就是使递归调用的函数内部不要出现外部定义的函数名。

我们可以通过命名表达式来实现(注意括号的使用):

var factorial = (function f(num){
if(num <= 1){
return num;
}else {
return num * f(num-1);
}
}); console.log(factorial(3)); //6 var anotherFactorial = factorial;
factorial = null; console.log(anotherFactorial(3)); //6

理解匿名函数

匿名函数:function后面没有跟着函数名的函数

我们常见的匿名函数有函数表达式:

var functionName = function(arguments){
//函数体
}

当然匿名函数也可以不要赋值给变量:

(function(num){
console.log(num+1);
})(3);

运行结果:

4

匿名函数也可以作为函数返回:

function person(){
return function(){
console.log("TabWeng");
}
}
person()();

运行结果:

TabWeng

对于 person()(),有些人不太理解,其实不难理解,这里解释一下:

首先来看一下 person(),我们是不是得到一个返回的匿名函数 function(){console.log("TabWeng");},既然我们得到了一个函数,对于函数的调用,是不是给它的尾部加一个括号就可以了,所以就写成person()()

理解闭包

闭包:有权访问另一个函数作用域的函数

匿名函数是function后面没有跟着函数名的函数,和闭包的定义不同,尽管这两个称呼经常说的是同一个函数,但是根据功能的不同,应该加以区分。

如果你理解作用域链,那么闭包就非常好理解。通过闭包的这种特性,我们可以来实现JavaScript的很多模式,更加灵活的运用JavaScript。

如果要访问一个函数的作用域,我们可以在函数里面创建一个闭包,对于闭包而言,被访问的函数处在闭包作用域链的第二层(从前端到终端的顺序),而作用域链的指针指向的是整个活动对象。

有一个典型的例子不得不讲:

function printNum(){

  var nums = [];
for(var i = 0; i < 3; i++){
nums[i] = function(){
return i;
}
} for(var j = 0; j < nums.length; j++){
console.log(nums[j]());
}
} printNum();

运行结果:

3
3
3

为什么会得到这样的结果,存在两个疑问:

  1. 为什么结果是3?不应该是2吗?
  2. 为什么结果都是3?

我们先来分析一下出现的原因:

首先针对问题2,在闭包里面有return i;,而在闭包中,i是没有定义的,根据作用域链,会向上一层作用域链寻找i,在上一层中我们发现了i,我们是通过寻找上一层的活动对象来找到i的,既然是活动对象,里面的数值就是最终的值,所有此时的i已经是最终的值3了,我获得的i就是最终的值3。因此打印出来每个结果都是3。

知道了问题2出现的原因,那么问题1也就是自然明了,在for循环中,i加到等于3,通过i<3,使i没有进入for循环的里面,尽管 i==3 没有进入for循环,但是闭包在获取i的时候,获取的就是i的最后一个值3。

通过立即执行函数来解决这个问题:

function printNum(){

  var nums = [];
for(var i = 0; i < 3; i++){
nums[i] = function(num){
return num;
}(i);
} for(var j = 0; j < nums.length; j++){
console.log(nums[j]);
}
} printNum();

运行结果:

0
1
2

性能优化

使用闭包时,因为闭包的作用域链会引用活动对象,使这个活动对象无法被回收(内存),而如果这个引用一直存在,那么内存将一直无法得到释放。通常的解决方法是解除这个引用。(解除引用的方法很多,主要是要有这个性能优化的思想,知道存在的问题)。

闭包的运用

(function(){
//私有作用域
})();

看这块代码,之所以称为私有,通过作用域链我们可以知道,外部不能访问里面。

而通过闭包可以实现提供公有方法而使外部能对私有变量进行访问。

function Person(name){
var name = name;
var sayName = function(){
console.log(name);
}; this.publicSayName = function(){
console.log("我访问了name这个属性了:"+name);
sayName();
};
} var p1 = new Person("TabWeng");
p1.publicSayName();

运行结果:

我访问了name这个属性了:TabWeng
TabWeng

这块代码也可以这样写:

function Person(name){
var name = name;
var sayName = function(){
console.log(name);
}; return function(){
console.log("我访问了name这个属性了:"+name);
sayName();
};
} var p1 = new Person("TabWeng");
p1();

运行结果:

我访问了name这个属性了:TabWeng
TabWeng

通过返回的匿名函数获得私有变量。

当然,也可以这样写:

function Person(name){
var name = name;
var sayName = function(){
console.log(name);
}; return {
printString:"我访问了name这个属性了:"+name,
sayName:sayName,
publicMethod:function(){
sayName();
}
};
} var p1 = new Person("TabWeng");
console.log(p1.printString);
p1.sayName();
p1.publicMethod();

运行结果:

我访问了name这个属性了:TabWeng
TabWeng
TabWeng

这就是模块模式,返回的是一个对象,尽管是对象字面量的形式,也可以得到私有变量。

参考

  • 《JavaScript高级程序设计》

JavaScript函数表达式的更多相关文章

  1. JavaScript 函数表达式

    JavaScript中创建函数主要有两种方法:函数声明和函数表达式.这两种方式都有不同的适用场景.这篇笔记主要关注的是函数表达式的几大特点以及它的使用场景,下面一一描述. 主要特点 可选的函数名称 函 ...

  2. JavaScript函数表达式、闭包、模仿块级作用域、私有变量

    函数表达式是一种非常有用的技术,使用函数表达式可以无需对函数命名,从而实现动态编程.匿名函数,是一种强大的方式,一下总结了函数表达式的特点: 1.函数表达式不同于函数声明,函数声明要求有名字,但函数表 ...

  3. JavaScript函数表达式与函数声明

    什么是函数? 函数是事件驱动或者被调用时执行的重复代码块. 作用域: 1. 全局作用域 2. 函数作用域(局部作用域) var i = 100; //全局作用域 function fun(){ var ...

  4. javascript-函数声明和函数表达式-call-apply

    1.函数声明与函数表达式 <script type="text/javascript"> //函数表达式,解析器在像执行环境中加载数据时,函数表达式是解析器执行到这段代 ...

  5. 深入理解javascript系列(4):立即调用的函数表达式

    本文来自汤姆大叔 前言 大家学JavaScript的时候,经常遇到自执行匿名函数的代码,今天我们主要就来想想说一下自执行. 在详细了解这个之前,我们来谈了解一下“自执行”这个叫法,本文对这个功能的叫法 ...

  6. 一步步学习javascript基础篇(6):函数表达式之【闭包】

    回顾前面介绍过的三种定义函数方式 1. function sum (num1, num2) { return num1 + num2; }  //函数声明语法定义 2. var sum = funct ...

  7. JavaScript 函数声明,函数表达式,匿名函数,立即执行函数之区别

    函数声明:function fnName () {-};使用function关键字声明一个函数,再指定一个函数名,叫函数声明. 函数表达式 var fnName = function () {-};使 ...

  8. 详解Javascript 函数声明和函数表达式的区别

    Javascript Function无处不在,而且功能强大!通过Javascript函数可以让JS具有面向对象的一些特征,实现封装.继承等,也可以让代码得到复用.但事物都有两面性,Javascrip ...

  9. JavaScript中的函数表达式

    在JavaScript中,函数是个非常重要的对象,函数通常有三种表现形式:函数声明,函数表达式和函数构造器创建的函数. 本文中主要看看函数表达式及其相关的知识点. 函数表达式 首先,看看函数表达式的表 ...

随机推荐

  1. .Net 中的反射(查看基本类型信息) - Part.2

    反射概述 和Type类 1.反射的作用 简单来说,反射提供这样几个能力:1.查看和遍历类型(及其成员)的基本信息和程序集元数据(metadata):2.迟绑定(Late-Binding)方法和属性.3 ...

  2. AE开发中栅格图层实现分级渲染

    GP工具IDW执行后,生成的栅格图层是黑白二色,需要手动进行分级渲染,似乎不是所有栅格图层都可以进行分级渲染,注意异常处理.注意ARCMAP中是有颜色的,无需自己处理. IRasterClassify ...

  3. JSP分页显示

    首先要定义四个变量: int pageSize: //每页显示多少条记录 int pageNow: //希望显示第几页 int pageCount: //一共有多少页 int rowCount: // ...

  4. C和指针 第十二章 使用结构和指针 双链表和语句提炼

    双链表中每个节点包含指向当前和之后节点的指针,插入节点到双链表中需要考虑四种情况: 1.插入到链表头部 2.插入到链表尾部 3.插入到空链表中 4.插入到链表内部 #include <stdio ...

  5. iOS界面跳转的一些优化方案

    原文地址: http://blog.startry.com/2016/02/14/Think-Of-UIViewController-Switch/ iOS界面跳转的一些优化方案 App应用程序开发, ...

  6. js的包管理工具bower安装

    bower需要:node 和 git node安装包下载:http://blog.csdn.net/myan/article/details/2028545 Git安装: 选择第二项:Use Git ...

  7. CentOS防火墙iptables的配置方法详解

    CentOS系统也是基于linux中的它的防火墙其实就是iptables了,下面我来介绍在CentOS防火墙iptables的配置教程,希望此教程对各位朋友会有所帮助. iptables是与Linux ...

  8. SET基本数据类型

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAO4AAADZCAIAAACo85tgAAAgAElEQVR4Aey9SdAs13XnV/P8jW8e8D

  9. poj1001_Exponentiation_java高精度

    Exponentiation Time Limit: 500MS   Memory Limit: 10000K Total Submissions: 162918   Accepted: 39554 ...

  10. firefox

    ctrl  l            定位到地址栏 f6            同ctrl  l ctrl  k            定位到搜索框 shift  delete        删除地址 ...