• kittyE
    2019-11-30
    我理解 x 这个单值表达式 是不是可以等价于 x => x; 的计算过程?毕竟直接return了 不知道理解的对不对

    作者回复: 非常赞。这个答案其实比我想的还要好。^^.

    
     4
  • 海绵薇薇
    2019-12-02
    Hello,老师好:),也不知道想问啥,感觉还是没有理清楚,望指点,问题如下:

    0.

    闭包是函数表达式的值?

    1.

    let c = function (a) {}

    c是闭包(上面匿名函数的实例),并且闭包中有标识符a(简单参数)

    let e = c

    e应该也是闭包,并且指向c(e === c 为true),也有标识符a

    现在看来e和c闭包中标识符a是同一个,但是不应该是同一个。应该是哪里有了根本性问题,导致这样的理解。

    2.

    函数一体两面,逻辑和数据。那么闭包指的是逻辑?数据?还是函数的另一个名字表示的逻辑和数据?
    展开

    作者回复: 先把你的第一个问题回复了,你试着再推演一下其它的。——你的第一个问题问得非常到位,是你思考上下文中的关键点。赞!

    “闭包是函数表达式的值?”答案即“是”,且“非”。

    首先说“是”的部分。单看ECMAScript这里:
    https://tc39.es/ecma262/#sec-function-definitions-runtime-semantics-evaluation

    你会发现,一个函数表达式执行之后,就是“Return closure”。所以,这个说法在ECMAScript层面是对的。

    但是我通常在这里说,这个过程得到的其实还只是一个“函数实例”。为什么呢,比如说:
    ```
    for (var arr = [], i = 0; i<10; i++) arr.push(function() {});
    ```
    这里有几个匿名函数在arr[]里面?答案是有10个。所以函数表达式每执行一次,就得到一个函数实例。你的示例`let c = function() {}`也一样,右操作数每执行一次就得到一个新的。

    但是这是函数实例。函数闭包其实是在它调用的时候才创建和初始化的。——只不过,所有的、因为函数调用而产生的闭包,都是上面这个实例的一个“副本”。所以,一个实例其实也可以有多个闭包。这里的意思是说,ECMAScript在上面把它叫闭包:既没错,它是所有其它闭包的“母本”;也不太对,因为它不是你在执行过程看到的任何一个闭包。

    ——一个函数实例可以有多个闭包,这很少见,但的确是存在的。比如递归,其实是一个函数的多次调用,那这个函数(实例)只有一个,闭包却是有多个了。

     1
     3
  • leslee
    2019-12-25
    为什么箭头函数不绑定 this 跟arguments

    作者回复: 他的设计便是如此。使用当前上下文中的this和arguments,有些时候是很有用的,对现有的normal function的设计也是补充。所以,除了开始使用的时候不习惯之外,在语言功能的设计上还是不错的。

    另外,箭头函数没有this,也意味着它不支持this(和arguments)的隐式传入,这在函数式语言特性上是更纯粹的,更接近lamada函数的概念。不过由于JavaScript语言自己的特殊性,它做不到无副作用罢了。

    
     2
  • 海绵薇薇
    2019-12-02
    Hello 老师好:)

    一直以来各大博客描述的包括我理解的闭包是:函数可以访问函数作用域外标识符的能力。但是本文描述闭包的角度完全不一样。这两者有联系吗?

    作者回复: "函数可以访问函数作用域外标识符的能力",这个与闭包没关系,而是scope-chains的事情。不过现在这个概念也改了,在ES6之后,这里被称为环境/作用域。所以,如果仅仅是在概念层面讨论这个话题,比较容易变形。

    回到提出这个概念的早期,在ES3~ES5的那个时代,概念比较简单清晰一些。有一个东西称为“对象闭包(object closure)”,这个东西写在spidermonkey的代码里面,也写在一个称为narcissus的开源项目里面,尤其是narcissus,它是JS之父本人Eich一直在维护的一个项目,也就是说,是一种正统的“概念”。——我说这一段是想说明,我下面的说法不是心血来潮随口胡诌。

    Ok。什么是对象闭包呢?所谓对象闭包,就是“with (x) ...”中打开的那个用`x`的属性表来形成的闭包。所以,如果(注意这个推论哟),如果对象闭包是一种闭包,那么要么是概念上属性表等义于闭包,要么是闭包是一种属性表。

    OVER。^^.

    
     2
  • ssala
    2019-12-21
    表达式也是3部分,如果以类比函数的思维来看待表达式,我认为:

    1. 表达式中用到的参数构成了表达式的“参数”
    2. 表达式本身构成了其逻辑体,这是表达式的逻辑部分
    3. 表达式的运算结果就是它的“返回值”

    单值表达式x在逻辑上等同于标题中的箭头函数。
    展开

    作者回复: 非常赞的思考!

    表达式和函数并没有本质不同,它们的抽象形式是一样的。事实上,如果你理解“当运算符等义于某个函数”时,那么你其实就看到了“函数式语言”范式的根本逻辑,关于这一点,我之前也讲过,在《JavaScript语言精髓与编程实践》中也是拿这个来引入函数式语言的。

    当运算符等义于函数,那么操作数就是入口参数,而运算结果就是函数返回。运算在本质上就是一个IO和一个处理。这样的理解会让你看到计算理论中最基础的那些模型。

    谢谢你提出这一点,真的很赞!

    
     1
  • 海绵薇薇
    2019-12-03
    Hello,老师好:)

    关于函数闭包又有了新的理解,函数形成的闭包是函数本身 + 函数当前的环境的集合。也就是说函数表达式的Result(闭包母本)中应该包含当前函数的环境(词法环境/执行逻辑的土壤)和函数体本身(对象/数据&逻辑)。

    作者回复: 赞的。^^.

    ```
    // f是函数x的一个实例
    f = function x() {}

    // 创建f的一个闭包、初始化该闭包并在其中执行代码
    f()
    ```

    
     1
  • 许童童
    2019-11-29
    加油,希望以后来回顾的时候,可以回答出来上面三个思考题。
    
     1
  • 小童
    2020-01-19
    看了两遍 然后再看留言 理解的更透彻了!
    
    
  • 小童
    2020-01-19
    老师 我测试的箭头函数可以显示的设置“use strict” 文中说的函数无法通过显式地"use strict"语句来切换到严格模式,但能接受它被包含在一个严格模式的语法块中(从而隐式地切换到严格模式);

    const test=()=>{
    a=1
    console.log(a)
    }
    test() 是可以输出的 模式情况下 也是非严格模式

    const test=()=>{
    "use strict"
    a=1

    }
    test()是会报错的 说明严格模式起效
    展开

    作者回复: 你得让函数先进入“非简单参数模式”,例如在参数中声明缺省参数。对于箭头函数来说也是如此。

    ```
    const test = (x=0) => {
      "use strict";
      a=1;
    }
    ```

    > SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list

    
    
  • Elmer
    2020-01-02
    在 ECMAScript 6 之前的函数声明中,它们的参数都是“简单参数类型”的。在 ECMAScript 6 之后,凡是在参数声明中使用了缺省参数、剩余参数和模板参数之一的,都不再是“简单的”(non-simple parameters)。在具体实现中,这些新的参数声明意味着它们会让函数进入一种特殊模式,由此带来三种限制:函数无法通过显式地"use strict"语句来切换到严格模式,但能接受它被包含在一个严格模式的语法块中(从而隐式地切换到严格模式);无论是否在严格模式中,函数参数声明都将不接受“重名参数”;无论是否在严格模式中,形式参数与 arguments 之间都将解除绑定关系。

    这一段没有理解。这个只是针对箭头函数吗?显示不是。。。
    在使用了简单参数的函数里是可以打印出来arguments的,这怎么解释解除了绑定关系呢?
    例如: function (test = 1) {console.log(arguments)} 是可以打印出来的
    展开

    作者回复: Oh... 这是你误解了。

    这里说的是“解除绑定关系”,而不是没有arguments。

    如下:

    ```
    // 非简单参数
    function foo(x = 2) {
      x = 100; // reset
      console.log(x, arguments[0]);
    }

    // 简单参数
    function foo2(x) {
      x = 100; // reset
      console.log(x, arguments[0]);
    }

    // 测试
    foo();
    foo2();
    ```

    另外,箭头函数是没有arguments的。如果你能访问到,那么应该是该箭头函数所在的(父级的)函数的。

     1
    
  • Jing
    2020-01-01
    在a=100中,存在“值和引用”,这里怎么理解呢,这里的引用是指a可能是引用吗?

    作者回复: 是的。

    它是“引用(规范类型)”。它不是语言层面理解的“值与引用”,也不是JavaScript的typeof可以识别的类型,它是ECMAScript在规范层面设计的“引用(规范类型)”。这是在第1讲里一再强调的。

    "a"是标识符,一个标识符从环境中被resolve到的时候,它就是一个“引用(规范类型)”。当它作为lhs时,它保持“作为引用(例如ref)”的特性不变;当它作为rhs的时候,它将被取值,即等义于GetValue(ref)。

    
    
  • 小童
    2019-12-27
    看了MDN对闭包的定义,函数与其状态的引用。
    var a=1;
    function f(){
       console.log(a);
    }
    f();
    在浏览器全局环境中,存在对a变量的引用,也是属于闭包。
    展开
    
    
  • 小童
    2019-12-26
    function f(a){

     return a;
    }

    var a=1
    var fObj=f(a);函数f调用 就产生了闭包了。

    函数是不是可以理解成这样吗? 被包裹在了一个小空间。
    (function f(a){
     return a;
    })

    那么fObj是否存在引用呢?
     
    展开

    作者回复: fObj不存在引用。

    函数中通过return来返回时,是取值的。所以函数并不能返回“引用(规范类型)”。所以在函数中`return obj.foo`时,得到的将是函数foo,而不是对象方法foo。

    不过我觉得你的这个问题,可能更多的讨论的是对“闭包”的理解。如果是这样,那么闭包就是一个名字表,只不过这个名字表是在`f()`这个调用的时候才创建得到的,如果不调用而只是引用函数`f`,例如`x = f`,那么这个过程中就根本没闭包什么事儿。

     1
    
  • 子笺
    2019-12-20
    听完老师这一讲,我脑海中有幅构图:
    函数有几个基本要素:执行的上下文【素材】,执行的过程【对素材的加工】,执行的结果【成果】
    为了完成成果,必须要有特定的方式去处理整合素材,有合理素材处理流程
    首先是整合素材:考虑到整合素材时,有普通的参数传递方式,还有一些特殊的(缺省、解构),就用了2种形式去整合素材。
    然后是对素材的加工:结合我对编译的认知,这些素材都有各自的占位,在解析阶段,其实已经有了对各个占位的处理流水线,在执行阶段这些占位真正有了自己的实物,直接在已经构建的流水线上去做处理。
    最终可以拿到成果

    同理,我想表达式也差不多。
    在这节课之前才看了老师关于如何学习的加餐,不管对错,先有自己的理解
    展开

    作者回复: 赞!这是极难得的进展!
    值得再赞一次!!

    
    
  • 可可
    2019-12-12
    没有JavaScript基础,看的云里雾里的,是不是要买老师的新书来看看啊

    作者回复: 新书还没出呢。😂

    不过可以先看《程序原本》啊,免费开放的电子书。在这里:
    https://github.com/aimingoo/my-ebooks

    与本课程相关的章节,以及对应关系我在加餐里面已经讲过了。另外,JavaScript基础有一些是最好,没有的话也不用太紧张,这门课程主要不是讲JavaScript应用的,所以语言就算不太熟悉,也不算特别大的障碍。所以我一般只强调“要有语言经验”,以及“最好JavaScript已经入门”。

    
    
  • IAmFineThankS
    2019-12-11
    函数闭包是函数执行时创建的,可是按照闭包的变量存储位置是在堆里的前提,函数每次执行完后的变量不应该被删除掉,但是实际上函数每次调用完后函数内部的变量都会被GC回收掉,感觉有点不是很懂~

    作者回复: 这里你是按传统的语言来理解的。并且,即使按“传统的语言”来理解,也并不太正确。

    首先,并不是函数(或闭包)的变量存储在堆里。在一些语言中(例如Delphi),函数内的局部变量也是可以存放在栈上的,并且多数情况下就是如此。所以一些传统的语言概念和实现思路,并不见得总是对的。

    其次,JavaScript是解释执行的,闭包(这里反过来说,或者函数)中的变量与标识符并不是使用堆/栈来管理的。所以它的回收也不是堆与栈的机制,通常这基于引用计数,但在不同的引擎中可能存在区别,是难以一概而论的。但是其中所谓“闭包”,才是回收所基于的一些基础组件,闭包代表了对对象、数据以及其它东西的引用,并且闭包自身也被作为引用对象——所以“函数每次调用完后函数内部的变量”其实在JavaScript中并不总是会被GC掉,因为闭包可能被别的地方引用了。例如简单的想法:你在函数内部return arguments.callee,那么函数(作为闭包)就被外部的环境引用了,那么显然变量之类的也不能被GC掉了。

    总之,JavaScript的函数与传统语言中的函数有着很大的区别。从语言特性上来说,它的动态语言特性和函数式语言特性,都会给传统语言背景下的学习者带来很大的困扰。这也是我在开篇词里面一再强调“要放下既有知识包袱”的原因。

    
    
  • IAmFineThankS
    2019-12-11
    处理函数参数的过程与此完全相同:参数被创建成“可变绑定”,如果它们是简单参数则被置以初值undefined,否则它们就需要一个所谓的“初始器”来赋初值。也就是说,并非 JavaScript 要刻意在这里将它作为 var/let 变量之一来创建,而只是用户逻辑执行到这个位置的时候,所谓的“可变绑定”还没有来得及赋初值罢了。
    什么意思? 为什么突然就能得出结论来了

    作者回复: let与var都是可变绑定,它们的不同不在于自身的数据结构(它们在引擎层面都是一样的)。

    然而var是有初值的,而let是无初值的,这个是它们在创建的时候(或者在创建之后“随即”)发生的变化。进一步的,规范约定“不能访问一个没有初值的绑定”。因此,let就不能在它的赋值代码之前访问,而var则可以(因为它有初值),这是他们之间的差异的由来。

    对于函数参数这个例子。简单参数是赋初值的,所以表现起来跟var一样。——对照起来,非简单参数也是一个“可变绑定”,但是没有赋初值(所以表现起来跟let一样)。文章中在这里的叙述,是想强调“使这个参数表现得像var/let”并不是原始的目的,原始的状况是这里本来没有什么目的性,只不过由于undefined这个值(作为缺省值)被缺省参数占用了,所以没办法用作初值而已。

    
    
我们在线,来聊聊吧