js 物件導向總結

阿那悟空發表於2019-03-04

物件導向的程式設計(Object-Oriented OO)

最近感覺,不知道該學點什麼,老是覺的自己什麼都不會,又感覺是會點什麼,說是也知道物件導向,但是讓系統的說一下這裡面的東西,感覺連不上線,成不了太系統的瞭解,所以就看了一下這的知識點,當自己的搬運工,假裝是發了一篇文章,等自己檢視方便;

建立物件

1. 工廠模式:用函式來封裝,以特定介面建立物件的細節

  1. 解決了建立多個相似物件的問題,
  2. 多用於建立多個含有相同屬性,和方法的物件,避免程式碼的重複編寫;
  3. 沒有解決物件識別的問題(即怎麼知道一個物件的型別)(它的 instanceof 只能是Object)
   function creatPerson(name,age,job) {
        var o = new Object();
        o.name= name;
        o.age= age;
        o.job= job;
        o.sayName = function(){
            alert(this.name);
        };
        return o;
    }
    var p1 = creatPerson("hh",23,"worker");
    var p2 = creatPerson("xx",26,"worker1");
    p1.__proto__===p2.__proto__===Object.prototype;

    物件的constructor 屬性最早是用來描述隊形型別的,檢測物件型別還是 instanceof 更可靠
    工廠模式建立的物件,constructor只有Object,這樣的例項沒有特定的型別;

複製程式碼

2. 建構函式模式:

  1. 解決物件識別的問題(即怎麼知道一個物件的型別)
  2. 缺點:每個方法都在每個例項上重新建立了一次,下面栗子中p1.sayName!=p2.sayName
  3. 解決方式:提出建構函式,在全域性寫一個函式申明;(這種解決方式的缺點:全域性函式只是區域性呼叫,方法過多就得建立多個全域性函式,沒什麼封裝性可言);
    function Person(name,age,job){
        this.age = age;
        this.name = name;
        this.job = job;
        this.sayName = function(){
            alert(this.name)
        }
        //this.sayName = sayName;   //解決方式
    }
    var p1 = new Person("hh",23,"worker");
    var p2 = new Person("xx",26,"worker1");
        
        //function sayName(){alert(this.name)}  //解決方式


    new的執行:
        {
            var obj ={};
            obj.__proto__ = Person.prototype;
            Person.call(obj);
            return obj
        }
    p1 instanceof Person //true;
    p2 instanceof Person //true;
    p1 instanceof Object //true;
    p2 instanceof Object //true;
    這樣是得p1和p2例項有了特定的型別, Person;

複製程式碼

3. 原型模式

  1. 優點:他省略了建構函式初始化引數這個環節,原型中所有屬性都被很多例項共享,共享對函式非常合適,基本屬性也還行
    通過在例項上新增同名屬性,可隱藏原型中的對應屬性值;

  2. 缺點: 他的共享屬性,對於包含引用型別值的屬性 如果例項重新賦值沒什麼影響,和基本型別一樣,如果是操作修改 就有些問題了,會使得所有例項獲取到的該屬性都被修改, 所以也不單獨使用

        function Person(){}
        Person.prototype={
            constructor:Person,
            name:"ss",
            friends:["s1","s2"],
            sayName:function(){alert(this.name)}
        }
        var p1= new Person();
        var p2= new Person();
        p1.name = "p1"
        console.log(p1.name) //p1
        console.log(p2.name) //ss
        p1.friends.push("s3");
        console.log(p1.friends) //["s1","s2","s3"]
        console.log(p2.friends) //["s1","s2","s3"]
    複製程式碼
  3. 用法:

    1. 一般用法:
          function Person(){}
          Person.prototype.name="ceshi"
          Person.prototype.age =12;
          Person.prototype.sayName = function(){
              alert(this.name)
          }
          //這樣就是實現了程式碼的共享
      
      複製程式碼
    2. 簡單寫法
      
          更簡單的寫法:
              Person.prototype={
                  //constructor:Person,
                  name:"test",
                  age:12,
                  sayName:function(){alert(this.name)}
              }
          var friend = new Person();
          friend instanceof Object //true
          friend instanceof Person //true
          friend.constructor==Person //false
          friend.constructor==Object //true
      
          這種簡單的寫法constructor 屬性就不在指向Person,而是指向Object建構函式;
          此時可以新增一個屬性constructor( 如上面註釋):但是此時的constructor 變成了可列舉的屬性,原生是不可列舉的,可以考慮用Object.defineProperty()
      
      複製程式碼
    3. 原型動態:
          var p1 = new Person();
          <!-- 1 -->
              Person.prototype.hh="ss";
              p1.hh//ss 是可以先建立例項在改邊原型在訪問的;
              
          <!-- 2 -->
              Person.prototype={
                  name:"ss"
              }
              p1.__proto__!=Person.prototype;
              p1.name // undefined 
      
      複製程式碼
    4. 原生物件的原型:
      1. 可以給原生引用型別(Object、Array、String。。。)新增修改方法
        String.prototype.strarWith=function(tesx){return this.indexOf(text)==0}

4. 組合使用建構函式模式和原型模式:(定義引用型別的一種預設模式)

  1. 建構函式模式用於定義例項屬性; 每個屬性在每個例項上都重新建立了一次;即使引用型別的修改也不會影響其他例項
  2. 原型模式用於定義方法和共享屬性;
    function Person(age,name){
        this.name = name;
        this.age = age;
        this.friends=[1,2,3]
    }
    Person.prototype={
        constructor:Person,
        sayName:function(){
            alert(this.name)
        }
    }
    var p1 = new Person("ss",12);
    var p2 = new Person("ss3",123);
    p1.friends.push(2);
    console.log(p1.friends) // [1,2,3,2]
    console.log(p2.friends)// [1,2,3]
複製程式碼

5. 動態原型模式:

  1. 原型和狗仔函式獨立開,感覺和其他語言的OO不一樣,動態原型就是把所有的資訊都封裝在建構函式中;
  2. 在建構函式中檢查某個應該從在的方法是否有效,來覺得是不是初始化原型,其實就是初始化一次原型

    function Person(name,age){
        this.name = name;
        this.age = age;
        if(typeof this.sayName !="function"){//sayName沒初始化 這裡對一個方法判斷就可以,然後初始化所有的,沒必要都每個方法都判斷

            Person.prototype.sayName=function(){alert(this.name)}
            Person.prototype.sayAge=function(){alert(this.age)};
            .....
        }
  ****注意****:此處不能使用物件字面量形式重寫原型, 因為這中寫法是先建立的例項,然後在修改的原型,要是用物件字面量重寫原型,會切斷現有例項和新原型之間的聯絡, 導致方法例項上無此方法;
    }

複製程式碼

6. 寄生建構函式模式:

  1. 和工廠模式差不多,只是用了new 初始化例項;
  2. 它返回的物件與建構函式和建構函式的原型沒有任何關係;
    function Person(name,age){
        var o = new Object();
        o.name=name;
        o.age=age;
        o.sayName=function(){
            alert(this.name)
        };
        return o;
    }
    var friends = new Person("xiaoming",12)
    friends.asyName()  // xiaoming

複製程式碼

7. 穩妥建構函式模式:

穩妥物件: 沒有公共屬性,並且其方法中不引用this的物件;

function Person(name,age){
        var o = new Object();
        o.sayName=function(){
            alert(name)
        };
        return o;
    }
    var friends = new Person("xiaoming",12)
    friends.asyName()  // xiaoming
複製程式碼

繼承 :

  1. 繼承一般包括:
    介面繼承:繼承方法和簽名;
    實現繼承:繼承實際的方法;ECMAScript 只支援實現繼承

  2. js主要通過原型鏈實現繼承,原型鏈的構建是通過將一個型別的例項賦值給另一個建構函式的原型實現的

繼承方式

1. 原型鏈(很少單獨使用)
  1. 問題:操作引用型別的資料(重新賦值不影響,相當於子類新增隱藏父類),會被所有例項共享受
  2. 問題er: 建立子類時候不能向父類的建構函式傳遞引數;???
    Son.prototype = new Father();
    
    //此時Son.constructor 被改寫為了 Father;
    Son.prototype.constructor = Son;
    
    //給子類修改或者是新增方法的時候,要放在替換語句,改版子類原型語句之後, 
    //原型繼承時,不能使用字面量方式建立原型方法,這樣就重新寫了原型鏈了
    Son.prototype.getName=function(){alert(this.name)};

複製程式碼
2. 建構函式繫結: call ,apply 繼承:(很少單獨使用)
  1. 問題: 父函式原型(prototype)中的方法,子類是不可以見的
  2. 問題兔:方法屬性都是在建構函式中定義的,每個例項和方法都重新建立一次,沒有複用之說z
    function Son(){
        Father.call(this,arguments)
    }
複製程式碼
3. 組合繼承:(常用繼承模式)
  1. 這樣可以使得例項分別擁有各自的屬性(包括引用型別的,例項間互補影響),又可以使用相同的方法;
  2. 缺陷: 無論什麼情況下,都會呼叫兩次父類的建構函式;
    function Father(){
        this.name ="ss";
        this.friends=[1,2,3,4]
    }
    function Son(){
        Father.call(this);
    }
    Son.prototype = new Father(); // Son 原型獲得Father上的屬性,name和friends


    var son1 = new Son(); // 此時呼叫Son建構函式為son1 例項上新增了屬性(name和friends), 這些屬性就遮蔽了原型中的同名屬性;
    // 呼叫兩次Father建構函式的結果就是有兩組屬性,一組在例項上,一組在原型上;

複製程式碼
4. 原型式繼承:
  1. 在沒必要興師動眾建立建構函式,而是隻想讓一個物件與另一個物件保持類似的情況,可以使用, 類似錢拷貝;
    function object(o){
        function F(){};
        F.prototype = o;
        return new F();
    }
    // 和 Object.creat() 傳遞一個引數時候相同
複製程式碼
5. 寄生式繼承:
  1. 繼承origin 的屬性和方法;
  2. 同時還有自己的方法;
    function creat(origin){
        var clone = object(origin); // 可以是任何返回新物件的函式
        clone.sayName(){alert("name")}; //這裡的函式每次都建立,不存在複用一說,
        return clone;
    }
複製程式碼
6. 寄生組合式:(理想的繼承實現方式)
  1. 解決組合繼承中的缺陷,生成兩組屬性,只在例項上生成原型;
  2. 通過建構函式來繼承屬性,通過原型鏈的混成形式來繼承方法(就是給子類原型,指定一個父類的副本即可)
    function inherit(son,father){

        var prototype = object(father.prototype); 
            // 上句話,建立父類原型的副本,prototype.__proto__ = Father.prototype;
            // prototype 繼承constructor prototype.constructor 取的是原型鏈上,原型的Father.prototype.constructor, 為 Father();即:prototype.constructor == prototype.__proto__.constructor  // true
            // prototype.hasOwnPrototyoe("constructor") //false
        

        prototype.constructor = son; // 彌補重寫原型造成的預設屬性的修改;
            //此時是給prototype 新增了constructor 屬性,賦值為son, 遮蔽了原型上的constructor屬性
            // prototype.hasOwnPrototype("constructor") // true
            // prototype.constructor = son;
            // prototype.__proto__.constructor = father
        son.prototype  = prototype;
            // 給子類原型賦值為父類原型的副本;
    }

    //使用:
    function Father(){
        this.name="11";
        this.age=12;
    }
    Father.prototype.sayName=function(){
        alert(this.name);
    }
    function Son(){
        Father.call(this);
        this.age=23;
    }
    inherit(Son,Father);
    Son.prototype.sayAge=function(){
        alert(this.age)
    }

複製程式碼

相關文章