当前位置: 首页 > news >正文

JavaScript函数表达式

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

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高级程序设计》

转载于:https://www.cnblogs.com/hlwyfeng/p/6082210.html

相关文章:

  • lolcat彩虹色输出文本
  • Bootstrap学习笔记系列5------Bootstrap图片显示
  • 二.第十一单元 系统恢复
  • linux学习笔记8
  • MarkdownPad2.5 注册码
  • Git 菜鸟记录
  • DTMF三种模式(SIPINFO,RFC2833,INBAND)
  • Oracle学习(一):Oracle数据库基础
  • js 单例
  • Nginx 指定不产生日志类型(不记录图片日志)
  • php对二维数组求差集
  • uplodidy代码
  • jQery简单Tab选项卡效果
  • 用linux mail命令发送邮件时指定发送人
  • SMTP补充
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • create-react-app做的留言板
  • Debian下无root权限使用Python访问Oracle
  • ES学习笔记(12)--Symbol
  • javascript 哈希表
  • js 实现textarea输入字数提示
  • LeetCode18.四数之和 JavaScript
  • Median of Two Sorted Arrays
  • mongo索引构建
  • mysql常用命令汇总
  • node-glob通配符
  • Swift 中的尾递归和蹦床
  • TypeScript实现数据结构(一)栈,队列,链表
  • 安卓应用性能调试和优化经验分享
  • 前端性能优化--懒加载和预加载
  • 让你的分享飞起来——极光推出社会化分享组件
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 大数据全解:定义、价值及挑战
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • # Panda3d 碰撞检测系统介绍
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • ( 10 )MySQL中的外键
  • (12)Linux 常见的三种进程状态
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (附源码)ssm高校升本考试管理系统 毕业设计 201631
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (一)VirtualBox安装增强功能
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • (转)程序员疫苗:代码注入
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • .NET 8 中引入新的 IHostedLifecycleService 接口 实现定时任务
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .net 简单实现MD5
  • .net 设置默认首页
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .Net6支持的操作系统版本(.net8已来,你还在用.netframework4.5吗)
  • .Net调用Java编写的WebServices返回值为Null的解决方法(SoapUI工具测试有返回值)