今天随手写了一段代码,被@流浪大法师吐槽了,于是整理出了关于一个比较友善的写法以及在这段代码中的思考。
本篇有一点js trick在里面,也有模块化、函数科里化的一些写法。
先看下面的糟糕的第一段代码
:
| var test = (function(arg0){ console.log("inner-arg0="+arg0); return function(arg0){ console.log("return-arg0="+arg0) } })();
|
这里我犯了一个错误,我习惯性地将函数最外层用括号包起来使其立即执行,这是声明式的用法,在这里运用错误。函数有两种形式,表达式和声明式,函数表达式本身可以立即执行。
第二段代码
修改了一下
| var test2 = function(arg0){ console.log("inner-arg0="+arg0); return function(arg0){ console.log("return-arg0="+arg0) } }();
|
去掉最外层括号之后本段代码也立即执行了。
若要让声明式立即执行,需要加一些小技巧。
最常见的写法
:
奇特的写法
:
赋值,逻辑,甚至是逗号,各种操作符都可以告诉解析器,这个不是函数声明,它是个函数表达式。
| !function(){alert("aaa")}
|
在chrome中的执行结果如下:
截图中可以看出,inner部分直接执行了,调用test()的时候return的匿名函数执行了。
因为之前写别的语言的缘故,习惯在函数的起始位置就传参,由于本段代码是立即执行的,所以第一次传参并没有起作用,后续的函数调用根本就没有调用到,这种写法真是太糟糕了。
以下是第三段代码
| var test3 = function(arg0){ console.log("inner-arg0="+arg0); return function(arg1){ console.log("return-arg1="+arg1) } };
|
在chrome中的执行结果如下:
这里放弃了立即执行,采用需要时调用的方式。
第一次调用函数表达式,第二次调用返回的匿名函数,根据调用次序的不同,可以满足不同的业务需求。
第一次调用时,传参进行初始化,参数在函数作用域中声明,当函数执行完毕之后,被gc掉(属于标记清除)。
第二次调用使用的是返回的函数,这里是一个闭包,注意执行的时候要谨慎,不要轻易赋值给全局变量,内存泄漏你们都懂。
第四段代码
是高阶函数的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| var funcA = function(arg0){ } function main(f,arguments){ f.apply(this,arguments); } main(funcA,123);
var funcB = function(){ return function(arguments-retB){ } } function main(){ return function(f,arguments){ } } main()(funcB,123);
|
code4-1是标准的高阶函数调用,在需要的时候将函数作为参数传入,code4-2通过返回匿名函数的方式传参。在这里优势不明显,研究完继续填坑。
接下来看第五段代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| define([],function(){ var exports = {}; exports.func = function(arg0){ console.log("moduleC-arg0="+arg0) }; return exports; });
require([moduleC],function(moduleC){ var exports = {}; exports.init = function(arg0){ console.log("transToC-arg0="+arg0) }; exports.otherFunc(){ moduleC.func('module-main-arg1'); } expots.init(555); return exports; })
|
这是commonjs的写法,这样写也为了函数解耦,个人觉得将函数绑定到对象的属性属于面向对象编程,和前面的说明式编程思想是不一样的。
| > 批注:[2016-4-17修正] > 今天吃饭的时候和WD大神讨论了这个问题,我这里的理解有误,更正一下。 > javascript本身没有类的概念,function A(){}作为“类”来使用的时候可以在原型链上添加方法, > 但是只有当其new一个对象实例时,才算是这个function达到了“类”的作用; > 只是将方法绑定在function A属性上,通过function A调用不能算是面相对象编程。
|
还有第六段代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var exports = function(){ return { funcA1:function(arg0){ } funcA2:functioin(){ } } } module.exports = exports;
var A = require("./muduleA")(); A.funcA1(123); A.funcA2();
|
pay attention
这种写法在nodejs中非常常见,这样写更符合事件触发机制,适用于将函数的执行作为参数传入,表示事件的传入,而事件发生时传参执行。
moduleA返回了函数对象,函数对象的属性的值是匿名函数,在moduleB中require了moduleA的返回函数,当需要使用moduleA的方法时,可以通过对象的键值调用。
思考
:
本文通过执行调用的顺序,结合以前写代码的习惯,讨论了javascript书写方式,若实践中得到更好的方式本文将持续修改。
附录
:
知乎上关于函数式编程的讨论