javascript 实现加法分离。 plus(3)(4); // => 得到 7
原文地址:http://cnodejs.org/topic/5230d5f0101e574521c86ff4
JavaScript 的设计是典型的函数式的编程范式
匿名函数
JSON数据本身就是字符串,经过JSON.parse才转化为javascript对象,你在console中输入的是对象,而不是JSON字符串。另外对象的key也可看作是字符串,例如一个对象{1:1},其中的属性名——1实际上是string类型,属性值——1则是number类型
什么是匿名函数?就是没有名字的函数。上一篇文章中举的例子:
setTimeout(function () {
// some code...
}, 1000);
其中的function
就是一个匿名函数(因为它没有名字)
如果这么写:
setTimeout(function something () {
// some code...
}, 1000);
或者这么写:
var something = function () {
// some code...
};
setTimeout(something, 1000);
这样就给这个函数起名为something
,所以就不是匿名函数了。而且,因为有了名字,我们就可以在别的地方,比如函数内部调用它,从而实现代码的复用或者递归调用。
**注意:**上面两种写法虽然都给函数起名为something
,但是这个名字的作用域不同,倒数第二种写法把something
的作用域限制在了他本身,也就是只能在函数内部递归调用,而倒数第一种写法中something
的作用域就是最外部的作用域,这也是setTimeout
可以把它作为参数的原因。
所以,匿名函数并没有什么神奇,和有名字的函数没什么本质区别。不过,按照Felix’s Node.js Style Guide的推荐,最好给大多数函数都起上名字,这样有利于代码的维护。也就是按照上面倒数第二种写法。
函数自调用
所谓的函数自调用,就是在定义函数的时候直接调用。
比如:
var result = function (x) { return x + 1; }(3); // => result 的值为4
这段代码的本质是:
var plusOne = function (x) {
return x + 1;
};
var result = plusOne(3);
这样写就很清楚了,我们先定义了一个函数plusOne
,然后调用它,并提供实参「3」。
而上面的自调用的写法就是把这两件事放在一起完成了。
通过function (x) { return x + 1; }
这一段代码,相当于写了一个「函数字面量」,而后面紧接着的(3)
就是给这个「函数字面量」一个参数来调用它。
换句话说,所有函数类对象,不论是一个现场定义的匿名函数,还是一个之前定义的有名字的函数,他们在不加括号的时候都代表了这个函数对象本身,而加了括号就代表调用这个函数,也就是这个函数return
的对象。
你可能会问,既然我在定义函数的时候直接调用它,我为什么不直接写函数里的代码呢?
// var result = function (x) { return x + 1; }(3); // => result 的值为4
var result = 3 + 1;
这样不是更好吗?
在有些情况下的确是这样更简洁,但在另一些情况下,我们希望递归调用,或者希望创造一个闭包,就需要使用函数自调用的技巧了。至于什么是闭包,后面会有解释。
返回函数的函数
重温刚才的例子:
var plusOne = function (x) {
return x + 1;
};
var result = plusOne(3);
这里的函数plusOne
返回参数加一以后的值。
实际上一个函数是可以返回任何对象的,我可以给plusOne
再套一层:
var plusOne = function () {
var realFunction = function (x) {
return x + 1;
};
return realFunction; // 这里不能加括号,因为我们要返回`realFunction`函数本身
};
var result = plusOne()(3);
// 等价于
// var newFunction = plusOne();
// var result = newFunction(3);
这个很好理解,plusOne
是第一个真正被调用的函数,通过plusOne()
加括号,我们得到了plusOne
函数return
的对象,也就是realFunction
函数,再通过plusOne()(3)
加括号,我们就得到了realFunction
这个函数传了参数「3」以后的return
的值,也就是4。(注意对于最后一行plusOne()
来说,realFunction
这个名字已经不存在)
我们也可以把x
放在外面:
var plusOne = function (x) {
var realFunction = function () {
return x + 1;
};
return realFunction; // 这里不能加括号,因为我们要返回`realFunction`函数本身
};
var result = plusOne(3)();
// 等价于
// var newFunction = plusOne(3);
// var result = newFunction();
这里,我们相当于先把「3」告诉plusOne
函数,然后由realFunction
负责把plusOne
获得的参数加一,并返回。所以我们需要在第一个括号里传3,第二个括号为空。
利用上面所说的返回函数的函数,我们可以做一些很有趣的事情。
比如,我们想写一个做两个数相加的工作的函数。传统的方法是:
function plus (a, b) {
return a + b;
}
plus(3, 4); // => 得到 7
现在我们可以这么写:
function plus (a) {
return function (b) {
return a + b;
};
}
plus(3)(4); // => 得到 7
这样写的好处在于我可以把加法动作分成两部完成,而不一定要同时知道加号两边的数字。这样就可以做到:
var anotherNumberIs = plus(3);
// some code...
anotherNumberIs(4); // => 得到 7
把一个动作分成两步完成还可以实现一定的私密性。想象一下,我们需要两个密码打开一把锁,但是我们又不希望其中任何一个人知道另一个人的密码。如果我们用类似传统的加法方式实现,那必定会有一个环境,是两个密码的作用域重叠的,比如:
function unlock(pw1, pw2) {
// unlocking
if (pw1 + pw2 === 'niubi') return true;
else return false;
}
var password1 = 'niu';
var password2 = 'bi';
unlock(password1, password2); // => true
而如果我们这么写:
function unlock (pw1) {
return function (pw2) {
if (pw1 + pw2 === 'niubi') return true;
else return false;
};
}
var myLock;
function () {
var password1 = 'niu';
myLock = unlock(password1);
}();
function () {
var password2 = 'bi';
myLock = myLock(password2);
}()
myLock; // => true
就能把两个密码的作用域完全分隔,保证两个密码的独立和安全。当然,这只是一个比喻,真正的安全问题比这复杂的多。
闭包(Closure)
闭包是经常被复杂化的概念。如果你仔细阅读并理解了上面的所有内容,其实你已经明白了闭包是怎么回事。
在上面的锁的例子中,我们用myLock = unlock(password1)
把密码1包在了此时的myLock
函数中,这个函数走到哪里,就把password1
带到哪里。比如带到了这里:myLock = myLock(password2)
。但是在用密码2解锁的时候,这个环境下无法得知myLock
里装着的password1
是什么,却可以把password2
交给myLock
函数来解锁。所以我们称它为「闭包」。
当然闭包不一定要那么复杂。非常粗略地说,如果一个函数包含着一些变量,函数走到哪里变量就走到哪里,但是这个变量从外面无法读取,那这就是一个闭包。
举个最简单的例子:
function incrementClosure () {
var current = 0;
return function () {
current = current + 1;
return current;
};
}
var inc = incrementClosure();
inc(); // => 1
inc(); // => 2
inc(); // => 3
inc(); // => 4
// ......
这就是最简单的一个闭包的例子,current
这个变量会一直跟着inc
函数走,所以每次inc()
都会对这个current
执行加一的操作,但是在调用inc
的时候,完全看不到里面发生了什么。所以使用闭包十分安全。
这时候,我们再回去看刚才的一个例子:
var plusOne = function (x) {
var realFunction = function () {
return x + 1;
};
return realFunction; // 这里不能加括号,因为我们要返回`realFunction`函数本身
};
var result = plusOne(3)();
这里其实也创造了一个闭包,通过plusOne(3)
把「3」这个参数存了下来,之后只要直接加()
就可以给3加1了。
闭包也有不足的地方,那就是这个变量会长期占用内存,因为运行环境不知道哪年哪月又会调用inc()
,所以这个current
就只能一直保留着,以备不时之需。
上面这几个概念都是 JavaScript 中非常重要也很常用的概念,在这里总结一下,供初学者参考。欢迎交流!
评论:
全局变量这个概念是依托作用域的概念的,一个东西即使一直存在内存里,但是当前的上下文无法访问,还是不能称为全局变量。
而且即使父函数不存在了这个current
应该还是会存在的。
比如:
var inc = function () {
var current = 0;
return function () {
current = current + 1;
return current;
};
}();
inc(); // => 1
inc(); // => 2
inc(); // => 3
inc(); // => 4
// ......
欢迎继续讨论~
javascript 实现加法分离。 plus(3)(4); // => 得到 7的更多相关文章
- 使用jquery-tmpl使JavaScript与HTML分离
背景:由于对JavaScript字符串拼接JavaScript变量产生了反感,也想用用JavaScript模板库,看了几个,由于时间原因选择了jQuery.tmpl.js,因为Visual Studi ...
- JavaScript中的加法运算
<head runat="server"> <title>JavaScript实现加法计算器</title> <script type=& ...
- JavaScript笔记(一),
加法函数 javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显 //调用:accAdd(arg1,arg2) //返回值:arg1加上arg2的精确结果 function accA ...
- JavaScript概述.pdf
第1章 JavaScript概述 第2章 使用JavaScript 第3章 语法.关键保留字及变量 第4章 数据类型 第5章 运算符 第6章 流程控制语句 第7章 函数 //没有参数的函数 funct ...
- JavaScript中有关数字的精确计算
问题这样的: 37.5*5.5=206.08 (JS算出来是这样的一个结果,我四舍五入取两位小数) ,我先怀疑是四舍五入的问题,就直接用JS算了一个结果为:206.08499999999998 怎么会 ...
- 38、重新复习javascript之三
前言 虽然吧,每天都没有什么太有技术性的工作者,但是技术不能丢,希望也要有,人如果没有希望那不就和咸鱼一样了吗?小伙加油吧 1.html与javascript结合 <!DOCTYPE html ...
- 精彩的javascript对象和数组混合相加
最近遇到一个让人困解的一个问题:一个简单的js加法运算表达式: +[]; //这里加上一个空数组得到什么???答案:'1'; 为什么答案是1,一开始我也很困惑:后来我读了一篇文章才知道:在javasc ...
- JavaScript事件---事件入门
内容提纲: 1.事件介绍 2.内联模型 3.脚本模型 4.事件处理函数 JavaScript事件是由访问Web页面的用户引起的一系列操作,例如:用户点击.当用户执行某些操作的时候,再去执行一系列代码. ...
- javascript中的cookie,以及事件解析
Cookie: 它的意思是在本地的客户端的磁盘上以很小的文件形式保存数据,Cookie的处理原则上需要在服务器环境下运行,目前Chrome不可以在客户端操作Cookie,其他浏览器均可以, Coo ...
随机推荐
- codeforces DIV2 D 最短路
http://codeforces.com/contest/716/problem/D 题目大意:给你一些边,有权值,权值为0的表示目前该边不存在,但是可以把0修改成另外一个权值.现在,我们重新建路, ...
- Android ADT安装时卡在Calculating requirements and dependencies
AndroidSDK及Eclipse安装都很顺利,但是在Eclipse下安装ADT插件时,先采用点击Help->installnew software->Add...,无论输入https: ...
- 关于web.xml的格式
先是filter 再是<filter-mapping> 然后<servlet> 再是<servlet-mapping> 这是一种规范基于j2ee 在开发的过程中一 ...
- 安装MSYS2过程遇到的问题及解决记录
1.在安装结束后按照官方教程开始更新系统是遇到了如下的错误 could not open file /var/lib/pacman/sync/msys32.db: Unrecognized archi ...
- java解析xml文件并输出
使用java解析xml文件,通过dom4j,代码运行前需先导入dom4j架包. ParseXml类代码如下: import java.io.File; import java.util.ArrayLi ...
- Android OpenGL ES(二)OpenGL ES管道(Pipeline) .
大部分图形系统都可以比作工厂中的装配线(Assemble line)或者称为管道(Pipeline).前一道的输出作为下道工序的输入.主CPU发出一个绘图指令,然后可能由硬件部件完成坐标变换,裁剪,添 ...
- Servlet Filter 过滤器
Filter也称之为过滤器,它是Servlet技术中最激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源: 例如Jsp, Servlet, 静态图片文件或静态 ht ...
- mysql 字段的类型有哪些
int型包括(tinyint, smallint, mediumint, int, bigint) tinyint是1个字节表达范围就是2的8次方(-128-128) 或者(0-255) 很多人不明白 ...
- ElasticSearch — 集群搭建
1.es需要java环境,故先检查java环境是否正常 2.下载elasticsearch安装包 http://www.elasticsearch.org/download/ 目前最新版本到1.4.0 ...
- 解析GenericOptionsParser
hadoop源代码分析(4)-org.apache.hadoop.util包-GenericOptionsParser类[原创] 一 准备 hadoop版本:1.0.3,GenericOptio ...