JavaScript是面向?qū)ο缶幊蹋∣bject Oriented Programming,OOP)語(yǔ)言。
面對(duì)對(duì)象編程的核心思想就是是將真實(shí)世界中各種復(fù)雜的關(guān)系,抽象成一個(gè)個(gè)對(duì)象,然后由對(duì)象之間分工合作,完成對(duì)真實(shí)世界的模擬。
何為對(duì)象?
對(duì)象是單個(gè)實(shí)物的抽象。
一本書、一輛汽車、一個(gè)人都可以是“對(duì)象”,一個(gè)數(shù)據(jù)庫(kù)、一張網(wǎng)頁(yè)也可以是“對(duì)象”。世界上所有的對(duì)象都可以是“對(duì)象”。
對(duì)象是一個(gè)容器,封裝了“屬性”(property)和“方法”(method)。
屬性,就是對(duì)象的狀態(tài),而方法,就是對(duì)象的行為。比如:我們可以把一輛汽車抽象成一個(gè)對(duì)象,它的屬性就是它的顏色、重量等,而方法就是它可以啟動(dòng)、停止等。
1、對(duì)象
在Javascript中,對(duì)象是一個(gè)基本數(shù)據(jù)類型。
對(duì)象是一種復(fù)合值:它將很多值集合在一起,可通過名字訪問這些值。對(duì)象也可看做一種無(wú)序的數(shù)據(jù)集合,由若干個(gè)“鍵值對(duì)”(key-value)構(gòu)成。
var o={
name:'a'
}
上面代碼中,大括號(hào)定義了一個(gè)對(duì)象,它被賦值給變量o。這個(gè)對(duì)象內(nèi)部包含一個(gè)鍵值對(duì)(又稱為“成員”),name是“鍵名”(成員的名稱),字符串a(chǎn)是“鍵值”(成員的值)。鍵名與鍵值之間用冒號(hào)分隔。如果對(duì)象內(nèi)部包含多個(gè)鍵值對(duì),每個(gè)鍵值對(duì)之間用逗號(hào)分隔。
鍵名:對(duì)象的所有鍵名都是字符串,所以加不加引號(hào)都可以。如果鍵名是數(shù)值,會(huì)被自動(dòng)轉(zhuǎn)為字符串。
對(duì)象的每一個(gè)“鍵名”又稱為“屬性”(property),它的“鍵值”可以是任何數(shù)據(jù)類型。如果一個(gè)屬性的值為函數(shù),通常把這個(gè)屬性稱為“方法”,它可以像函數(shù)那樣調(diào)用。
var o = {
go: function(x){
return x+1;
}
};
o.go(2) // 3
如果鍵名不符合標(biāo)識(shí)名的條件(比如第一個(gè)字符為數(shù)字,或者含有空格或運(yùn)算符),也不是數(shù)字,則必須加上引號(hào),否則會(huì)報(bào)錯(cuò)。
上面的代碼中,如果鍵名'1a'不用引號(hào)引起來(lái),就會(huì)報(bào)錯(cuò)。
注意:為了避免這種歧義,JavaScript規(guī)定,如果行首是大括號(hào),一律解釋為語(yǔ)句(即代碼塊)。如果要解釋為表達(dá)式(即對(duì)象),必須在大括號(hào)前加上圓括號(hào)。
2、創(chuàng)建對(duì)象
在JavaScript中,有三種方法創(chuàng)建對(duì)象
對(duì)象直接量: var o={};
關(guān)鍵字new: var o=new Object();
Object.create()函數(shù): var o=Object.create(null)
2.1對(duì)象直接量對(duì)象直接量是由若干名/值對(duì)組成的映射表。鍵名與鍵值之間用冒號(hào)分隔。如果對(duì)象內(nèi)部包含多個(gè)鍵值對(duì),每個(gè)鍵值對(duì)之間用逗號(hào)分隔。整個(gè)映射表用花括號(hào)括起來(lái)。
在ECMAScript 5中,保留字可以用做不帶引號(hào)的屬性名。
注意:對(duì)象直接量中的最后一個(gè)屬性后的逗號(hào)可有可無(wú),但是在ie中,如果多了一個(gè)逗號(hào),會(huì)報(bào)錯(cuò)。
2.2通過new創(chuàng)建對(duì)象
new運(yùn)算符創(chuàng)建并初始化一個(gè)新對(duì)象。關(guān)鍵字new后跟隨一個(gè)函數(shù)調(diào)用,這個(gè)函數(shù)稱做構(gòu)造函數(shù)(constructor)。
例子:
var o1 = {};
var o2 = new Object();
var o3 = Object.create(null);
上面三行語(yǔ)句是等價(jià)的。
對(duì)象最常見的用法是創(chuàng)建(create)、設(shè)置(set)、查找(query)、刪除(delete)、檢測(cè)(test)和枚舉(enumerate)它的屬性。
屬性包括名字(鍵名)和值(鍵值)。
屬性名可以是包含空字符串在內(nèi)的任意字符串,但對(duì)象中不能存在兩個(gè)同名的屬性。
3、屬性特性
可寫(writable attribute):可設(shè)置該屬性的值。
可枚舉(enumerable attribute):可通過for/in循環(huán)返回該屬性。
可配置(configurable attribute):可刪除或修改屬性。
4、讀取屬性
讀取對(duì)象的屬性,有兩種方法,一種是使用點(diǎn)運(yùn)算符,還有一種是使用方括號(hào)運(yùn)算符。
var o = {
name : 'a'
}
o.name // "a"
o['name'] //"a"
注意:數(shù)值鍵名不能使用點(diǎn)運(yùn)算符(因?yàn)闀?huì)被當(dāng)成小數(shù)點(diǎn)),只能使用方括號(hào)運(yùn)算符。
JavaScript對(duì)象是動(dòng)態(tài)的,可新增屬性也可刪除屬性。但注意,我們是通過引用而非值來(lái)操作對(duì)象。
5、屬性的查詢和設(shè)置在JavaScript中,我們可以通過點(diǎn)(.)或方括號(hào)([])運(yùn)算符來(lái)獲取屬性的值。運(yùn)算符左側(cè)應(yīng)當(dāng)是一個(gè)表達(dá)式,它返回一個(gè)對(duì)象。
for...in
for...in循環(huán)用來(lái)遍歷一個(gè)對(duì)象的全部屬性。
var o = {
name : 'a',
age : 12
}
for(var i in o){
console.log(o[i]
}
// "a"
// 12
查看所有屬性
查看一個(gè)對(duì)象本身的所有屬性,可以使用Object.keys方法,返回一個(gè)數(shù)組。
var o = {
name : 'a',
age : 12
}
Object.keys(o) //['name','age']
刪除屬性
delete運(yùn)算符可以刪除對(duì)象的屬性。
var o={
name : 'a'
}
delete o.name //true
o.name //undefined
注意:delete運(yùn)算符只能刪除自有屬性,不能刪除繼承屬性。
刪除一個(gè)不存在的屬性,delete不報(bào)錯(cuò),而且返回true。
只有一種情況,delete命令會(huì)返回false,那就是該屬性存在,且不得刪除。
檢測(cè)屬性
在JavaScript中,有多種方法檢測(cè)某個(gè)屬性是否存在于某個(gè)對(duì)象中。
用“!==”來(lái)判斷一個(gè)屬性是否是undefined
hasOwnPreperty()方法
propertyIsEnumerable()方法
只有檢測(cè)到是自有屬性且這個(gè)屬性的可枚舉性為true時(shí)才返回true。
in運(yùn)算符
in運(yùn)算符左側(cè)是屬性名(字符串),右側(cè)是對(duì)象。如果對(duì)象的自有屬性或繼承屬性中包含這個(gè)屬性就返回true。
var o = {
name : 'a'
}
'name' in o //true
6、對(duì)象的三個(gè)屬性
每一個(gè)對(duì)象都有與之相關(guān)的原型(prototype)、類(class)和可擴(kuò)展性(extensible attribute)
將對(duì)象作為參數(shù)傳入Object.getPrototypeOf()可以查詢它的原型。
檢測(cè)一個(gè)對(duì)象是否是另一個(gè)對(duì)象的原型,可以使用isPrototypeOf()方法。
7、序列化對(duì)象
對(duì)象序列化是指將對(duì)象的狀態(tài)轉(zhuǎn)換為字符串,也可將字符串還原為對(duì)象。
在JavaScript中,提供了內(nèi)置函數(shù)JSON.stringify()和JSON.parse()用來(lái)序列化和還原JavaScript對(duì)象。
NaN、Infinity和-Infinity序列化的結(jié)果是null
var o = {
name : 'a',
age : 12,
intro : [false,null,'']
}
s= JSON.stringify(o) // s {"name":"a","age":12,"intro":[false,null,""]}
p=JSON.parse(s) // p是o的深拷貝
注意:JSON.stringify()只能序列化對(duì)象可枚舉的自有屬性。對(duì)于一個(gè)不能序列化的屬性來(lái)說(shuō),在序列化后的輸出字符串中會(huì)將這個(gè)屬性省略掉。
8、構(gòu)造函數(shù)
構(gòu)造函數(shù),是用來(lái)生成“對(duì)象”的函數(shù)。一個(gè)構(gòu)造函數(shù)可生成多個(gè)對(duì)象,這些對(duì)象都有相同的結(jié)構(gòu)。
構(gòu)造函數(shù)的特點(diǎn):
函數(shù)體內(nèi)使用了this關(guān)鍵字,代表了所要生成的對(duì)象實(shí)例
生成對(duì)象時(shí),必需用new命令
構(gòu)造函數(shù)名字的第一個(gè)字母通常大寫。
例子:
function Car(){
this.color = 'black';
}
var c = new Car();
上面的代碼生成了Car的實(shí)例對(duì)象,保存在變量c中。
構(gòu)造函數(shù)也可以傳入?yún)?shù):
function Car(color){
this.color = color;
}
var c = new Car('red');
new命令本身就可以執(zhí)行構(gòu)造函數(shù),所以后面的構(gòu)造函數(shù)可以帶括號(hào),也可以不帶括號(hào)。下面兩行代碼是等價(jià)的。
var c = new Car();
var c = new Car;
每一個(gè)構(gòu)造函數(shù)都有一個(gè)prototype屬性。
8.1 this關(guān)鍵字
this總是返回一個(gè)對(duì)象,簡(jiǎn)單說(shuō),就是返回屬性或方法“當(dāng)前”所在的對(duì)象。
上面的代碼中,this就代表property屬性當(dāng)前所在的對(duì)象。
由于對(duì)象的屬性可以賦給另一個(gè)對(duì)象,所以屬性所在的當(dāng)前對(duì)象是可變的,即this的指向是可變的。
var A = {
name: '張三',
describe: function(){
return this.name;
}
};
var B = {
name: '李四'
};
B.describe = A.describe;
B.describe();
// "李四"
注意:如果一個(gè)函數(shù)在全局環(huán)境中運(yùn)行,那么this就是指頂層對(duì)象(瀏覽器中為window對(duì)象)。
8.1.1 改變this指向
在JavaScript中,提供了call、apply、bind三種方法改變this的指向。
(1)funciton.prototype.call()
call(obj, arg1, arg2, ...)
第一個(gè)參數(shù)obj是this要指向的對(duì)象,也就是想指定的上下文;arg1,arg2..都是要傳入的參數(shù)。
注意:如果參數(shù)為空、null和undefined,則默認(rèn)傳入全局對(duì)象。
(2)funciton.prototype.apply()
apply(obj,[arg1,arg2....])
apply()和call()方法原理類似,只不過,它第二個(gè)參數(shù)一個(gè)數(shù)組,里面的值就是要傳入的參數(shù)。
(3)function.prototype.bind()
bind方法用于將函數(shù)體內(nèi)的this綁定到某個(gè)對(duì)象,然后返回一個(gè)新函數(shù)。
9、原型
9.1 原型
每一個(gè)JavaScript對(duì)象(null除外)都和另一個(gè)對(duì)象相關(guān)聯(lián),也可以說(shuō),繼承另一個(gè)對(duì)象。另一個(gè)對(duì)象就是我們熟知的“原型”(prototype),每一個(gè)對(duì)象都從原型繼承屬性。只有null除外,它沒有自己的原型對(duì)象。
所有通過對(duì)象直接量創(chuàng)建的對(duì)象都具有同一個(gè)原型對(duì)象,并可以通過JavaScript代碼Object.prototype獲得對(duì)原型對(duì)象的引用。
通過關(guān)鍵字new和構(gòu)造函數(shù)調(diào)用創(chuàng)建的對(duì)象的原型就是構(gòu)造函數(shù)的prototype屬性的值。比如:通過new Object()創(chuàng)建的對(duì)象繼承自O(shè)bject.prototype;通過new Array()創(chuàng)建的對(duì)象的原型就是Array.prototype。
沒有原型的對(duì)象為數(shù)不多,Object.prototype就是其中之一,它不繼承任何屬性。
所有的內(nèi)置構(gòu)造函數(shù)都具有一個(gè)繼承自O(shè)bject.prototype的原型。
9.2 原型鏈
對(duì)象的屬性和方法,有可能是定義在自身,也有可能是定義在它的原型對(duì)象。由于原型本身也是對(duì)象,又有自己的原型,所以形成了一條原型鏈(prototype chain)。比如,a對(duì)象是b對(duì)象的原型,b對(duì)象是c對(duì)象的原型,以此類推。
如果一層層地上溯,所有對(duì)象的原型最終都可以上溯到Object.prototype,即Object構(gòu)造函數(shù)的prototype屬性指向的那個(gè)對(duì)象。那么,Object.prototype對(duì)象有沒有它的原型呢?回答可以是有的,就是沒有任何屬性和方法的null對(duì)象,而null對(duì)象沒有自己的原型。
“原型鏈”的作用:
當(dāng)讀取對(duì)象的某個(gè)屬性時(shí),JavaScript引擎先尋找對(duì)象本身的屬性,如果找不到,就到它的原型去找,如果還是找不到,就到原型的原型去找。如果直到最頂層的Object.prototype還是找不到,則返回undefined。
繼承
JavaScript對(duì)象具有“自有屬性”,也有一些屬性是從原型對(duì)象繼承而來(lái)的。
當(dāng)查詢一個(gè)不存在的屬性時(shí),JavaScript不會(huì)報(bào)錯(cuò),返回undefined。
如果對(duì)象自身和它的原型,都定義了一個(gè)同名屬性,那么優(yōu)先讀取對(duì)象自身的屬性,這叫做“覆蓋”(overiding)。
9.2.1 contructor屬性
prototype對(duì)象有一個(gè)constructor屬性,默認(rèn)指向prototype對(duì)象所在的構(gòu)造函數(shù)。
9.3 操作符
(1)instanceof運(yùn)算符
instanceof運(yùn)算符返回一個(gè)布爾值,表示指定對(duì)象是否為某個(gè)構(gòu)造函數(shù)的實(shí)例。
var c = new Car();
c instanceof Car //true
instanceof運(yùn)算符的左邊是實(shí)例對(duì)象,右邊是構(gòu)造函數(shù)。它的運(yùn)算實(shí)質(zhì)是檢查右邊構(gòu)建函數(shù)的原型對(duì)象,是否在左邊對(duì)象的原型鏈上。
(2)Object.getPrototypeOf()
Object.getPrototypeOf方法返回一個(gè)對(duì)象的原型。這是獲取原型對(duì)象的標(biāo)準(zhǔn)方法
Object.getPrototypeOf(c) === Car.prototype //true
(3)Object.setPrototypeOf()
Object.setPrototypeOf方法可以為現(xiàn)有對(duì)象設(shè)置原型,返回一個(gè)新對(duì)象。Object.setPrototypeOf方法接受兩個(gè)參數(shù),第一個(gè)是現(xiàn)有對(duì)象,第二個(gè)是原型對(duì)象。
(4)Object.create()
Object.create方法用于從原型對(duì)象生成新的實(shí)例對(duì)象,可以替代new命令。
它接受一個(gè)對(duì)象作為參數(shù),返回一個(gè)新對(duì)象,后者完全繼承前者的屬性,即原有對(duì)象成為新對(duì)象的原型。
(5)Object.prototype.isPrototypeOf()
對(duì)象實(shí)例的isPrototypeOf方法,用來(lái)判斷一個(gè)對(duì)象是否是另一個(gè)對(duì)象的原型。
Object.prototype.isPrototypeOf({}) //true
(6)Object.prototype.__proto__
__proto__屬性(前后各兩個(gè)下劃線)可以改寫某個(gè)對(duì)象的原型對(duì)象。
(7)Object.getOwnPropertyNames()
Object.getOwnPropertyNames方法返回一個(gè)數(shù)組,成員是對(duì)象本身的所有屬性的鍵名,不包含繼承的屬性鍵名。
(8)Object.prototype.hasOwnProperty()
對(duì)象實(shí)例的hasOwnProperty方法返回一個(gè)布爾值,用于判斷某個(gè)屬性定義在對(duì)象自身,還是定義在原型鏈上。
更多建議: