正在看 JS 的书,于是...
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function() {
return i;
};
}
return result;
}
alert(createFunctions()[0]());
大家不妨先目测一下结果,然后运行试试猜对没有...
然后我想看看 Ruby 对应应该怎么写,但是我只能写出这样的来...
def create_functions
result = []
(0..9).each do |i|
result[i] = lambda { return i}.call
end
result
end
puts create_functions[0]
结果不一样... 如果想要得出跟上面 JS 代码一样的结果来,Ruby 代码应该是怎么样的呢?还是根本没有对应的写法?
Thanks
def create_functions
result = []
for i in 0..9
result[i] = lambda { i }
end
result
end
puts create_functions[0].call
EDIT: 忽略了 i++ 的问题。见 #2 楼
1 是 lambda call 早了 2 是 js 里的 i 是多个 functions 之间共享的变量,而你的代码里 i 是 block local 的 3 是 for 循环最后 i 会变成 10, 而用 each i 最多变成 9
def create_functions
result = []
i = 0
while i < 10
result[i] = lambda { i }
i += 1
end
result
end
create_functions[0].call
话说回来,现在写 JS 的也未必要用 for 循环。有 ES5 环境的直接用 Array#each
, 被迫兼容旧 IE 的话也可以用 jQuery.each
, underscore 的 _.each
等。就不用担心这个问题了。
这段应该是书里用来证明 for 循环里起闭包很容易出错的:
我们写代码时经常会在循环里建造一系列的闭包 function, 以为每个都对应了不同的 i
, 结果全部都是同一个 i 还是超出范围的那个...
所以 js 里其实推荐用 array.forEach 的,或者再包个 function 让 result 的每个元素都有自己的一个 i (文绉绉一点说,就是 "不用共享状态来传递消息,而用传递消息来共享状态") :
for (var i = 0; i < 10; i++) {
result[i] = (function(i) {
return (function() {
return i;
})
})(i);
} // 爆炸了没? 还是乖乖用 forEach 吧
btw.
js 的 array.forEach 不能 break
和 continue
, 大大限制了其用途,所以依然是个蛋疼的语言...
其实为了 forEach 支持 break 和 continue, 很多语言都做了超多的工作。
例如 C++ 和 java 教程里都会唱 iterator 很好使:用 iterator, 就能把抽象的元素 traverse 放到普通的 for 里面去,break 和 continue 都能自由使用了还容易做优化 -- 唯一问题是一碰到并发/并行访问一个容器,iterator 就从救世主变成了万恶之源...
而在纯函数式编程语言里,一般是只有 map/fold/filter 而没有 for 的。一般需要 break 的地方,程序员可以自行脑内转换成 find
调用,而本来要 continue 的地方,程序员都自行包个条件判断或者模式匹配... ES5 的话就是 array.every()
了 (这个函数相当于 ruby 里的 Array#all?
, 却是 js 里唯一具备按返回值中断循环之神奇功效的 functional primitive ...), 但我基本没见过有人用它来替代 break ...
书里不敢再用 i
了,换了变量名
function createFunctions(){ var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
JS 确实看得我很头大,刚看了继承有 Prototype Chaining
, Constructor Stealing
, Prototypal Inheritance
, Parasitic Inheritance
, Parasitic Combination Inheritance
已经差不多要自杀了... 还看到用多一层 function 来模拟其他语言中的 block scope ...
#11 楼 @blacktulip 额有些写法是为了解决 IE5 不能释放循环引用而弄的吧,大部分都是没用的花拳绣腿...
我觉得理解 coffeescript 的 class / 继承 做法 和 jquery 的 extend 就够了...
#8 楼 @blacktulip 看文档用 JS 语法,写代码用 coffee, 不影响. 表示我是了解 JS 语法后先学成了 coffee 的,路走得通
create_functions = -> [0...10].map (x) -> -> x
console.log create_functions()[0]()
console.log create_functions()[1]()
create_functions = ->
result = []
(do (i) -> result.push -> i) for i in [0...10]
result
console.log create_functions()[0]()
console.log create_functions()[1]()
我的学习路线是大学时 js 语法明白 工作以后用的 jquery js 一直是个半吊子水平 后来直接用 coffee 慢慢就力不从心了 然后重新看的 javascript 搞基程序设计 目前为止基本够用...
#11 楼 @blacktulip 这个对比的例子很好,还可以用来对比说明一下两种语言的作用域问题。 JS 会在 function 定义中打开一个新的作用域范围(即 变量的可见范围),这点跟 Ruby 的 def 一样。 不同的地方在于 Ruby 并不能在 def 定义的作用域里面看到外围作用域中的变量, 而 JS 则在定义新 function 的时候,外部变量仍能从里面看到。 下面两段代码展示了这个不同。
function a() {
var v = 10;
function b() {
return v;
}
return b();
}
console.log(a()); // 10
# 上面 JS 代码的一对一翻译
def a
v = 10
def b
v
end
b
end
puts a # NameError
因此,楼主的 JS 代码并不能一一对照翻译成 Ruby,而需要使用 lambda。 由此,我们也可以看到,Ruby 里面的 lambda 跟 def 不同,它没有打开一个新的作用域,而是继续使用当前作用域。
另,看到了这么多英文术语,长姿势了。