JavaScript设计模式与开发实践 面向对象

1 动态类型语言

  静态类型语言编译时就已经确定变量类型,动态类型语言的变量类型要到程序运行时,变量被赋予某个值后,才会有类型。

  静态语言的优点:首先,编译时就能发现类型不匹配错误,其次如果程序明确规定数据类型,编译器还能针对这些信息对程序进行优化,提高程序执行速度。

    静态语言的缺点:迫使程序员按照强契约编写程序,并且增加更多代码。

  

  动态语言的优点:编写的代码更少,程序员能把精力放到业务逻辑。

  动态语言的缺点:无法保证变量的类型,再程序运行时可能发现类型相关错误。

  动态类型无需进行类型检测,我们可以尝试钓鱼任何对象的任意方法,不用考虑它原本是否被设计为拥有该方法。

2 多态

  多态的实际含义:同意操作作用于不同的对象上,可以产生不同的解释和不同的结果。也就是,给不同的对象发送同一个消息,这些对象会根据这个消息分别给出不同反馈。

    var makeSound = function( animal ){
        if ( animal instanceof Duck ){
            console.log( '嘎嘎嘎' );
        }else if ( animal instanceof Chicken ){
            console.log( '咯咯咯' );
        }
    };

    var Duck = function(){};
    var Chicken = function(){};

    makeSound( new Duck() ); // 嘎嘎嘎
    makeSound( new Chicken() ); // 咯咯咯

  这段代码体现了多态性,但再增加一个动物后需要改动makeSound函数。

  多态的思想是将不变的事物和可变的事物分开。把不变的部分隔离,把可变的封装,,这给予我们扩展程序的能力。

    //把不变的部分隔离,即动物都会发出叫声
    var makeSound = function( animal ){
        animal.sound();
    };
    
    //把可变的部分各自封装,多态实际上是对象的多态
    var Duck = function(){};

    Duck.prototype.sound = function(){
        console.log( '嘎嘎嘎' );
    };

    var Chicken = function(){};

    Chicken.prototype.sound = function(){
        console.log( '咯咯咯' );
    };

    makeSound( new Duck() ); // 嘎嘎嘎
    makeSound( new Chicken() ); // 咯咯咯

    var Dog = function(){};

    Dog.prototype.sound = function(){
        console.log( '汪汪汪' );
    };

    makeSound( new Dog() ); // 汪汪汪

  多态最根本的作用是通过把过程化的条件分局转化为对象的多态性,从而消除条件分句。将行为分布在各个对象中,并让对象各自负责自己的行为,正是面向对象设计的优点。

    var googleMap = {
        show: function(){
            console.log( '开始渲染谷歌地图' );
        }
    };

    var baiduMap = {
        show: function(){
            console.log( '开始渲染百度地图' );
        }
    };
   //  使用条件分句
    var renderMap = function( type ){
        if ( type === 'google' ){
            googleMap.show();
        }else if ( type === 'baidu' ){
            baiduMap.show();
        }
    };

    renderMap( 'google' ); // 输出:开始渲染谷歌地图
    renderMap( 'baidu' ); // 输出:开始渲染百度地图
    //使用多态
    var renderMap = function( map ){
        if ( map.show instanceof Function ){
            map.show();
        }
    };

    renderMap( googleMap ); // 输出:开始渲染谷歌地图
    renderMap( baiduMap ); // 输出:开始渲染百度地图

  

3 封装

  封装的目的是将信息隐藏。封装包括封装数据、封装实现、封装类型和封装变化。封装不仅仅是隐藏数据,还包括隐藏实现细节、设计细节以及隐藏对象的类型等。

  封装数据:JavaScript没有public、private、protected等,只能用变量作用域实现封装特性,只能模拟public和private。

  封装实现:封装使得对象之间的耦合松散,对象之间只通过暴露的API接口通信。修改一个对象时,只要对外的接口没有变化,就不会影响程序的其他功能。

  封装类型:JavaScript没有对抽象类和接口的支持.

  封装变化:《设计模式》总结了23种设计模式,分别被划分为创建新模式、结构型模式和行为型模式。创建型模式,要创建一个对象是一种抽象行为,具体创建什么对象是可以变化的,

        创建型模式的目的是封装对象的变化。结构型模式封装的是对象直接的组合关系。行为型模式封装的是对象的行为变化。

4 原型模式和基于原型继承的JavaScript对象系统

原型编程范型的部分基本规则:

  • 所有的数据都是对象
  • 要得到一个对象,不少通过实例化类,而是找到一个对象作为原型并克隆它
  • 对象会记住它的原型
  • 如果对象无法响应某个请求,会把这个请求委托给它的原型

1 所有的数据都是对象

JavaScript的基本类型包括undefined、number、string、boolean、object、function。除了undefined,number、boolean、string都可以通过包装类编程对象类型,JavaScript的根对象是Object.prorotype。

2 要得到一个对象,不少通过实例化类,而是找到一个对象作为原型并克    function Person( name ){

this.name = name;
    };

    Person.prototype.getName = function(){
        return this.name;
    };

    var a = new Person( 'sven' )
    console.log( a.name ); // 输出:sven
    console.log( a.getName() ); // 输出:sven
//ES5中的Object.getPrototypeOf可以看对象 console.log( Object.getPrototypeOf( a ) === Person.prototype ); // 输出:true

当使用new调用函数,函数就是一个构造器,先克隆Object.prototype对象,再进行其他操作。

在Chrome和Firefox等暴露了对象__proto__属性的浏览器理解new运算的过程

    var objectFactory = function(){
        var obj = new Object(), // 从Object.prototype 上克隆一个空的对象
        Constructor = [].shift.call( arguments ); // 取得外部传入的构造器,此例是Person
        obj.__proto__ = Constructor.prototype; // 指向正确的原型
        var ret = Constructor.apply( obj, arguments ); // 借用外部传入的构造器给obj 设置属性
        return typeof ret === 'object' ? ret : obj; // 确保构造器总是会返回一个对象
    }; 

3 对象会记住它的原型

某个对象的_proto_属性默认会指向它的构造器的原型对象,即Constructor.prototype。正是因为对象要通过__prototype__属性记住它的构造器的原型,模拟new创建对象时需要手动给obj对象设置正确的__proto__指向。

原文地址:https://www.cnblogs.com/surahe/p/5988897.html