
面向?qū)ο蟮某绦蛟O(shè)計
NO.1 對象
OO語言都有一個標志,就是都有類的概念,通過類可以創(chuàng)建多個具有相同屬性的方法的對象。但JS里沒有類的概念,所以它的對象也有所不同。JS把對象定義為無序?qū)傩缘募稀?/p>
JS的每個對象都是基于一個引用類型創(chuàng)建的。
JS中有兩種屬性,數(shù)據(jù)屬性和訪問器屬性;
1.數(shù)據(jù)屬性:
數(shù)據(jù)屬性包含一個數(shù)據(jù)值的位置。在這個位置可以讀取和寫入值。數(shù)據(jù)屬性有四個描述其行為的特性:
Configurable:表示能否通過delete刪除屬性從而重新定義屬性。
Enumerable: 表示能否通過for-in循環(huán)返回屬性。
Writable: 表示能否修改屬性的值。
Value: 包含這個屬性的數(shù)據(jù)值。
這四個是對象的默認屬性。要修改默認是屬性的特性,可以用Object.defindProperty()方法.例如:把屬性定義為只讀,把屬性定義為不能刪除等。
var book = {
_year: 2004,
edtion: 1
}
_year是一種常見的記號,表示中能通過對象方法訪問的屬性。
創(chuàng)建對象:
雖然Object構(gòu)造函數(shù)和字面的方法都可以用來創(chuàng)建單個對象,但是這些方法都有明顯的缺點,就是當一個接口創(chuàng)建很多對象時,會有很多重復(fù)代碼。為了解決這個問題,人們使用工廠模式的一種變體。
工廠模式抽象了創(chuàng)建對象的具體過程,例如:
function createPerson(name, age, job){
var o = new Object();
o.name = name;
p.age = age;
o.job = job;
o.sayname = function(){
alert(this.name);
};
return o;
}
工廠模式雖然解決了對個類似對象的問題,但是沒有解決對象識別問題(怎么樣知道一個對象的類型)。于是有了構(gòu)造函數(shù)模式:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("yzq", 25, "software engineer");
構(gòu)造函數(shù):
構(gòu)造函數(shù)和其他的函數(shù)的唯一區(qū)別是調(diào)用的方式不同,構(gòu)造函數(shù)都是通過new來調(diào)用的。
構(gòu)造函數(shù)的問題:
構(gòu)造函數(shù)雖然好用,但是在用構(gòu)造函數(shù)創(chuàng)建對象時,當對象有方法時,就需要在每個構(gòu)造函數(shù)里創(chuàng)建一個方法。為了解決這一個問題。將函數(shù)定義轉(zhuǎn)移到構(gòu)造函數(shù)的外面:
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName
}
function sayName(){
alert(this.name);
};
var person1 = new Person("yzq", 25, "software engineer");
原型模式:
我們創(chuàng)建的每個函數(shù)都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,這個對象包含由特定類型所有實例共享的屬性和方法。
例子:
function Person(){};
Person.prototyoe.name = "yzq";
Person.prototyoe.age = 25;
Person.prototyoe.job = "cc"
Person.prototyoe.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"yzq"
var person2 = new Person();
person2.sayName(); //"yzq"
通過這種方式,將屬性和方法共享。
理解原型對象:
1.只要一個新函數(shù)被創(chuàng)建時,就會根據(jù)一組的特定的規(guī)則為該函數(shù)創(chuàng)建一個prototype屬性。
2.這個prototype屬性是一個指針,指向函數(shù)的原型對象。
3.然后在默認情況下,原型對象都會自動獲得一個構(gòu)造函數(shù)屬性。這個屬性是指向prototype屬性所在函數(shù)的指針。例如:Person.prototype.constructor指向Person
通過構(gòu)造函數(shù),可以繼續(xù)為原型對象添加其他屬性和方法。
4.在創(chuàng)建了自定義的構(gòu)造函數(shù)之后,其原型對象默認只會取得constructor。其他方法,都是從Object繼承而來
5.當用構(gòu)造函數(shù)創(chuàng)建了一個新實例后,實例內(nèi)部將包含一個指針為[[Prototype]]

個人理解:
Person.prototype指向原型對象,原型對象的構(gòu)造函數(shù)Person.prototype.constructor又指回了Person。原型對象中除了包含構(gòu)造函數(shù)屬性之外,還有其他的屬性。
而真正的每個實例都包含一個內(nèi)部屬性,數(shù)值只指向Person.prototype。實例和構(gòu)造函數(shù)是沒有任何直接聯(lián)系的。
當代碼讀取某個對象的某個屬性時,都會先從對象實例本身開始,如果在實例本身中找到了給定的名字的屬性,則返回屬性的值,如果找不到,則繼續(xù)搜索指針指向的原型對象。例如:在調(diào)用person1.sayName()的時候,在person1中沒有找到sayName屬性,但是在person1的原型中有sayName屬性。
另外,當對象實例可以訪問原型中的屬性,但是不能重寫原型中的屬性。所以當對象實例中有原型中的同名屬性時,實例中的該同名屬性會屏蔽原型里的屬性。要想重新訪問原型里的屬性,可以用DELETE刪除實例中的屬性。

NO.2 原型和in操作符
有兩種方式使用in操作符,單獨使用和在for-in循環(huán)中使用。
單獨使用時,in操作符會通過對象能夠訪問給定屬性時,返回true。無論該屬性存在實例還是原型中。
alert("name in person1")一直都是true,當屬性存在時。不管在實例中還是原型對象里。
要想確定屬性在對象中還是存在原型中,可以同時使用hasOwnProperty()和in操作符。方法如下:
function hasPrototypeProperty(object, name)[
return !object.hasOwnproperty(name) && (name in object);
}
hasOwnproperty只有在實例中才返回true。所以以上那個函數(shù)為true時,可以判斷屬性是在原型中。
要獲取對象上所有可枚舉的實例屬性,可以使用Object.keys()方法。這個方法接受一個對象作為參數(shù),返回一個所有屬性的字符串數(shù)組
var keys = Object.keys(Person.prototype);
alert(keys); //"name,age,job,sayName"
var p1 = new Person()
p1.name = "yzq"
p1.age = 25;
var p1keys = Object.keys(p1);
alert(p1keys); // "name,age"
更簡單的原型語法:
function Person(){
}
Person.prototype = {
name : "yzq",
age : 25,
sayName : function(){
alert(this.name);
}
}
注意:使用上面語法會導致constructor的屬性等于Object而不等于Person了。
如果constructor真的很重要,可以自己定義。
function Person(){
}
Person.prototype = {
constructor : Person,
name : "yzq",
age : 25,
sayName : function(){
alert(this.name);
}
}
NO.3原型的動態(tài)性
先創(chuàng)建實例,在修改原型,指在原型里添加屬性和方法。這樣是OK的。
var friend = new Preson();
Person.prototype.sayHi = function(){
alert("hi");
}
friend.sayHi(); //"hi"
但是先創(chuàng)建實例,在重新寫整個原型。這樣就會報錯。
function Person(){
}
var friend = new Preson();
Person.prototype = {
constructor : Person,
name :"yzq",
age :25,
sayName: function(){
alert(this.name);
}
}
friend.sayName(); //error
NO.4原型模式的缺點

因為原型模式中的屬性和方法都是共用的,所以,當一個實例修改原型模型中的屬性時,其他實例中的屬性也會改變。
所以原型模式和構(gòu)造函數(shù)模式一般是組合使用:

NO.5 動態(tài)原型模式
為了解決同時寫構(gòu)造函數(shù)和原型這個問題,于是出現(xiàn)了動態(tài)原型模式。
function person(name,age,job){
//屬性
this.name = name;
this.age = age;
this.job = job
//方法
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
typeof this.sayName != "function"指的是當sayName方法不存在的時候,將方法放入原型中。