您好,登錄后才能下訂單哦!
這篇文章主要介紹Javascript的原型和原型鏈是什么意思,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
想要介紹原型,就不得不提為什么我們要使用原型,在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 方法我們發現這是同一個方法。這樣我們完美的解決了構造函數的問題。
首先清楚兩個概念:
引用類型,都具有對象特性,即可自由擴展屬性。(引用類型:Object、Array、Function、Date、RegExp)
每個函數
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、現在自身屬性中查找,找到返回
2、找不到則繼續沿著__proto__這條鏈向上查找,找到返回
3、如果最終沒有找到。返回undefined設置對象的屬性值時,不會查找原型鏈,如果當前對象中沒有此屬性,直接添加此屬性并設置其值方法一般定義在原型中,屬性一般通過構造函數定義在對象本身
原型鏈就是一個過程,原型是原型鏈這個過程中的一個單位,貫穿整個原型鏈
圖解
//練習題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()
原型、原型鏈的意義與使用場景:
原型對象的作用,是用來存放實例中共有的那部分屬性、方法、可以大大減少內存消耗。
以上是“Javascript的原型和原型鏈是什么意思”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。