<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
想要介紹原型,就不得不提為什麼我們要使用原型,在js早期,我們建立一個物件,比較流行的做法是使用物件字面量去建立一個物件,例如:
const person = { name: "wywy", age: 21, hobby: "聽周杰倫" }
用這種方式去建立物件,雖然簡潔明瞭,但是我們如果需要大批次的建立這一類的物件,就像person這個物件,我們可能需要去建立多個不同的人,那麼每一次都需要去宣告建立,這樣工作量是巨大的,為了解決這個問題我們引入了工廠函數的概念。
什麼是工廠函數,顧名思義就可以把工廠函數看作是一個流水線的工廠,這個工廠的作用就是批次生產person物件,就像下面這樣:
function createPerson(name,age,hobby){ const obj = {}; obj.name = name; obj.age = age; obj.hobby = hobby; return obj; } const person1 = createPerson("zs", 23, "滑板"); const person2 = createPerson("ls", 22, "聽歌"); console.log(person1); console.log(person2);
這裡我們在建立person物件的時候只需要呼叫這個函數即可,並把每個物件對應的屬性值傳入就好了,這樣相對於用物件字面量去建立一個個的person確實簡化了程式碼量,但是工廠函數也有自身的缺點,就是我們不能去判斷出這個物件的型別,按我們知道的在 js 中複雜參照資料型別進行細分,有 Array,Function,String 等,但是通過工廠函數模式建立的這些物件用控制檯列印去全部都是 Object 型別,假如你有多個工廠函數,用多個工廠函數建立了多個範例,但是你卻並不知道這些物件屬於哪個工廠。為此又推出了建構函式
這個概念。
關於建構函式其實它和工廠函數非常相似,在js的函數中其實並沒有單獨的一類函數叫做建構函式,建構函式總是和new關鍵字一起使用,更準確地說一個函數被構造呼叫了。當一個建構函式不使用new關鍵字呼叫時,他和普通的函數無異。
function CreatePerson(name, age, hobby) { this.name = name; this.age = age; this.hobby = hobby; } const person1 = new CreatePerson("zs", 23, "滑板"); const person2 = new CreatePerson("ls", 22, "聽歌"); console.log(person1); console.log(person2);
仔細觀察,不難發現我們對上面的工廠函數進行了以下幾點改造:
1、我們將函數名的首字母大寫了,在 js 中有個規定如果你以後打算將一個函數作為建構函式去使用那麼最好把它的函數名首字母大寫,來提醒使用者這是一個建構函式。其實不大寫也不會有什麼語法錯誤。
2、我們取消了在函數內部顯示的宣告一個物件 “const obj = {}”,並一併取消了最後返回這個物件的操作 “return obj”。
3、在呼叫這個這個 CreatePerson 函數時在前面加上了 new 關鍵字。
然後我們看這個結果,這個時候我們列印的物件不在是 Object 了,而是我們自己建立的函數 CreatePerson。看來建構函式確實解決了物件無法判定型別的問題。
那麼神奇的new關鍵字在後臺做了什麼呢?其實它做了下面的五件事
1、在記憶體中建立一個新的物件。
2、讓新物件的內部特性 [[Prototype]] 儲存函數 CreatePerson 的 prototype 的屬性值,也就是把函數的原型的指標儲存到了 [[Prototype]] 中。
3、把函數內部的 this 指向這個新建立的物件。
4、執行函數內部的程式碼。
5、如果函數本身沒有返回物件,那麼就把這個新物件返回。
我們看建構函式原來在後臺為我們做了這麼多事。那麼建構函式就完美了嗎?並不是的,我們想一個 person 是不是應該也該給他加一個方法呢?那麼我們就給加一個說話的方法吧:
function CreatePerson(name, age, hobby) { this.name = name; this.age = age; this.hobby = hobby; // 新增一個方法 this.sayHi = function() { console.log("你好,我叫" + this.name) } } const person1 = new CreatePerson("zs", 23, "滑板"); const person2 = new CreatePerson("ls", 22, "聽歌"); console.log(person1); console.log(person2);
但是我們發現這樣做無疑為每一個範例物件都新增了一個 sayHi 方法,而且每個範例上的方法都不相等,但我們的目的是讓每個範例都有這麼一個功能就好了,不必建立這麼多的 sayHi 方法而去浪費記憶體空間。說的直白一點,比如家裡有五個孩子,每個孩子都想玩 switch 遊戲,那麼家長要給每個孩子買一臺 switch 嗎?當然家裡有礦的當我沒說,一般家庭是不是就買一臺,然後哪個孩子想玩就管家長去要就行了是不是。
同樣的,程式碼就是對現實生活的抽象,那麼我們是不是也可以這樣做,把方法新增到這些範例都擁有的一個爸爸是不是就好了,而仔細想想在 new 的五步中第二步是不是做了這麼一件事,沒錯他就是 js 中的原型。這個原型就是這些範例的爸爸。
說了這麼多我們總算要講原形了!!!
通過上面的講解我們似乎對原型的作用有了大致的理解,就是把範例物件上需要用到的共有方法新增到原型上,而範例物件的自己的私有屬性寫在建構函式內部。
接下來我們對建構函式進行再一次改造:
function CreatePerson(name, age, hobby) { this.name = name; this.age = age; this.hobby = hobby; // 新增一個方法 this.sayHi = function() { console.log("你好,我叫" + this.name) } } const person1 = new CreatePerson("zs", 23, "滑板"); const person2 = new CreatePerson("ls", 22, "聽歌"); console.log(person1); console.log(person2);
首先向大家說明一點我並沒有按著定義一個建構函式,然後在建構函式的原型上新增 sayHi 方法,接著使用 new 建立範例的順序來寫程式碼。而是先 new 建立了範例,然後再在原型上新增方法,這樣的目的是想告訴大家,原型是具有動態性的,即你先建立了範例,在範例之後給原型新增了方法那麼範例依然是可以存取的。而且可以看到通過比較 person1 和 person2 的 sayHi 方法我們發現這是同一個方法。這樣我們完美的解決了建構函式的問題。
首先清楚兩個概念:
每個函數
function都有一個顯示原型prototype
,每個範例物件
都有一個隱式原型__proto__
function Fn() { // 內部語句:this.prototype = {} } // 1、每個函數function都有一個prototype,即顯示原型(屬性) console.log(Fn.prototype); // 2、每個範例物件都有一個__proto__,可稱為隱式原型(屬性) var fn = new Fn(); // 內部語句: this.__proto__ = Fn.prototype console.log(fn.__proto__);
兩個準則:
在設計js原型原型鏈的時候遵循以下兩個準則:
準則一: 原型物件(即Fn.prototype)的 constructor 指向建構函式本身。
準則二: 範例物件(即 fn )的__proto__ 指向其建構函式的顯示原型。
function Fn() {} var fn = new Fn(); // 原型物件的 constructor 指向建構函式本身 console.log(Fn.prototype.constructor === Fn); // true // 物件的隱式原型的值為其對應建構函式的顯示原型的值 console.log(Fn.prototype === fn.__proto__); // true
理解Function與Object特例
每個函數都是 Function 的範例,所以每個函數既有顯示原型又有隱式原型,所有函數的隱式原型指向 Function.prototype; 構造器Function的構造器是它自身。
// function Foo() {} 相當於 var Foo = new Function() // Function = new Function() => Function.__proto__ = Function.prototype // Object 作為建構函式時,其 __proto__ 內部屬性值指向 Function.prototype // Object.__proto__ = Function.prototype // Function.constructor=== Function;//true
Object建構函式建立一個物件包裝器。JavaScript中的所有物件都來自 Object,所有物件都是Object的範例;所有物件從Object.prototype繼承方法和屬性,儘管它們可能被覆蓋。
// Fn的原型物件(Fn.prototype)也來自Object,故Fn.prototype.__proto__ = Object.prototype function Fn() {}
原型鏈:
讀取某個物件的屬性時,會自動找到原型鏈中查詢。
原型鏈就是一個過程,原型是原型鏈這個過程中的一個單位,貫穿整個原型鏈
圖解
//練習題1 function A(){ } A.prototype.n = 1; var b = new A(); A.prototype = { n:2, m:3 } var c = new A(); console.log(b.n,b.m,c.n,c.m) // 1 undefined 2 3
// 測試題2 var F = function() { }; Object.prototype.a = function() { console.log('a()'); }; Function.prototype.b = function() { console.log('b()'); }; var f = new F(); f.a(); // a() f.b(); // 報錯:Uncaught TypeError: f.b is not a function F.a(); // a() F.b(); // b()
原型、原型鏈的意義與使用場景:
原型物件的作用,是用來存放範例中共有的那部分屬性、方法、可以大大減少記憶體消耗。
本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45