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