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 · 2930 次点击
    这是一个创建于 2101 天前的主题,其中的信息可能已经有所发展或是发生改变。

    设计模式

    抽象类

    抽象类的表现

    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:23 +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   ·   我们的愿景   ·   实用小工具   ·   2449 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 16:03 · PVG 00:03 · LAX 09:03 · JFK 12:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.