咱們知道,建立對象能夠這樣javascript
var person = new Object();
person.name = 「Nicholas」;
person.age = 29;
person.job = 「Software Engineer」;
person.sayName = function(){ alert(this.name);};
複製代碼
這個對象有name, age, job屬性,還有一個sayName()的方法。這是你們經常使用的建立對象的方法,能夠不斷添加新屬性,可是若是要建立多個相似的對象的話,就須要不斷地重複這段代碼,耗費不少內存。java
這時候能夠用工廠模式來解決這個問題bash
function createPerson(name, age, job){
var o = new Object(); //1.建立一個空對象並賦值給變量o
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o; //2.返回變量o
}
var person1 = createPerson(「Nicholas」, 29, 「Software Engineer」);
var person2 = createPerson(「Greg」, 27, 「Doctor」);
複製代碼
工廠模式還能夠更加簡練,好比能夠建立一個語法糖,省略掉上一段代碼的1和2 這時候便涉及到了Constructor模式了ide
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person(「Nicholas」, 29, 「Software Engineer」);
var person2 = new Person(「Greg」, 27, 「Doctor」);
複製代碼
注意constructor老是以大寫開頭,這是它的書寫規範。 相比工廠模式,當咱們用new這個關鍵字的時候,它幫咱們寫了一下步驟:函數
alert(person1 instanceof Person); //true
複製代碼
一樣的,contructor模式也存在一個問題,在上一段代碼中,咱們添加了一個sayName()
的方法優化
this.sayName = function(){
alert(this.name);
};
複製代碼
若是須要建立100個不一樣的person, 也須要建立100個sayName()
函數.ui
alert(person1.sayName == person2.sayName); //false
複製代碼
可是這個函數代碼都是同樣的,實際上只須要建立一次便可。this
function createPerson(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
};
var person1 = createPerson('Nicholas', 29, 'Software Engineer');
var person2 = createPerson('Greg', 27, 'Doctor');
複製代碼
若是要建立不少不一樣的對象,就必須建立不少不一樣的全局方法,很容易產生衝突,這時候另外一種方式就大顯身手了(原型)spa
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();
person2.sayName(); //」Nicholas」
alert(person1.sayName == person2.sayName); //true
複製代碼
每一個函數建立的時候,它的prototype屬性也會被建立,指向一個普通的對象,這個對象會有一個constructor屬性,指回這個函數,上面那段代碼中 Person.prototype.constrctor
會從新指向Person。而每次建立一個新的實例,實例的__proto__屬性會指向這個ProtoTypeprototype
person1.sayName()
的時候,它自己是沒有這個方法的,因此會自動到
person1.__proto__
去找,若是尚未,就再上一層,去
person1.__proto__.__proto__
找,直到找到或者返回null爲止。 讀取
person1
的其餘屬性也是同樣的,若是對象自己沒有這個屬性,會到它的___proto__去找,也就是
Person.prototype
,若是有的話,就返回這個值,沒有的話,會再到
Person.prototype.__proto__
找,因爲
Person.prototype
是個普通的對象實例,因此
Person.prototype.__proto__
指向的是
Object.prototype
判斷一個屬性是否屬於自身的對象仍是對象的原型,能夠用hasOwnProperty()
判斷
原型一樣能夠用下列代碼的方式建立,可是會覆蓋掉原來默認的Prototype, 也就沒有默認的constructor屬性。
function Person(){ }
Person.prototype = {
name : 「Nicholas」,
age : 29,
job : 「Software Engineer」,
sayName : function () {
alert(this.name); }
};
複製代碼
下面這段代碼也會運行錯誤,緣由是噹噹實例被建立的時候,它的__proto__已經指向了默認的Person.prototype,這個時候你將Person.prototype指向另外一個地址,實例的__proto__是沒辦法訪問到的
function Person(){ }
var person = new Person();
Person.prototype = {
constructor: Person,
name : 「Nicholas」,
age : 29,
job : 「Software Engineer」,
sayName : function () {
alert(this.name); }
};
person.sayName(); //error
複製代碼
__proto__和prototype有什麼區別呢,__proto__是在新的實例對象生成的時候,自動綁定到構造函數的prototype上面的,全部的對象都有__proto__屬性。prototype則是在建立函數的時候,它的prototype屬性也會被建立,指向一個普通的對象,這個對象會有一個constructor屬性,指回這個函數。prototype只有函數纔有的屬性。
常見的建立對象的方式爲下列代碼,這樣避免了共享對象的衝突
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,Court,Van」
alert(person2.friends); //」Shelby,Court」
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true
複製代碼
缺點:每次建立一個子實例,父方法會重複構建屢次,而且不會繼承父類的prototype
function SuperType(){
this.property = true;
this.run = function() {}
}
function SubType(){
SuperType.call(this)
}
var instance = new SubType();
複製代碼
缺點:一個子類的實例更改子類原型從父類構造函數中繼承來的共有屬性就會直接影響到其餘子類
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
複製代碼
經過SubType.prototype = new SuperType();
完成了原型的繼承。爲何要用SuperType
的實例來綁定SubType
的prototype
呢,由於這個實例的__proto__屬性會指向SuperType.prototype
, 經過這個方式來繼承SuperType
的屬性和方法
toString()
和
valueOf()
等函數,也能夠直接調用,由於這些函數已經在Object裏面定義好了
若是要添加SubType的方法能夠
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//new method
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
//override existing method
SubType.prototype.getSuperValue = function (){
return false;
};
// 下面的方式是錯的,SubType.prototype被從新賦值,不可以再訪問父函數的方法了
<!--SubType.prototype = {-->
<!-- getSubValue: function (){--> <!-- return this.subproperty;--> <!-- }--> <!--}--> var instance = new SubType(); alert(instance.getSuperValue()); //false 複製代碼
原型鏈存在的問題
function SuperType(){
this.colors = [「red」, 「blue」, 「green」];
}
function SubType(){ }
//inherit from SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType(); instance1.colors.push(「black」);
alert(instance1.colors); //」red,blue,green,black」
var instance2 = new SubType();
alert(instance2.colors); //」red,blue,green,black」
複製代碼
解決辦法
function SuperType(){
this.colors = [「red」, 「blue」, 「green」];
}
function SubType(){ //inherit from SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push(「black」);
alert(instance1.colors); //」red,blue,green,black」
var instance2 = new SubType();
alert(instance2.colors); //」red,blue,green」
複製代碼
可是上述方法也存在子對象沒法引用父對象prototype中的方法等問題
function SuperType(name){
this.name = name;
this.colors = [「red」, 「blue」, 「green」];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
//inherit properties
SuperType.call(this, name);
this.age = age;
}
//inherit methods
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType(「Nicholas」, 29); instance1.colors.push(「black」);
alert(instance1.colors); //」red,blue,green,black」
instance1.sayName(); //」Nicholas」;
instance1.sayAge(); //29
var instance2 = new SubType(「Greg」, 27); alert(instance2.colors); //」red,blue,green」
instance2.sayName(); //」Greg」;
instance2.sayAge(); //27
複製代碼
上述方法會調用SuperType的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);
};
複製代碼
或者
// Shape - 父類(superclass)
function Shape() {
this.x = 0;
this.y = 0;
}
// 父類的方法
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Shape moved.');
};
// Rectangle - 子類(subclass)
function Rectangle() {
Shape.call(this); // call super constructor.
}
// 子類續承父類
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Is rect an instance of Rectangle?',
rect instanceof Rectangle); // true
console.log('Is rect an instance of Shape?',
rect instanceof Shape); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
複製代碼
使用Object.create()是將對象繼承到__proto__屬性上