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

js 怎么来写一个基类

  •  
  •   xiaomeimei 2017-09-12 10:25:59 +08:00 5823 次点击
    这是一个创建于 2976 天前的主题,其中的信息可能已经有所发展或是发生改变。
    • 最近想整理一下 js 代码,想用 java 或者 C# 的思想去写,发现好麻烦啊,写一个基类都痛苦。

    • 虽然 python 也是脚本语言,但它写起基类也没什么难度,接口形式的编程也很方便。

    • 设计模式在 js 上很难套用。什么工厂模式、外观模式统统都写不了

    • 比如,我想实现一个接口或者抽象基类,要求所有继承此类的子类都必须实现所有的方法。js 不知道怎么来写

    67 条回复    2017-09-13 09:11:38 +08:00
    mooncakejs
        1
    mooncakejs  
       2017-09-12 10:33:51 +08:00
    在 es 里接口声明比较麻烦,抽象基类还有办法,定义一个方法,实现直接 throw
    mooncakejs
        2
    mooncakejs  
       2017-09-12 10:34:08 +08:00
    上 typescript,和 c# 基本差不了多少了。
    thisisgpy
        3
    thisisgpy  
       2017-09-12 10:34:08 +08:00   1
    class gay {}
    arzusyume
        4
    arzusyume  
       2017-09-12 10:34:53 +08:00
    class Base {}
    class A extends Base {}
    murmur
        5
    murmur  
       2017-09-12 10:35:18 +08:00   1
    都上了 js 还在想设计模式 你中毒了。。js 要什么设计模式。。不都是推翻重来。。还要继承。。
    SuperMild
        6
    SuperMild  
       2017-09-12 10:36:15 +08:00
    js 本身不合适,如果不愿意享受原型链的乐趣,最好上 typescript
    donlxn22
        7
    donlxn22  
       2017-09-12 10:36:57 +08:00
    TypeScript
    lamada
        8
    lamada  
       2017-09-12 10:37:46 +08:00
    TypeScript 你值得拥有
    xiaomeimei
        9
    xiaomeimei  
    OP
       2017-09-12 10:42:43 +08:00
    @mooncakejs @thisisgpy @SuperMild @donlxn22 @lamada TypeScript 写完后还要「编译」成 Javascript,而且还比较麻烦
    xiaomeimei
        10
    xiaomeimei  
    OP
       2017-09-12 10:43:33 +08:00
    @murmur 但有些基础的东西确实感觉可以用设计模式
    murmur
        11
    murmur  
       2017-09-12 10:45:50 +08:00
    @xiaomeimei 就算是 ES6/TS 背后还是丑陋的原型链
    SuperMild
        12
    SuperMild  
       2017-09-12 10:48:10 +08:00
    @xiaomeimei 不麻烦,比手写模拟真 OO 方便很多了。
    xiaomeimei
        13
    xiaomeimei  
    OP
       2017-09-12 10:49:35 +08:00
    @murmur 是啊,背后相当丑陋。throw 的那种方法我想过了,但感觉好丑
    murmur
        14
    murmur  
       2017-09-12 10:50:22 +08:00
    @xiaomeimei 那就学习 vue,用 mixin 的概念代替 extend

    react 不也改成 createComponent 了么
    xiaomeimei
        15
    xiaomeimei  
    OP
       2017-09-12 10:50:57 +08:00
    @SuperMild 又要我装一下编辑器了
    zjsxwc
        16
    zjsxwc  
       2017-09-12 10:51:08 +08:00
    es5 基本不会去用继承(其实语法上也实现不了), 而是和 golang 一样通过组合来实现继承的作用, 比如我想要 obj2 拥有 obj1 的 methodB 能力, 可以这么写:

    ```
    function Class1(constructParamA, constructParamB){
    this.cOnstructParamA= constructParamA;
    this.cOnstructParamB= constructParamB;
    }
    Class1.prototype.methodA = function (){};
    Class1.prototype.methodB = function (){
    console.log(this.constructParamA+this.constructParamB);
    };


    function Class2(constructParamA){
    this.cOnstructParamA= constructParamA;
    }
    Class2.prototype.methodB = function (){
    this.constructParamA.methodB();
    };


    var obj1 = new Class1("foo","bar");
    var obj2 = new Class2(obj1);
    obj2.methodB();

    ```
    mooncakejs
        17
    mooncakejs  
       2017-09-12 10:51:45 +08:00
    @xiaomeimei 配上 vscode 就不麻烦了。
    xiaomeimei
        18
    xiaomeimei  
    OP
       2017-09-12 10:52:17 +08:00
    @murmur vue 已经在用了,用在网站上。而我们的浏览器扩展上用的是纯 js,直接换 vue,时间成本太高,划不来
    xiaomeimei
        19
    xiaomeimei  
    OP
       2017-09-12 10:58:18 +08:00
    @zjsxwc 这种方式只可以实现子类使用了父类的 MethodB 方法,但不能强制子类也拥有 MethodA 方法
    otakustay
        20
    otakustay  
       2017-09-12 11:00:12 +08:00
    @murmur react 改成 createComponent 了?我感觉要么我穿越回了以前,要么我刚从火星回来

    另外原型链丑陋在哪里了?
    Biwood
        21
    Biwood  
       2017-09-12 11:06:59 +08:00
    原型链继承怎么就不能用了?看了这么多层回复没一个像样的,根本不需要什么 ES6 语法,老老实实写代码会死么
    mooncakejs
        22
    mooncakejs  
       2017-09-12 11:12:54 +08:00
    @Biwood 来个 abstruct method 或者 interface 看看
    zjsxwc
        23
    zjsxwc  
       2017-09-12 11:13:25 +08:00
    @xiaomeimei #19 实现你这种需求的写法很多的,比如这么写:
    ```
    function Class1(constructParamA, constructParamB){
    this.cOnstructParamA= constructParamA;
    this.cOnstructParamB= constructParamB;
    }
    Class1.prototype.methodA = function (){
    console.log("methodA >>"+this.constructParamA+this.constructParamB);
    };
    Class1.prototype.methodB = function (){
    console.log(this.constructParamA+this.constructParamB);
    };


    function Class2(objOfClass1){
    this.objOfClass1 = objOfClass1;
    for (prop in objOfClass1) {
    this[prop] = objOfClass1[prop];
    //if ((typeof objOfClass1[prop]) == "function") {
    // this[prop] = objOfClass1[prop];
    //}
    }
    }


    var obj1 = new Class1("foo","bar");
    var obj2 = new Class2(obj1);
    obj2.methodA();
    obj2.methodB();
    ```
    bramblex
        24
    bramblex  
       2017-09-12 11:14:59 +08:00   2
    在很多语言里面,基类就是破裤子上面的补丁,用来补泛型这个坑的,你现在拿着一条没有破洞的裤子问补丁在哪?你让我们怎么回答?
    learnshare
        25
    learnshare  
       2017-09-12 11:17:10 +08:00
    TypeScript 认可度不低嘛
    maomaomao001
        26
    maomaomao001  
       2017-09-12 11:18:11 +08:00 via Android
    原型链明明很好的呀,哪里丑陋了,不过这个时代,直接用 ts, es6.es7 不更好嘛
    otakustay
        27
    otakustay  
       2017-09-12 11:23:07 +08:00
    另外几乎所有的设计模式都是面向接口来做的,基类?为什么一定要有基类?
    murmur
        28
    murmur  
       2017-09-12 11:51:17 +08:00
    @otakustay 打错了。。我以为新版改成 createReactComponent 了 我还没升级用的还是 15 的 es5 语法

    至于你说原型链丑陋不丑陋 你和正统的 oo 比一下看父子那些关系不用 trick 能实现不就可以了
    Tunar
        29
    Tunar  
       2017-09-12 11:53:06 +08:00 via Android
    js 怎样写一个鸡肋
    wobuhuicode
        30
    wobuhuicode  
       2017-09-12 11:57:33 +08:00
    JS 写继承:原型链继承 -> 构造函数继承 -> 组合继承 -> 寄生组合继承
    MinonHeart
        31
    MinonHeart  
       2017-09-12 11:58:22 +08:00
    @Tunar 鸡肋
    otakustay
        32
    otakustay  
       2017-09-12 12:10:57 +08:00
    @murmur 弱的是语言本身而不是原型链啊,class based OO 的所有功能在原型链上都可以做,trick 是语法层面上的工作,你不能怪罪于底下的概念对吧
    murmur
        33
    murmur  
       2017-09-12 12:18:40 +08:00
    @otakustay 我是有点不太喜欢语法糖的,浏览器里跑的不还都是 es5 语法么,super 字我记得是 es6 的糖

    也不太相信 code map,我都升级到 chrome 60.0 了,动不动 code map 断点打不上,断点起飞,所以我宁可用 es5 语法,至少出错的时候停住的位置就是出错的位置

    webpack1 打出来的东西还能停住断点,webpack2 的模块就全变成 eval("xxxxxxxxx")了,出错了断点都停不了。。

    回楼上一些,不好意思扯远了,我是不太喜欢前端盲目继承的,

    但是也不能说基类是泛型的坑,ts 是强类型,不喜欢基类 List<Object>你也可以,越强的约束管水平不好的越好,所以 java 能让一群水平一般的人堆起来做个大项目还能跑,在不考虑性能的前提下,当然对大牛无所谓

    见过最恶心的继承就是 EasyUI,dialog->window->panel,但是实际上谁会用 window 和 panel ?我需要的是 dialog=panel+button+header,而不是 dialog extends window extends panel
    Hanggi
        34
    Hanggi  
       2017-09-12 12:27:31 +08:00
    class Gay {
    SorM() {

    }
    }
    qiqico
        35
    qiqico  
       2017-09-12 12:32:00 +08:00
    @murmur webpack2 打断点也没问题的,看你选哪种 sourcemap 方式了
    murmur
        36
    murmur  
       2017-09-12 12:35:20 +08:00
    @qiqico 可以让 hmr 的时候和 webpack1 一样展开全部代码不用 eval 那种么

    sourcemap 是没问题 就是 50%的概率没用 不要说版本不一致我当然知道要刷新

    现在是不打断点怎么让他停在可以看出是那句出错的地方 就是 pause on exceptions
    SilentDepth
        37
    SilentDepth  
       2017-09-12 12:37:22 +08:00
    拿 OOP 的经验生套到 JS 上,不觉得这样有多高效率,JS 本身也不是干这个的。如果实在要接口,写 TS 就好了,背后被转成了什么样何须操那么大的心。

    @murmur #33
    ES 版本更迭这事儿就扯远了,历史包袱是客观存在的,但咱还得向前看不是。至少箭头函数在 Chrome 里跑得飞起(尽管那不是一个语法糖)。Sourcemap 的问题,确实有断点打不准的情况,但通常不会差太多,不至于开发调试进行不下去(除非你一行代码套了 N 层 Lambda )。Webpack 2 出来的也是可以调的,我确信是你的配置有问题或你的代码风格需要适应一下。

    最后说一句:Vue 大法好~
    Mutoo
        38
    Mutoo  
       2017-09-12 12:50:12 +08:00
    abstract 和 interface 是静态编译的语言在编译阶段的约束,作为脚本语言,只有在运行时才去处理这些东西没有什么意义。
    X41822N
        39
    X41822N  
       2017-09-12 13:03:14 +08:00
    ts
    wangyufeng
        40
    wangyufeng  
       2017-09-12 13:14:27 +08:00
    @SuperMild 乐趣?
    SuperMild
        41
    SuperMild  
       2017-09-12 13:21:16 +08:00
    @wangyufeng 哈哈哈,对,就是乐趣,对于很多情况来说,原型链已经够用,甚至比 OO 更简单、清晰。
    mcfog
        42
    mcfog  
       2017-09-12 13:42:36 +08:00
    都是图灵完备的语言,只有 JS 上不需要用的设计模式,不存在 JS 上实现不了的设计模式

    楼主需要抛开其他所有 OO 语言的经验来看 JS,市面上基于原型链的语言几乎只有 JS 这一家,而几乎没有设计模式或者讨论 OO 的文章会基于 JS 来聊,太多的关于 JS 的 OO 的文章也都是生搬硬套其他语言的做法来误人子弟

    回到楼主的比如,比如别的语言里你可能写 interface MyInterface {methodA(); methodB();} class MyClass implements MyInterface {.....} ,实际上在表达的是“给我 methodA 和 methodB 这两个方法,我帮你对接剩余的逻辑”,那么换到 JS 里,直接把接口能力作为依赖就行了

    class EsClass {
    constructor(methodA, methodB){}
    ...
    }
    sensui7
        43
    sensui7  
       2017-09-12 14:31:56 +08:00
    很讨厌楼主跟风用这个头像
    qiuyk
        44
    qiuyk  
       2017-09-12 14:47:01 +08:00
    @sensui7 我还以为你刚吐槽完 py 又来吐槽 js 哈哈哈

    老实说以前写 C++写 C#,老是纠结于怎么定义好基类呀接口

    自从写 js 以来好像就没想过这些事情
    xiaomeimei
        45
    xiaomeimei  
    OP
       2017-09-12 15:53:33 +08:00
    @mooncakejs 我试试吧。今天上午被禁了 1800s,回复太频繁了
    xiaomeimei
        46
    xiaomeimei  
    OP
       2017-09-12 15:54:13 +08:00
    @otakustay 面向接口是对的
    xiaomeimei
        47
    xiaomeimei  
    OP
       2017-09-12 15:57:39 +08:00
    @sensui7 哈哈~~~来咬我
    xiaomeimei
        48
    xiaomeimei  
    OP
       2017-09-12 16:02:24 +08:00
    @mcfog 是啊,不能生搬硬套,要根据语言特点来写
    sensui7
        49
    sensui7  
       2017-09-12 16:22:49 +08:00
    @xiaomeimei 深刻诠释了抄袭者的嘴脸, 我无所谓, 这本来就是恶搞的头像, 对比两个图片, 看细节
    yunying
        50
    yunying  
       2017-09-12 17:10:05 +08:00
    js 为啥不能用设计模式?我天天写代码都在用
    otakustay
        51
    otakustay  
       2017-09-12 17:29:02 +08:00
    @murmur 你说的这些都很有道理,但是却和你的问题无关
    原型链 - Javascript/ES - JS 引擎,这是 3 个层面的东西,你说的所有问题,都存在于语言和引擎之上,因此去推导原型链本身丑陋是不合适的,JS 不是唯一的基于原型的语言,比如 LUA 就要好看不少,R 和 Perl 加上原型相关的包后也不丑
    lizhenda
        52
    lizhenda  
       2017-09-12 18:29:10 +08:00
    ```Javascript
    // 刚好我之前项目想用写了个,供参考
    ```
    lizhenda
        53
    lizhenda  
       2017-09-12 18:30:22 +08:00
    /**
    * Javascript Inheritance Class
    *
    * 实现了继承和定义对象,初始化自动调用 ctor() 构造函数,自带一个简单的 clone() 拷贝函数
    * 每个 Class 都带有一个唯一 ID : _pid,每个 new 出来的对象也带一个唯一 ID : _uid (可以用于相同 Class 的不同对象)
    *
    * @author : zhenda.li
    */

    /**
    * @name ClassManager
    */
    var ClassManager = {
    pid: (0 | (Math.random() * 998)), // 每个 Class 的唯一 ID
    uid: (0 | (Math.random() * 998)), // 每个 New 出来的对象唯一 ID

    getPID: function () {
    return this.pid++;
    },

    getUID: function () {
    return this.uid++;
    }
    };

    (function () {
    var fnTest = /\b_super\b/;

    this.Class = function () {
    };

    /**
    * Create a new Class that inherits from this Class
    * @static
    * @param {object} prop
    * @return {function}
    */
    Class.extend = function (prop) {
    var _super = this.prototype;
    var prototype = Object.create(_super);
    var desc = {writable: true, enumerable: false, configurable: true};

    function Class() {
    this._uid = ClassManager.getUID();
    if (this.ctor) {
    this.ctor.apply(this, arguments);
    }
    }

    desc.value = ClassManager.getPID();
    Object.defineProperty(prototype, '_pid', desc);

    for (var name in prop) {
    var isFunc = isFunction(prop[name]);
    var override = isFunction(_super[name]);
    var hasSuperCall = fnTest.test(prop[name]);

    if (isFunc && override && hasSuperCall) {
    desc.value = (function (name, fn) {
    return function () {
    var tmp = this._super;
    this._super = _super[name];
    var ret = fn.apply(this, arguments);
    this._super = tmp;
    return ret;
    };
    })(name, prop[name]);
    Object.defineProperty(prototype, name, desc);
    } else {
    prototype[name] = prop[name];
    }
    }

    Class.prototype = prototype;

    desc.value = Class;
    Object.defineProperty(Class.prototype, 'constructor', desc);

    /**
    * 快捷设置属性方法
    * e.g : obj.attr({ x : 1, y : 2 });
    */
    Class.prototype.attr = function (attrs) {
    for (var key in attrs) {
    if (!isUndefined(this[key]))
    this[key] = attrs[key];
    }
    };

    /**
    * 每个对象自带拷贝方法
    * @return Class
    */
    Class.prototype.clOne= function () {
    var newObj = (this.constructor) ? new this.constructor : {};
    for (var name in this) {
    var copy = this[name];
    if ((typeof copy) === "Class") {
    newObj[name] = copy.clone();
    } else {
    newObj[name] = copy;
    }
    }
    return newObj;
    };

    Class.extend = arguments.callee;

    return Class;
    };
    })();
    lizhenda
        54
    lizhenda  
       2017-09-12 18:32:32 +08:00
    评论不能写 MD 么,至于用法,

    var Test = Class.extend({

    /**
    * 构造函数, 初始化
    */
    ctor: function () {
    this.func();
    },

    func: function () {

    }
    });
    lizhenda
        55
    lizhenda  
       2017-09-12 18:33:28 +08:00
    当然,最好的办法是用 es6,直接上 class
    azh7138m
        56
    azh7138m  
       2017-09-12 18:37:34 +08:00 via Android
    工厂模式怎么就不能用了。。。。阿里云国际的控制台字段校验那个部分就是工厂模式啊
    lijsh
        57
    lijsh  
       2017-09-12 18:45:32 +08:00
    littleshy
        58
    littleshy  
       2017-09-12 18:51:46 +08:00
    JS 是原型语言,不要用面向对象的思想去使用它。
    chegde
        59
    chegde  
       2017-09-12 19:35:53 +08:00 via iPhone
    TypeScript 大法好
    aleung
        60
    aleung  
       2017-09-12 20:13:31 +08:00 via Android
    不能说 js 不是 OO,js 是 prototype based OO
    aleung
        61
    aleung  
       2017-09-12 20:18:23 +08:00 via Android
    大家说面向对象的时候是不是只是在说 GOF 的设计模式呢?设计模式只是基于 OO 思想的一些使用场景,部分与语言特性相关。
    exoticknight
        62
    exoticknight  
       2017-09-12 20:19:49 +08:00
    请面向需求编程,不要为了编程而编程
    springmarker
        63
    springmarker  
       2017-09-12 20:27:36 +08:00
    用 js 写一个 jvm,然后在上面开发
    Pastsong
        64
    Pastsong  
       2017-09-12 20:33:41 +08:00
    原型链是非常吊的设计,不要用 Java 和 C++ 的思想写 JS
    libook
        65
    libook  
       2017-09-13 01:00:17 +08:00
    “想用 java 或者 C# 的思想去写 JS ”这注定会导致痛苦的体验。就像:
    “想用开火车的思想去开轮船”、
    “想用做馒头的思想做面包”……
    软件工程学中的面向对象编程思想是一种抽象思想,为了实现运用这种思想,Java 有 Java 的实现方式,C#有 C#的实现方式,JS 有 JS 的实现方式,为何不用 JS 自己的方式来实现这一思想呢?
    simo
        66
    simo  
       2017-09-13 08:57:01 +08:00
    理解设计思想,知道取舍;熟悉 js 语言特性,可自定义设计。
    其实 js 越来越适合 c#/java 等开发人员转入了。
    同时,js 语言特性的改变,逐渐的工程化,也增高入门门槛,可以筛掉很多前端混子
    rwecho
        67
    rwecho  
       2017-09-13 09:11:38 +08:00
    传统设计模式针对的是面向对象编程语言
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4418 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 05:34 PVG 13:34 LAX 21:34 JFK 00:34
    Do have faith in what you're doing.
    ubao msn 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