好程序员技术文档HTML5开发中的javascript闭包

  好程序员技术文档HTML5开发中的javascript闭包,事实上,通过使用闭包,我们可以做很多事情。比如模拟面向对象的代码风格;更优雅,更简洁的表达出代码;在某些方面提升代码的执行效率,同时避免对命名空间的污染,最重要的是可以从一个域中取出原本访问不到的变量去使用。

  函数的作用域:

  1.变量的声明会提前。

  2.全局变量和局部变量。

  3.函数作用域的生命周期。

  变量的声明

  tt='ee';

  function b(){

  function test(){

  alert(tt);

  var tt='ff';

  }

  test()

  }

  b()

  第一段函数返回的是一个undefined, 因为变量的声明会提前相当于

  var tt='123'

  function test(){

  var tt;

  alert(tt);

  tt='ff';

  }

  2.

  tt='ee';

  function b(){

  function test(){

  alert(tt);

  /var tt='ff';/

  }

  test()

  }

  b()

  变量作用域

  1.function outFn(){

  function innerFn(){

  var inner=0;

  inner++;

  console.log('inner='+inner)

  }

  return innerFn;

  }

  var fn1=outFn();

  fn1();

  fn1();

  var fn2=outFn();

  fn2();

  fn2();

  //每当这个内部函数被调用的时候,都会重新生命一个inner变量,然后让这个变量自增。

  2.

  var inner=0;

  function outFn(){

  function innerFn(){

  inner++;

  console.log('inner='+inner)

  }

  return innerFn;

  }

  var fn1=outFn();

  fn1();

  fn1();

  var fn2=outFn();

  fn2();

  fn2();

  //每当内部函数被调用时,都会让全局变量inner自增。

  如果让这个函数变成父元素的局部变量,会是什么结果?

  3.

  function outFn(){

  var inner=0;

  function innerFn(){

  inner++;

  console.log('inner='+inner)

  }

  return innerFn;

  }

  var fn1=outFn();

  fn1();

  fn1();

  var fn2=outFn();

  fn2();

  fn2();

  一段概念:

  这次的结果可能会出人预料,因为当内部函数在定义它的作用域的外部被引用的时候,就创建了改内部函数的一个闭包,这种情况下我们称既不是局部变量也不是其参数的变量为自由变量,称内部函数的调用环境为封闭闭包的环境。从本质上将,如果内部函数引用了位于外部函数中的变量,相当于授权该变量能够被延迟使用。因此,当外部函数调用完成后,这些变量的内存不会被释放,因为闭包仍然需要使用它们。

  闭包

  一句话理解闭包: JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。

  一个故事理解闭包

  我的年龄是秘密,你想知道。

  但是每次我都含糊其辞的对你说 undefined;

  为了防止我自己也忘记或搞错自己的年龄,我办了一张身份证,上面记录我的年龄信息,藏在我家里。

  你知道了这件事,为了得到我的年龄,决定对我投其所好,

  于是你送我一只逗比间谍猫。

  作为感谢我给了你一把我家的钥匙,方便你有空来看猫。

  这只猫实在太神奇了,每次都能找到我的身份证,并把信息传递给你。

  于是你每次想知道我的年龄的时候就来看猫,

  然后间谍猫每次都能把我的最新的年龄信息反馈给你。

  var day=0;

  var timer=setInterval("dayChanges()",(246060*1000)); //定时器,

  function dayChanges(){

  day++;

  } //每过24小时 次函数运行一次,我的年龄又多了一天

  //------------ 下面的代码就是一个比较常见的闭包。

  function isMyHome(){ //我家

  var myAge=0; //我的身份证信息

  if(day%365==0){ //我的年龄变化

  myAge+=1;

  }

  function myHomeKey(){ // 我家钥匙

  return myAge; //return 就是间谍猫

  }

  return myHomeKey; //给你我家的钥匙。

  }

  var key=isMyHome(); //你拿到我家钥匙

  var you=key(); //得到年龄。

  匿名函数:

  (functoin( ){

  alert( 2 )

  })()

  特性:1.自调用

  2.参数传入方式;

  闭包的使用方法:

  1.

  function outFn(){

  console.log('我是外部函数');

  function innerFn(){

  console.log('我是内部函数');

  }

  innerFn()

  }

  outFn();

  //可以在外部函数调用到内部函数,但是并不能在外部调用内部函数。这种技术适合用于小型,单用途的函数。

  2.在任何地方调用内部函数,通过一个全局变量可以访问到内部函数;

  var global;

  function outFn(){

  console.log('我是外部函数')

  function innerFn(){

  console.log('我是内部函数')

  }

  global=innerFn;

  }

  outFn();

  global();

  3.一个最简单的闭包,可以迅速调用,并且不会污染全局变量。

  var data = {

  table : [1,2,3,4,5],

  tree : {}

  };

  (function(data){

  alert(data.table)

  //build dm.tree

  })(data);

  4.可以通过对变量进行封装实现闭包。

  var obj=(function(){

  var name='找不见的名字';

  return {

  getName:function(){

  return name;

  },

  setName:function(newName){

  return newName

  }

  }

  })()

  obj.getName()

  高阶函数英文叫Higher-order function。那么什么是高阶函数?

  JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

  一个最简单的高阶函数:

  function add(x, y, f) {

  return f(x) + f(y);

  }

  当我们调用add(-5, 6, Math.abs)时,参数x,y和f分别接收-5,6和函数Math.abs,根据函数定义,我们可以推导计算过程为:

  x = -5;

  y = 6;

  f = Math.abs;

  f(x) + f(y) ==> Math.abs(-5) + Math.abs(6) ==> 11;

  return 11;

  用代码验证一下:

  add(-5, 6, Math.abs); // 11

  编写高阶函数,就是让函数的参数能够接收别的函数

  1. 高阶函数map

  举例说明,比如我们有一个函数f(x)=x2,要把这个函数作用在一个数组[1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map实现如下:

  由于map()方法定义在JavaScript的Array中,我们调用Array的map()方法,传入我们自己的函数,就得到了一个新的Array作为结果:

  function pow(x) {

  return x * x;

  }

  var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

  arr.map(pow); // [1, 4, 9, 16, 25, 36, 49, 64, 81]

  map()传入的参数是pow,即函数对象本身。

  你可能会想,不需要map(),写一个循环,也可以计算出结果:

  var f = function (x) {

  return x * x;

  };

  var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

  var result = [];

  for (var i=0; i

  result.push(f(arr[i]));

  }

  的确可以,但是,从上面的循环代码,我们无法一眼看明白“把f(x)作用在Array的每一个元素并把结果生成一个新的Array”。

  所以,map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,比如,把Array的所有数字转为字符串:

  var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];

  arr.map(String); // ['1', '2', '3', '4', '5', '6', '7', '8', '9']

  只需要一行代码。

  2. 高阶函数reduce

  再看reduce的用法。Array的reduce()把一个函数作用在这个Array的[x1, x2, x3...]上,这个函数必须接收两个参数,reduce()把结果继续和序列的下一个元素做累积计算,其效果就是:

  [x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)

  比方说对一个Array求和,就可以用reduce实现:

  var arr = [1, 3, 5, 7, 9];

  arr.reduce(function (x, y) {

  return x + y;

  }); // 25

  练习:利用reduce()求积。

  练习:把[1, 3, 5, 7, 9]变换成整数13579,用reduce()。

  练习:请用map()把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:['adam', 'LISA', 'barT'],输出:['Adam', 'Lisa', 'Bart']。

  3. 高阶函数filter

  filter也是一个常用的操作,它用于把Array的某些元素过滤掉,然后返回剩下的元素。

  和map()类似,Array的filter()也接收一个函数。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是true还是false决定保留还是丢弃该元素。

  例如,在一个Array中,删掉偶数,只保留奇数,可以这么写:

  var arr = [1, 2, 4, 5, 6, 9, 10, 15];

  var r = arr.filter(function (x) {

  return x % 2 !== 0;

  });

  r; // [1, 5, 9, 15]

  把一个Array中的空字符串删掉,可以这么写:

  var arr = ['A', '', 'B', null, undefined, 'C', ' '];

  var r = arr.filter(function (s) {

  return s && s.trim(); // 注意:IE9以下的版本没有trim()方法

  });

  r; // ['A', 'B', 'C']

  可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数。

  练习:请使用filter()筛选出一个数组中所有的素数

  函数作为返回值

  高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

  我们来实现一个对Array的求和。通常情况下,求和的函数是这样定义的:

  function sum(arr) {

  return arr.reduce(function (x, y) {

  return x + y;

  });

  }

  sum([1, 2, 3, 4, 5]); // 15

  但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数!

  function lazy_sum(arr) {

  var sum = function () {

  return arr.reduce(function (x, y) {

  return x + y;

  });

  }

  return sum;

  }

  当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:

  var f = lazy_sum([1, 2, 3, 4, 5]); // function sum()

  调用函数f时,才真正计算求和的结果:

  f(); // 15

  在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

  请再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

  var f1 = lazy_sum([1, 2, 3, 4, 5]);

  var f2 = lazy_sum([1, 2, 3, 4, 5]);

  f1 === f2; // false

  f1()和f2()的调用结果互不影响。

  闭包

  注意到返回的函数在其定义内部引用了局部变量arr,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。

  还有一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:

  function count() {

  var arr = [];

  for (var i=1; i<=3; i++) {

  arr.push(function () {

  return i * i;

  });

  }

  return arr;

  }

  var results = count();

  var f1 = results[0];

  var f2 = results[1];

  var f3 = results[2];

  在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都添加到一个Array中返回了。

  你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:

  f1(); // 16

  f2(); // 16

  f3(); // 16

  全部都是16!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了4,因此最终结果为16。

  返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

  如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

  function count() {

  var arr = [];

  for (var i=1; i<=3; i++) {

  arr.push((function (n) {

  return function () {

  return n * n;

  }

  })(i));

  }

  return arr;

  }

  var results = count();

  var f1 = results[0];

  var f2 = results[1];

  var f3 = results[2];

  f1(); // 1

  f2(); // 4

  f3(); // 9

  注意这里用了一个“创建一个匿名函数并立刻执行”的语法:

  (function (x) {

  return x * x;

  })(3); // 9

  理论上讲,创建一个匿名函数并立刻执行可以这么写:

  function (x) { return x * x } (3);

  但是由于JavaScript语法解析的问题,会报SyntaxError错误,因此需要用括号把整个函数定义括起来:

  (function (x) { return x * x }) (3);

  通常,一个立即执行的匿名函数可以把函数体拆开,一般这么写:

  (function (x) {

  return x * x;

  })(3);

  说了这么多,难道闭包就是为了返回一个函数然后延迟执行吗?

  当然不是!闭包有非常强大的功能。举个栗子:

  在面向对象的程序设计语言里,比如Java和C++,要在对象内部封装一个私有变量,可以用private修饰一个成员变量。

  在没有class机制,只有函数的语言里,借助闭包,同样可以封装一个私有变量。我们用JavaScript创建一个计数器:

  'use strict';

  function create_counter(initial) {

  var x = initial || 0;

  return {

  inc: function () {

  x += 1;

  return x;

  }

  }

  }

  它用起来像这样:

  var c1 = create_counter();

  c1.inc(); // 1

  c1.inc(); // 2

  c1.inc(); // 3

  var c2 = create_counter(10);

  c2.inc(); // 11

  c2.inc(); // 12

  c2.inc(); // 13

  在返回的对象中,实现了一个闭包,该闭包携带了局部变量x,并且,从外部代码根本无法访问到变量x。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。

  闭包还可以把多参数的函数变成单参数的函数。例如,要计算xy可以用Math.pow(x, y)函数,不过考虑到经常计算x2或x3,我们可以利用闭包创建新的函数pow2和pow3:

  function make_pow(n) {

  return function (x) {

  return Math.pow(x, n);

  }

  }

  // 创建两个新函数:

  var pow2 = make_pow(2);

  var pow3 = make_pow(3);

  pow2(5); // 25

  pow3(7); // 343

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/279760.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

亚马逊echo中国使用_如何使用亚马逊的主要照片备份所有照片

亚马逊echo中国使用Millions of people are Amazon Prime subscribers, but many of them don’t realize that in addition to free shipping and Prime Instant Video, they also get unlimited photo storage for all their computers and mobile devices. 数以百万计的人是…

抽象SQL查询:SQL-MAP技术的使用

什么是参数化查询&#xff1f;我们来看百度百科对此的定义和示例&#xff1a; 一&#xff0c;定义 ------------------------------------------------------------------ 参数化查询&#xff08;Parameterized Query 或 Parameterized Statement&#xff09;是指在设计与数据库…

EF ORM

//新增UserInfo userInfo new UserInfo();userInfo.UserName "YANG";userInfo.UserPass "123";userInfo.Email "253qq.com";userInfo.RegTime System.DateTime.Now;Model1Container db new Model1Container();db.UserInfoSet.Add(userInfo…

如何使用echo.js实现图片的懒加载(整理)

如何使用echo.js实现图片的懒加载&#xff08;整理&#xff09; 一、总结 一句话总结&#xff1a;a、在img标签中添加data-echo属性加载真实图片&#xff1a;<img class"loading" src"blank.gif" data-echo"图片真实路径" />&#xff0c;在…

还原出厂设置 擦除frp_如何备份,擦除和还原Apple Watch

还原出厂设置 擦除frpThe Apple Watch is, in its own right, a little tiny computer with data backup and security needs. Read on as we show you how to ensure your Apple Watch is backed up, wiped, and restored just like you’d do with your smartphone. Apple Wa…

exchange 2010 search mailbox 的幕后强大功能

铃……….半夜中被一阵急促的手机铃声吵醒&#xff0c;年度服务客户打来电话需要进行邮件的排查和删除工作。问其原因&#xff0c;原来是组织中有人发了一封关于领导的不健康的邮件&#xff0c;并在企业内部进行了转发&#xff0c;领导要求立即找出此类邮件并进行删除。管理员深…

Apache HBase的现状和发展

一、&#xff28;Base是什么 HBase(Hadoop Database)&#xff0c;是一个基于Google BigTable论文设计的高可靠性、高性能、可伸缩的分布式存储系统。 它有以下特征&#xff1a; 1.HBase仍然是采用行存储的&#xff0c;采用松散表的结构来获得动态列的功能&#xff1b; 2.原生海…

Java的接口、继承与多态

接口 java只支持单继承&#xff0c;即一个类只能有一个父类&#xff0c;因此需要接口来实现多重继承。 接口的定义 类和接口的区别&#xff1a;一个类通过继承接口的方式&#xff0c;从而来继承接口的抽象方法。类描述对象的属性和方法&#xff0c;接口则包含类要实现的方法。 …

dvd刻录软件_如何在Windows 7中刻录照片和视频DVD(无需额外的软件)

dvd刻录软件Software like DVD Flick is great for burning video to DVDs, but Windows 7 actually includes built-in DVD burning software. Strangely, it’s the last time the company did so—while Windows 8 and Windows 10 can play back DVD movies, they can’t cr…

如何实现office不同语言界面切换

前面我讨论了《如何实现win7不同语言界面切换》&#xff0c;很巧今天又有网友问到如何实现 office的语言界面切换呢。嘿&#xff0c;那我们就继续来玩转界面吧。 office2007和office2010也支持轻松的进行语言界面切换&#xff0c;操作步骤也很简单。 Office 语言界面包 (LIP) 是…

Mysql-高可用集群[MyCat中间件使用](三)

服务器-节点: 4台 mysql-主: 192.168.2.40mysql-从-node-0: 192.168.2.41mysql-从-node-1: 192.168.2.42mycat: 192.168.2.45操作过程 1.搭建mysql主从节点2.搭建mycat中间件节点3.mycat服务配置4.测试读写分离,读的分发1.搭建mysql主从节点 Mysql-高可用集群主从单一模式-binl…

yum安装mysql5.6

1.检查系统是否安装其他版本的MYSQL数据 yum list installed | grep mysql yum -y remove mysql-libs.x86_64 2.安装及配置 wget http://repo.mysql.com/mysql-community-release-el6-5.noarch.rpm rpm -ivh mysql-community-release-el6-5.noarch.rpm yum repolist all | grep…

离开互联网行业_如何使用互联网再也不会离开家

离开互联网行业Thanks to the Internet, activities like “going outside” or “being a productive member of the community” are becoming increasingly optional parts of daily life. When your inner hermit feels like putting on his vampire cape, simple tricks l…

iOS 11开发教程(十三)iOS11应用编辑界面添加视图

iOS 11开发教程&#xff08;十三&#xff09;iOS11应用编辑界面添加视图 在iOS中添加视图的方式有两种&#xff1a;一种是使用编辑界面添加视图&#xff1b;另一种是使用代码添加视图。以下是这两个方式的详细介绍。 1.编辑界面添加视图 使用编辑界面添加视图是一个相当简单的工…

限流算法(记录cyc大佬的专栏)

限流的必要性 如果一段时间内请求的数量过大&#xff0c;就会给服务器造成很大压力&#xff0c;可能导致服务器无法提供其它服务。 计数器算法 通过一个计数器 counter 来统计一段时间内请求的数量&#xff0c;并且在指定的时间之后重置计数器。该方法实现简单&#xff0c;但是…

bzoj 1024 [ SCOI 2009 ] 生日快乐 —— 递归

题目&#xff1a;https://www.lydsy.com/JudgeOnline/problem.php?id1024 因为每次把一块切成两块&#xff0c;所以可以枚举从哪里切开&#xff0c;然后递归求解&#xff1b; 一开始用了不太对的贪心思路&#xff0c;想着一定去切较长边&#xff0c;但看来不一定。 代码如下&a…

HBase存储剖析与数据迁移

1.概述 HBase的存储结构和关系型数据库不一样&#xff0c;HBase面向半结构化数据进行存储。所以&#xff0c;对于结构化的SQL语言查询&#xff0c;HBase自身并没有接口支持。在大数据应用中&#xff0c;虽然也有SQL查询引擎可以查询HBase&#xff0c;比如Phoenix、Drill这类。但…

windows os x_如何立即在OS X上获取Windows样式的窗口捕捉

windows os xApple’s recent announcement that the upcoming OS X release (El Capitan or 10.11) will finally, at long last, come with the ability to snap windows to your screen edges. A feature Windows users have enjoyed since 2009. 苹果公司最近宣布即将发布的…

Install Odoo 11 on CentOS 7

2019独角兽企业重金招聘Python工程师标准>>> Odoo is the most popular all-in-one business software in the world. It offers a range of business applications including CRM, website, e-Commerce, billing, accounting, manufacturing, warehouse, project m…

创建maven项目,配置maven地址

2019独角兽企业重金招聘Python工程师标准>>> 在eclipse中&#xff0c;新建maven项目next第二步&#xff0c;选择webapp输入id及包名&#xff0c;完成新建给项目build path&#xff0c;添加1.8版本jdk和tomcat项目右键属性 project Facets&#xff0c;切换成web项目&…