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

Javascript 设计模式 一些笔记

  •  
  •   cijian 2018-06-27 11:29:45 +08:00 3441 次点击
    这是一个创建于 2664 天前的主题,其中的信息可能已经有所发展或是发生改变。

    设计模式

    抽象类

    抽象类的表现

    1. 不能被实例,只能被继承
    2. 最少有一个抽象方法(多态的具体体现)
    // 汽车抽象类,当使用其实例对象的方法时会抛出错误 var Car = function() {}; Car.prototype = { getPrice: function() { return new Error("抽象方法 getPrice 不能调用。"); }, getSpeed: function() { return new Error("抽象方法 getSpeed 不能调用。"); } }; 

    上面 Car 类其实什么都没有做,但用原型的方法还会直接报错,这一特点非常有必要,因为总会有一些子类去继承父类,这些父类经常会去定义一些必要的方法,却没有具体的实现.

    一旦子类创建了一个对象,但是子类没有重写父类的方法而被调用,就会直接报错,这个对大型项目中对子类的约束是非常有必要的,代码页更加清晰

    单一职责原则

    ... 

    里氏替换原则

    子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法 继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能会产生故障。

    function Foo(){ this.aa='sdf'; } Foo.prototype.func1 = function(a,b){ return a-b; }; function Sub(){ } Sub.prototype = new Foo(); var demo = new Sub(); console.log('100-50='+demo.func1(100,50)); //运行结果: //100-50=50 //后来,我们需要增加一个新的功能:完成两数相加,然后再与 100 求和,由类 B 来负责。即类 B 需要完成两个功能: function Sub1 (){ } Sub1.prototype = new Foo(); Sub1.prototype.func1 = function(a,b){ return a+b; }; Sub1.prototype.func2 = function(a,b){ return this.func1(a,b)+100; }; var demo1 = new Sub1(); console.log('100-50='+demo1.func2(100,50)) //运行结果: //100-50=250 

    我们发现原本运行正常的相减功能发生了错误。原因就是类 Sub1 在给方法起名时无意中重写了父类的方法,造成所有运行相减功能的代码全部调用了类 Sub1 重写后的方法,造成原本运行正常的功能出现了错误。在本例中,引用基类 Foo 完成的功能,换成子类 Sub1 之后,发生了异常。在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。如果非要重写父类的方法,比较通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖、聚合,组合等关系代替。

    里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下 4 层含义: 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。 子类中可以增加自己特有的方法。 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。 看上去很不可思议,因为我们会发现在自己编程中常常会违反里氏替换原则,程序照样跑的好好的。所以大家都会产生这样的疑问,假如我非要不遵循里氏替换原则会有什么后果? 后果就是:你写的代码出问题的几率将会大大增加。

    参考文档:http://blog.csdn.net/zhengzhb/article/details/7281833

    开闭原则

    function Foo(){ } Foo.prototype.getPerson = function(name){ if(name==='tom'){ return { name:"tom", age:'20' } }else if(name==="jack"){ return { name:"tom", age:'20', sex:"boy" } } } console.log(new Foo().getPerson('tom')); //这个时候 如果我要获取名为 lily 这个人的属性,就需要更改 getPerson,这样所有调用该接口的对象都会受到影响,这个时候我们如何来重构 function Foo(){ } Foo.prototype.persOnInfo= function(){};//写一个抽象方法 Foo.prototype.getPerson = function(){ return this.personInfo(); }; var a = new Foo(); a.persOnInfo=function(){ return { name:'lily', age:20, sex:'girl' } }; console.log(a.getPerson()); 

    状态模式

    状态模式( State )允许一个对象在其内部状态改变的时候改变它的行为,对象看起来似乎修改了它的类。

    状态模式的使用场景也特别明确,有如下两点: 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。(有些对象通常会有好几个状态,在每个状态都只可以做当前状态才可以做的事情,而不能做其它状态能做的事儿) 一个操作中含有大量的分支语句,而且这些分支语句依赖于该对象的状态。状态通常为一个或多个枚举常量的表示。

    一、有限状态机

    状态总数( state )是有限的。 任一时刻,只处在一种状态之中。 某种条件下,会从一种状态转变( transition )到另一种状态。

    // 状态机 var FSM = { off: { buttonWasPressed: function() { console.log("关灯"); this.button.innerHTML = "下一次按我是开灯"; // 这是 Light 上的属性!!! this.currState = FSM.on; // 这是 Light 上的属性!!! } }, on: { buttonWasPressed: function() { console.log("开灯"); this.button.innerHTML = "下一次按我是关灯"; this.currState = FSM.off; } }, }; var Light = function() { this.currState = FSM.off; // 设置当前状态 this.button = null; }; Light.prototype.init = function() { var button = document.createElement("button"); self = this; button.innerHTML = "已关灯"; this.button = document.body.appendChild(button); this.button.Onclick= function() { // 请求委托给 FSM 状态机 self.currState.buttonWasPressed.call(self); } } var light = new Light(); light.init(); 
    9 条回复    2018-06-28 01:00:14 +08:00
    qsnow6
        1
    qsnow6  
       2018-06-27 11:33:45 +08:00
    看的好难受
    ipwx
        2
    ipwx  
       2018-06-27 11:36:3 +08:00
    都 8102 年了,为啥不用 Babel 和 ES6 ?完整 class 语法。。。
    kingf2e
        3
    kingf2e  
       2018-06-27 11:57:19 +08:00
    是做前端开发的吗?技术如何?
    我们公司目前主技术栈是 vue.js ,目前缺前端 leader。有兴趣吗?
    我的 VX:ak phil,(去掉空格)。
    zjp
        4
    zjp  
       2018-06-27 11:58:37 +08:00 via Android
    除了状态模式,前面都是 OOP 设计原则
    jennifertxwoodma
        5
    jennifertxwoodma  
       2018-06-27 13:14:46 +08:00
    设计模式 和 OOP 基本原则还是有一点区别的吧 小宝贝
    bbzt
        6
    bbzt  
       2018-06-27 14:10:13 +08:00
    从 es5 的设计意图理解,估计是当时认为 Java 完整的 class 类写起来太麻烦,写个构造函数就代替类好了。

    于是要加字段、方法的话,就通过给构造函数的 prototype 上追加就行。

    于是要继承父类的话,就是替换子类构造函数的 prototype 为父类的一个实例就行。

    嗯,这样多态、类继承什么的都有了。

    然后浏览器在实际实践的时候就用__proto__这种继承链来实现了。
    bbzt
        7
    bbzt  
       2018-06-27 14:11:39 +08:00
    @bbzt

    然后 es6 又被扳回开始照搬 Java 的 class 写法了。。
    OSF2E
        8
    OSF2E  
       2018-06-27 15:56:40 +08:00
    @ipwx @bbzt
    其实这么想心理负担会小一点 ES 世界只有 prototype 而没有 class。
    当然事实也是如此,目前来看,包括 ES6 的 class 在内,ES6+只是基于 ES5 做了一些语言特性方面扩展,ES 面向对象方面的特性还是基于 prototype 来构建的,那么研究研究 prototype 也就是顺理成章的事情了。
    换句话说,没了 prototype,ES 也就不是 ES 了。
    Sparetire
        9
    Sparetire  
       2018-06-28 01:00:14 +08:00
    @ipwx 为啥我觉得 ES6 class 并不完整...相反还有些残缺, 比如 class 语法实现继承 null 就是个坑, 不过这是 js 本身的残缺就是了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     910 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 22:36 PVG 06:36 LAX 15:36 JFK 18:36
    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