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方法爲方法體。