主要摘自《Javascript高級程序設計》es6
var person = function(name,age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function() {
console.log(this.name)
}
return o;
}
var person1 = person('li', 20);
var person2 = person('xiao', 18);
person1.sayName(); //li
person2.sayName(); //xiao
複製代碼
工廠模式雖然解決了建立多個類似對象的問題,可是卻沒有解決對象識別的問題,即怎樣知道一個對象的類型。數組
var Person = function(name,age) {
this.name = name;
this.age = age;
this.sayName = function() {
console.log(this.name)
}
}
var person1 = new Person('li',20)
var person2 = new Person('xiao',18)
person1.sayName() //li
person2.sayName() //xiao
複製代碼
構造函數和工廠函數不一樣之處在於:bash
** 調用構造函數會經歷下面4個步驟(即new幹了什麼)**:app
上面的person1和person2分別保存Person的一個不一樣實例,這兩個對象都有一個constructor(構造函數)屬性,該屬性執行Person函數
person1.constructor == person2.constructor == Person
ui
能夠用constructor來識別對象類型,可是用instanceof 更可靠this
person1和person2同時也是object的實例,由於全部的對象都繼承自Objectes5
若是構造函數裏面有方法,那方法都會在每一個實例上都建立一遍,所以不一樣實例上的函數是不相等的spa
person1.sayName == person2.sayName // false
prototype
雖然能夠把方法移到構造函數外部,設置成全局函數,可是若是對象須要定義不少方法,就要定義不少個全局函數,
function Person(){};
Person.prototype.name = "li";
Person.prototype.age = 20;
Person.prototype.sayName = function(){
console.log(this.name)
};
var person1 = new Person()
var person2 = new Person()
person1.sayName() //li
person2.sayName() //li
person2.name = 'xiao' //person2這個實例加了一個name屬性,重寫name屬性,但不會改變原型的值
person1.sayName() //li
person2.sayName() //xiao //若是實例上有name屬性,取的是實例上的
複製代碼
可讓全部對象實例共享它所包含的屬性和方法。
person1.sayName !== person2.sayName //true
只要建立了一個函數,就會爲該函數建立一個prototype屬性,這個屬性指向函數的原型對象。在默認狀況下,全部原型對象都會自動得到一個constructor(構造函數)屬性,這個屬性包含一個指向prototype屬性所在函數的指針(媽呀,很拗口,看代碼和圖比較直觀)
建立了自定義的構造函數後,其原型對象默認只會取得constructor屬性,至於其餘方法,都是從object繼承而來。當調用構造函數建立一個實例,該實例內部將包含一個指針,指向構造函數的原型對象,能夠用_proto_屬性訪問。這個連接存在與實例與構造函數的原型對象之間,而不存在於實例與構造函數之間。
Person.prototype.constructor = Person
person1._proto_ == Person.prototype
Person.prototype.isPrototypeOf(person1) // true
Object.getPrototypeOf(person1) == Person.prototype // true
Object.getPrototypeOf(person1).name //li
person1.hasOwnProperty('name') // false
person2.hasOwnProperty('name') //true
複製代碼
'name' in Person // true
hasOwnProperty和in一塊兒能夠判斷屬性是否是在原型中
!object.hasOwnProperty(name) && ('name' in object)
咱們將Person.prototype 設置爲等於一個以對象字面量形式建立的新對象,最終結果相同,可是constructor屬性再也不指向Person。這裏的語法,本質上完成重寫了默認的portotype對象,所以它的原型是Object,constructor是指向Object的構造函數。可是用instanceof 操做符還能返回正確結果
function Person(){};
Person.prototype = {
name: 'li',
age: 20,
sayName: function(){
console.log(this.name)
}
};
var person1 = new Person()
console.log(Person.prototype.constructor == Person) //false
console.log(Person.prototype.constructor == Object) //true
console.log(person1 instanceof Person) // true
複製代碼
function Person(){};
var person1 = new Person()
Person.prototype = {
name: 'li',
age: 20,
sayName: function(){
console.log(this.name)
}
};
person1.sayName() //person1.sayName is not a function
複製代碼
先建立實例,在重寫其原型對象,會切斷現有原型與以前任何已經存在的對象實例之間的聯繫。person1引用的仍是原來的原型。能夠用下面這種寫法
function Person(){};
var person1 = new Person()
Person.prototype.sayName = function(name){
console.log(name)
}
person1.sayName('li') //li
複製代碼
var Person = function(name,age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
console.log(this.name)
};
var person1 = new Person('li',20)
var person2 = new Person('xiao',18)
person1.sayName() //li
person2.sayName() //xiao
複製代碼
function Person(name, age){
this.name = name;
this.age = age
if(typeof this.sayName != function) {
Person.prototype.sayName = function(name){
console.log(this.name)
}
}
};
複製代碼
判斷這個代碼只會在初次調用構造函數時執行
js只支持實現繼承 主要是依靠 原型鏈 來實現的.
利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法
方法:
每個構造函數都有一個原型對象,原型對象包含一個指向構造函數的指針,而實例都包含一個指向原型對象的指針。那麼,若是咱們讓原型對象等於另外一個類型的實例,此時的原型對象將包含一個指向另外一個原型的指針,相應的,另外一個原型中也包含着一個指向另外一個構造函數的指針,假如另外一個原型又是另外一個類型的實例。。。如此層層遞進,就構成了實例和原型的鏈條
若是引用對象(實例instance)的某個屬性,會如今這個對象內找,若是找不到,就會到這個對象的原型上去找,原型上找不到,就到原型的原型上去找,直到找到這個屬性或者返回null爲止
function Parent(name) {
this.name = name
}
Parent.prototype.sayName = function() {
console.log(this.name)
}
function Children(age) {
this.age = age
}
Children.prototype = new Parent('Amy') //子類的原型是父類的實例
let instance = new Children(10)
instance.sayName()
複製代碼
在子類型構造函數的內部調用超類型構造函數
沒法複用函數,全部類型只能使用構造函數模式
原型鏈和借用構造函數組合,使用原型鏈實現對原型屬性和方法的繼承,經過借用構造函數來實現對實例屬性的繼承
function Parent(name) {
this.name = name
this.colors = ['red']
}
Parent.prototype.sayName = function() {
console.log(this.name)
}
function Children(name,age) {
Parent.call(this,name);
this.age = age
}
Children.prototype = new Parent()
let instance1 = new Children('Amy',10)
instance1.sayName()
instance1.colors.push('green')
console.log(instance1.colors)
let instance2 = new Children('Mike',20)
instance2.sayName()
instance2.colors.push('blue')
console.log(instance2.colors)
複製代碼
調用兩次超類型構造函數,
function object(o) {
function F(){};
F.prototype = o; //F的原型指向傳入的對象o
return new F()
}
複製代碼
Object.create(),這個方法接收兩個參數,一個是用做新對象原型的對象和(可選)一個爲新對象定義額外屬性的對象,只有一個參數時,Object.create()與Objec()同樣
包含引用類型值的屬性始終都會共享相應的值
建立一個僅用於封裝繼承過程的函數。不能作到函數複用
經過借用構造函數來實現對實例屬性的繼承,經過原型鏈的混成模式來繼承方法。僅調用一次超類型構造函數,避免建立沒必要要的多餘的屬性
function Parent(name) {
this.name = name;
this.colors = ['yellow']
}
Parent.prototype.sayName = function() {
console.log(this.name)
}
function Children(name,age) {
Parent.call(this,name);
this.age = age
}
function inheritPrototype(parent,children) {
var prototype = Object(parent.prototype)
prototype.constructor = children;
children.prototype = prototype
}
inheritPrototype(Parent, Children);
var instance1 = new Children('li', 20)
instance1.sayName()
複製代碼
ES5 的繼承,實質是先創造子類的實例對象this,而後再將父類的方法添加到this上面(Parent.apply(this))。ES6 的繼承機制徹底不一樣,實質是先將父類實例對象的屬性和方法,加到this上面(因此必須先調用super方法),而後再用子類的構造函數修改this。
若是子類沒有定義constructor方法,這個方法會被默認添加,在子類的構造函數中,只有調用super以後,纔可使用this關鍵字,不然會報錯。這是由於子類實例的構建,基於父類實例,只有super方法才能調用父類實例。