建立物件
工廠模式
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");
複製程式碼
但這種方式有一個缺點:無法判斷某個物件是什麼型別。
建構函式模式
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()方法雖然作用相同,但這兩個方法並不是同一個函式
解析一道題
new操作符做了什麼:
(1)建立一個新物件
(2)將建構函式的作用域賦給新物件(因此this就指向了這個新物件)
(3)執行建構函式中的程式碼
(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"
複製程式碼
當我們改變 值為引用型別的物件的屬性 時,這個改變的結果會被其他物件共享。
建構函式 + 原型模式
建構函式模式的屬性沒毛病。缺點是:無法共享方法
原型模式的方法沒毛病。缺點是:當原形物件的屬性的值為引用型別時,對其進行修改會反映到所有例項中
那我們就將兩者的結合,物件的屬性使用建構函式模式建立,方法則使用原型模式建立
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高階語言程式設計第六章(物件導向的程式設計)