JS面向對象設計模式

建立對象

工廠模式

function createPerson(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 person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
複製代碼

但這種方式有一個缺點:沒法判斷某個對象是什麼類型。es6

構造函數模式

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function(){
        alert(this.name);
    };
}
var p1 = new Person("Nicholas", 29, "Software Engineer");
var p2 = new Person("Greg", 27, "Doctor");
複製代碼

構造函數也存在問題,每一個方法都要在實例上建立一遍。也就是說p1和p2的sayName()方法雖然做用相同,但這兩個方法並非同一個函數bash

解析一道題函數

new操做符作了什麼:ui

(1)建立一個新對象this

(2)將構造函數的做用域賦給新對象(所以this就指向了這個新對象)spa

(3)執行構造函數中的代碼prototype

(4)返回新對象設計

原型模式

function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
    alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
複製代碼

當咱們改變 值爲引用類型的對象的屬性 時,這個改變的結果會被其餘對象共享。3d

構造函數 + 原型模式

構造函數模式的屬性沒毛病。缺點是:沒法共享方法code

原型模式的方法沒毛病。缺點是:當原形對象的屬性的值爲引用類型時,對其進行修改會反映到全部實例中 

那咱們就將二者的結合,對象的屬性使用構造函數模式建立,方法則使用原型模式建立

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
}
Person.prototype = {
    constructor : Person,
    sayName : function(){
        alert(this.name);
    }
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Count,Van"
alert(person2.friends); //"Shelby,Count"
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true
複製代碼

繼承

原型鏈

JavaScript中引入了原型鏈的概念,具體思想: 子構造函數的原型對象初始化爲父構造函數的實例,孫構造函數的原型對象初始化爲子構造函數的實例…… ,這樣子對象就能夠經過原型鏈一級一級向上查找,訪問父構造函數中的屬性以及方法。

function SuperType(){
     this.property = true;
};
SuperType.prototype.getSuperValue = function(){
    return this.property;
}
function SubType(){
    this.subproperty = false;
};
// 繼承Supertype
SubType.prototype = new SuperType();
​
SubType.prototype.getSubValue = function(){
    return this.subproperty;
}
​
var instance = new SubType();
alert(instance.getSuperValue());
複製代碼

當咱們改變 值爲引用類型的原型對象的屬性 時,這個改變的結果會被全部子對象共享。這個缺點某些時候至關致命,因此咱們不多使用這種方法來繼承

借用構造函數

function SuperObject(){
  this.colors = ['red','blue'];
  this.sayBye= function(){
    console.log('Bye')
  }
}
function SubObject(){
  SuperObject.call(this);  // 在子類中調用父類的構造方法,實際上子類和父類已經沒有上下級關係了
}
​
​
var instance1 = new SubObject();
instance1.colors.push('yellow');
var instance2 = new SubObject();
console.log(instance2.colors); //['red','blue']
console.log(instance2 instanceof SuperObject); // false
console.log(instance1.sayBye === instance2.sayBye)  // false
複製代碼

這個方法雖然彌補了原型鏈的缺點,可是又暴露出了新的缺點:

 1 子類和父類沒有上下級關係,instance2 instanceof SuperObject 結果是false

 2 父類中的方法在每一個子類中都會生成一遍,父類中的方法沒有被複用。

組合繼承

組合繼承就是將原型鏈繼承和借用構造方法繼承組合,發揮二者之長。

function SuperType(name){
    this.name = name;
    this.colors = ['blue'];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
}
​
function SubType(name, age){
    // 引用父類型的屬性,又調用了一次父函數
    SuperType.call(this,name);
    this.age = age;
}
// 繼承父類型的方法,調用了一次父函數
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
}
​
var instance1 = new SubType('zz',18);
instance1.sayName(); //zz
instance1.sayAge(); //18
instance1.colors.push('red');
console.log(instance1.colors); // ['blue','red']
​
var instance2 = new SubType('tt',22);
console.log(instance2.colors); // ['blue']
組合繼承會調用兩次父類型函數

原型式繼承
代碼塊
function object(o){
    function F(){};
    F.prototype = o;
    return new F();
}
​
var Person = {
    name: 'Nicholas'
    friends: ['zz','cc']
}
var anotherPerson = object(Person);
anotherPerson.friends.push('dd');
console.log(anotherPerson.friends); //['zz','cc','dd']
複製代碼

寄生式繼承

function createAnother(o){
    var clone = object(o);
    o.sayHi = function(){
        console.log('hi');
    }
    return clone;
}
複製代碼

寄生組合式繼承

雖然組合繼承沒啥大缺點,可是愛搞事情的有強迫症的程序猿們以爲,組合繼承會調用兩次父類型函數(在上面的代碼中標註了),不夠完美。因而道格拉斯就提出了寄生組合繼承。

思路是構造一箇中間函數,將中間函數的prototype指向父函數的原型對象,將子函數的prototype指向中間函數,並將中間函數的constructor屬性指向子函數。

function object(o){
    function F(){};
    F.prototype = o;
    return new F();
}
​
function inheritPrototype(subType,superType){
    var prototype = object(superType.prototype);
    prototype.constructor = subType;
    subType.prototype = prototype;
}
​
function SuperType(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
}
function SubType(name,age){
    SuperType.call(this,name);
    this.age = age;
}
inheritPrototype(SubType,SuperType);
​
SubType.prototype.sayAge = function(){
    alert(this.age)
}
var instance1 = new SubType('zz', 18);
instance1.sayName();
複製代碼

ES6的繼承

這段代碼有兩條原型鏈

參考資料

MDN:developer.mozilla.org/zh-CN/docs/…

JS高級語言程序設計第六章(面向對象的程序設計)

ES6(類):es6.ruanyifeng.com/#docs/class

相關文章
相關標籤/搜索