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

还在纠结 JavaScript 的继承?给你们看看什么是黑科技

  •  
  •   bramblex · 2015-10-21 19:28:53 +08:00 · 7604 次点击
    这是一个创建于 3315 天前的主题,其中的信息可能已经有所发展或是发生改变。

    猜猜下面的代码你们猜会是什么效果?

    var A = Class('A', Object)
      .method('constructor', function(){
        this.name = 'a';
      });
    
    var B = Class('B', A);
    B.method('run', function(){
      console.log('run');
    });
    
    var C = B.extend('C')
      .method('run', function(a){
        console.log('run a: ', a);
      })
      .method('run', function(a,b){
        console.log('run a, b: ', a, b);
      });
    
    var c = C();
    c.run();
    c.run(1);
    c.run(1,2);
    

    BlxClass

    GitHub

    https://github.com/bramblex/BlxClass

    简介

    一个 JavaScript 面对对象的库,让你在 JavaScript 用上靠谱的面对对象特性。

    使用

    将本项目中的 dist/Class.js 复制到你的项目下

    1. 在 nodejs 中使用

    var Class = require('path/to/Class.js');
    var ClassA = Class('ClassA', Object);
    

    2. 在浏览器中使用

    <script src="path/to/Class.js"></script>
    <script>
        var ClassA = Class('ClassA', Object);
    </script>
    

    API

    1. Class( class_name, parent_class)

    Class 函数接受两个参数返回一个新类,第一个参数是新类的命名,第二个参数是继承自哪个类。如果并没有继承自别的类,那么直接写 Object 就好了。

    var ClassA = Class('ClassA', Object);
    var ClassB = Class('ClassB', ClassA);
    

    2. name

    类的名字

    var ClassA = Class('ClassA', Object);
    console.log(ClassA.name) // => ClassA
    var ClassB = Class('some name', ClassA);
    console.log(ClassB.name) // => some name
    

    3. parent

    类的父类

    var ClassA = Class('ClassA', Object);
    ClassA.parent === Object; // => true
    var ClassB = Class('ClassB', ClassA);
    ClassB.parent === ClassA; // => true
    

    4. method( method_name, function )

    定义方法,方法会被子类继承,并且能够重载。

    var ClassA = Class('ClassA', Object)
        .method('constructor', function(){
            // 构造函数
            this.name = 'no name';
        })
        .method('constructor', function(name){
            // 重载构造函数
            this.name = name;
        })
        .method('run', function(){
            // 普通方法
            console.log('run');
        })
        .method('run', function(a,b){
            // 重载上面定义的 run 方法
            console.log('run a, b: ', a, b);
        })
        .method('run', '*', function(){
            // 其他任意参数的情况
            console.log(arguments);
        });
    
    var a = ClassA();
    var b = ClassA('Li Lei');
    console.log(a.name); // => no name
    console.log(b.name); // => Li Lei
    a.run(); // => run
    a.run(1,2); // => run a, b: 1 2
    a.run(4,5,6); // => [4,5,6]
    a.run(7,8,9,0,1,2,3); // => [7,8,9,0,1,2,3]
    

    5. classmethod( method_name, function )

    定义类方法,类方法不会被子类继承,也不能重载。

    var ClassA = Class('ClassA', Object)
        .classmethod('run', function(){
            // 类方法
            console.log('class method run');
        });
    
    ClassA.run(); // => class method run
    

    6. extend( class_name )

    继承出新类。

    var ClassA = Class('ClassA', Object);
    
    // 下面两种写法是等价的
    var ClassB = Class('ClassB', ClassB);
    var ClassB = ClassA.extend('ClassB');
    

    7. alias( alias_name , method_name )

    给方法取别名

    var ClassA = Class('ClassA', Object)
        .method('run', function(){
            // 普通方法
            console.log('run');
        });
    
    ClassA.alias('aliasRun', 'run');
    
    var a = ClassA();
    a.run(); // => run
    a.aliasRun(); // => run
    a.run === a.aliasRun; // => true
    

    8. uper( method_name )

    调用父类方法

    var ClassA = Class('ClassA', Object)
        .method('run', function(){
            // 普通方法
            console.log('ClassA run');
        });
    
    var ClassB = ClassA.extend('ClassB')
        .method('run', function(){
            ClassB.uper('run').apply(this, arguments);
            console.log('ClassB run');
        });
    
    var ClassC = ClassB.extend('ClassC')
        .method('run', function(){
            ClassC.uper('run').apply(this, arguments);
            console.log('ClassC run');
        });
    
    var c = ClassC();
    a.run();
    // => ClassA run
    // => ClassB run
    // => ClassC run
    
    61 条回复    2015-10-22 23:31:36 +08:00
    timeship
        1
    timeship  
       2015-10-21 19:35:06 +08:00
    嘿嘿 在这也能碰见你
    Gonster
        2
    Gonster  
       2015-10-21 19:36:52 +08:00 via iPhone
    啦啦啦, lz 又放黑魔法啦๛ก(ー̀ωー́ก)
    bramblex
        3
    bramblex  
    OP
       2015-10-21 19:43:10 +08:00 via Smartisan T1
    @timeship 不然咧~
    bramblex
        4
    bramblex  
    OP
       2015-10-21 19:45:36 +08:00 via Smartisan T1
    @Gonster 我的黑科技从来都是很逆天的
    lizhenda
        5
    lizhenda  
       2015-10-21 19:49:23 +08:00
    很厉害的样子,确实不错。
    qdwang
        6
    qdwang  
       2015-10-21 19:49:39 +08:00 via Android
    这科技 黑在哪里ǖ
    domty
        7
    domty  
       2015-10-21 19:53:31 +08:00
    只看了第一个 demo
    lz 是用 js 实现了单继承和方法重载?
    iwege
        8
    iwege  
       2015-10-21 19:56:46 +08:00
    `(function(a,b){}).length`?
    bramblex
        9
    bramblex  
    OP
       2015-10-21 19:59:42 +08:00
    @qdwang

    /w\ 其实代码里面用了很多黑科技的
    bramblex
        10
    bramblex  
    OP
       2015-10-21 20:01:13 +08:00
    @lizhenda 必须不错~ /w\
    joyee
        11
    joyee  
       2015-10-21 20:02:53 +08:00
    然而 ES6 已经有 class 和 extend 了……以及更加干净的新语法
    icymorn
        12
    icymorn  
       2015-10-21 20:10:31 +08:00
    @joyee 恩, es6 的 class 很惊艳,不过 @bramblex 还有一些更新的特性,比如根据参数长度重载…… 当然,还是要说,函数式语言好,模板匹配才是王道 ( 逃
    zrp1994
        13
    zrp1994  
       2015-10-21 20:11:01 +08:00 via iPhone
    黑在哪里…
    bramblex
        14
    bramblex  
    OP
       2015-10-21 20:13:43 +08:00
    @joyee

    1 ,没好看也没好用多少。
    2 ,不能把类打开添加新方法。
    3 ,不能重载。
    4 ,没有配套的工具,比如最简单的,假设父类对我是一个黑箱,我需要调用父类的方法怎么办?
    5 ,其实我的这个才更符合人们对基于类继承的面向对象的认知。
    6 ,如果 JavaScript 要好好做成基于原型的面向对象语言,那么参考 Io lang ,我也没必要折腾这些了。
    bramblex
        15
    bramblex  
    OP
       2015-10-21 20:17:42 +08:00
    @icymorn JS 在大杂烩这条路上越走越远了
    chendeshen
        16
    chendeshen  
       2015-10-21 20:18:45 +08:00
    黑科技 @!!!
    bramblex
        17
    bramblex  
    OP
       2015-10-21 20:19:15 +08:00
    @zrp1994 js 下的方法重载……还不够黑
    bramblex
        18
    bramblex  
    OP
       2015-10-21 20:20:59 +08:00
    @domty

    是的,单继承和方法重载。

    继承出来的对象能够用 instanceof 检验类型
    bramblex
        19
    bramblex  
    OP
       2015-10-21 20:21:15 +08:00
    @iwege 对的 /w\ 是这样玩的
    icymorn
        20
    icymorn  
       2015-10-21 20:21:25 +08:00
    @bramblex es6 可重载,而且对于我来说,看起来和其它语言里面的类声明更相近也更容易理解。
    父类是个黑箱…… 这本来就是面向对象的思路吧,不关心黑箱,当然,要是有个逗逼写了个类太难用,只好 hack 回去就没法说了。
    es6 目前支持度着急,浏览器上自己实现类的确按照你这样有很多便利。
    haibocui
        21
    haibocui  
       2015-10-21 20:21:40 +08:00
    流弊
    bramblex
        22
    bramblex  
    OP
       2015-10-21 20:22:43 +08:00
    @icymorn 我的目的一向是到哪里都能用 /w\。
    hronro
        23
    hronro  
       2015-10-21 20:23:36 +08:00
    这个确实给力!
    iwege
        24
    iwege  
       2015-10-21 21:40:09 +08:00   ❤️ 1
    @bramblex

    1 ,没好看也没好用多少。
    从 stage 0 开始看起. import 反正我已经吐槽过了。但是 class 绝对是一个好帮手。

    2 ,不能把类打开添加新方法。
    prototype 还是可以访问的。也可以用子类的方式扩展。还有 es7 的 decorators 草稿标准。


    3 ,不能重载。
    可以

    4 ,没有配套的工具,比如最简单的,假设父类对我是一个黑箱,我需要调用父类的方法怎么办?
    if(super.fn){super.fn()}

    5 ,其实我的这个才更符合人们对基于类继承的面向对象的认知。
    有争议

    6 ,如果 JavaScript 要好好做成基于原型的面向对象语言,那么参考 Io lang ,我也没必要折腾这些了。
    我觉得是一个方向性错误。
    iwege
        25
    iwege  
       2015-10-21 21:43:03 +08:00
    https://gist.github.com/iwege/d77cca95bb1d8c8a41f9

    上面是用 stage 0 的标准模仿楼主写的简单的方法。逻辑上可能有副作用,只是证明可行性。

    上面实际执行的结果如下:
    1
    print all {}
    print all { '0': 1, '1': 2, '2': 3 }
    [Function: fn2]
    fn2, print from base
    fn2 from Class A
    joyee
        26
    joyee  
       2015-10-21 22:31:30 +08:00
    @bramblex 2 3 4 均不成立……原因同 @iwege

    1 总归是比现有的不修改语法的其他方法好看些 5 6 他们( TC 39 )志不在此 支持度有 transpiler 帮手……
    banri
        27
    banri  
       2015-10-21 22:46:52 +08:00
    @joyee 拜大神
    iamcho
        28
    iamcho  
       2015-10-21 22:48:06 +08:00
    mark
    bramblex
        29
    bramblex  
    OP
       2015-10-21 22:54:19 +08:00
    @joyee

    那还不如造新语言来的靠谱……真的……
    bramblex
        30
    bramblex  
    OP
       2015-10-21 22:55:30 +08:00
    @iwege

    支持度欠佳, chrome 上面还硬性要求 strict mode
    bramblex
        31
    bramblex  
    OP
       2015-10-21 22:58:51 +08:00
    @joyee

    志不在此那最好,把基于类继承的面向对象忘的干干净净,老老实实看看别人 Io lang 基于原型链继承多正常。

    现在这个大杂烩搞得乱七八糟的,还一堆不兼容问题。好歹我这个扔那里都能用
    liangqing
        32
    liangqing  
       2015-10-21 23:09:52 +08:00
    提醒楼主,函数的 name 属性是个只读属性,所以设置也没用,有的浏览器还会抛异常。
    bramblex
        33
    bramblex  
    OP
       2015-10-21 23:19:44 +08:00
    @liangqing

    以前留的坑,忘了改了
    icymorn
        34
    icymorn  
       2015-10-21 23:22:16 +08:00
    @iwege
    第一次接触这种写法,学到了,非常感谢。
    iwege
        35
    iwege  
       2015-10-22 00:14:01 +08:00
    @bramblex
    这只是一个简单的 demo ,特性上我觉得没有什么,只是觉得语法上你的这种写法很啰嗦而已,部分实现的话新特性也可以支持,比如上面 fn 重复定义的问题,使用我的那个方法也可以简单的解决掉。上面的案例已经修改了。
    https://gist.github.com/iwege/d77cca95bb1d8c8a41f9

    同时尝试了自定义 class ,估计有点麻烦,但是也不是不可行。 overloadClass 可以支持拿到 class function 本身。

    至于所谓的支持度,在有 transpiler 的情况下我向来不关心,我关心的是前端工程化,编码高效化以及问题定位速度。就像习惯了 coffee 或者 om 之后你让我再回去写 es5 的 js ,我是断然不乐意的。我的研究也只要能在 V8 里面跑就 OK 。



    @icymorn

    我也是第一次尝试用这个特性来做,因为工程化的话这种灵活性可能会是一种麻烦。当然既然用了 stage 0 干脆连`::`的写法也一起尝试用了。不过楼主的特性还不能完全支持,不知道混用两种方法能不能达到完全处理。
    miro
        36
    miro  
       2015-10-22 01:02:45 +08:00
    少用继承...
    HowardMei
        37
    HowardMei  
       2015-10-22 01:12:08 +08:00
    好像很黑科技的样子,比 Typescript 的 Overload 强大,可人家是故意不让用 :)
    fo2w
        38
    fo2w  
       2015-10-22 01:52:23 +08:00
    对于 lz 这种闲的蛋疼的人我只想说...


    已粉...
    joyee
        39
    joyee  
       2015-10-22 02:04:49 +08:00 via Android   ❤️ 1
    @bramblex 世界上只有两种语言,没人用的和被人骂的。。。论语言设计, 随便路边拦一个谁都能完爆 js ,又何苦拿它开刀。正是因为用的人太多,才不好丢掉历史包袱啊。

    另外你用 transpiler 能有多少兼容性问题,转译出来的代码也就跟人手写这种类的差不多。。。
    adspe
        40
    adspe  
       2015-10-22 09:37:03 +08:00
    JavaScript 适用的场景用一般的几种模式的继承已经够了。如果放在一些 scale 比较大的场景会有很多意想不到的后果。
    bramblex
        41
    bramblex  
    OP
       2015-10-22 10:07:13 +08:00
    @joyee

    也是哈 /w\, 就是我比较喜欢造轮子
    bramblex
        42
    bramblex  
    OP
       2015-10-22 10:08:00 +08:00
    @adspe

    比如捏?什么意想不到的后果?
    bramblex
        43
    bramblex  
    OP
       2015-10-22 10:08:38 +08:00
    @iwege

    /w\ 好吧……
    bramblex
        44
    bramblex  
    OP
       2015-10-22 10:09:32 +08:00
    @miro

    面向对象语言我不用继承用什么?

    难道有 typeclass 给我用嘛……
    coolicer
        45
    coolicer  
       2015-10-22 10:18:44 +08:00
    好东西,但是很少用继承。
    magicdawn
        46
    magicdawn  
       2015-10-22 11:36:05 +08:00
    看了 xxxScript 源码, 炒鸡反感~没想到 LZ 还拿出说~
    wdhwg001
        47
    wdhwg001  
       2015-10-22 12:07:22 +08:00
    还是觉得 Blackscript 法比较好,就是阮一峰提到的所谓 Minimalist Approach …
    http://www.gabordemooij.com/blackscript

    不得不说.method 有点太丑了。
    脑补一个语法的话…
    https://gist.github.com/wdhwg001/9f83c9c21754d538886e

    大致想了一下可以用 get:function()和 set:function()一类的诸多黑魔法实现…
    bramblex
        48
    bramblex  
    OP
       2015-10-22 13:01:43 +08:00
    @wdhwg001

    丑是丑,但是用起来还算顺手。如果要更简洁的语法的话,只能改语言语法本身了。
    bramblex
        49
    bramblex  
    OP
       2015-10-22 13:05:09 +08:00
    @magicdawn

    嗯,等你造一个更好用的我就用你的。 OwO
    zythum
        50
    zythum  
       2015-10-22 13:05:54 +08:00
    一般吧。这个多态判断不麻烦。
    比较难的设计是访问权限 publish private, 和 优雅的 super 的设计 用 apply 的方式却是可用而且实用,但是太丑了了
    bramblex
        51
    bramblex  
    OP
       2015-10-22 13:08:40 +08:00
    @zythum

    其实重点不是难不难,判断麻不麻烦,而是好像没什么人想到……
    zythum
        52
    zythum  
       2015-10-22 13:09:52 +08:00
    @bramblex 什么没有人想到啊。老早就有了。只是你没有发现而已...
    ggiiss
        53
    ggiiss  
       2015-10-22 13:30:36 +08:00
    请参考 John Resig 大神 08 年的作品 http://ejohn.org/blog/simple-javascript-inheritance/
    catface
        54
    catface  
       2015-10-22 17:28:05 +08:00
    项目描述是“ JavaScript 的面对对象库”
    ianva
        55
    ianva  
       2015-10-22 17:53:23 +08:00
    以前到热衷鼓捣这些东西, class 实现过这些,还有 mixin , attribute , aop 的功能,不过现在看来,除非整套体系都是自己造的,现在意义没这么大,这个时代已经不是基础工具造轮子的时代了,语言层面的标准化在是重要的,比如之前的 yui 3 的 base , widget 都很强大,但现在已经不维护了。
    aivier
        56
    aivier  
       2015-10-22 17:59:37 +08:00
    这么久了,我还是不知道“面相对象”是个什么。。。
    Justineo
        57
    Justineo  
       2015-10-22 18:24:37 +08:00
    黑科技黑在哪里……实现了啥独有的功能?根据参数长度重载?随便 Google 一下 JavaScript overloading ,上来就有 John Resig 的这篇:

    http://ejohn.org/blog/javascript-method-overloading/ 你自己看看时间吧……

    再搜下会发现也有根据参数类型重载的。

    还有你给的这些示例代码,命名一会儿全小写,一会儿下划线分隔,一会儿驼峰。还有调用父类方法的那个 `uper` 我猜是 `upper` 拼错了吧,但其实应该是 `super`。
    bramblex
        58
    bramblex  
    OP
       2015-10-22 18:40:00 +08:00
    @Justineo

    首先命名规范你喷个毛线?类 开头大写驼峰, 方法开头小写驼峰, 变量小写下划线这个有槽点我没看出来那里有槽点?

    其次,我自己什么都没看就实现了跟 John Resig 一样的东西,那你的意思是不是我跟 John Resig 大神一个水平啊?那你有资格喷个毛?

    我造一个玩具碍着你事了?无语
    bramblex
        59
    bramblex  
    OP
       2015-10-22 18:53:55 +08:00
    @ggiiss

    看这个大神的作品对我没有任何提高……
    kokutou
        60
    kokutou  
       2015-10-22 19:12:29 +08:00
    @aivier 在 IDE 里面,万物皆可打点,然后出一大堆东西。。。
    Justineo
        61
    Justineo  
       2015-10-22 23:31:36 +08:00
    @bramblex 这点喷就受不了了,真的是好可爱。你怎么不反驳一下 uper 是什么啊。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3386 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 00:50 · PVG 08:50 · LAX 16:50 · JFK 19:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.