最近學習了《Javascript高級程序設計》面向對象部分,結合書中的例子總結一下原型鏈和繼承部分的內容。函數
在Js當中沒有類這個概念,當咱們想要建立具備相同屬性的對象的時候,有以下解決方法:學習
其中,原型模式在Js中應用更加普遍,下面逐一對上述模式進行介紹。this
在ECMAScript中,所謂的工廠模式其實就用一個函數進行封裝,建立對象時傳入相應的參數便可。prototype
function createPerson(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.sayName = function() { alert(this.name); } return o; } var person1 = createPerson("Nick", 20, "Teacher"); var person2 = createPerson("Nancy", 21, "Doctor");
工廠模式一目瞭然,但它的缺點是,咱們沒法得知咱們建立的person1
和person2
究竟屬於什麼類型,咱們只知道它們都是object
, 但咱們但願更加具體。由於它們有相同的行爲,咱們但願person1
和person2
都屬於一個叫Person
的「類」。構造函數模式能夠知足這個要求。設計
直接上代碼:指針
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name); } } var person1 = new Person("Nick", 20, "Teacher"); var person2 = new Person("Nancy", 21, "Doctor");
下面解釋一下對象建立的過程,code
Person
函數中的代碼如今,咱們能夠看一下person1
和person2
的類型,對象
alert(person1 instanceof Object); // true alert(person1 instanceof Person) // true
用構造函數建立對象也很方便,但有個缺點,注意上面例子中this.sayName
方法,這種建立方式意味着咱們每建立一個新的Person
實例,該實例內部都會新建一個sayName
方法。而實際上,這些方法的做用都相同,沒有重複建立的必要。若是把這個函數放在構造函數以外,做爲全局函數的話,能夠解決重複的問題,但卻犧牲了Person
的封裝性,所以咱們推薦下一種模式,原型模式。blog
咱們須要一個「倉庫」存儲同一類型的對象的共有的屬性和方法,在js裏面,這個「倉庫」是prototype指向的對象(即__原型對象__)。繼承
咱們建立的每個函數都有一個prototype(原型)屬性,這個屬性指向「倉庫」(prototype自己是一個指針)。只要把所需的函數、屬性添加到「倉庫」中,即可在該類型對象的實例中共用。
舉例以下:
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' var person2 = new Person(); peron2.sayName(); //'Nicholas' alert(person1.sayName() == peron2.sayName()); //true
原型對象是一個很重要的概念,它就是咱們上面提到的「倉庫」(可能比喻不是很恰當),先來理解一下它:
結合剛纔的代碼,有下圖:
有:
在調用person1.sayName()的時候,解析器會先詢問person1中是否有sayName方法,發現沒有,就會查找person1的原型;在person1原型中發現有,就會使用原型中的sayName方法;
來看另一個例子:
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(); var person2 = new Person(); person1.name = "Gerg"; person1.sayName(); //'Gerg' person2.sayName(); //'Nicholas'
在這裏,能夠看到設置了person1
的name
屬性後,該屬性即存在於person1
實例中,無需再從原型中查找,在person1
中,至關於將原型的name
屬性覆蓋。
而person2
不受影響。
語法簡化:
上面的代碼有點冗長,能夠用對象字面量來重寫原型對象,代碼以下:
function Person() { } Person.prototype = { constructor : Person, name : "Nicholas", age : 29, job : "software Engineer", sayName : function() { alert(this.name); } }
原型的動態性:
function Person() { } var friend = new Person(); Person.prototype = { constructor : Person, name : "Nicholas", age : 29, job : "software Engineer", sayName : function() { alert(this.name); } } friend.sayName(); //error
從圖中能夠看出以前生成的實例的原型並無改變。
咱們經過構造函數的方式生成每一個實例獨自享有的屬性和方法,經過原型生成共享的屬性和方法:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.friend = [shelby', 'Court']; } Person.prototype = { constructor : Person, sayName : function() { alert(this.name); } } var person1 = new Person('Nicholas', 29, 'software engineer'); var person2 = new Person('Gerg', 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
未完待續...