闭包的设计
发布于 6 天前 作者 dou4cc 936 次浏览 最后一次编辑是 5 天前 来自 问答

闭包的思想来源没有追溯过,但我觉得它是个非自然的失败设计:闭包创造了状态黑盒,外面不能访问里面,反之居然可以。有时候我想把函数当虚拟机用,发现根本行不通。哪有虚拟机能访问真机,真机却不能访问虚拟机的?状态透明也符合Unix思想。

47 回复

大家怎么想?

这不是很正常吗,局部作用区域当然可以访问全局的变量,其他语言不也一样,什么函数虚拟机我不懂,状态透明跟这个有啥关系,再说了,虚拟机本来就应该可以访问机器啊,外部访问虚拟机不都是通过暴露的API么,function可以通过prototype和创建对象来生成对外可用API的,楼主理论不能乱套用啊

@dlutwuwei 虚拟机关机后,真机可以访问虚拟机的虚拟硬盘文件。虚拟机是否可以访问真机,取决于真机是否把硬盘挂上去,控制权在真机。

@dlutwuwei C语言没有闭包,Java好像也没有。

我建议楼主别想太多, 还没实际工作中使用过吧? 真实生产环境才是王道, 程序就是用来赚钱的, 如果实际使用中闭包不好用,才说其失败! 否则闭包非常好用, 就不能说是失败的设计 对于,我来说, 闭包机制非常成功, 我们小公司几百万的收益, 闭包起了无比重要的作用, 没有闭包的其他语言,开发过程中我们曾经为此付出了惨痛的代价

@151263 用了好几年闭包,才发现它真的不好用。

@151263 其实我想反对的是完好的封装。

@151263 比如你如何复制一个闭包?

闭包保证了你的几百万收益?搞笑么

我们用nodejs做ERP, 也做了5年了, 做了2千多个页面

@sagacite2 我们用nodejs做ERP, 也做了5年了, 做了2千多个页面, 闭包起了很重要的作用, 没说"保证", 看清楚了

闭包是解决FP中函数执行环境和定义环境不一致的最简洁直白的方式 闭包里的变量就是函数的私有变量,何来外部访问一说?

@ravenwang FP因为很多东西是只读的,闭包涉及的量可以在定义时用惰性求值化简掉,其余参数动态作用域照样解决。JS则不同,闭包常用来做状态机,这时闭包的实质已经变了。

闭包是为了实现静态作用域(词法作用域)而使用的一种结构,对于支持高阶函数的语言来说闭包是不可缺少的,对程序语言的发展也有重大的意义。如果不想使用闭包,那么你不得不放弃高阶函数这一特性(本质上一个函数也会有一个相关的闭包,暂且不讨论),这将是不可接受的。

如果要在函数里面嵌套函数,没有闭包那怎么给里面的函数传参数? 直接都是全局参数吗? 还是一级一级向下传?

@xiaofuzi @yakczh

'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 你可以尝试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,试想一种语言,语法简单到只有“括号要配对”。

@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 我反对的就是这种不透明的封装。

大神们有什么看法吗?@alsotang @i5ting

@dou4cc: 外面不能访问里面,反之则可以,是因为两个原因:

  1. 外面是“总”,里面是“分”,如果外面访问里面的话重名不好处理。
  2. 外面执行的时候里面可能还未执行,所以逻辑上外面访问里面也行不通。

别把它看成虚拟机。建议把它看成HTML的iframe,iframe能访问外面,但外面不能访问iframe,这是安全原因。因为当你的网站嵌入其他网站的iframe时,可以看作是你信任这个网站,所以里面可以访问外面。反之,由于任何网站都可以被嵌入,所以里面不能选择是否信任外面,所以外面必须不能访问里面。

@whmabc: 我倒不觉得有了类以后,闭包就没有价值了。首先,纯函数式是没有类的,只有闭包。其次,现在的语言发展方向是“多范式”的,就像C#,原先只有类没有闭包,但现在也有了闭包。如果没有闭包的话,mapfilter就完全没有办法做到那么简洁了。

@zhanzhenzhen 外面访问里面可以以统一作用域和字典(object)两个概念解决命名问题。iframe真的不能与之类比,iframe里面可能藏着一些storage什么的,子作用域里面有什么隐藏的呢?为了防止手滑,就把不太可能碰到的东西私有化吗?这还可以类比Unix为什么倡导文本文件。

禁用闭包理论上有如下好处:函数toString后没有信息损失,可以用eval还原,即函数变得字面化、可存储;对于用闭包封装好的数据结构,即使外面不了解里面的格式,也可以在传输时把它们简单地打碎,在另一头原样还原……

回到顶部