V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
asanelder
V2EX  ›  程序员

闭包和对象的区别?

  •  1
     
  •   asanelder · 2021-01-08 17:48:15 +08:00 · 6086 次点击
    这是一个创建于 1422 天前的主题,其中的信息可能已经有所发展或是发生改变。
    在看 js 的闭包, 感觉和面向对象中的对象差不多啊

    闭包: 行为以及数据的绑定
    对象: 行为以及数据的绑定

    感觉没差啊, 是不是可以使用对象来代替闭包?
    第 1 条附言  ·  2021-01-09 18:01:50 +08:00

    javascript版本

    function createIncrementor(start) {
     return function () {
         return start++;
     };
    }
    
    var inc = createIncrementor(5);
    
    inc() // 5
    inc() // 6
    inc() // 7
    

    java 版

    class Incrementor {
     private int start;
     
     Incrementor(int start) {
        this.start = start;
     }
     
     int inc() {
        start++;
     }
     
    }
    
    Incrementor inc = new Incrementor(5);
    inc.inc();
    inc.inc();
    
    46 条回复    2021-01-16 21:42:13 +08:00
    ConradG
        1
    ConradG  
       2021-01-08 17:53:07 +08:00   ❤️ 3
    本来俩就是一个东西——方法执行上下文
    fpure
        2
    fpure  
       2021-01-08 17:53:26 +08:00   ❤️ 1
    闭包是穷人的对象,对象是穷人的闭包
    你可以用闭包来模拟对象,或者用对象来模拟闭包,除非有必要的话
    asanelder
        3
    asanelder  
    OP
       2021-01-08 17:55:32 +08:00
    @ConradG #1
    @fpure #2

    是啊, 记得之前看过很多讲这个的文章, 感觉说的很不明觉厉, 今天又仔细看了一下,感觉和对象差不多.

    什么保存上下文环境, 驻留在内存中, 这不就是对象么.
    asanelder
        4
    asanelder  
    OP
       2021-01-08 17:56:17 +08:00
    同一个东西, 使用闭包和对象来实现, 感觉也差不多.


    ```javascript
    function createIncrementor(start) {
    return function () {
    return start++;
    };
    }

    var inc = createIncrementor(5);

    inc() // 5
    inc() // 6
    inc() // 7
    ```

    ```java
    class Incrementor {
    private int start;

    Incrementor(int start) {
    this.start = start;
    }

    int inc() {
    start++;
    }

    }
    ```
    codehz
        5
    codehz  
       2021-01-08 18:01:55 +08:00
    是这样的,你可以
    function Class() {
    let x = 1;
    let y = 2;
    return function(action, params) {
    switch(action) {
    case "get-x": return x; break;
    case "set-x": x = params[0]; break;
    case "get-y": return y; break;
    case "set-y": y = params[0]; break;
    default: throw new Error("not implemented");
    }
    }
    }
    然后
    let instance = Class()
    console.log(instance("get-x")); // 1
    instance("set-x", [5]);
    console.log(instance("get-x")); // 5
    顺带提示,这种方法还可以实现继承,多态等其他面向对象特性
    fpure
        6
    fpure  
       2021-01-08 18:03:00 +08:00
    别想的太复杂,闭包就是用来解决返回一个函数之后的上下文问题,而对象就是通常用来组织数据的结构;本质都是存储状态;不用把他们想的太神秘,用的时候你自然就知道该用闭包还是对象了
    asanelder
        7
    asanelder  
    OP
       2021-01-08 18:08:21 +08:00
    @fpure #6 嗯嗯, 主要是网上说的太高大上了, 之前心里有些害怕, 哈哈
    codehz
        8
    codehz  
       2021-01-08 18:09:56 +08:00
    对象代替闭包,在某些没有闭包的语言里也是可行的(前面看反了(
    甚至没有类这种机制的情况下都是可行的方案
    主要方法就是将局部变量变成对象属性,或者结构体属性
    然后作为一个环境参数在多个普通函数之间传递即可
    ( c 库主流的设置回调函数要给一个 void*作为用户指针就是这样来的)
    OxOzZ
        9
    OxOzZ  
       2021-01-08 18:12:17 +08:00   ❤️ 2
    SICP 第二章
    SuperMild
        10
    SuperMild  
       2021-01-08 18:19:38 +08:00
    话不能这么说,

    自行车:代步工具
    小轿车:代步工具

    光看定义是感觉没差,但能不能相互代替?

    一个比较轻,一个比较重;一个适用于简单(短距离)场境,一个适用于复杂(远距离)场境;一个结构简单,一个结构复杂。
    ngn999
        11
    ngn999  
       2021-01-08 18:40:42 +08:00
    《代码的未来》里说: 对象与闭包是同一事物的正反两面.

    https://www.cnblogs.com/edisonchou/p/4873315.html
    ianva
        12
    ianva  
       2021-01-08 18:50:39 +08:00   ❤️ 1
    真该看看 SICP,原本很简单的世界就是因为面向对象这一个抽象把东西都搞复杂了,java 缺少这种函数是一等公民这种最基本的规则
    ianva
        13
    ianva  
       2021-01-08 18:54:53 +08:00
    从面向对象的语言入门编程真会有很多偏见,SICP 选的 Scheme 这种精简到不能再精简的语言才能呈现编程的最原本的东西
    ianva
        14
    ianva  
       2021-01-08 18:58:29 +08:00
    asanelder
        15
    asanelder  
    OP
       2021-01-08 19:04:33 +08:00
    @ianva #14
    @ianva #13
    @ianva #12

    嗯, 将来一定要好好拜读一下 SICP
    Leviathann
        16
    Leviathann  
       2021-01-08 19:07:46 +08:00 via iPhone
    闭包是穷人的对象
    对象是穷人的闭包
    faceRollingKB
        17
    faceRollingKB  
       2021-01-08 19:11:14 +08:00
    es6 出现以前,js 没有私有属性 /方法的概念,这才有了闭包,利用作用域来实现私有
    asanelder
        18
    asanelder  
    OP
       2021-01-08 19:14:07 +08:00
    @faceRollingKB #17 是不是 es6 出现后, 之前使用闭包的场景就大大减少了啊
    ianva
        19
    ianva  
       2021-01-08 19:18:15 +08:00
    所有函数式编程的场景都是靠闭包过的,这也是 JavaScript 最有用的特性了,举个例子 https://ramdajs.com/
    特别是 es6 之后有箭头函数了,应用的就越来越广泛了
    Cbdy
        20
    Cbdy  
       2021-01-08 19:18:22 +08:00 via Android
    @asanelder 不是
    xiangbohua
        21
    xiangbohua  
       2021-01-08 19:27:23 +08:00
    我的理解中,闭包有两层含义:
    一种是闭包的直接定义。
    一种是实际编码中定义的函数。
    我们通常说某种语言是否支持闭包的时候,我们用的第一层含义,当我们写出一个匿名函数用到闭包特性的时候,我们可以说我们写了一个闭包。
    实际上,支持闭包的编程语言通常都支持、将一个方法或者函数当成参数传递,C#的委托、JS 的会调函数、Java 里面的匿名代码块等等。
    我们经常会说到,函数或者方法能够跻身为某种语言里面一等公民的时候,通常也就支持闭包,那么此时我们定义的 C#的委托、JS 的会调函数、Java 里面的匿名代码块都已经是一个对象了。
    所以:
    我觉得,当我们说闭包是什么的时候,我们说的是一种定义,当我们讨论某个具体代码的时候我们讨论的是一个对象,只不过这个对象具有闭包特性。
    以上
    如果不正确请指正哈
    xiangbohua
        22
    xiangbohua  
       2021-01-08 19:28:12 +08:00
    @ianva Java 现在已经有了额
    darknoll
        23
    darknoll  
       2021-01-08 21:08:40 +08:00
    什么叫差不多?闭包是函数式编程及其核心思想“Lambda 计算法”( Lambda Calculus )的必备基本设定。js 不是面向对象的语言,好好看看函数式编程吧,别整天把啥东西都往面向对象那一套上边靠。
    billlee
        24
    billlee  
       2021-01-08 21:49:40 +08:00
    C++03 就是用类来代替闭包的
    nthhdy
        25
    nthhdy  
       2021-01-08 21:58:52 +08:00
    @darknoll lambda calculus 里面没有闭包的概念吧。闭包是在 scheme 语言里最初实现的,就是为了做到为 free variable 求值时到函数定义的环境中去找值,而不是到函数调用的环境里找。早期的 lisp 实现就是到函数调用环境里找的,据说 emacs lisp 到现在都是,导致写出来的代码非常反直觉。lambda calculus 里只有 application 和 abstraction,都没有现代编程语言里的“变量”这种东西。
    asanelder
        26
    asanelder  
    OP
       2021-01-08 22:10:24 +08:00
    @xiangbohua #21 铁子的解释可以
    walpurgis
        27
    walpurgis  
       2021-01-08 22:20:44 +08:00
    闭包写习惯了根本懒得用 es6 class 语法,毕竟函数都作为变量满天飞了,函数返回函数再正常不过的事情了
    ghostheaven
        28
    ghostheaven  
       2021-01-08 22:24:58 +08:00 via Android   ❤️ 4
    对象:带方法的数据
    闭包:带数据的方法
    ruyu
        29
    ruyu  
       2021-01-08 22:30:59 +08:00
    在一些语言中, 方法只是第一个参数是 this/self 的函数. 但闭包不是, 闭包是自带状态的.
    gtx990
        30
    gtx990  
       2021-01-08 22:41:42 +08:00 via Android
    fp 爱好者也别啥都往 fp 上靠
    secondwtq
        31
    secondwtq  
       2021-01-08 23:06:17 +08:00
    "fp 爱好者“表示,可以去看一下 TAPL 的 “Imperative Objects” 这一章,这一章使用带简单 subtyping 以及 records,references,fix 操作符等基本扩展的 lambda calculus 对“面向对象”的基本行为进行了“approximation”。
    我看的时候总有一种初学 JavaScript 的逮虾户 ...
    但是最后最 tricky 的是 open recursion 的模拟(作者将 open recursion 归为 OOP 的“fundamental features”之一),这个是以前看 JS 教程的时候没有意识到的——这东西在大多数非 FP 语言里面几乎是天经地义的事情,我也 take it for granted 了 ...
    FrankHB
        32
    FrankHB  
       2021-01-09 06:46:47 +08:00
    @nthhdy 你自己都说 free variable 了,这不是变量是什么。
    LC 的变量和现代编程语言中的一般意义上的变量(c.f. IEC 2382) 本来就是一回事,无非是默认共享命名空间无视作用域规则。把变量曲解为可变状态是历史上出现得晚得多的脑补,到现在仍然是片面的(按这说法纯 FP 语言里就没变量了)。
    @secondwtq 实现所谓的 open recursion 不需要 OOP 。
    名称解析直接整个 late binding 就行。不少动态语言的函数体里就是这样做的,函数定义时不管,拖到调用时才确定自然就 open 了,缺的只是没给在对象语言构造捕获环境(能够让你引用 this 这样的东西)的方法罢了。
    darknoll
        33
    darknoll  
       2021-01-09 10:48:41 +08:00
    就说了些上学学过的东西就变成了 fp 爱好者,真棒
    faceRollingKB
        34
    faceRollingKB  
       2021-01-09 13:23:52 +08:00
    @asanelder 有了 class 以及 module,闭包基本就不需要了,但实践中还是会偶尔用用,使用场景还是有的
    user8341
        35
    user8341  
       2021-01-10 10:43:35 +08:00
    @faceRollingKB
    es6 怎么定义私有属性?
    looking0truth
        36
    looking0truth  
       2021-01-10 20:49:50 +08:00   ❤️ 1
    @user8341 es2020,变量名前加#号
    yaphets666
        37
    yaphets666  
       2021-01-10 21:56:03 +08:00
    @walpurgis 日常哪里会用到闭包呢? 不是库里头应用的 是自己写代码的时候 实现什么功能会用到闭包呢?
    user8341
        38
    user8341  
       2021-01-11 05:13:33 +08:00
    @looking0truth 嗯。es2020 是 es11 了,不是 es6 。这么说 es6 应该是没有此功能的。
    brust
        39
    brust  
       2021-01-11 09:46:29 +08:00
    czq?
    faceRollingKB
        40
    faceRollingKB  
       2021-01-11 12:15:15 +08:00
    @user8341 好吧,平时太依赖 ts 、babel 了,没注意这个问题,my bad
    gaolingyi
        41
    gaolingyi  
       2021-01-11 13:10:16 +08:00
    闭包是早期没有模块化发明的私有变量的方法,现在 js 也有 class 了
    julyclyde
        42
    julyclyde  
       2021-01-11 17:58:47 +08:00
    主要是逼格的问题
    闭包更加 bigger
    对象一看就是陈旧的概念了
    walpurgis
        43
    walpurgis  
       2021-01-11 22:16:02 +08:00
    @yaphets666 闭包无处不在,可能你已经用了很多次而自己都没察觉,比如给 DOM 元素添加事件就是一个典型闭包应用场景

    {
    let a = 0
    document.body.addEventListener('click', () => {
    a = a + 1
    console.log(a)
    })
    }

    其实这已经是闭包了,不是在函数里返回函数才叫闭包,是因为 JS 以前只有函数作用域,原谅我上面的回帖考虑不周
    出了这个块级作用域,再也不可能拿到变量 a,理应被销毁,但事件回调函数内引用了 a,需要维持环境,所以 a 不会被销毁,点击 body 可以看到 a 在不断+1
    非常简单的就创建了一个安全的私有变量,这不比 class 写法爽多了?
    asanelder
        44
    asanelder  
    OP
       2021-01-11 22:30:54 +08:00
    @walpurgis #41 感谢老铁解惑, 闭包确实是一种轻量级的解决方案. 写 class 总要有那么现实世界有对应之物. 显的很重
    yaphets666
        45
    yaphets666  
       2021-01-12 09:11:08 +08:00
    @walpurgis 明白你说的了
    learningman
        46
    learningman  
       2021-01-16 21:42:13 +08:00
    @codehz 原来这个*p 是干这个的
    写了一万个 NULL
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2570 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 05:18 · PVG 13:18 · LAX 21:18 · JFK 00:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.