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