面向對象的程序設計
NO.1 對象
OO語言都有一個標志,就是都有類的概念,通過類可以創(chuàng)建多個具有相同屬性的方法的對象瞬项。但JS里沒有類的概念,所以它的對象也有所不同锨天。JS把對象定義為無序屬性的集合。
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構造函數(shù)和字面的方法都可以用來創(chuàng)建單個對象,但是這些方法都有明顯的缺點菩咨,就是當一個接口創(chuàng)建很多對象時吠式,會有很多重復代碼。為了解決這個問題抽米,人們使用工廠模式的一種變體特占。
工廠模式抽象了創(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;
}
工廠模式雖然解決了對個類似對象的問題云茸,但是沒有解決對象識別問題(怎么樣知道一個對象的類型)是目。于是有了構造函數(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");
構造函數(shù):
構造函數(shù)和其他的函數(shù)的唯一區(qū)別是調用的方式不同,構造函數(shù)都是通過new來調用的标捺。
構造函數(shù)的問題:
構造函數(shù)雖然好用懊纳,但是在用構造函數(shù)創(chuàng)建對象時,當對象有方法時宜岛,就需要在每個構造函數(shù)里創(chuàng)建一個方法长踊。為了解決這一個問題功舀。將函數(shù)定義轉移到構造函數(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.然后在默認情況下,原型對象都會自動獲得一個構造函數(shù)屬性催首。這個屬性是指向prototype屬性所在函數(shù)的指針扶踊。例如:Person.prototype.constructor指向Person
通過構造函數(shù),可以繼續(xù)為原型對象添加其他屬性和方法郎任。
4.在創(chuàng)建了自定義的構造函數(shù)之后秧耗,其原型對象默認只會取得constructor。其他方法舶治,都是從Object繼承而來
5.當用構造函數(shù)創(chuàng)建了一個新實例后分井,實例內(nèi)部將包含一個指針為[[Prototype]]
個人理解:
Person.prototype指向原型對象车猬,原型對象的構造函數(shù)Person.prototype.constructor又指回了Person。原型對象中除了包含構造函數(shù)屬性之外尺锚,還有其他的屬性珠闰。
而真正的每個實例都包含一個內(nèi)部屬性,數(shù)值只指向Person.prototype瘫辩。實例和構造函數(shù)是沒有任何直接聯(lián)系的铸磅。
當代碼讀取某個對象的某個屬性時,都會先從對象實例本身開始杭朱,如果在實例本身中找到了給定的名字的屬性阅仔,則返回屬性的值,如果找不到弧械,則繼續(xù)搜索指針指向的原型對象八酒。例如:在調用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原型模式的缺點
因為原型模式中的屬性和方法都是共用的,所以卵皂,當一個實例修改原型模型中的屬性時秩铆,其他實例中的屬性也會改變。
所以原型模式和構造函數(shù)模式一般是組合使用:
NO.5 動態(tài)原型模式
為了解決同時寫構造函數(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方法不存在的時候,將方法放入原型中添祸。