說起原型和原型鏈接描扯,著實讓我這個前端菜鳥胡搞了好一陣子。雖然有點繞口的緣故趟薄,但是更多的還是自己比較浮躁帶來的后果绽诚,這一塊據(jù)說是前端的基礎(chǔ),看了很多遍才差不多有點頭目杭煎。分享一下我領(lǐng)悟到的武林秘籍恩够,希望能給您帶來一點啟迪,如果存在任何問題羡铲,請及時指正我蜂桶,謝謝。????????
一也切、 淺談數(shù)據(jù)屬性和訪問器屬性
1. 創(chuàng)建對象:
通常創(chuàng)建對象一般都會有兩種方法:
//利用object來創(chuàng)建對象
var person = new Person();
person.name = "klivitam";
person.age = 23;
person.sayName = function(){
alert(this.name)
}
// 對象字面量法扑媚,推薦使用這種方法
var person = {
name:"klivitam",
age:23,
sayName: function(){
alert(this.name);
}
}
2. 屬性類型
在javascript中腰湾,對象的屬性一共分為兩種:數(shù)據(jù)屬性和訪問器屬性。
- 數(shù)據(jù)屬性
configurable:表示能否通過delete刪除屬性從而重新定義屬性疆股,能否修改屬性的特性费坊,或能否把屬性修改為訪問器屬性,默認(rèn)為true
enumerable:表示能否通過for-in循環(huán)返回屬性
writable:表示能否修改屬性的值
value:包含該屬性的數(shù)據(jù)值押桃。默認(rèn)為undefined
數(shù)據(jù)屬性包含一個數(shù)據(jù)值的位置葵萎,在這個位置可以讀取和寫入值。以上就是描述數(shù)據(jù)值行為的四個特性唱凯。
okey羡忘,可能全憑著口述概念無法了解這個意思,現(xiàn)在就實操代碼吧磕昼。
// "use strict"
var worker = {}
Object.defineProperty(worker, "job", {
writable: false,
value: "碼農(nóng)"
})
console.log(worker.job)
worker.job = "教師"
console.log(worker.job)
當(dāng)把writable的屬性改成true的時候卷雕,
// "use strict"
var worker = {}
Object.defineProperty(worker, "job", {
// writable: false,
configurable:false,
value: "碼農(nóng)"
})
console.log(worker.job)
delete(worker.job)
console.log(worker.job)
當(dāng)configurable為false的時候峰鄙,使用delete方法會失效浸间,并且在嚴(yán)格模式下,delete會報錯吟榴。同理改成true的時候,則為undefined魁蒜,說明刪除成功了。
至于最后一個我覺得就沒必要代碼進(jìn)行演示了兜看,同理可得。
- 訪問器屬性
configurable:表示能否通過delete刪除屬性從而重新定義屬性狭瞎,能否修改屬性的特性细移,或能否把屬性修改為訪問器屬性,默認(rèn)為false
enumerable:表示能否通過for-in循環(huán)返回屬性,默認(rèn)為false
Get:在讀取屬性時調(diào)用的函數(shù),默認(rèn)值為undefined
Set:在寫入屬性時調(diào)用的函數(shù),默認(rèn)值為undefined
訪問器屬性不包含數(shù)據(jù)值熊锭,包含的是一對get和set方法弧轧,在讀寫訪問器屬性時,就是通過這兩個方法來進(jìn)行操作處理的碗殷。并且訪問器屬性不能直接定義精绎,要通過Object.defineProperty()這個方法來定義。
直接上代碼吧:
var worker = {
_job:"碼農(nóng)",
age: 23
}
Object.defineProperty(worker,"job",{
get:function(){
return this._job;
},
set:function(newJob){
if(newJob!==this._job){
this._job = newJob;
this.age ++
}
}
})
console.log(Object.getOwnPropertyDescriptor(worker,"job"));
console.log(worker.job)
worker.job = "教師"
console.log(worker.job)
console.log("更換職業(yè)就變老一年亿扁,5555~:"+worker.age)
二捺典、 js設(shè)計模式
1、 工廠模式
工廠模式是一個很基礎(chǔ)的一個模式吧从祝,反正我在學(xué)java (android)的時候經(jīng)常會遇到這種模式襟己,主要是抽象了創(chuàng)建對象的具體過程引谜。具體的代碼如下:
//屌絲程序員,只能偶爾意淫一哈 = l =,別噴我
function addBeatiGrilWx(name,age,job){
var gril = new Object();
gril.name = name;
gril.age = age;
gril.job = job;
gril.sayHi = function(){
console.log("hi! " + this.name)
};
return gril
}
var lyf = addBeatiGrilWx("liuyifei",18,"actor");
lyf.sayHi();
console.log(lyf);
效果如下工廠模式雖然解決了創(chuàng)建多個相似對象的問題擎浴,但是卻沒有解決對象識別的問題(即不清楚一個對象的類型)员咽,于是出現(xiàn)了構(gòu)造函數(shù)模式。
2贮预、 構(gòu)造函數(shù)模式
構(gòu)造函數(shù)可以用來創(chuàng)造特定類型的對象贝室。具體的代碼如下
function BeautiGril(name,age,photo){
this.name = name;
this.age = age;
this.photo = photo;
this.sayHi = function(){
console.log("hi! "+this.name)
}
}
var lyf = new BeautiGril("liuyifei",18,"baidu");
lyf.sayHi();
console.log(lyf)
console.log(lyf.constructor == BeautiGril)
console.log(lyf instanceof BeautiGril)
console.log(lyf instanceof Object)
具體的效果如下:創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來可以將它的實例標(biāo)識為一種特定的類型,這也是前面提到他相較于工廠模式的優(yōu)勢仿吞。
構(gòu)造函數(shù)模式看似很好滑频,但是也存在一個問題,就拿上面的代碼來說如果我創(chuàng)建多個實例唤冈,不止lyf(畢竟還是想多要幾個小姐姐的 咳咳)一個峡迷。而此時sayHi這個公共的方法就會被多次重復(fù)創(chuàng)建。這樣其實不太可取的你虹,如果把sayHi方法放置出去绘搞,
function BeautiGril(name,age,photo){
this.name = name;
this.age = age;
this.photo = photo;
}
function sayHi(){
console.log("hi! "+this.name)
}
那么相當(dāng)新建了一個全局方法,這樣豈不是更加的沒有必要了么傅物?此時就要引入到原型模式了夯辖。
3. 原型模式
我們創(chuàng)建的函數(shù)都有prototype(原型)屬性,這個屬性是指針董饰,指向一個對象蒿褂,而這個對象的用途是包含由特定類型的所有實例所共享的屬性和方法,使用原型對象就可以讓所有實例對象均包含這些屬性及方法尖阔。
function Worker(){}
Worker.prototype.name = "programmer";
Worker.prototype.work = "programming...";
Worker.prototype.heartSound = function(){
console.log(this.name+" want rest,but he still "+this.work)
}
var xiaoZhang = new Worker();
xiaoZhang.heartSound();
var xiaoWang = new Worker();
xiaoWang.heartSound();
console.log(xiaoZhang.heartSound ==xiaoWang.heartSound)
這里我還是說一下贮缅,我將heartSound()方法和所有的屬性直接添加到了Woker的原型屬性中榨咐,然后通過new創(chuàng)建對象介却,在原型模式中這些屬性和方法對于所有的實例是共享的。
但是這里存在有點問題--那就是并不是所有的worker都是程序員块茁。這就引發(fā)了最后一種模式的混用齿坷。
3. 原型模式+構(gòu)造器模式
這個模式在我的理解上來說,主要是為了避免單獨用原型模式所帶來弊病数焊,就拿上一份代碼來說永淌,并不是所有的worker都是程序員,如果想一個醫(yī)生想去復(fù)用這個類的時候佩耳,就必須改變其原型上的值遂蛀,如果改變其原型的值,那么整個都會亂套了干厚。于是我想到構(gòu)造器模式
function Worker(name,work){
this.name = name;
this.work = work;
}
Worker.prototype.heartSound = function(){
console.log(this.name+" want rest,but he still "+this.work)
}
var xiaozhang = new Worker("programmer","programmer....");
xiaozhang.heartSound(); // programmer want rest,but he still programmer....
//醫(yī)生也需要休息
var xiaomei = new Worker("doc","sos");
xiaomei.heartSound(); // doc want rest,but he still sos
三李滴、 原型鏈
前面也差不多談到了原型這個概念螃宙,什么叫原型呢?其實我有一個不太好所坯,但是又很恰當(dāng)?shù)睦觼砻枋鲞@些個概念(看嗯哼家小狗谆扎、小貓想到的):
- 小狗是小狗媽媽生的、小貓是小貓媽媽生的芹助。小狗和小貓被稱為對象的實例堂湖,狗媽媽、貓媽媽被稱為對象的原型
- 狗媽媽和狗爸爸能通過交配生出一大堆小狗出來状土,其中交配就被稱為構(gòu)造函數(shù)
- 狗媽媽有很多狗寶寶无蜂,但是狗寶寶卻只有一個狗媽媽,這可以被稱之為原型的唯一性
- 我們可以通過狗寶寶找到狗媽媽蒙谓,狗媽媽也可以找到狗外婆酱讶,以此類推 這就是相當(dāng)于原型鏈
- 大家都知道狗有很多品種,很多品種里面也有發(fā)育好的彼乌,發(fā)育差的泻肯、胖的瘦的...例如胖的泰迪也是泰迪=>泰迪也是狗=>狗=>哺乳動物=>動物=>生物∥空眨總之一切的一切都有一個起點灶挟,這條鏈的終點將會被指向同一處,這就好比原型鏈最終指向null
- 小泰迪生下來之后毒租,它的樣貌會跟泰迪媽媽大同小異稚铣,這就類比于原型的繼承。
- 小泰迪的主人領(lǐng)養(yǎng)小泰迪之后將其打扮成另外的模樣墅垮,這就類比于對象屬性可以覆蓋原型屬性惕医。但是小泰迪的模樣并不會改變小泰迪弟弟的模樣,這就類比于對象屬性的改變不會影響原型的改變算色。
其實有了上面的一個基本的了解之后抬伺,我們再來一步一步寫代碼就會比較容易了。
{
function Dog(name){
this.name = name
}
Dog.prototype.action = function(){
console.log(this.name+" wang..");
}
let xiaogou1 = new Dog("xiaogou1");
xiaogou1.action(); // xiaogou1 wang..
let xiaogou2 = new Dog("xiaogou2")
xiaogou2.action(); // xiaogou2 wang..
let xiaogou3 = new Dog("xiaogou3");
xiaogou3.action(); // xiaogou3 wang..
}
如上面所示 xiaogou1灾梦、xiaogou2峡钓、xiaogou3被稱為對象實例而Dog被稱為這群小狗的原型∪艉樱可以通過構(gòu)造方法來創(chuàng)建出1能岩,2,3三只小狗萧福。
{
function Cat(name){
this.name = name;
}
Cat.prototype.action=function(){
console.log(this.name+" miao!!!")
}
function Dog(name){
this.name = name
}
Dog.prototype.action = function(){
console.log(this.name+" wang..");
}
let xiaogou = new Dog("xiaogou");
xiaogou.action(); // xiaogou wang..
let xiaomao = new Cat("xiaomao");
xiaomao.action(); // xiaomao miao!!!
}
上面的代碼中可以瞧出來:小狗能繼承小狗原型上面的action方法去“wang...”,小貓會繼承小貓的原型方法“miao!!!”拉鹃。
{
function Dog(name){
this.name = name
}
Dog.prototype.action = function(){
console.log(this.name+" wang..");
}
let taidi= new Dog("taidi");
taidi.action = function(){
console.log(this.name+ " miao!!!");
}
taidi.action() // taidi miao!!!
let others = new Dog("other dog");
others.action(); // other dog wang..
}
看上面的代碼可以看出:當(dāng)我們?nèi)娦凶宼aidi的action方法改變的話,我們再進(jìn)行訪問的時候會先訪問到實例上面的屬性“ taidi miao!!!”,但是此時我們再用原型去創(chuàng)造實例的時候膏燕,我們并不會改變新增實例的action方法炭庙,這個說明了實例屬性改變會覆蓋原型屬性,但是不會原型上面的額屬性煌寇。
delete taidi.action
taidi.action() // taidi wang..
如上所示焕蹄,當(dāng)我們將泰迪action刪除掉,再訪問action方法則會重新顯示原型上面的方法阀溶。如果我們重復(fù)調(diào)用上面的方法腻脏,卻發(fā)現(xiàn)無法刪除action方法,這進(jìn)一步說明對象屬性不能改變原型的屬性银锻。
四永品、 原型鏈的繼承和徹底了解原型鏈
談到面向?qū)ο竽兀渴紫染蜁氲降氖抢^承击纬。我在這里呢鼎姐?也就來觸類旁通,希望用繼承起手徹底搞清楚這一個東西更振。
{
//這個是java入門繼承的最好的例子炕桨,拿來講解一哈
function Animal(name){
this.name = name
}
Animal.prototype.action = function(){
console.log(this.name+" have running...")
}
Animal.prototype.need = function(){
console.log(this.name+" need breathing")
}
function Fish (name){
Animal.call(this,name)
}
Fish.prototype = Object.create(Animal.prototype);
// Fish.prototype = new Animal() // 如果構(gòu)造函數(shù)有值的時候,這里就不知道該填寫什么了肯腕,就很尷尬
// Fish.prototype = Animal.prototype; // 如果Fish想重寫父類方法的時候献宫,父類方法也會變化
Fish.prototype.constructor = Fish;
Fish.prototype.action = function(){
console.log(this.name+" have Swimming...")
}
let fish = new Fish("fish");
fish.name = "鯉魚";
console.log(fish.name) // 鯉魚
fish.action();
fish.need();
fish.best(); // es5:undefined,es6:報錯
console.log(fish.toString())
}
上面是我手寫的一個js繼承,結(jié)合我下面手繪的一張結(jié)構(gòu)圖來看一下(我找了好多畫圖工具实撒,并沒有發(fā)現(xiàn)好用姊途,希望讀者能推薦一款好用的mac畫圖工具)。
- 實例魚調(diào)用name屬性知态,發(fā)現(xiàn)原型里面存在捷兰,就會直接輸出。
- 實例魚在調(diào)用action方法的時候首先會在實例的屬性里面去查找负敏,然后發(fā)現(xiàn)實例的屬性表里面并沒有action屬性贡茅,于是就會向上查找,找到魚的原型原在,然后在父類的原型中找到action就會執(zhí)行其方法友扰。
- 實例魚在實例屬性中沒有發(fā)現(xiàn)need方法彤叉,于是就會向上查找庶柿。但是發(fā)現(xiàn)Fish的原型中也沒有need方法就會再向上查找,發(fā)現(xiàn)在Animal的實例中發(fā)現(xiàn)有need方法秽浇,于是便執(zhí)行輸出浮庐。
- 實例魚在實例屬性落竹、Fish原型袜匿、Animal原型上面都沒有發(fā)現(xiàn)toString方法,于是再向上查找,終于在object原型中找到toString方法恶阴,于是便執(zhí)行輸出
-
實例魚在所有的原型中都沒有找到best方法,而Object的原型向上查找會返回null助泽,于是便執(zhí)行返回undefined熬北。(es6中默認(rèn)開啟嚴(yán)格模式,而在嚴(yán)格模式下面未定義的值會報錯)
四璧坟、說在最后
其實這篇文章寫了很久既穆,不知道是因為最近狀態(tài)低迷的緣故 還是時間喚起了我的懶散。我原本是想著上周末的時候就寫完這篇文章雀鹃,然后去專門來搞ts的幻工,結(jié)果上周日自己很蠢的看了兩場世界杯,然后買贏的德國輸了黎茎、買贏的巴西平了囊颅。
誒,盡管身邊一個朋友提醒我:世界杯有人在操盤傅瞻。但是還是不能泯滅我當(dāng)一個偽球迷的熱情踢代。算了,不說了 不說了嗅骄,
日本都贏球了奸鬓,你還有什么理由怨天尤人--致將去洗澡的我