摘要:但是該方法有個(gè)缺點(diǎn)就是看不出該對象的類型,于是乎構(gòu)造函數(shù)模式應(yīng)運(yùn)而生。當(dāng)然,如果細(xì)心的朋友應(yīng)該會(huì)發(fā)現(xiàn)函數(shù)名首字母大寫了,這是約定在構(gòu)造函數(shù)時(shí)將首字母大寫。這時(shí)候,聰明的人應(yīng)該都可以想到,將構(gòu)造函數(shù)模式和原型模式組合起來就可以了。
一.什么是js對象 1.簡單理解js對象
在了解原型鏈之前,我們先要弄清楚什么是JavaScript的對象,JavaScript對象又由哪些組成。有人說一個(gè)程序就是一個(gè)世界,那么我們可以把對象稱之為這個(gè)世界的組成類型,可以是生物,植物,生活用品等等。我們在java中管這些類型叫做類,但是在JavaScript中沒有類的說法,當(dāng)然ES6新標(biāo)準(zhǔn)中開始出現(xiàn)了類。但是在此之前,我們都管這些類型叫做對象。那么對象創(chuàng)建出來的實(shí)例就是就是組成該世界的各個(gè)元素,如一個(gè)人、一只小狗、一棵樹等等。這些就稱之為對象的實(shí)例。那么每種類型都有它不同的屬性和方法,同樣的JavaScript對象也是由對象屬性和對象方法組成。當(dāng)然了每個(gè)實(shí)例還可以存在與對象不一樣的方法與屬性。
var person = { name:"xiaoming", //對象屬性 sayName:function(){ //對象方法 console.log(this.name); } }2.js對象屬性的特性
在JavaScript對象中,每個(gè)屬性都有其各自的特性,比如你的性別具有不可修改的特性。那么下面簡單粗略介紹一下這幾個(gè)特性。這些特性在JavaScript中是不能直接訪問的,特性是內(nèi)部值。
[[Configurable]]: 表示能不能刪除重新定義屬性,能不能修改屬性等 默認(rèn)true
[[Enumerable]]: 表示能不能通過for-in遍歷等 默認(rèn)true
[[Writeable]]: 表示能不能修改屬性值 默認(rèn)true
[[Value]]: 表示屬性的值,寫入到這里,讀從這里讀 默認(rèn)undefined
如果要修改屬性的默認(rèn)特性,可以使用Object.defineProperty()方法,當(dāng)然在這里就不再繼續(xù)展開了。接下來我們開始介紹對象的創(chuàng)建
二.創(chuàng)建JavaScript對象 1.工廠模式function createPerson(name,sex){ let obj = new Object(); obj.name = name; obj.sex = sex; obj.sayName = function(){ console.log(this.name); } return obj; } let p1 = new createPerson("小明","男");
這就是工廠模式,在函數(shù)內(nèi)創(chuàng)建對象,然后在函數(shù)內(nèi)封裝好后返回該對象。但是該方法有個(gè)缺點(diǎn)就是看不出該對象的類型,于是乎構(gòu)造函數(shù)模式應(yīng)運(yùn)而生。
2.構(gòu)造函數(shù)模式function Cat(name,color){ this.name = name; this.color = color; this.sayName = { console.log("我是"+name+"貓"); } } let Tom = new Cat("Tom","灰白"); let HelloKity = new Cat("HelloKity","粉紅");
構(gòu)造函數(shù)模式和工廠模式的區(qū)別在于,構(gòu)造函數(shù)模式?jīng)]有用return語句,直接把屬性賦給了this語句,并且沒有顯式的創(chuàng)建對象。當(dāng)然,如果細(xì)心的朋友應(yīng)該會(huì)發(fā)現(xiàn)函數(shù)名首字母大寫了,這是約定在構(gòu)造函數(shù)時(shí)將首字母大寫。
用構(gòu)造函數(shù)創(chuàng)建新實(shí)例時(shí),必須要用new操作符。同時(shí),每個(gè)由構(gòu)造函數(shù)創(chuàng)建的實(shí)例都會(huì)有一個(gè)constructor指向該構(gòu)造函數(shù)
Tom.constructor == Cat //true
這時(shí)候我們就會(huì)想一個(gè)問題,我們在創(chuàng)建不同的Cat實(shí)例時(shí),我們就會(huì)創(chuàng)建多個(gè)不同sayName函數(shù),但是他們執(zhí)行的功能都是一樣的,這時(shí)候我們就會(huì)想要一種更優(yōu)化的方法。這時(shí),我們需要引入原型屬性(prototype)的概念了
3.原型模式我們創(chuàng)建的每個(gè)函數(shù)里面都會(huì)有個(gè)prototype屬性,這個(gè)就是原型屬性,這個(gè)屬性是個(gè)指針,指向一個(gè)該函數(shù)的原型對象。我們可以捋一捋對象,對象原型,實(shí)例這三者的關(guān)系,簡單來說,我們可以把對象想象成爸爸,那么對象原型就是爺爺,實(shí)例的話好比是兒子。爺爺有的東西(屬性、方法),每個(gè)兒子都會(huì)遺傳到的,當(dāng)然如果爸爸把爺爺?shù)臇|西修改了一下,那么到兒子手上的就是爸爸修改過的東西了(方法重寫)。當(dāng)然,兒子也算是爺爺骨肉嘛,那么兒子就會(huì)有個(gè)指針[[prototype]]指向爺爺,在Chrome、Firefox等瀏覽器上面可以用屬性__proto__可以訪問到。
那么prototype和__proto__區(qū)別在哪?
這么說,簡單的說prototype是指向各自的爸爸,__proto__是指向各自的爺爺。當(dāng)然這說法只是為了更好理解這兩者是有區(qū)別的。接下來我給大家做一個(gè)圖讓大家更好的理解這兩者的區(qū)別。
這大概也是明白為什么對象實(shí)例存在個(gè)constructor指針指向?qū)ο罅?,因?yàn)閷ο笤蜕厦娲嬖谶@個(gè)屬性指向該對象,而且原型最初只包含該constructor屬性。而實(shí)例尋找屬性值的時(shí)候會(huì)向上找,先在實(shí)例中搜索該屬性,沒有的話向?qū)ο笤蛯ふ摇K宰詈笳业讲⒎祷卦撝?/strong>。這樣就能很清楚的分開prototype和__proto__的區(qū)別了。prototype是對象的屬性,而__proto__是對象實(shí)例的屬性。
那么我們基本了解prototype屬性以后,我們就可以給大家說說原型模式了。
function Cat(){ } Cat.prototype.name = "Tom"; Cat.prototype.color = "灰白"; Cat.prototype.sayName = function(){ console.log(this.name); } let cat1 = new Cat(); let cat2 = new Cat(); cat1.sayName(); //"Tom" cat2.sayName(); //"Tom" console.log(cat1.color); //"灰白" console.log(cat2.color); //"灰白" //因?yàn)閷ο笤褪枪蚕韺傩耘c方法,所以所有實(shí)例都可以訪問到 //接下來玩點(diǎn)更復(fù)雜的 Cat.sayName = function(){ console.log("我是Cat"); } cat1.sayName = function(){ console.log("我是cat1"); } let cat3 = new Cat(); cat1.sayName(); //"我是cat1" cat2.sayName(); //"Tom" cat3.sayName(); //"Tom" Cat.sayName(); //"我是Cat"
這時(shí)候很多人就懵了,為什么cat3說的是"Tom",而不是輸出"我是Cat"。這是因?yàn)?Cat.sayName 這個(gè)函數(shù)是類方法,我們要注意一點(diǎn),Cat也是一個(gè)函數(shù),函數(shù)就是一個(gè)對象,可以為其添加方法和屬性。所以我們在實(shí)例中調(diào)用sayName并不是調(diào)用該類方法。我們還需要分清類方法與對象方法的區(qū)別。
function Person(){ //通過對象實(shí)例調(diào)用 this.say = function(){ console.log("我是Person對象方法"); } } Person.say = function(){ //只能通過Person調(diào)用 console.log("我是Person類方法"); } Person.prototype.say = function(){ //通過對象實(shí)例調(diào)用 console.log("我是Person對象原型方法"); }
到這里,也許還是會(huì)有點(diǎn)懵,為什么后面的cat1.sayName(); //"我是cat1",因?yàn)閷ο髮?shí)例方法會(huì)屏蔽掉原型的方法。我們之前說過,當(dāng)代碼讀取對象的某個(gè)屬性時(shí),它會(huì)先從該對象實(shí)例開始搜索,如果找不到再往上搜索。所以當(dāng)你定義了對象實(shí)例的方法時(shí),如果跟對象原型中的同名,那么該對象實(shí)例的方法就會(huì)屏蔽掉對象原型中的方法。所以cat1第二次輸出的是我是cat1。
到這里,我再總結(jié)一下對象原型,對象與對象實(shí)例之間的關(guān)系。對象原型內(nèi)的方法與屬性可以供所有的對象實(shí)例訪問,實(shí)現(xiàn)共享性。
對象的prototype屬性可以找到對象原型,而對象實(shí)例的[[proto]]可以找到對象原型
對象實(shí)例可以重寫對象原型方法,使其屏蔽對象原型的方法
對象原型一開始只有constructor屬性,該屬性指向該對象
分清對象原型方法,對象方法,對象實(shí)例方法,類方法區(qū)別。類方法不需要通過實(shí)例化對象去訪問,而其他的都要對象實(shí)例去訪問
那么到這里我們已經(jīng)弄懂了對象原型,對象與對象實(shí)例之間的關(guān)系。下面我再介紹一種簡單的原型語法。
function Cat(){ } Cat.prototype = { name:"Tom", color:"灰白", sayName:function(){ console.log(this.name); }, }
這樣我就以字面量的形式創(chuàng)建了新對象,但是有個(gè)不一樣的地方就是constructor屬性不指向Cat,因?yàn)槲覀儎?chuàng)建一個(gè)函數(shù)就會(huì)創(chuàng)建它的原型對象,原型對象里面自動(dòng)獲得constructor屬性,那么我們再這樣的情況下,重寫了整個(gè)原型對象。所以此時(shí)的constructor屬性指向了Object。那么我們?nèi)绻且@個(gè)屬性怎么辦?很好辦,我們自己給它加上就好。
function Cat(){ } Cat.prototype = { constructor:"Cat", name:"Tom", color:"灰白", sayName:function(){ console.log(this.name); }, }
最后我們講一下原型模式的缺點(diǎn),原型模式的缺點(diǎn)也很明顯,就是它的共享性。成也共享敗也共享。這讓我突然想起共享單車。廢話不多說,直接擼碼上來
function Cat(){ } Cat.prototype.name = "Tom"; Cat.prototype.color = "灰白"; Cat.prototype.catchMouse = ["Jerry"]; Cat.prototype.sayName = function(){ console.log(this.name); } let cat1 = new Cat(); let cat2 = new Cat(); cat1.catchMouse.push("Mickey"); console.log(cat1.catchMouse); //["Jerry", "Mickey"] console.log(cat2.catchMouse); //["Jerry", "Mickey"]
因?yàn)樵蜕厦娴膶傩允撬袑?shí)例都可以訪問的,那么當(dāng)添加往catchMouse屬性添加一個(gè)值時(shí),所有實(shí)例皆可以訪問到該屬性。這就讓人們很頭疼了,每個(gè)實(shí)例的屬性應(yīng)該都是不一樣的才對,起碼正常來說,但是這樣弄得大家都一樣的話,就有問題了。這時(shí)候,聰明的人應(yīng)該都可以想到,將構(gòu)造函數(shù)模式和原型模式組合起來就可以了。
4.組合構(gòu)造函數(shù)模式和原型模式將其組合起來,結(jié)合他們兩的優(yōu)點(diǎn),是普遍認(rèn)同度最高的對象創(chuàng)建模式
function Cat(name,color){ this.name = name; this.color = color; this.catchMouse = []; } Cat.prototype.sayName = function(){ console.log(this.name); } let cat1 = new Cat("Tom","灰白"); let cat2 = new Cat("HellowKity","粉紅"); cat1.catchMouse.push("Jerry"); cat1.sayName(); //"Tom" cat2.sayName(); //"HellowKity" console.log(cat1.catchMouse); //["Jerry"] console.log(cat2.catchMouse); //[]最后
本篇介紹了對象與對象的創(chuàng)建方法。同時(shí)引入并介紹了對象原型的概念。解析了對象原型,對象與對象實(shí)例間的關(guān)系。我們在下一篇將會(huì)解析原型鏈的概念以及對象的繼承與原型鏈的關(guān)系,帶大家敲開原型鏈的奧秘。
原創(chuàng)文章,轉(zhuǎn)載請注明出處
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/94530.html
摘要:不然原型鏈會(huì)斷開。喵喵喵這樣會(huì)使上一條語句失效,從而使原型鏈斷開。這是在原型鏈里面無法做到的一個(gè)功能。屬性使用借用構(gòu)造函數(shù)模式,而方法則使用原型鏈。 一、對象的繼承 1.了解原型鏈 在上一篇我們講過關(guān)于原型對象的概念,當(dāng)然如果不了解的建議去翻看第一篇文章,文末附有連接。我們知道每個(gè)對象都有各自的原型對象,那么當(dāng)我們把一個(gè)對象的實(shí)例當(dāng)做另外一個(gè)對象的原型對象。。這樣這個(gè)對象就擁有了另外一...
摘要:函數(shù)式編程,一看這個(gè)詞,簡直就是學(xué)院派的典范。所以這期周刊,我們就重點(diǎn)引入的函數(shù)式編程,淺入淺出,一窺函數(shù)式編程的思想,可能讓你對編程語言的理解更加融會(huì)貫通一些。但從根本上來說,函數(shù)式編程就是關(guān)于如使用通用的可復(fù)用函數(shù)進(jìn)行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數(shù)式編程(Functional Programming),一...
摘要:下圖給出一個(gè)簡單的列表圖什么是哈希和哈希值為理解挖礦的代碼機(jī)制,首先解決幾個(gè)概念。第一個(gè)就是哈希。哈希值為十六進(jìn)制表示的數(shù),且長度固定。也正是哈希值的這些特點(diǎn),賦予了其加密信息時(shí)更高的安全性。 第四期 挖礦的相關(guān)算法(2) 卡酷少Wechat:13260325501 看過(1)篇,相信你一定對挖礦的機(jī)制有了一點(diǎn)了解。那么本篇,我們來一起看一下挖礦中涉及的算法。 在本篇文章中,如果在...
摘要:當(dāng)我們的視圖和數(shù)據(jù)任何一方發(fā)生變化的時(shí)候,我們希望能夠通知對方也更新,這就是所謂的數(shù)據(jù)雙向綁定。返回值返回傳入函數(shù)的對象,即第一個(gè)參數(shù)該方法重點(diǎn)是描述,對象里目前存在的屬性描述符有兩種主要形式數(shù)據(jù)描述符和存取描述符。 前言 談起當(dāng)前前端最熱門的 js 框架,必少不了 Vue、React、Angular,對于大多數(shù)人來說,我們更多的是在使用框架,對于框架解決痛點(diǎn)背后使用的基本原理往往關(guān)注...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過...
閱讀 2894·2021-11-24 09:39
閱讀 3151·2021-11-19 10:00
閱讀 1552·2021-10-27 14:17
閱讀 1822·2021-10-14 09:43
閱讀 977·2021-09-03 10:30
閱讀 3422·2019-08-30 15:54
閱讀 2748·2019-08-30 13:05
閱讀 2021·2019-08-30 11:02