zoukankan      html  css  js  c++  java
  • js面向对象

    一、封装

      原文链接:http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html

      1.1 原始模型

    var Cat = {//原型
       name : "",
      color :"" }

    var cat1 = {};//实例
    cat1.name = "小花";
    cat1.color = "花";

    var cat2 = {};//实例
    cat2.name = "小黑";
    cat2.name = "黑";

      这就是最简单的封装了,把两个属性封装在一个对象中,但是这样的封装有两个缺点

       1、如果写多个实例会很麻烦

      2、实例也原型之间没有任何关系

    1.2 原始模型的改进

      写一个函数解决代码重复问题

      

    function Cat(name,color){
      return {
        name : name,
        color:color
      }
    }
    var cat1 = Cat("小花",“花”);
    var cat2 = Cat("小黑" , "黑");

      存在的问题:cat1和cat2之间没有任何内在关系,不能看出他们是同一原型的实例

    1.3 构造函数模式

      为了解决从原型对象生成实例问题,js提供一个构造函数模式。

      所谓的构造函数就是普通的函数,内部使用this对象,对构造函数内部使用new运算符就可以生成实例,并且this变量会绑定在实例对象上

    var Cat = function(name,color){
      this.name = name;
      this.color = color;
    }
    var cat1 = new Cat("小花",“花”);
    var cat2 = new Cat("小黑",“黑”);

    js提供了一个instanceof 运算符,验证原型对象和实例对象之间的关系
    alert( cat1 instanceof Cat);//ture
    alert( cat2 instanceof Cat);//true

      存在的问题:如果存在不变的属性或方法时,比如var Cat = function(name,color){  this.name = name;

      this.color = color;
      this.style = "猫科"
      this.eat = function(){
        alert("吃老鼠");
      }
    }

    var cat1 = new Cat("小黑",“黑”);
    var cat2 = new Cat("小花",“花”) ;
    alert(cat1.style);//猫科
    cat1.eat();//吃老鼠
    alert(cat2.style);//猫科
    cat2.eat();//吃老鼠

      对于每一个实例对象,type和eat都是一样的内容,每一次生成一个实例,都必须为重复内容,多占用一些内存,既不环保也缺乏效率

    解决办法:prototype模式

    每一个构造函数都有一个prototype属性,指向prototype对象,这个对象的所有属性和方法都被构造函数的实例继承

    这意味着,我们可以把那些不变的属性和方法直接定义在prototype上

    var Cat = function(name,color){
      this.name = name;
      this.color = colr;
    }
    Cat.prototype = {
      constructor : Cat,
      style : "猫科",
      eat : function(){
        alert("吃老鼠");
      }
    }
    var cat1 = new Cat("小黑",“黑”);
    cat1.eat();
    alert(cat1.style);

    var cat2 = new Cat("小花",“花”);
    alert(cat2.style);
    cat2.eat();

     prototype验证方法

     isPrototypeOf方法判断,某个prototype对象和实例之间的关系

      

    alert( Cat.prototype isPrototypeOf (cat1) ) ;//true
    alert(Cat.prototype isPrototypeOf( cat2 ) );//true

     instanceof 判断实例和父类之间的关系

    alert( cat1 instanceof Cat);//true
    alert(cat2 instanceof Cat);//true

      每一个实例对象都有hasOwnProperty()方法,判断属性是本地属性还是继承prototype的属性

    alert( cat1 hasOwnProperty(name) );//true 为本地属性
    alert( cat1 hasOwnProperty(eat) );//false 为继承属性

      in 运算符用于判断实例是否有某个属性,无论是本地属性还是继承属性

    alert( "name" in cat1 );//true
    alert("age" in cat1);//false

    in还可以变量对象中的所有属性
    for( var pro in cat1){
      alert("cat1的”+pro +"属性值为:"+cat1[pro]);
    }

    二、继承

    方法一、构造函数的继承

    function Animal (){
      this.species = "动物";
    }

    function Cat(name,color){
      this.name = name;
      this.color = color;
    }
    怎样才能是猫继承动物呢?
    方法一:构造函数绑定
    function Animal(){
      this.species = "动物";
    }
    function Cat(name,color){
      Animal.apply(this,arguments);
      this.name = name;
      this.color = color;
    }
    var cat1 = new Cat("小黑",“黑”);
    alert( cat1.species );//“动物”

     方法二:原型模式

    如果Cat的prototype成为Animal的实例,cat就可以继承Animal了
    Cat.prototype = new Animal();
    Cat.prototype.constructor = Cat;
    var cat1 = new Cat();
    alert( cat1.species );//"动物"
    

      

    任何一个构造函数的原型对象都有一个constructor属性,此属性都指向这个构造函数。
    当没有Cat.prototype = new Animal();的时候,Cat.prototype.constructor指向的是Cat,但是继承了Animal后,Cat.prototype.constructor = Animal;
    所以要进行更正,Cat.prototype.constructor = Cat;
    更重要的是实例也有constructor属性,指向的是构造函数原型对象的constructor属性
    alert( cat1.constructor == Cat.prototype.constructor);//true
    因此当Cat.prototype = new Animal();后 cat1.constructor = Animal,cat1明明是使用构造函数Cat生成的,但此时确是Animal,导致了原型链的混乱,所以要手动更正constructor,
    Cat.prototype.constructor = Cat

    方法三:直接继承prototype
    方法三是方法二的改进:由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。
    function Animal(){}
    Animal.prototype.species = "动物";

    然后将Cat的prototype指向Animal.prototype
    Cat.prototype = Animal.prototype ;
    Cat.prototype.constructor = Cat;
    var cat1 = new Cat("小黑",“黑”);
    alert(cat1.species);//动物


      

    与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。

    所以,上面这一段代码其实是有问题的。请看第二行

      Cat.prototype.constructor = Cat;

    这一句实际上把Animal.prototype对象的constructor属性也改掉了!

      alert(Animal.prototype.constructor); // Cat

    方法四:利用空对象作为中介
    var F = function(){};
    F.prototype = Animal.prototype;
    Cat.prototype = new F()
    Cat.prototype.constructor = Cat;
    F作为空对象,几乎不占内存,此时修改Cat的prototype对象,就不会修改Animal的prototype对象了
    将上面方法封装成一个函数方便使用
    function extend(Child,Parent){
      var F = function(){};
      F.prototype = Parent.prototype;
      Child.prototype = new F();
      Child.prototype.constructor = Child;
      Child.uber = Parent.prototype;
    }

    extend(Cat,Animal);
    var cat1 = new Cat();
    alert( cat1.species);//动物

    这个是YUI库实现继承的方法

    另外,说明一点,函数体最后一行

      Child.uber = Parent.prototype;

    意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。(uber是一个德语词,意思是"向上"、"上一层"。)

    这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。

    方法五:拷贝继承
    将父对象的所有属性和方法都拷贝到子对象中
    function Animal(){}
    Anima.prototype.species = "动物";

    function extend2( Child,Parent){
      var p = Parent.prototype;
      var c = Child,prototype;
      for(var i in p){
        c[i] = p[i];
      }
      Child.uber = p
    }

    extend2( Cat,Animal );
    var cat1 = new Cat("小花",“花”);
    alert(cat1.species);//动物

     三、非构造函数的继承

    var Chines  = {
      nation : "中国"
    };
    var Doctor = {
      career:"医生";
    }
    让医生继承中国,成为中国医生。

      方法一:object()方法

    function object(parent){
      var F = function(){};
      F.prototype = parent;
      return new F();
    }

    var Doctor = object(Chinese);
    Doctor.career = "医生";
    alert(Doctor.nation);//中国

      方法二:浅拷贝

    function extend3(p){
      var c = {};
      for(var i in p){
        c[i] = p[i];
      }
      c.uber = p;
      return c;
    }

    var Doctor = extend3(Chines);
    Doctor.career = "医生";
    alert(Doctor.nation);//中国

     

    但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。

    请看,现在给Chinese添加一个"出生地"属性,它的值是一个数组。

      Chinese.birthPlaces = ['北京','上海','香港'];

    通过extendCopy()函数,Doctor继承了Chinese。

      var Doctor = extendCopy(Chinese);

    然后,我们为Doctor的"出生地"添加一个城市:

      Doctor.birthPlaces.push('厦门');

    发生了什么事?Chinese的"出生地"也被改掉了!

      alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门

      alert(Chinese.birthPlaces); //北京, 上海, 香港, 厦门

    所以,extendCopy()只是拷贝基本类型的数据,我们把这种拷贝叫做"浅拷贝"。这是早期jQuery实现继承的方式。

    方法三:深拷贝

    function deepCopy(parent,c){
      var c = c || {};
      for( var i in parent){
        if( typeof(parent[i] == "object") ){
          c[i] = (parent[i].constructor == Array ) ? [] : {};
          deepCopy(parent[i] , c[i]);
        }else{
          c[i] = parent[i];
        }
      }
      return c;
    }

    var Doctor = deepCopy(Chinese);
    Doctor.carrer = "医生";
    Chinese.brithPlace = ["上海",“北京”,“南京”]
    alert(Doctor.nation);//"中国"
    Doctor.brithPlace.push ("内蒙古");
    alert(Doctor.brithPlace);//["上海",“北京”,“南京”,“内蒙古”]

      目前jquery库使用的是这种继承方法

     另加:面向对象抽象:原文链接:http://www.cnblogs.com/wangfupeng1988/p/3687346.html

    如何更好的解决变化的问题?

    提取抽象,隔离具体

    什么是“抽象”? 抽象就是不变的东西,

    什么是“具体”?具体是实际执行的,

    我们应该依赖于抽象编程,而不是依赖于具体编程。应该把程序中的共性抽象出来,并且把具体实现的部分隔离开来,让他们都依赖于抽象,并且互不影响。这其实就是设计。

    2.3 SOLID五大原则

    系统设计的5大原则,简写分别是S、O、L、I、D。

    • S - 类职责单一原则: 即职责划分要清晰,不同职责的不要搅和在一起。每个类应该只有一个让他发生改变的原因。
    • O - 开放封闭原则: 对扩展开发,对修改封闭。即如果系统要变化,就去扩展、新增新类,不要修改现有的类。
    • L - LISKOV原则: 子类应该能充分覆盖父类,并且让使用者分不出差别。
    • I - 接口分离原则:每个接口只管一个功能,不要出现“胖接口”。增加功能时,要加接口,而不是改接口
    • D - 依赖倒置原则:具体应该依赖于抽象,而不是抽象依赖于具体,即低层要依赖于高层。

    如果详细分析这5大原则,其实他们都是围绕着“提取抽象、隔离具体”来的。

    • S - 类职责单一原则: 隔离
    • O - 开放封闭原则: 依赖于抽象,隔离具体
    • L - LISKOV原则:抽象
    • I - 接口独立原则:隔离
    • D - 依赖倒置原则:依赖于抽象

     

     



      

     
    
    
     


     

      

  • 相关阅读:
    【LeetCode】-- 73. Set Matrix Zeroes
    (第三场) A PACM Team 【dp,五维背包】
    POJ 1745 【0/1 背包】
    (第三场) H Diff-prime Pairs 【数论-素数线性筛法+YY】
    POJ 2299 【树状数组 离散化】
    树状数组 && 线段树应用 -- 求逆序数
    HDU 1698 【线段树,区间修改 + 维护区间和】
    HDU 1166 【线段树 || 树状数组,单点修改 维护区间和】
    (第二场)D Money 【dp贪心】
    (第二场)A Run 【动态规划】
  • 原文地址:https://www.cnblogs.com/wxiaona/p/6019764.html
Copyright © 2011-2022 走看看