欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript高級(jí)程序設(shè)計(jì)(第3版)學(xué)習(xí)筆記10 再訪js對(duì)象

 更新時(shí)間:2012年10月11日 15:30:06   作者:  
在ECMAScript中,兩個(gè)核心主題就是對(duì)象與函數(shù),而這兩個(gè)主題也有些互相纏繞的,在前面幾個(gè)博文中大略的過了一遍函數(shù)相關(guān)的基礎(chǔ)知識(shí),這篇文章再回到對(duì)象主題上來

1、對(duì)象再認(rèn)識(shí)

(1)對(duì)象屬性和特性

  什么是屬性(Property),什么是特性(Attribute),這有什么區(qū)別?我不想也不會(huì)從語義學(xué)上去區(qū)分,對(duì)于這系列文章來說,屬性就是組成對(duì)象的一個(gè)部分,廣義上也包括對(duì)象的方法,而特性則是指被描述主體所具有的特征,換句話說,屬性是我們可以通過編碼來訪問的具體存在,而特性則主要是為了便于理解概念的抽象存在,當(dāng)然,特性也可以通過相應(yīng)的屬性來具體外化。這一小節(jié)所講的對(duì)象屬性的特性就是對(duì)對(duì)象屬性特征的一個(gè)描述,主要來自于ECMA-262規(guī)范的第5版,該規(guī)范使用兩個(gè)中括號(hào)的形式來描述不能直接訪問的內(nèi)部特性。

A、屬性類型(先給屬性分下類):

  • 數(shù)據(jù)屬性:直接訪問屬性值的屬性
  • 訪問器屬性:通過getter/setter方法來訪問屬性值的屬性
  • 內(nèi)部屬性:不能通過代碼直接訪問的屬性,只是為了規(guī)范說明目的而存在,在規(guī)范中也使用兩個(gè)中括號(hào)的形式來描述

B、對(duì)象內(nèi)部屬性

  內(nèi)部屬性不能通過代碼直接訪問,它主要是為了描述規(guī)范,也是給ECMAScript實(shí)現(xiàn)者參考的,而對(duì)于開發(fā)者來說,了解這些可以便于理解一些內(nèi)部機(jī)制。比如在給一個(gè)屬性賦值時(shí),在實(shí)現(xiàn)中會(huì)調(diào)用[[Put]]內(nèi)部方法,而讀取一個(gè)屬性值時(shí),則調(diào)用[[Get]]方法。

所有對(duì)象公共的內(nèi)部屬性 個(gè)別對(duì)象特有的內(nèi)部屬性
名稱 規(guī)范 名稱 規(guī)范 對(duì)象
[[Prototype]] Object/Null [[PrimitiveValue]] primitive Boolean|Date|Number|String
[[Class]] String [[Construct]] SpecOp(a List of any) → Object new
[[Extensible]] Boolean [[Call]] SpecOp(any, a List of any) → any|Reference call
[[Get]] SpecOp (propName) →any [[HasInstance]] SpecOp(any) → Boolean Function
[[GetOwnProperty]] SpecOp (propName) →Undefined|Property Descriptor [[Scope]] Lexical Environment Function
[[GetProperty]] SpecOp (propName) →Undefined|Property Descriptor [[FormalParameters]] List of Strings Function
[[Put]] SpecOp (propName, any, Boolean) [
復(fù)制代碼 代碼如下:
]
ECMAScript code Function
[[CanPut]] SpecOp (propName) → Boolean [[TargetFunction]] Object Function.prototype.bind
[[HasProperty]] SpecOp (propName) → Boolean [[BoundThis]] any Function.prototype.bind
[[Delete]] SpecOp (propName, Boolean) → Boolean [[BoundArguments]] List of any Function.prototype.bind
[[DefaultValue]] SpecOp (Hint) → primitive [[Match]] SpecOp(String, index) → MatchResult RegExp
[[DefineOwnProperty]] SpecOp (propName, PropDesc, Boolean) → Boolean [[ParameterMap]] Object  

說明:

  • 每一個(gè)對(duì)象都有一個(gè)原型對(duì)象[[Prototype]],一般我們不能在代碼中直接訪問這個(gè)內(nèi)部屬性,但可以通過Object.getPrototypeOf(object)來獲取原型對(duì)象(在Firefox中可以通過__proto__來直接訪問)。
  • 在Object.prototype.toString方法中,按照規(guī)范內(nèi)建對(duì)象會(huì)返回包含[[Class]]的值“[object class]”,而內(nèi)建對(duì)象的[[Class]]值就是相應(yīng)的名稱(比如Array對(duì)象的[[Class]]值為'Array'),因此可以通過Object.prototype.toString.call(value) == '[object Array]'來判斷value是否是一個(gè)Array。
  • 給一個(gè)屬性賦值時(shí),后臺(tái)調(diào)用[[Put]]來實(shí)現(xiàn),獲取一個(gè)屬性值時(shí),后臺(tái)調(diào)用[[Get]]來獲取。
  • 使用new操作符調(diào)用一個(gè)函數(shù)時(shí),后臺(tái)調(diào)用[[Construct]],而使用call方法來調(diào)用函數(shù)時(shí),后臺(tái)會(huì)調(diào)用[[Call]]。
  • [[HasInstance]]方法返回給定參數(shù)是否是通過調(diào)用函數(shù)來創(chuàng)建的,和Object的方法isPrototypeOf(obj)類似。
  • 當(dāng)一個(gè)函數(shù)被執(zhí)行時(shí),就會(huì)創(chuàng)建一個(gè)[[Scope]]對(duì)象,可以理解為[[Scope]]就是我們前面所說的活動(dòng)對(duì)象,也就是說this、arguments、形參、函數(shù)內(nèi)部定義的變量和函數(shù)都是的[[Scope]]對(duì)象的屬性。

C、屬性特性(用來描述屬性的特性)

內(nèi)部特性 配置屬性 屬性類型 數(shù)據(jù)類型 默認(rèn)值 含義 備注
[[Configurable]] configurable

數(shù)據(jù)屬性

訪問器屬性

Boolean

true

能否通過delete刪除屬性從而重新定義屬性

能否修改屬性的特性

能否把屬性修改為訪問器特性

一旦把屬性定義為不可配置的,就不能再變?yōu)榭膳渲玫?/P>

如果為false,不能做刪除、也不能修改屬性特性,但是允許修改屬性值

非嚴(yán)格模式下會(huì)忽略相應(yīng)操作,嚴(yán)格模式下則拋出異常

[[Enumerable]] enumerable

數(shù)據(jù)屬性

訪問器屬性

Boolean true 能否通過for-in循環(huán)返回屬性 為true時(shí)可以通過for-in來枚舉,否則不能通過for-in枚舉
[[Writable]] writable 數(shù)據(jù)屬性 Boolean true 能否修改屬性的值 為false時(shí)不能修改屬性值,非嚴(yán)格模式下會(huì)忽略相應(yīng)操作,嚴(yán)格模式下則拋出異常
[[Value]] value 數(shù)據(jù)屬性 任意類型 undefined 屬性值  
[[Get]] get 訪問器屬性 Undefined/Function undefined 讀取屬性時(shí)調(diào)用的函數(shù) 為一個(gè)函數(shù)時(shí),會(huì)無參數(shù)調(diào)用這個(gè)函數(shù),并將返回值作為屬性值返回
[[Set]] set 訪問器屬性 Undefined/Function undefined 寫入屬性時(shí)調(diào)用的函數(shù) 為一個(gè)函數(shù)時(shí),會(huì)將傳入的值作為參數(shù)調(diào)用這個(gè)函數(shù),賦給屬性

說明:

  • 配置屬性是指使用下面要講的屬性定義方法時(shí)用以定義相關(guān)特性的配置項(xiàng)名稱。
  • 對(duì)于訪問器屬性,[[Get]]、[[Set]]不一定都有,沒有[[Get]]的屬性不能讀(返回undefined,嚴(yán)格模式下拋出異常),沒有[[Set]]的屬性不能寫(會(huì)忽略,嚴(yán)格模式下拋出異常)。
  • 注意區(qū)分對(duì)象內(nèi)部屬性和對(duì)象屬性的特性。

D、屬性定義方法(用來定義屬性的方法)

最常見的定義屬性的方法就是直接在對(duì)象上添加屬性,比如obj.name = 'linjisong',這種情況下定義的屬性所具有的內(nèi)部特性都是默認(rèn)的,如果想定義一個(gè)值不能被修改的屬性要怎么做呢?在ES中給我們提供了幾個(gè)方法用于實(shí)現(xiàn)類似的功能。

方法名 功能說明 參數(shù)和返回值 說明 調(diào)用示例
defineProperty() 定義一個(gè)屬性

(1)目標(biāo)對(duì)象

(2)屬性的名字

(3)屬性描述符對(duì)象

使用屬性定義方法時(shí) 
[[Enumerable]]
[[Configurable]]
[[Writable]]
默認(rèn)值為false

// 創(chuàng)建一個(gè)包含一個(gè)默認(rèn)屬性job的對(duì)象(job屬性可以修改、刪除、在for-in中枚舉)
var person = {job:'it'};
// 添加一個(gè)不能被修改、刪除的name屬性
Object.defineProperty(person, 'name', {
value:'linjisong',//這里的配置屬性和上面特性列表中的配置屬性一致
enumerable:true
});
// 定義多個(gè)屬性(數(shù)據(jù)屬性year和訪問器屬性age)
Object.defineProperties(person, {
year:{
value : 2012,
configurable:true,
writable:true
},
age:{
get : function(){
return this.year-1983;
}
}
});

var job = Object.getOwnPropertyDescriptor(person, 'job');
console.info(job.configurable);//true,直接添加屬性時(shí)默認(rèn)為true

var name = Object.getOwnPropertyDescriptor(person, 'name');
console.info(name.configurable);//false,使用屬性定義方法添加屬性時(shí)默認(rèn)為false
console.info(person.name);//linjisong
person.name = 'oulinhai';//由于不能修改,所以值不會(huì)改變,在嚴(yán)格模式下會(huì)拋出異常
console.info(person.name);//linjisong

person.year = 2015;
console.info(person.year);//2015
console.info(person.age);//32,在修改year的同時(shí),也修改了age屬性
defineProperties() 定義一組屬性

(1)目標(biāo)對(duì)象

(2)多個(gè)屬性描述符組成的一個(gè)對(duì)象

getOwnPropertyDescriptor() 獲取屬性的特性

(1)目標(biāo)對(duì)象

(2)屬性的名字

(3)返回一個(gè)包括了屬性特性的對(duì)象

 

注:這些方法設(shè)置或獲取的屬性特殊和屬性的類型有關(guān),比如數(shù)據(jù)屬性只能設(shè)置[[Confirurable]]、[[Enumerable]]、[[Writable]]、[[Value]]。

(2)防篡改對(duì)象

  所謂防篡改對(duì)象,就是給對(duì)象一定級(jí)別的保護(hù)以防止在這個(gè)級(jí)別上對(duì)對(duì)象的變更,在ES5規(guī)范中,定義了依次升高的三種保護(hù)級(jí)別:

保護(hù)級(jí)別 描述 操作方法 判斷方法 說明
不可擴(kuò)展 不能給對(duì)象添加新屬性和方法,但可以修改已有屬性和方法 preventExtensions() isExtensible():不能擴(kuò)展時(shí)返回false  
密封 不可擴(kuò)展,并且已有成員的[[Configurable]]設(shè)置為false,不能刪除屬性,但可以修改屬性值 seal() isSeal():被密封時(shí)返回true isSeal()為true時(shí)一定有isExtensible()為false
凍結(jié) 密封,其[[Writable]]設(shè)置為false,但如果定義了[[Set]],訪問器屬性仍然可寫 freeze() isFrozen():被凍結(jié)時(shí)返回true isFrozen()為true時(shí)一定有isSeal()為true,isExtensible()為false

注:一旦定義成了防篡改對(duì)象,就不能撤銷。

(3)對(duì)象的其它方法

名稱 描述
create(prototype[,descriptors]) 創(chuàng)建一個(gè)具有指定原型且可選擇性地包含指定屬性的對(duì)象
getOwnPropertyNames(object) 返回對(duì)象的屬性(方法)的名稱
getPrototypeOf(object) 返回對(duì)象的原型
keys(object) 返回對(duì)象的可枚舉屬性(方法)的名稱

這里的create(prototype[,descriptors])是一個(gè)非常有意思的方法,規(guī)范中這樣描述它的行為:

[code]
①如果prototype不是Null或Object,拋出TypeError異常
②var obj = new Object()
③設(shè)置obj的內(nèi)部屬性[[Prototype]]為prototype
④如果descriptors存在且不為undefined,使用Object.defineProperties(obj,descriptors)來添加屬性
⑤返回obj


由于一般對(duì)象的[[Prototype]]不能直接訪問,可以使用函數(shù)來進(jìn)行下面模擬實(shí)現(xiàn):
復(fù)制代碼 代碼如下:

(function(){
function Base(){};
Object.create = function(prototype, descriptors){
var typeVal = typeof prototype;
if(typeVal !== null && typeVal !== 'object' && typeVal !== 'function'){
throw new TypeError('類型錯(cuò)誤,請(qǐng)檢查第一個(gè)參數(shù)的類型');
}

Base.prototype = prototype;
var result = new Base();
if(descriptors){
Object.defineProperties(result, descriptors);
}
return result;
};
})();

測試一下:
復(fù)制代碼 代碼如下:

try{
var one = Object.create(1);//異常
}catch(e){
console.info(e);//TypeError
}
var base = {
name:'linjisong',
getName:function(){
return this.name;
}
};
var two = Object.create(base);
console.info(two.name);//linjisong
console.info(two.getName());//linjisong
var three = Object.create(base, {
name:{value:'oulinhai'},
age:{value:23}
});
console.info(three.getName());//oulinhai
console.info(three.age);//23

這里實(shí)現(xiàn)了一個(gè)簡單的繼承,這也引出下一個(gè)主題。

2、原型對(duì)象

(1)原型與原型鏈

  每個(gè)對(duì)象都有一個(gè)原型對(duì)象,而原型對(duì)象本身也是一個(gè)對(duì)象,也有自己的原型對(duì)象,這樣就形成了一個(gè)原型鏈直至null對(duì)象。對(duì)象的內(nèi)部屬性[[Prototype]]指向的就是對(duì)象的原型對(duì)象,而Object.prototype的原型對(duì)象為null。

(2)屬性查找

  在訪問一個(gè)對(duì)象的屬性(方法)時(shí),引擎會(huì)先查找對(duì)象本身有沒有這個(gè)屬性,如果有,返回這個(gè)屬性值,如果沒有,則查找對(duì)象的原型是否有這個(gè)屬性,如果有,返回,如果沒有就繼續(xù)查找原型對(duì)象的原型直至最后一個(gè)原型對(duì)象。

  注意區(qū)分屬性查找和和前面說過的標(biāo)識(shí)符查找的異同。屬性查找是沿著原型鏈,標(biāo)識(shí)符查找是沿著作用域鏈,但都有一個(gè)逐級(jí)查找的過程。

(3)對(duì)象的原型對(duì)象[[Prototype]]與函數(shù)的原型屬性prototype

•每一個(gè)對(duì)象都有一個(gè)原型對(duì)象,在規(guī)范中使用[[Prototype]]來表示,這個(gè)對(duì)象一般不能直接訪問,但可以通過getPrototypeOf()這個(gè)方法來獲取,而在Firefox中還可以通過__proto__直接訪問,來驗(yàn)證一下:
復(fù)制代碼 代碼如下:

var obj = {};
console.info(obj.__proto__===Object.getPrototypeOf(obj));//true
console.info(obj.__proto__===Object.prototype);//true

•每一個(gè)函數(shù)都有一個(gè)屬性prototype,這個(gè)屬性是在函數(shù)定義過程中添加的,它指向的對(duì)象就是所有使用該函數(shù)創(chuàng)建的實(shí)例對(duì)象的原型對(duì)象。
復(fù)制代碼 代碼如下:

var fn = function(){};
console.info(typeof fn.prototype);//object,一旦定義了函數(shù),就會(huì)添加prototype屬性,指向原型對(duì)象
var obj1 = new fn();
console.info(fn.prototype === Object.getPrototypeOf(obj1));//true,所有使用fn創(chuàng)建的實(shí)例的原型對(duì)象都指向fn.prototype
var obj2 = new fn();
console.info(fn.prototype === Object.getPrototypeOf(obj2));//true
console.info(Object.getPrototypeOf(fn) === Function.prototype);//true

當(dāng)然,fn本身也是一個(gè)對(duì)象,也有自己的原型對(duì)象,它的原型對(duì)象就是Function的屬性prototype了(fn.__proto__===Function.prototype)。

  我們知道,每一個(gè)對(duì)象都可以訪問一個(gè)屬性constructor,指向創(chuàng)建這個(gè)對(duì)象的函數(shù)(構(gòu)造函數(shù)),實(shí)際上,constructor屬性只不過是構(gòu)造函數(shù)的原型對(duì)象的一個(gè)屬性而已,因此通過構(gòu)造函數(shù)創(chuàng)建的實(shí)例都能夠訪問constructor。
復(fù)制代碼 代碼如下:

var fn = function fn(){};
var obj1 = new fn();
console.info(fn.constructor);//Function()
console.info(fn.prototype.constructor);//fn(),函數(shù)原型對(duì)象的constructor指向函數(shù)本身console.info(obj1.hasOwnProperty('constructor'));//false,實(shí)例本身沒有constructor屬性console.info(fn.prototype.constructor === obj1.constructor);//true,實(shí)例可以訪問到原型對(duì)象中的constructor屬性

•函數(shù)的原型對(duì)象具有動(dòng)態(tài)性,即便先創(chuàng)建實(shí)例,后修改原型對(duì)象,也還是能夠通過實(shí)例訪問到對(duì)原型對(duì)象所做的變更。
復(fù)制代碼 代碼如下:

var fn = function fn(){};
var obj = new fn();
console.info(obj.name);//undefined
fn.prototype.name = 'linjisong';
console.info(obj.name);//linjisong


3、創(chuàng)建對(duì)象

創(chuàng)建方式 示例 說明
傳統(tǒng)方式
var person = new Object(); 
person.name = 'linjisong'; 
person.job = 'it';
傳統(tǒng)方式創(chuàng)建對(duì)象容易產(chǎn)生大量重復(fù)的代碼
對(duì)象字面量
var person = { 
name : 'linjisong', 
job : 'it' 
};
使用對(duì)象字面量創(chuàng)建簡潔明了,非常適合創(chuàng)建作為函數(shù)實(shí)參的對(duì)象
工廠模式
function createPerson(name, job){ 
var o = new Object(); 
o.name = name; 
o.job = job; 
return o; 
} 
var person = createPerson('linjisong','it');

1、工廠模式能有效解決重復(fù)代碼問題。

2、但是不能判定對(duì)象的類型

構(gòu)造函數(shù)模式
function Person(name, job){ 
this.name = name; 
this.job = job; 
this.getName = function(){ 
return this.name; 
} 
} 
var person = new Person('linjisong','it');

構(gòu)造函數(shù)模式能解決重復(fù)代碼問題,也能夠判定對(duì)象的類型

但是這種模式下創(chuàng)建的每個(gè)實(shí)例都有一份屬性和方法的Copy

對(duì)于方法來說,每個(gè)實(shí)例都保存一份是沒有必要的

使用new調(diào)用構(gòu)造函數(shù)的內(nèi)部步驟:

(1)創(chuàng)建一個(gè)新對(duì)象

(2)將構(gòu)造函數(shù)的作用域賦給新對(duì)象(構(gòu)造函數(shù)內(nèi)this指向新創(chuàng)建對(duì)象)

(3)執(zhí)行構(gòu)造函數(shù)中的代碼

(4)返回新對(duì)象

原型模式
function Person(){} 
Person.prototype.name = 'linjisong'; 
Person.prototype.job = 'it; 
Person.prototype.getName = fucntion(){ 
return this.name; 
}; 
var person = new Person();

原型模式能夠解決構(gòu)造函數(shù)模式的方法實(shí)例有多個(gè)副本的問題

但是同時(shí)每個(gè)實(shí)例的屬性也共享了,對(duì)于引用類型的屬性來說

這會(huì)導(dǎo)致非常嚴(yán)重的問題,修改一個(gè)實(shí)例的屬性會(huì)導(dǎo)致另一個(gè)實(shí)例也修改

而且也不能接受參數(shù)

function Angle(){}; 
Angle.prototype.coordinate = [0,0]; 

var a1 = new Angle(); 
var a2 = new Angle(); 

a1.coordinate[0] = 1; 
console.info(a2.coordinate);//[1,0]修改a1會(huì)導(dǎo)致a2變更
組合構(gòu)造原型模式
function Person(name, job){ 
this.name = name; 
this.job = job; 
} 
Person.prototype.getName = fucntion(){ 
return this.name; 
}; 
var person = new Person('linjisong','it');

結(jié)合構(gòu)造函數(shù)模式和原型模式

使用構(gòu)造函數(shù)模式創(chuàng)建屬性,每個(gè)實(shí)例保存一份

使用原型模式共享方法,所有實(shí)例共享保存一份

這是目前使用最廣泛的對(duì)象創(chuàng)建方式

動(dòng)態(tài)原型模式
function Person(name, job){ 
this.name = name; 
this.job = job; 
if(!Person.prototype.getName){ 
Person.prototype.getName = fucntion(){ 
return this.name; 
}; 
} 
} 
var person = new Person('linjisong','it');

這種模式實(shí)際上是對(duì)于不習(xí)慣將構(gòu)造函數(shù)和原型分離而引入的

在判斷的時(shí)候,可以只判斷其中一個(gè)屬性

寄生構(gòu)造函數(shù)模式
function Person(name, job){ 
var o = new Object(); 
o.name = name; 
o.job = job; 
o.getName = fucntion(){ 
return this.name; 
}; 
return o; 
} 
var person = new Person('linjisong','it');

工廠模式不使用new,寄生構(gòu)造函數(shù)模式使用new操作符

構(gòu)造函數(shù)模式不返回,寄生構(gòu)造函數(shù)模式返回對(duì)象

不能使用instanceof判斷類型

穩(wěn)妥構(gòu)造函數(shù)模式
function Person(name, job){ 
var o = new Object(); 
o.getName = fucntion(){ 
return name; 
}; 
return o; 
} 
var person = Person('linjisong','it');

穩(wěn)妥對(duì)象:不使用this和new

穩(wěn)妥構(gòu)造模式類似寄生構(gòu)造模式,但只能通過提供的方法訪問成員

不能使用instanceof判斷類型

各種創(chuàng)建對(duì)象的模式需要根據(jù)具體情況來看,最常用的還是對(duì)象字面量和組合構(gòu)造原型方式。

4、繼承

在ECMAScript中,沒有接口繼承,只有實(shí)現(xiàn)繼承,這些繼承主要是通過原型鏈來實(shí)現(xiàn)的。像對(duì)象創(chuàng)建一樣,下面也通過一張表格來瀏覽一下一些實(shí)現(xiàn)繼承的方法。

繼承方式 示例 說明
原型鏈
function Square(){//正方形 
this.width = 10;//邊長 
this.coordinate = [0,0];//左上頂點(diǎn)的坐標(biāo)  
} 
Square.prototype.getArea = function(){//計(jì)算面積 
return this.width * this.width; 
}; 

function ColorSquare(){//有顏色的正方形 
this.color = 'red'; 
} 
ColorSquare.prototype = new Square();//實(shí)現(xiàn)了繼承 
ColorSquare.prototype.getColor = function(){//獲取顏色 
return this.color; 
} 

var cs = new ColorSquare(); 
console.info(cs.width);//10 
console.info(cs.getArea());//100 
console.info(cs.color);//red 
console.info(cs.getColor());//red

1、通過修改子類型創(chuàng)建函數(shù)的原型實(shí)現(xiàn)繼承。

2、通過原型給子類型添加新方法時(shí),一定要在替換子類型原型之后添加,而后也不能通過對(duì)象字面量修改子類型的原型。

3、可以通過兩種方法確定原型和實(shí)例之間的關(guān)系:只要實(shí)例原型鏈中出現(xiàn)過構(gòu)造函數(shù)fn,都返回true

(1)instance instanceof fn

(2)fn.prototype.isPrototype(instance)

4、使用原型鏈繼承時(shí),創(chuàng)建子對(duì)象時(shí)無法傳遞參數(shù)。

5、引用類型的父類屬性會(huì)被所有子類型實(shí)例共享從而產(chǎn)生問題:

修改一個(gè)子類型實(shí)例的引用類型屬性會(huì)導(dǎo)致其它所有子類型實(shí)例相應(yīng)的修改

var cs2 = new ColorSquare(); 
console.info(cs2.coordinate);//[0,0] 
cs.coordinate[1] = 1; 
console.info(cs2.coordinate);//[0,1],修改cs會(huì)導(dǎo)致cs2也修改
借用構(gòu)造函數(shù)
function Square(){//正方形 
this.width = 10;//邊長 
this.coordinate = [0,0];//左上頂點(diǎn)的坐標(biāo)  
} 

Square.prototype.getArea = function(){//計(jì)算面積 
return this.width * this.width; 
}; 

function ColorSquare(){//有顏色的正方形 
Square.call(this);//實(shí)現(xiàn)繼承 
this.color = 'red'; 
} 

var cs = new ColorSquare(); 
var cs2 = new ColorSquare(); 
console.info(cs.coordinate);//[0,0] 
console.info(cs2.coordinate);//[0,0] 
cs.coordinate[1] = 1; 
console.info(cs.coordinate);//[0,1] 
console.info(cs2.coordinate);//[0,0],互相獨(dú)立,修改cs不影響cs2 
try{ 
console.info(cs.getArea());//異常,不能訪問父類原型中方法 
}catch(e){ 
console.info(e);//TypeError 
}

1、使用借用構(gòu)造函數(shù)時(shí),可以在call調(diào)用時(shí)傳遞參數(shù)。

2、同時(shí)也不存在引用類型共享的問題。

3、借用構(gòu)造函數(shù)的缺點(diǎn)是,子類不能訪問父類原型中定義的方法

組合繼承
function Square(){//正方形 
this.width = 10;//邊長 
this.coordinate = [0,0];//左上頂點(diǎn)的坐標(biāo)  
} 

Square.prototype.getArea = function(){//計(jì)算面積 
return this.width * this.width; 
}; 

function ColorSquare(){//有顏色的正方形 
Square.call(this);//創(chuàng)建子類實(shí)例時(shí),第二次調(diào)用父類構(gòu)造函數(shù) 
this.color = 'red'; 
} 

ColorSquare.prototype = new Square();//第一次調(diào)用 
ColorSquare.prototype.getColor = function(){//獲取顏色 
return this.color; 
} 

var cs = new ColorSquare(); 
var cs2 = new ColorSquare(); 
console.info(cs.coordinate);//[0,0] 
console.info(cs2.coordinate);//[0,0] 
cs.coordinate[1] = 1; 
console.info(cs.coordinate);//[0,1] 
console.info(cs2.coordinate);//[0,0],互相獨(dú)立,修改cs不影響cs2 
console.info(cs.getArea());//100,可以訪問

1、組合繼承也稱為偽經(jīng)典繼承,是將原型鏈和借用構(gòu)造函數(shù)兩種方式結(jié)合起來的繼承方式。

2、基本思想是:

(1)使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承

(2)使用借用構(gòu)造函數(shù)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承

3、組合繼承避免了原型鏈和借用構(gòu)造函數(shù)的缺點(diǎn),融合了它們的優(yōu)點(diǎn),是最常用的繼承方式。

4、組合繼承的缺點(diǎn)是需要調(diào)用兩次父類的構(gòu)造函數(shù)

原型式繼承
function create(o){ 
var fn = function(){}; 
fn.prototype = o; 
return new fn(); 
} 

var square = { 
width:10, 
coordinate:[0,0] 
}; 

var cs = create(square); 
var cs2 = create(square); 
console.info(cs.coordinate);//[0,0] 
console.info(cs2.coordinate);//[0,0] 
cs.coordinate[1] = 1; 
console.info(cs.coordinate);//[0,1] 
console.info(cs2.coordinate);//[0,1],和原型鏈一樣,會(huì)有共享問題

1、這種方式實(shí)際上就是前面說的模擬ES5中create函數(shù)來實(shí)現(xiàn)繼承。

2、ES5及前面模擬的create還可以接受另外的屬性描述參數(shù)。

3、和原型鏈與借用構(gòu)造函數(shù)不同的是,這種方式需要先有一個(gè)對(duì)象,然后直接創(chuàng)建子對(duì)象。

前者是構(gòu)造函數(shù)的繼承,而后者是對(duì)象實(shí)例的繼承。

4、和使用原型鏈繼承一樣,也會(huì)有引用類型實(shí)例屬性的共享問題。

寄生式繼承
function create(o){ 
var fn = function(){}; 
fn.prototype = o; 
return new fn(); 
} 

var square = { 
width:10, 
coordinate:[0,0] 
}; 

function colorSquare(original){ 
var s = create(original); 
s.color = 'red'; 
return s; 
} 

var cs = colorSquare(square); 
console.info(cs.width);//10 
console.info(cs.coordinate);//[0,0]

1、首先,這里的create函數(shù)不是必需的,任何返回新對(duì)象的函數(shù)都可以。

2、其次,這種模式也有引用類型實(shí)例屬性共享的問題。

3、這種方式,可以看成將上面的對(duì)象繼承包裝成構(gòu)造函數(shù)。

寄生組合式繼承
function create(o){ 
var fn = function(){}; 
fn.prototype = o; 
return new fn(); 
} 

function inherit(sub, sup){ 
var prototype = create(sup.prototype); 
prototype.constructor = sub; 
sub.prototype = prototype; 
} 

function Square(){//正方形 
this.width = 10;//邊長 
this.coordinate = [0,0];//左上頂點(diǎn)的坐標(biāo)  
} 
Square.prototype.getArea = function(){//計(jì)算面積 
return this.width * this.width; 
}; 

function ColorSquare(){//有顏色的正方形 
Square.call(this); 
this.color = 'red'; 
} 
inherit(ColorSquare, Square); 
ColorSquare.prototype.getColor = function(){//獲取顏色 
return this.color; 
} 

var cs = new ColorSquare(); 
console.info(cs.width);//10 
console.info(cs.getArea());//100 
console.info(cs.color);//red 
console.info(cs.getColor());//red 

var cs2 = new ColorSquare(); 
console.info(cs2.coordinate);//[0,0] 
cs.coordinate[1] = 1; 
console.info(cs2.coordinate);//[0,0]

1、這種方式只調(diào)用了一次父類構(gòu)造函數(shù),從而避免了在子類型的原型對(duì)象上創(chuàng)建不必要的屬性。

2、能夠保證原型鏈不變,從而可以正常使用instanceof和isPrototypeOf()。

相關(guān)文章

  • JavaScript的21條基本知識(shí)點(diǎn)

    JavaScript的21條基本知識(shí)點(diǎn)

    這篇文章主要介紹了JavaScript的21條基本知識(shí)點(diǎn)的相關(guān)資料,需要的朋友可以參考下
    2014-03-03
  • 淺談javascript 迭代方法

    淺談javascript 迭代方法

    這篇文章主要介紹了淺談javascript 迭代方法的相關(guān)資料,需要的朋友可以參考下
    2015-01-01
  • JavaScript splice()方法詳解

    JavaScript splice()方法詳解

    這篇文章介紹了JavaScript splice()方法,有需要的朋友可以參考一下
    2013-11-11
  • JavaScript語言對(duì)Unicode字符集的支持詳解

    JavaScript語言對(duì)Unicode字符集的支持詳解

    這篇文章主要介紹了JavaScript語言對(duì)Unicode字符集的支持詳解,需要的朋友可以參考下
    2014-12-12
  • JavaScript中setTimeout和setInterval函數(shù)的傳參及調(diào)用

    JavaScript中setTimeout和setInterval函數(shù)的傳參及調(diào)用

    這篇文章主要介紹了JavaScript中setTimeout和setInterval函數(shù)的傳參及調(diào)用,著兩個(gè)函數(shù)可以把要執(zhí)行的代碼在設(shè)定的一個(gè)時(shí)間點(diǎn)插入js引擎維護(hù)的一個(gè)代碼隊(duì)列中,需要的朋友可以參考下
    2016-03-03
  • Javascript Throttle & Debounce應(yīng)用介紹

    Javascript Throttle & Debounce應(yīng)用介紹

    Throttle:無視一定時(shí)間內(nèi)所有的調(diào)用Debounce:一定間隔內(nèi)沒有調(diào)用時(shí),接下來將為大家介紹下Throttle & Debounce的應(yīng)用,感興趣的朋友可以參考下哈
    2013-03-03
  • Javascript中引用示例介紹

    Javascript中引用示例介紹

    Javascript腳本中,引用的參數(shù)內(nèi)部可以修改,但參數(shù)對(duì)應(yīng)的引用不能修改,下面為大家詳細(xì)介紹下
    2014-02-02
  • Javascript 按位左移運(yùn)算符使用介紹(<<)

    Javascript 按位左移運(yùn)算符使用介紹(<<)

    這篇文章主要介紹了Javascript 按位左移運(yùn)算符 (<<) 將表達(dá)式數(shù)字轉(zhuǎn)換成二進(jìn)制,之后向左移表達(dá)式的位的相關(guān)資料,需要的朋友可以參考下
    2014-02-02
  • javascript 注釋代碼的幾種方法總結(jié)

    javascript 注釋代碼的幾種方法總結(jié)

    為了程序/代碼的易讀性,基本上每一種編程語言都有注釋的功能,javascript也不例外,javascript注釋代碼有多種形式,本文章向大家介紹javascript注釋代碼的兩種方法,需要的朋友可以參考一下
    2017-01-01
  • JavaScript入門教程(6) Window窗口對(duì)象

    JavaScript入門教程(6) Window窗口對(duì)象

    他是JavaScript中最大的對(duì)象,它描述的是一個(gè)瀏覽器窗口。一般要引用它的屬性和方法時(shí),不需要用“window.xxx”這種形式,而直接使用“xxx”。一個(gè)框架頁面也是一個(gè)窗口。
    2009-01-01

最新評(píng)論