最近在看《javascript高級程序設計》,看完以後,以爲感觸,而後我今天又看到了一篇文章,說的很搞笑。就想整理下本身所學的。javascript
首先,若是咱們把ECMAScript的對象想象爲散列表,即一組名值對,其中值能夠是數據或函數。java
那究竟對象、原型對象、構造函數、繼承、原型鏈、 原型屬性的共享、原型的動態性、原型的總體重寫呢,來一組簡單粗暴的描述哈,仍是挺搞笑的。數組
1)人是人他媽生的,妖是妖他媽生的。人和妖都是對象實例,而人他媽和妖他媽就是原型。原型也是對象,叫原型對象。函數
2)人他媽和人他爸啪啪啪能生出一堆人寶寶、妖他媽和妖他爸啪啪啪能生出一堆妖寶寶,啪啪啪就是構造函數,俗稱造人。測試
3)人他媽會記錄啪啪啪的信息,因此能夠經過人他媽找到啪啪啪的信息,也就是說能經過原型對象找到構造函數。this
4)人他媽能夠生不少寶寶,但這些寶寶只有一個媽媽,這就是原型的惟一性。prototype
5)人他媽也是由人他媽他媽生的,經過人他媽找到人他媽他媽,再經過人他媽他媽找到人他媽他媽……,這個關係叫作原型鏈。設計
6)原型鏈並非無限的,當你經過人他媽一直往上找,最後發現你會發現人他媽他媽他媽……的他媽都不是人,也就是原型鏈最終指向null。對象
7)人他媽生的人會有人的樣子,妖他媽生的妖會有妖的醜陋,這叫繼承。繼承
8)你繼承了你媽的膚色,你媽繼承了你媽他媽的膚色,你媽他媽……,這就是原型鏈的繼承。
9)你談對象了,她媽讓你帶上房產證去提貨,你若沒有,那她媽會問你媽有沒有,你媽沒有那她媽會問你媽她媽有沒有……這就是原型鏈的向上搜索。
10)你會繼承你媽的樣子,可是你也能夠去染髮洗剪吹,就是說對象的屬性能夠自定義,會覆蓋繼承獲得的屬性。
11)雖然你洗剪吹了染成黃毛了,但你不能改變你媽的樣子,你媽生的弟弟妹妹跟你的黃毛洗剪吹沒一點關係,就是說對象實例不能改動原型的屬性。
12)可是你家被你玩火燒了的話,那就是說你家你媽家你弟們家都被燒了,這就是原型屬性的共享。
13)你媽外號阿珍,鄰居大娘都叫你阿珍兒,但你媽頭髮從飄柔作成了金毛獅王后,隔壁大嬸都改口叫你包租仔,這叫原型的動態性。
14)你媽愛漂亮,又跑到韓國整形,整到你媽他媽都認不出來,即便你媽頭髮換回飄柔了,但隔壁鄰居仍是叫你金毛獅王子。由於沒人認出你媽,整形後的你媽已經回爐再造了,這就是原型的總體重寫。
哈哈,是否是簡單粗暴,還特別容易理解。
ECMAScript中有兩種屬性:數據屬性和訪問器屬性
(1)數據屬性:
[[Configurable]] 表示可否經過delete刪除屬性從而從新定義屬性,可否修改屬性,或者可否把屬性修改成訪問器屬性
[[Enumerable]] 表示可以經過for-in循環返回屬性
[[Writable]]表示可否修改屬性的值
[[Value]]包含這個屬性的數據值,
修改默認的屬性使用 Object.defineProperty() 這個方法
(2)訪問器屬性:包含getter和setter函數。getter:負責返回有效的值,setter函數傳入新值,訪問器屬性的4個特性:
[[Configurable]]表示可否經過delete刪除屬性從而從新定義屬性,可否修改屬性的特性,可否把屬性修改成數據屬性。默認爲true.
[[Enumerable]]表示可否經過for-in循環返回屬性。
[[Get]]在讀取屬性時調用的函數,默認值爲undefined
[[Set]]在寫入屬性時調用的函數,默認值爲undefined
訪問器的屬性用Object.defineProperty()來定義
書上給了一個例子,來挺好理解的:
var book = {
_year:2004,
//_year前面的下劃線是一種經常使用的記號,**用來表示只能經過對象方法訪問的屬性**
edition:1
};
Object.defineProperty(book,"year",{
get:function(){
return this._year;
}
set:function(newYear){
if(newValue >2004){
this._year = newValue;
this.edition += newValue -2004;
}
}
});
//定義訪問器的舊有方法
book.__defineGetter__("year",function(){
return this._year;
})
book.__defineSetter__("year",function(newValue){
if(newValue >2014){
this._year = newValue;
this.edition += newValue -2004;
}
})
book.year = 2015;
alert(book.edition); //2
讀取屬性的特性:Object.getOwnPropertyDesciptor()取得給定屬性的描述符。
最後說回原型鏈:原型鏈繼承的主要問題在於屬性的共享,不少時候咱們只想共享方法而並不想要共享屬性,理想中每一個實例應該有獨立的屬性。所以,原型繼承就有了下面的兩種改良方式:
1)組合繼承
function Mother(age){
this.age = age;
this.bobby = ['running','football']
}
Mother.prototype.showAge = function(){
console.log(this.age);
};
function Person(name,age){
Mother.call(this.age); //第二次執行
this.name = name;
}
Person.prototype = new Mother(); //第一次執行
Person.prototype.constructor = Person;
Person.prototype.showName = function(){
console.log(this.name);
}
var p1 = new Person('Jack',20);
p1.hobby.push('basketball'); //p1:'Jack';_proto_:20,['running','football']
var p2 = new Person('Mark',18); //p2:'Mark';_proto_:18,['running','football']
(2)寄生組合式繼承
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function inheritPrototype(Person,Mother){
var prototype = object(Mother.prototype);
prototype.constructor = Person;
Person.prototype = prototype;
}
function Mother(age){
this.age = age;
this.hobby = ['running','football']
}
Mother.prototype.showAge = function(){
console.log(this.age);
};
function Person(name,age){
Mother.call(this,age);
this.name = name;
}
inheritPrototype(Person,Mother);
Person.prototype.showName = function(){
console.log(this.name);
}
var p1 = new Person('Jack',20);
p1.hobby.push('backetball'); //p1:'Jack';_proto_:20,['running','football']
var p2 = new Person('Mark',18); //p2:'Mark';_proto_:18,['running','football']
js建立對象的各類方法
(1)原型模式
//1原型模式:對象字面量方式
var person = {
name : 'Jack',
age:18,
sayName:function(){alert(this.name);}
};
//2原型模式:Object構造函數方式
var person = new Object();
person.name = 'Jack';
person.age = 18;
person.sayName = function(){
alert(this.name);
};
(2)工廠模式
//工廠模式,定義一個函數建立對象
function creatPerson(name,age){
var person = new Object();
person.name = name;
person.age = age;
person.sayName = function(){
alert(this.name);
};
return person;
}
工廠模式就是批量化生產,簡單調用就能夠進入造人模式(啪啪啪。。。)指定姓名年齡就能夠造一堆小寶寶啦,解放雙手。可是因爲是工廠暗箱操做的,因此你不能識別這個對象究竟是什麼類型、是人仍是狗傻傻分不清(instanceof 測試爲 Object),另外每次造人時都要建立一個獨立的temp對象,代碼臃腫
(3)構造函數
//3構造函數模式,爲對象定義一個構造函數
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
alert(this.name);
};
}
var p1 = new Person('Jack',18); //建立一個p1對象
Person('Jack',18);
構造函數與C++、JAVA中類的構造函數相似,易於理解,另外Person能夠做爲類型識別(instanceof 測試爲 Person 、Object)。可是全部實例依然是獨立的,不一樣實例的方法實際上是不一樣的函數。這裏把函數兩個字忘了吧,把sayName當作一個對象就好理解了,就是說張三的 sayName 和李四的 sayName是不一樣的存在,但顯然咱們指望的是共用一個 sayName 以節省內存。
(4)原型模式
//原型模式,直接定義prototype屬性
function Person(){}
Person.prototype.name = 'Jack';
Person.prototype.age = 18;
Person.prototype.sayName = function(){alert(this.name);};
//原型模式。字面量定義方式
function Person(){}
Person.prototype = {
name:'Jack',
age:18,
sayName:function(){alert(this.name);}
};
var p1 = new Person(); //name = 'Jack'
var p2 = new Person(); //name = 'Jack'
這裏須要注意的是原型屬性和方法的共享,即全部實例中都只是引用原型中的屬性方法,任何一個地方產生的改動會引發其餘實例的變化。
(5)混合模式
//原型構造函數組合模式
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype = {
hobby:['running','football'];
sayName:function(){alert(this.name);},
sayAge:function(){alert(this.age);}
}
var p1 = new Person('Jack',20);
var p2 = new Person('Mark',18);
作法是將須要獨立的屬性方法放入構造函數中,而能夠共享的部分則放入原型中,這樣作能夠最大限度節省內存而又保留對象實例的獨立性。