V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
fenglala
V2EX  ›  JavaScript

新人 JavaScript 求助语法问题

  •  
  •   fenglala · 2022-05-26 10:29:47 +08:00 · 2650 次点击
    这是一个创建于 672 天前的主题,其中的信息可能已经有所发展或是发生改变。
    function sleepPromise() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log("promise")
            }, 1000)
        })
    }
    
    sleepPromise() // 这里没有分号
    
        (function () {
            console.log("hhh")
        })()
    

    为啥调用 sleepPromise()的时候不加分号,就会跑不起来,加了分号就可以了

    PS C:\Users\lala\Downloads> node c.js
    C:\Users\lala\Downloads\c.js:11
        (function () {
        ^
    
    TypeError: sleepPromise(...) is not a function
        at Object.<anonymous> (C:\Users\lala\Downloads\c.js:11:5)
        at Module._compile (node:internal/modules/cjs/loader:1105:14)
        at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
        at Module.load (node:internal/modules/cjs/loader:981:32)
        at Function.Module._load (node:internal/modules/cjs/loader:822:12)
        at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
        at node:internal/main/run_main_module:17:47
    

    加了分号之后

    function sleepPromise() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log("promise")
            }, 1000)
        })
    }
    
    sleepPromise(); // 这里有分号
    
        (function () {
            console.log("hhh")
        })()
    
    PS C:\Users\lala\Downloads> node c.js
    hhh
    promise
    

    我会 C ,但是不会 JavaScript ,求指教,感谢!

    26 条回复    2022-05-26 21:14:01 +08:00
    Baymaxbowen
        1
    Baymaxbowen  
       2022-05-26 10:32:54 +08:00 via iPhone
    下面自执行函数的问题
    renmu123
        2
    renmu123  
       2022-05-26 10:34:37 +08:00 via Android
    大概是因为被解析成这样了 sleepPromise()(function...)()
    function..被当成了函数 sleeppromise 的参数,sleeppromise 又不是函数所以加报错了
    noe132
        3
    noe132  
       2022-05-26 10:35:44 +08:00
    a()
    (b)()
    其实是 a()(b)()
    a();
    (b)()
    才是 a();(b)()
    这是自动分号插入的机制。括号开头的行会有这个问题。
    wangxiang
        4
    wangxiang  
       2022-05-26 10:36:27 +08:00
    论写分号的重要性
    shintendo
        5
    shintendo  
       2022-05-26 10:36:34 +08:00
    搜索关键词:javascript ASI
    ipwx
        6
    ipwx  
       2022-05-26 10:40:54 +08:00
    horseInBlack
        7
    horseInBlack  
       2022-05-26 10:42:21 +08:00
    和 promise 没关系,这样写也就会报错

    function testFunction(){
    console.log('test')
    }

    testFunction()

    (function () {
    console.log("hhh")
    })()

    报的是 Uncaught TypeError: testFunction(...) is not a function
    也就是说 JS 认为 testFunction()返回了一个函数,然后这个函数执行的参数是

    function () {
    console.log("hhh")
    })(

    而不是预期的作为立即执行函数执行

    加上分号以后 JS 就知道那句话结束了,不会连在一起执行

    所以写代码的时候可以配合自动格式化工具,团队的话可以共用一套规则

    理论上来说加上分号减少了 JS 分析分词的步骤效率更高,减少歧义,而且自动执行的事也不费什么事
    TomatoYuyuko
        8
    TomatoYuyuko  
       2022-05-26 10:42:55 +08:00
    首先编译的时候浏览器会帮你补分号,然后识别你的代码,你的这块代码是:sleepPromise()(function () {console.log("hhh")})(),浏览器看你这么写麻了,他也不知道该怎么断句了,因为有歧义,所以要加
    daimubai
        9
    daimubai  
       2022-05-26 10:53:03 +08:00
    在大多数情况下,换行意味着一个分号。但是“大多数情况”并不意味着“总是”!

    JavaScript 无法确定是否真的需要自动插入分号的情况

    alert("Hello")

    [1, 2].forEach(alert);

    这种情况也会报错。

    来源 https://zh.javascript.info/
    Shy07
        10
    Shy07  
       2022-05-26 10:53:20 +08:00
    除了 ( ,还有 [ 和 `,这三个符号作为行首,建议前面加分号

    可以参考这个: https://standardjs.com/rules-zhcn.html#%E5%85%B3%E4%BA%8E%E5%88%86%E5%8F%B7
    shakukansp
        11
    shakukansp  
       2022-05-26 10:55:10 +08:00
    举个例子
    const fn = () => {
    return function () {}
    }

    fn()() 什么意思
    fn()(function () {
    console.log("hhh")
    })什么意思
    fn();()什么意思
    fn();(function () {
    console.log("hhh")
    })什么意思

    自己想想
    Justin13
        12
    Justin13  
       2022-05-26 10:55:59 +08:00 via Android
    不会走,就想跑,老老实实加分号不好么
    fenglala
        13
    fenglala  
    OP
       2022-05-26 10:56:02 +08:00
    看懂了,感谢大家的回复!

    我搞了个让不加分号也能跑起来的例子,虽然实际代码中想表达的不是这个意思,但是让我理解了:

    function returnFunction() {
    console.log('function return function return function')
    return () => {
    console.log('function return function')
    return () => {
    console.log('function')
    }
    }
    }

    returnFunction()
    (function () {
    console.log("hhh")
    })()
    Leviathann
        14
    Leviathann  
       2022-05-26 11:00:59 +08:00   ❤️ 3
    立即执行函数前面必须加分号
    molvqingtai
        15
    molvqingtai  
       2022-05-26 11:02:27 +08:00
    原因楼上都说了,建议使用 eslint 或 prettier ,如果配置无分号的风格,会自动在有歧义的语句上加上分号
    MegrezZhu
        16
    MegrezZhu  
       2022-05-26 11:04:45 +08:00
    所以说分号是多么重要……
    july1995
        17
    july1995  
       2022-05-26 11:09:48 +08:00 via Android
    14 楼加 1 。 立即执行函数前需要加分号。 具体为啥 老师讲了,没记住,就记住了一个结论了。
    Pastsong
        18
    Pastsong  
       2022-05-26 11:10:27 +08:00
    +(function () {})()
    前面写个+也是可以的,如果你真的很讨厌分号
    cheneydog
        19
    cheneydog  
       2022-05-26 11:15:33 +08:00
    学到了
    libook
        20
    libook  
       2022-05-26 11:21:48 +08:00
    https://262.ecma-international.org/5.1/#sec-7.9

    官方文档里面说明了自动补充分号的思路,你需要在省略分号的同时,确保代码不会产生歧义,自己做的话可能会有些心智负担,除非你用 StandardJS (注意这只是个工具名称,并不真的是 JS 的 Standard )之类的 Linter ,可以帮你自动规避不写分号会产生歧义的情况。

    但我个人推荐能写分号的地方都写分号,特别是可能会涉及到代码 minification 的情况,你源代码执行的时候可能自动补充分号是正常的,但 minify 之后很可能就会出现问题。

    分号的存在就是为了避免歧义的情况,因为 JS 不靠缩进和换行来表示表达式的终止或层级关系,有些情况下无法避免写分号,刻意不写分号的意义就不那么大了。
    wangtian2020
        21
    wangtian2020  
       2022-05-26 15:09:07 +08:00
    sleepPromise()(function () {console.log("hhh")})()

    sleepPromise()(/* something */)()

    如果你用 prettier 格式化一下,他就会变成这个样子

    很容易看出问题所在。根本原因是 JavaScript 语法的冲突
    ryougifujino
        22
    ryougifujino  
       2022-05-26 15:17:18 +08:00
    以前我也是坚定的分号党,其实无分号反而更简洁,后来成为了无分号党。只需要稍微注意一下就行了。简单来说就是“一行开头是括号或者方括号的时候加上分号就可以了”
    参考:
    JavaScript 语句后应该加分号么? - 尤雨溪的回答 - 知乎
    https://www.zhihu.com/question/20298345/answer/49551142
    ryougifujino
        23
    ryougifujino  
       2022-05-26 15:31:14 +08:00
    @ryougifujino #22 append 一下,还得加一个字符串模板开头。https://zhuanlan.zhihu.com/p/24612490
    tutou
        24
    tutou  
       2022-05-26 16:40:09 +08:00
    所以一般 js 插件自执行函数自己会加个分号
    weiwoxinyou
        25
    weiwoxinyou  
       2022-05-26 20:16:22 +08:00
    @fenglala op 的这个例子不是很符合你的原意,这个例子之所以能运行是因为符合了编译器的解析规则,题干中的代码本质上是未插入分号导致了
    f()
    (fx())()
    被解析成了 f()(fx())(), 在编译器解析你的代码时,从左向右读取,也就是说编译器的解析顺序是
    f() => // 1
    f()(fx()) => // 2
    f()(fx())() // 3
    在第一步中,你的函数本正确执行了,但是在第二步中,编译器认为第一步的结果是一个函数,后面括号内的是函数传递的参数,但是由于第一步运行完没有返回函数,因此第一步的返回值被定义为空,显然空值并不是函数,于是产生报错,产生报错后,错误向外层抛出,而顶层调用函数名叫 sleepPromise ,于是,该报错为 sleepPromise is not a function.

    op 的例子之所以能执行,是因为你返回了一个函数,这个函数里面可以被递归调用,递归返回的也是一个函数,刚好满足了被编译器解析的对一个函数进行 3 次调用的规则,本质上是将返回的匿名函数作为参数传递给了调用者,JS 里面与这种调用很相似调用的叫函数柯里化
    fenglala
        26
    fenglala  
    OP
       2022-05-26 21:14:01 +08:00
    @weiwoxinyou 谢谢你的详细讲解!又学到一个之前都没听说过的新东西 函数柯里化
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1022 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:56 · PVG 03:56 · LAX 12:56 · JFK 15:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.