发现一种反模式写法,问问大家的意见 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
autoxbc
V2EX    Javascript

发现一种反模式写法,问问大家的意见

  •  1
     
  •   autoxbc 2018-12-03 01:00:58 +08:00 6957 次点击
    这是一个创建于 2505 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有如下一小段代码,能否发现这里有什么问题?

    const math = { sin(val){ return Math.sin(val); }, cos(val){ return Math.cos(val); }, tan(val){ const sin = this.sin ; const cos = this.cos ; return sin(val)/cos(val); }, }; 

    一个粗心的程序员可能会这么续写

    const { sin , cos , tan } = math ; console.log( tan(.5) ); 

    这里就会报错了,因为 tan 方法内部需要的 this 对象在解构赋值的时候丢失了。要想正确使用,必须把代码写成这样的丑陋形式

    let { sin , cos , tan } = math ; [ sin , cos , tan ] = [ sin , cos , tan ].map( e => e.bind(math) ); console.log( tan(.5) ); 

    不过,其实只要在开始的时候,把这两行

    const sin = this.sin ; const cos = this.cos ; 

    写成

    const sin = math.sin ; const cos = math.cos ; 

    这样,就消除了隐患。显然,对于 tan 方法,动态绑定 this 对象是没有意义的,因为这个值既不会也不允许改变

    总结一下就是,this 是针对动态绑定需求设计的,如果没有这个需求,就不要强行使用,哪怕表面上看上去很优雅

    第 1 条附言    2018-12-03 15:26:54 +08:00

    看到好几个同学说字面量定义本身就是种反模式,并用闭包演示如何解决持有变量

    可是,闭包无法实现字面量写法提供的自注册效果,像这样

    const math = { ... , cot(val){ // <-- 自注册 const tan = math.tan ; return 1/tan(val); }, }; const math = ( () => { ... ; const cot = val => 1/tan(val); return { ... , tan , cot }; // <-- 手动注册 } )(); 
    第 2 条附言    2018-12-04 17:11:20 +08:00
    还有些同学偏楼到 OO , prototype , class , new 上的,这里和这些都没有关系

    这里说的是,当你提供给别人一个静态方法集时,如果这些方法互相引用,那么不要用动态绑定 this 的方法去查找父对象。这样你的方法就可以直接解构赋值,消除使用者的隐患

    这里隐患的本质是不当使用 this 对象,这种不当是种反模式,远离了 this 的初衷
    35 条回复    2018-12-04 19:04:44 +08:00
    yaozeyuan93
        1
    yaozeyuan93  
       2018-12-03 02:32:49 +08:00   1
    如果一定依赖 this 的话, 为什么不在初始化时直接使用呢

    ```Javascript
    const math = {
    sin(val){
    return Math.sin(val);
    },
    cos(val){
    return Math.cos(val);
    },
    tan(val){
    const sin = math.sin ;
    const cos = math.cos ;
    return sin(val)/cos(val);
    },
    };
    ```
    sker101
        2
    sker101  
       2018-12-03 04:53:50 +08:00
    个人认为在非对象里面用 this 没啥意义, 纯粹自找 bug
    真要那么用可以

    ```

    const math = {
    sin(val){
    return Math.sin(val);
    },
    cos(val){
    return Math.cos(val);
    },
    tan(val){
    const sin = this.sin ;
    const cos = this.cos ;
    return sin(val)/cos(val);
    },
    };

    const { sin, cos, tan } = math;


    console.log(tan.call(math, 1));


    ```
    zn
        3
    zn  
       2018-12-03 07:50:40 +08:00 via iPhone
    js 里的 this 是我见过的最恶心的东西之一。
    0xff0x77
        4
    0xff0x77  
       2018-12-03 08:00:19 +08:00
    个人觉得 js 少了很多东西,OO 很不成熟,所以我看到的大部分 js 库都是函数编程的。
    wangxiaoaer
        5
    wangxiaoaer  
       2018-12-03 08:08:54 +08:00
    const { sin , cos , tan } = math ;

    说实在的,非常恶心这种写法,可能是因为 java 写多了吧。

    因为这样子在阅读代码的时候根本就不知道 sin cos tan 是本文件定义的还是引入的,甚至是哪个库、哪个包都不清楚,需要去 import 里面看。

    而 math.sin math.cos 就明显多了。
    Jex
        6
    Jex  
       2018-12-03 08:22:36 +08:00   1
    @wangxiaoaer Java 里面 import static 可是后来才加的特性,ES 也照抄了。真的,对 JS 程序员来讲多学一点 Java 是有好处的,然后记得换一个支持 Go to definition 的 IDE。
    ChefIsAwesome
        7
    ChefIsAwesome  
       2018-12-03 08:53:48 +08:00   1
    生搬硬套,半桶水,以为写了个对象就叫面向对象了。
    azh7138m
        8
    azh7138m  
       2018-12-03 09:14:52 +08:00 via Android
    @wangxiaoaer 就一个解构,C++都有的写法,为啥会恶心。
    解构出来的场景大部分是个局部变量,方便优化。
    zhyl
        9
    zhyl  
       2018-12-03 09:20:23 +08:00 via Android   1
    既然要持有相关量,为何不用闭包?
    shintendo
        10
    shintendo  
       2018-12-03 09:26:57 +08:00
    我选择
    const math = (function() {
    const sin = funtion(val) {
    return Math.sin(val)
    }
    cosst cos = function(val) {
    return Math.cos(val)
    }
    const tan = function(val) {
    return sin(val) / cos(val)
    }
    return {sin, cos, tan}
    })()
    petelin
        11
    petelin  
       2018-12-03 09:28:54 +08:00 via iPhone
    其实我根本不知道搞这些东西在干什么,尤其是写了 Go 之后。
    1010543618
        12
    1010543618  
       2018-12-03 09:36:50 +08:00
    Jex
        13
    Jex  
       2018-12-03 09:41:27 +08:00   1
    @wangxiaoaer 差点忘了黑一次 NPM,看看 lodash-modularized,就 NPM 这种风气,import module; module.method 这种写法?不存在的!


    https://www.npmjs.com/search?q=keywords:lodash-modularized
    wly19960911
        14
    wly19960911  
       2018-12-03 09:47:54 +08:00
    楼主的代码就有问题了,没有哪个语言直接定义对象来定义 this 的关系。这根本不是面向对象好不好

    js 就因为对象定义就直接用对象? 你这个写法完全不符合闭包的策略 /
    FakeLeung
        15
    FakeLeung  
       2018-12-03 09:54:53 +08:00
    用 class 来写就好懂很多。
    wangxiaoaer
        16
    wangxiaoaer  
       2018-12-03 10:13:59 +08:00
    @Jex #6 static 特性用的不多,而且跟 node 这种类似,不直观,基本不用。
    wangxiaoaer
        17
    wangxiaoaer  
       2018-12-03 10:15:45 +08:00
    @azh7138m #8 局部变量我是认可的,比如在一个局部函数里面,但是现在看看 npm 上的库,多少事直接在库文件根结构直接就用这中写法,美其名曰只引入需要的库,减少依赖,也是醉了。
    Justin13
        18
    Justin13  
       2018-12-03 10:50:45 +08:00 via Android
    非对象,非 new 的用例为啥要用 this?
    知道有坑就绕,而不是学如何从坑里爬出来。
    AV1
        19
    AV1  
       2018-12-03 11:16:18 +08:00 via Android
    非 OO 开发时,JS 的 this 毫无使用的必要。
    wxsm
        20
    wxsm  
       2018-12-03 11:53:57 +08:00
    这就是所谓的引战帖吧。明明是 LZ 自己写的一坨翔,本来是静态方法的东西偏要加 this,还说自己写的“看上去很优雅”,啧啧。
    tommyZZM
        21
    tommyZZM  
       2018-12-03 12:09:13 +08:00
    正确的做法是这样。

    ```
    function sin(val) {
    return Math.sin(val);
    }

    function cos(val) {
    return Math.cos(val);
    }

    function tan(val) {
    return sin(val)/cos(val);
    },

    const math = {
    sin, cos, tan
    };
    ```

    什么`this`啊, `Function.prototype.bind`啊其实都是糟粕,能不用就不用
    tommyZZM
        22
    tommyZZM  
       2018-12-03 12:14:15 +08:00
    有一个很核心的思路是,尽可能降低函数对外部环境的隐式依赖,同时函数执行时不应该隐式地影响外部环境。

    函数之间的关联应该是通过参数的传入联系起来的。

    使用了 this 实际上就依赖了调用环境上下文,是一种不好的做法。
    no1xsyzy
        23
    no1xsyzy  
       2018-12-03 13:31:17 +08:00
    @ChefIsAwesome 对啊,这个明显是函数式写法啊,为什么要加 this
    no1xsyzy
        24
    no1xsyzy  
       2018-12-03 13:34:45 +08:00
    “论如何同时激怒两拨人”
    写一个函数式的代码,并且用上 this 并开始谈论对象。

    ----

    const sin = this.sin ;
    const cos = this.cos ;

    这两行直接删去即可。
    66beta
        25
    66beta  
       2018-12-03 13:52:21 +08:00 via Android
    js 严格来讲没有“对象”
    class 也只是模拟,将来指不定就成了糟粕
    marcong95
        26
    marcong95  
       2018-12-03 15:29:23 +08:00
    @66beta js 是没有类吧,js 无处不在的对象,你总不能说那是个哈希表吧
    autoxbc
        27
    autoxbc  
    OP
       2018-12-03 15:37:00 +08:00
    @no1xsyzy #24 删去能运行刚好是碰运气,后面写成 const { tan } = math 不引入 sin cos 就无效了
    66beta
        28
    66beta  
       2018-12-03 18:37:50 +08:00 via Android
    @marcong95 你说的对,我话不严谨,是 OOP 里的 oo
    mskf
        29
    mskf  
       2018-12-03 19:40:38 +08:00
    你是说静态方法只能在静态方法中被引用吗
    rabbbit
        30
    rabbbit  
       2018-12-03 20:17:35 +08:00
    royzxq
        31
    royzxq  
       2018-12-03 23:08:35 +08:00
    意义不明。
    zealot0630
        32
    zealot0630  
       2018-12-04 06:06:21 +08:00
    楼主对 OO 的理解有严重问题,OO 设计中,成员函数是放在 prototype 或 meta 里面,而不是对象里面。
    cyssxt
        33
    cyssxt  
       2018-12-04 08:16:59 +08:00 via iPhone
    math 本来就封装好的 为什么再来一次
    nullcc
        34
    nullcc  
       2018-12-04 08:43:42 +08:00   1
    说 JS this 恶心的应该是那些完全不理解 JS 对象模型的人
    zealot0630
        35
    zealot0630  
       2018-12-04 19:04:44 +08:00
    @nullcc

    JS 的 this 是恶心,这点没得洗,都是历史遗留的包袱。

    this 现在大部分情况作为函数的一个额外参数使用,而不是用于调用成员函数,访问成员变量。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2739 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 07:38 PVG 15:38 LAX 00:38 JFK 03:38
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86