V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
autoxbc

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

  •  1
     
  •   autoxbc · Dec 3, 2018 · 7300 views
    This topic created in 2705 days ago, the information mentioned may be changed or developed.

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

    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 是针对动态绑定需求设计的,如果没有这个需求,就不要强行使用,哪怕表面上看上去很优雅

    Supplement 1  ·  Dec 3, 2018

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

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

    const math = {
        ... ,
        cot(val){    // <-- 自注册
            const tan = math.tan ;
            return 1/tan(val);
        },
    };
    
    const math = ( () => {
        ... ;
        const cot = val => 1/tan(val);
    
        return { ... , tan , cot }; // <-- 手动注册
    } )();
    
    Supplement 2  ·  Dec 4, 2018
    还有些同学偏楼到 OO , prototype , class , new 上的,这里和这些都没有关系

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

    这里隐患的本质是不当使用 this 对象,这种不当是种反模式,远离了 this 的初衷
    35 replies    2018-12-04 19:04:44 +08:00
    yaozeyuan93
        1
    yaozeyuan93  
       Dec 3, 2018   ❤️ 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  
       Dec 3, 2018
    个人认为在非对象里面用 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  
       Dec 3, 2018 via iPhone
    js 里的 this 是我见过的最恶心的东西之一。
    0xff0x77
        4
    0xff0x77  
       Dec 3, 2018
    个人觉得 js 少了很多东西,OO 很不成熟,所以我看到的大部分 js 库都是函数编程的。
    wangxiaoaer
        5
    wangxiaoaer  
       Dec 3, 2018
    const { sin , cos , tan } = math ;

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

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

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


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

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

    ```
    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  
       Dec 3, 2018
    有一个很核心的思路是,尽可能降低函数对外部环境的隐式依赖,同时函数执行时不应该隐式地影响外部环境。

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

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

    ----

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

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

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

    this 现在大部分情况作为函数的一个额外参数使用,而不是用于调用成员函数,访问成员变量。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1055 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 92ms · UTC 22:41 · PVG 06:41 · LAX 15:41 · JFK 18:41
    ♥ Do have faith in what you're doing.