《javascript語言精粹》讀書筆記——第5章:繼承

javascript是一門弱類型語言,從不須要類型轉換。對象繼承關係變得可有可無。對於一個對象來講重要的是它能作什麼,而不是它從哪裏來。javascript是一門基於原型的語言,這意味着對象直接從其餘對象繼承。 javascript

一、僞類 java

javascript不直接讓對象從其餘對象繼承,反而是插入了一箇中間層:經過構造器函數產生對象。 json

當一個函數被建立時,Function 構造器產生的函數對象會運行相似這樣的一些代碼: 數組

this.prototype = {constructor : this};
新建立的函數對象被賦予了一個prototype屬性。對於普通函數,這個屬性沒有任何做用。由於javascript沒有提供一種方法去肯定那個函數是用來打算作構造器的,因此給每個函數對象都賦予了這樣一個prototype屬性。對於做爲構造器的函數對象來講,這個prototype屬性是用來存放繼承特徵的地方。

 

函數做爲構造器去構造新對象時,就是採用構造器調用模式去調用函數 ,也就是使用new 前綴去調用一個函數。此時的函數就至關於構造對象的一個類,固然這不是一個真正意義上的類,只是javascript爲了刻意模擬面向對象系統而構造的一種語法。因此構造器函數是一個僞類。 閉包

如今咱們來實現一個new 方法來替代 new 操做符,經過這個new 方法咱們來看看到底new 操做符作了哪些操做: app

 

Function.prototype.method = function(name, fun) {
 this.prototype[name]= fun;
};


Object.create = function(pro){
 var F = function(){};
 F.prototype = o;
 return new F();
};


Function.method('new', function(){
    var that = Object.create(this.prototype);
    var other = this.apply(that, arguments);
    return (other && typeof other === 'object') || that;
});

上面例子首先給Function的原型添加一個method方法,這個方法被全部的函數對象繼承(Function本身也是函數對象),用於給本身的原型添加某個方法。這個用這個函數構造出來的新對象將會共享(繼承)這個方法。 函數

而後又給Object添加了一個create方法,這個方法用於建立一個新的對象,並以參數pro 做爲構造函數的原型。這個新建立的對象將會共享(繼承)pro的全部屬性。 this

最後,咱們給Function原型添加了一個new方法,這個方法模擬了new操做符在構建新對象時所做的操做。讓咱們看一個例子: spa

var Person = function (name, age ){
    this.name = name;
    this.age  = age;
};

var p = Person.new("clopopo",12);

p.name // "clopopo"

p.age // 12

 二、對象說明符 prototype

編寫構造器函數時,參數用一個對象說明符來代替一大串參數

//使用一大串參數來構造對象
var myObject = make(f, l, m, c, s);

//使用對象說明符來代替一大串參數
myObject = make({
 first  : f,
 middle : m,
 last   : l,
 state  : s,
 city   : c
});

至關於把參數組織成了相似json對象的東西。優勢是不須要記住參數的順序,另外一個就是能夠直接傳入json對象,來生成真正的對象實例。

三、原型

基於原型的繼承比基於類的繼承模型在概念上要更簡單:一個新對象能夠繼承一箇舊對象的屬性。利用上面Object的create方法,咱們徹底能夠避免使用new這樣使人迷惑的模擬類的繼承方式(本質上仍是基於原型)。

首先,利用對象字面量來定義一個有用的對象:

var myMammal = {
    name : 'Herb the Mammal',
    get_name : function() {
        return this.name;
    },
    says : function(){
        return this.saying || '';
    }
}

一旦有一個想要的對象,咱們就能夠利用Object的create方法(上文本身定義的)基於這個對象定製新的對象。

var myCat = Object.create(myMammal);
myCat.name = 'Henrietta';
myCat.saying = 'meow';
myCat.getName = function(){
    return this.says + ' ' + this.name + ' ' + this.says;
};

這是一種差別化繼承,myCat查找屬性時,若是自身存在就調用自身的屬性,不然就去查找myCat鏈接的原型對象。

四、函數化

上文的幾種繼承方式有一個弱點就是沒法保護隱私,就是咱們沒法獲得私有函數和私有變量。有一種很是不可取的方法就是被稱爲「假裝私有」模式。就是給私有方法或變量去一個怪模怪樣的名字,並但願使用代碼的用戶僞裝看不到這些變量,這是典型的自欺欺人啊。幸好javascript的閉包特性給咱們提供了一種叫作應用模塊的模式。

var mammal = function(spec){
    var that = {};
    that.get_name = function(){
         return spec.name;
   };
   that.says = function(){
       return spec.saying || ' ';
   };
   return that;
}

var myMammal = mammal({name: 'Herb'});

再來看看原型繼承

var cat = function(spec){
    spec.saying = spec.saying || '';
    var that = mammal(spec);
    that.get_name = function(){
        return that.says() + ' ' + spec.name; 
    }
    return that;
}
var myCat = cat('kitty');

函數化模式還給咱們提供了一個處理父類方法的方法。用閉包封存了調用superior方法對象的this和當時name屬性對應的方法。

Object.method('superior', function(name){
    var that   = this,
        method = that[name];
    return function(){
        return method.apply(that, arguments);
    }; 
});

先看看這個方法的用法:

var coolcat = function(spec){
    var that = cat(spec),
        super_get_name = that.superior('get_name');
    
    that.get_name = function(n){
        return 'like ' + super_get_name() + ' baby';
    };
    return that;
};

super_get_name 獲得的是cat對象建立時的get_name方法,根據superior方法定義知道,該方法永遠都會以調用superior時的那個對象爲上下文,上例中就是cat對象。因此調用super_get_name()時就是以cat爲上下文,cat建立時的get_name方法爲方法體。

相關文章
相關標籤/搜索