闭包的思想来源没有追溯过,但我觉得它是个非自然的失败设计:闭包创造了状态黑盒,外面不能访问里面,反之居然可以。有时候我想把函数当虚拟机用,发现根本行不通。哪有虚拟机能访问真机,真机却不能访问虚拟机的?状态透明也符合Unix思想。
大家怎么想?
<div data-reactid=".0.1.0.5.2.0.0"><p data-reactid=".0.1.0.5.2.0.0.$0"><span data-reactid=".0.1.0.5.2.0.0.$0.0">这不是很正常吗,局部作用区域当然可以访问全局的变量,其他语言不也一样,什么函数虚拟机我不懂,状态透明跟这个有啥关系,再说了,虚拟机本来就应该可以访问机器啊,外部访问虚拟机不都是通过暴露的API么,function可以通过prototype和创建对象来生成对外可用API的,楼主理论不能乱套用啊</span></p></div>
@dlutwuwei 虚拟机关机后,真机可以访问虚拟机的虚拟硬盘文件。虚拟机是否可以访问真机,取决于真机是否把硬盘挂上去,控制权在真机。
@dlutwuwei C语言没有闭包,Java好像也没有。
Java有的
我建议楼主别想太多, 还没实际工作中使用过吧? 真实生产环境才是王道, 程序就是用来赚钱的, 如果实际使用中闭包不好用,才说其失败! 否则闭包非常好用, 就不能说是失败的设计 对于,我来说, 闭包机制非常成功, 我们小公司几百万的收益, 闭包起了无比重要的作用, 没有闭包的其他语言,开发过程中我们曾经为此付出了惨痛的代价
@151263 用了好几年闭包,才发现它真的不好用。
@151263 其实我想反对的是完好的封装。
@151263 比如你如何复制一个闭包?
闭包保证了你的几百万收益?搞笑么
我们用nodejs做ERP, 也做了5年了, 做了2千多个页面
@sagacite2 我们用nodejs做ERP, 也做了5年了, 做了2千多个页面, 闭包起了很重要的作用, 没说"保证", 看清楚了
闭包是解决FP中函数执行环境和定义环境不一致的最简洁直白的方式 闭包里的变量就是函数的私有变量,何来外部访问一说?
@ravenwang FP因为很多东西是只读的,闭包涉及的量可以在定义时用惰性求值化简掉,其余参数动态作用域照样解决。JS则不同,闭包常用来做状态机,这时闭包的实质已经变了。
闭包是为了实现静态作用域(词法作用域)而使用的一种结构,对于支持高阶函数的语言来说闭包是不可缺少的,对程序语言的发展也有重大的意义。如果不想使用闭包,那么你不得不放弃高阶函数这一特性(本质上一个函数也会有一个相关的闭包,暂且不讨论),这将是不可接受的。
如果要在函数里面嵌套函数,没有闭包那怎么给里面的函数传参数? 直接都是全局参数吗? 还是一级一级向下传?
'use strict'; //ES6
const global = this;
const f = x => {
if(typeof x !== 'number'){
throw new EvalError;
}
return x === 0
? 0
: global.eval(`
(function thunk(x){
if(typeof x !== 'number'){
throw new EvalError;
}
const sum = (${x.toString()});
return x === 0
? sum
: global.eval('(' + thunk.toString().replace(/\\((\\d|\\.|-)+\\)/, '(' + (sum + x).toString() + ')') + ');');
});
`);
};
f(0);
//0
f(1)(0);
//1
f(1)(2)(0);
//3
f(1)(2)(3)(0);
//6
@dou4cc 这段代码你确定没用到闭包?
@ravenwang 确定。
@ravenwang 你可以尝试f(1).toString()
。
ES6有了Class,闭包就可以退休了,写法确实让人感觉不爽,脱了裤子放屁的感觉。
哦,没细看代码,以为嵌套的那个eval生成了闭包
@dou4cc 不过我想你这个例子恰恰说明了闭包的价值,没有闭包的话要用这么tricky的方式来处理,我认为实际上你是做了个类似闭包的东西,直接用闭包实现要简单易懂得多
var sum = 0;
function thunk(x) {
if (typeof x !== 'number') {
throw new EvalError;
}
if (x === 0) {
var result = sum;
sum = 0;
return result;
} else {
sum = sum + x;
return thunk;
}
};
@dou4cc 不要搞eval这种奇怪的东西,如果觉得闭包没必要,就举个正常点的例子来 学过编译原理的都知道词法作用域和动态作用域是什么,闭包当然也就不难理解。
@ravenwang @xiaofuzi 之所以用了一堆tricky,是因为eval没有AST层面的支持。
@ravenwang @xiaofuzi 用eval构造函数返回,还有一个好处就是可以根据情况改变返回的函数的结构,这是闭包做不到的。
@dou4cc 逻辑不通啊,你说闭包设计失败,现在闭包简单直接搞定了你要用eval或者AST来处理的问题,你这么搞实际上是在自己实现闭包而已
@dou4cc 喂喂,eval和闭包不是一回事哪,说闭包做不了eval能做的事来黑闭包也太勉强了吧,闭包还不能上天呢……
“use strict”; var x = 2; function evalFunc(){ var x = 5 return function(){ console.info(eval(“x”)); // 5 } }
evalFunc()() console.info(x); // 2 eval也是一个求值的过程,和函数没有本质的区别,既然求值就涉及作用域,回到之前的问题,执行作用域是在声明是确定的,那么必然用到了闭包。(严格模式,非严格模式eval使用的是全局作用域)
@ravenwang 使用eval和AST实际上把作用域扁平化,这样编出来的高阶函数可以自我进化,而且能toJSON。AST的复杂仅针对JS,试想一种语言,语法简单到只有“括号要配对”。
@xiaofuzi 你没看懂……
@dou4cc 语法简单不代表没有作用域,没有闭包,schemer采用S表达式的语法,可以理解为你所说的“括号配对”,但闭包的正是因它而生。schemer是lisp的方言,Lisp最开始是动态作用域的,schemer在其基础上加上闭包,实现了静态作用域,这也使得编程变得更为友好。
@xiaofuzi 我没说喜欢Lisp的作用域,实际上我讨厌它们。
@whmabc 面向对象已经被函数式编程社区喷烂了,实际上它就是个噱头,见:https://cnodejs.org/topic/56f67d5487688ffc6e356e8b
@dou4cc 怎么扯到编程范式去了,这里不讨论面向对象好,还是函数式编程好,🐰。
@ravenwang 其实我不完全反对闭包的存在,只是想让它变得透明、可控。能不能去掉闭包,由Function.prototype.bind来满足这些需要呢?最好再加个Function.prototype.unbind。
先理解好什么是闭包吧
@xiaofuzi bind可以不用闭包实现。
切勿将this和闭包扯到一起 对闭包的简单理解,http://yangxiaofu.com/2016/03/05/js/再谈闭包/ 如有错误,欢迎指正
//我觉得闭包不错啊,至少下面这个例子很好
var createId = (function(){
var i = 0;//这个i在函数外部无法访问,不被污染
return function(){
return i++;//这里可以访问i,很好
}
})();
@xiaofuzi bind主要是绑定arguments的,绑定this用::
。
@hanyuzhou2006 我反对的就是这种不透明的封装。
@dou4cc: 外面不能访问里面,反之则可以,是因为两个原因:
- 外面是“总”,里面是“分”,如果外面访问里面的话重名不好处理。
- 外面执行的时候里面可能还未执行,所以逻辑上外面访问里面也行不通。
别把它看成虚拟机。建议把它看成HTML的iframe,iframe能访问外面,但外面不能访问iframe,这是安全原因。因为当你的网站嵌入其他网站的iframe时,可以看作是你信任这个网站,所以里面可以访问外面。反之,由于任何网站都可以被嵌入,所以里面不能选择是否信任外面,所以外面必须不能访问里面。
@whmabc:
我倒不觉得有了类以后,闭包就没有价值了。首先,纯函数式是没有类的,只有闭包。其次,现在的语言发展方向是“多范式”的,就像C#,原先只有类没有闭包,但现在也有了闭包。如果没有闭包的话,map
、filter
就完全没有办法做到那么简洁了。
@zhanzhenzhen 外面访问里面可以以统一作用域和字典(object)两个概念解决命名问题。iframe真的不能与之类比,iframe里面可能藏着一些storage什么的,子作用域里面有什么隐藏的呢?为了防止手滑,就把不太可能碰到的东西私有化吗?这还可以类比Unix为什么倡导文本文件。
禁用闭包理论上有如下好处:函数toString后没有信息损失,可以用eval还原,即函数变得字面化、可存储;对于用闭包封装好的数据结构,即使外面不了解里面的格式,也可以在传输时把它们简单地打碎,在另一头原样还原……