JavaScript原型與原型鏈

一、概述

在 JavaScript 中,是一種面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言,但是 JS 本身是沒(méi)有 “類(lèi)” 的概念,JS 是靠原型和原型鏈實(shí)現(xiàn)對(duì)象屬性的繼承。

在理解原型前,需要先知道對(duì)象的構(gòu)造函數(shù)是什么,構(gòu)造函數(shù)都有什么特點(diǎn)?

1. 構(gòu)造函數(shù)

// 構(gòu)造函數(shù) Person()
function Person(name, gender) {
    this.name = name;
    this.gender = gender;
}

var person = new Person("周杰倫", "男");

// 最后創(chuàng)建出來(lái)的對(duì)象實(shí)例 person
person

{
    name: "周杰倫",
    gender: "男"
}

以上代碼,普通函數(shù) Person(),加上 new 關(guān)鍵字后,就構(gòu)造了一個(gè)對(duì)象 person

所以構(gòu)造函數(shù)的定義就是普通函數(shù)加上 new 關(guān)鍵字,并總會(huì)返回一個(gè)對(duì)象。

2. 函數(shù)對(duì)象
同時(shí),JS 中的對(duì)象分為一般對(duì)象和函數(shù)對(duì)象。那什么是一般對(duì)象,什么又是函數(shù)對(duì)象呢?

JavaScript 的類(lèi)型分為基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型,基本數(shù)據(jù)類(lèi)型目前有 6 種(null, undefined, string, number, boolean, Symbol)。 其余的數(shù)據(jù)類(lèi)型都統(tǒng)稱(chēng)為 object 數(shù)據(jù)類(lèi)型,其中,包括 Array, Date, Function等,所以函數(shù)可以稱(chēng)為函數(shù)對(duì)象。

let foo = function(){

}
foo.name = "bar";
foo.age = 24;
console.log(foo instanceof Function)  //true
console.log(foo.age)  // 24

以上代碼就說(shuō)明了函數(shù)其實(shí)是一個(gè)對(duì)象,也可以具有屬性。

二、原型鏈

JavaScript 中的對(duì)象,有一個(gè)特殊的 [[prototype]] 屬性, 其實(shí)就是對(duì)于其他對(duì)象的引用(委托)。當(dāng)我們?cè)讷@取一個(gè)對(duì)象的屬性時(shí),如果這個(gè)對(duì)象上沒(méi)有這個(gè)屬性,那么 JS 會(huì)沿著對(duì)象的 [[prototype]]鏈 一層一層地去找,最后如果沒(méi)找到就返回 undefined;

這條一層一層的查找屬性的方式,就叫做原型鏈。

var o1 = {
    age: 6
}

那么,為什么一個(gè)對(duì)象要引用,或者說(shuō)要委托另外一個(gè)對(duì)象來(lái)尋找屬性呢?

本文開(kāi)篇的第一句話(huà),就指出來(lái)的,JavaScript 中,和一般的 OOP 語(yǔ)言不同,它沒(méi)有 '類(lèi)'的概念,也就沒(méi)有 '模板' 來(lái)創(chuàng)建對(duì)象,而是通過(guò)字面量或者構(gòu)造函數(shù)的方式直接創(chuàng)建對(duì)象。那么也就不存在所謂的類(lèi)的復(fù)制繼承。

三、原型

那什么又是原型呢?

既然我們沒(méi)有類(lèi),就用其他的方式實(shí)現(xiàn)類(lèi)的行為吧,看下面這句話(huà)↓↓。

1. 每個(gè)函數(shù)都有一個(gè)原型屬性 prototype 對(duì)象

function Person() {

}

Person.prototype.name = 'JayChou';

// person1 和 person2 都是空對(duì)象
var person1 = new Person();
var person2 = new Person();

console.log(person1.name) // JayChou
console.log(person2.name) // JayChou

通過(guò)構(gòu)造函數(shù)創(chuàng)造的對(duì)象,對(duì)象在尋找 name 屬性時(shí),找到了 構(gòu)造函數(shù)的 prototype 對(duì)象上。

這個(gè)構(gòu)造函數(shù)的 prototype 對(duì)象,就是 原型

用示意圖來(lái)表示:

查找對(duì)象實(shí)例屬性時(shí),會(huì)沿著原型鏈向上找,在現(xiàn)代瀏覽器中,標(biāo)準(zhǔn)讓每個(gè)對(duì)象都有一個(gè) __proto__ 屬性,指向原型對(duì)象。那么,我們可以知道對(duì)象實(shí)例和函數(shù)原型對(duì)象之間的關(guān)系。

2. 每個(gè)原型對(duì)象都有一個(gè) constructor 屬性指向關(guān)聯(lián)的構(gòu)造函數(shù)

為了驗(yàn)證這一說(shuō)話(huà),舉個(gè)例子。

function Person() {}

Person === Person.prototype.constructor; // true

那么對(duì)象實(shí)例是構(gòu)造函數(shù)構(gòu)造而來(lái),那么對(duì)象實(shí)例是不是也應(yīng)該有一個(gè) constructor 呢?

function Person() {}

const person = new Person();
person.constructor === Person // true

但事實(shí)上,對(duì)象實(shí)例本身并沒(méi)有 constructor 屬性,對(duì)象實(shí)例的 constructor 屬性來(lái)自于引用了原型對(duì)象的 constructor 屬性

person.constructor === Person.prototype.constructor // true

3. 原型鏈頂層:Object.proto** == null**

既然 JS 通過(guò)原型鏈查找屬性,那么鏈的頂層是什么呢,答案就是 Object 對(duì)象,Object 對(duì)象其實(shí)也有 __proto__屬性,比較特殊的是 Object.__proto__ 指向 null, 也就是空。

Object.prototype.__proto__ === null

我們回過(guò)頭來(lái)看函數(shù)對(duì)象:

所有函數(shù)對(duì)象的proto都指向Function.prototype,它是一個(gè)空函數(shù)(Empty function)

Number.__proto__ === Function.prototype  // true
Number.constructor == Function //true

Boolean.__proto__ === Function.prototype // true
Boolean.constructor == Function //true

String.__proto__ === Function.prototype  // true
String.constructor == Function //true

// 所有的構(gòu)造器都來(lái)自于Function.prototype,甚至包括根構(gòu)造器Object及Function自身
Object.__proto__ === Function.prototype  // true
Object.constructor == Function // true

// 所有的構(gòu)造器都來(lái)自于Function.prototype,甚至包括根構(gòu)造器Object及Function自身
Function.__proto__ === Function.prototype // true
Function.constructor == Function //true

Array.__proto__ === Function.prototype   // true
Array.constructor == Function //true

RegExp.__proto__ === Function.prototype  // true
RegExp.constructor == Function //true

Error.__proto__ === Function.prototype   // true
Error.constructor == Function //true

Date.__proto__ === Function.prototype    // true
Date.constructor == Function //true

所有的構(gòu)造器都來(lái)自于 Function.prototype,甚至包括根構(gòu)造器Object及Function自身。所有構(gòu)造器都繼承了·Function.prototype·的屬性及方法。如length、call、apply、bind

以圖會(huì)友,這就是網(wǎng)上經(jīng)??吹降?JS 原型和原型鏈關(guān)系圖:

對(duì)于以上看似很復(fù)雜的關(guān)系圖,只需要理解 5 點(diǎn):

  1. 每個(gè)函數(shù)都有一個(gè)原型屬性 prototype 對(duì)象
  2. 普通對(duì)象的構(gòu)造函數(shù)是 Object(),所以 Person.prototype.__proto__ === Object.prototype
  3. 函數(shù)對(duì)象都來(lái)自于 Function.prototype
  4. 函數(shù)對(duì)象也是對(duì)象,所有 Function.prototype.__proto__ === Object.prototype
  5. Object.__proto__ 是 null

總結(jié)

以上就是 JavaScript 中原型和原型鏈的知識(shí)。由于 JS 沒(méi)有'類(lèi)', 所以采用了原型的方式實(shí)現(xiàn)繼承,正確的說(shuō)法是引用或者委托,因?yàn)閷?duì)象之間的關(guān)系不是復(fù)制,而是委托。在查找屬性的時(shí)候,引用(委托)原型對(duì)象的屬性,也就是我們常說(shuō)的原型繼承。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 什么是原型語(yǔ)言 只有對(duì)象,沒(méi)有類(lèi);對(duì)象繼承對(duì)象,而不是類(lèi)繼承類(lèi)。 “原型對(duì)象”是核心概念。原型對(duì)象是新對(duì)象的模板,...
    zhoulujun閱讀 2,441評(píng)論 0 12
  • 一、數(shù)據(jù)類(lèi)型JavaScript的數(shù)據(jù)類(lèi)型可以分為基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型。 基本數(shù)據(jù)類(lèi)型(6種)StringN...
    qfstudy閱讀 432評(píng)論 1 0
  • JS中原型鏈,說(shuō)簡(jiǎn)單也簡(jiǎn)單。 首先明確: 函數(shù)(Function)才有prototype屬性,對(duì)象(除Object...
    前小白閱讀 4,066評(píng)論 0 9
  • 之前在忙于各種事情,今天用一天的時(shí)間寫(xiě)論文
    燁_8cd1閱讀 104評(píng)論 0 0
  • 1 對(duì)比 把自己和榜樣做對(duì)比,看看為什么他們可以做的更好,哪些訓(xùn)練方法幫助他們實(shí)現(xiàn)了更卓越的成就。 2 門(mén)檻 一萬(wàn)...
    白菜花君閱讀 272評(píng)論 0 3

友情鏈接更多精彩內(nèi)容