1. 程式人生 > >JavaScript中Function和Object的原型和原型鏈

JavaScript中Function和Object的原型和原型鏈

我們之前寫了原型和原型鏈的文章,但是後面發現比較特殊的一種情況:Function和Object的原型和原型鏈的關係比較複雜,所以這篇專門來講兩者的關係。

主要參考文章:https://www.jianshu.com/p/dee9f8b14771

1.前言

我們先來講述幾個結論:
prototype是函式的一個屬性;
__proto__是一個物件擁有的內建屬性,是JS內部使用尋找原型鏈的屬性;

Object為函式,所以是Function的例項;
Function.prototype為物件,所以是Object例項(因為原型物件就是一個物件)。
所有的構造器都來自於Function.prototype,甚至包括根構造器Object及Function自身。

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

2.Function.prototype沒有prototype

原型物件其實就是普通物件(但 Function.prototype 除外,它是函式物件,但它很特殊,他沒有prototype屬性(前面說道函式物件都有prototype屬性))。看下面的例子:

function Person(){};
 console.log(Person.prototype) //Person{}
 console.log(typeof Person.prototype) //Object
 console.log(typeof Function.prototype) // Function,這個特殊
 console.log(typeof Object.prototype) // Object
 console.log(typeof Function.prototype.prototype) //undefined

Function.prototype 為什麼是函式物件呢?

var A = new Function ();
 Function.prototype = A;

上文提到凡是通過 new Function( ) 產生的物件都是函式物件。因為 A 是函式物件,所以Function.prototype 是函式物件。

3.__proto__

JS 在建立物件(不論是普通物件還是函式物件)的時候,都有一個叫做__proto__ 的內建屬性,用於指向建立它的建構函式的原型物件。
物件 person1 有一個 __proto__屬性,建立它的建構函式是 Person,建構函式的原型物件是 Person.prototype ,所以:
person1.__proto__

== Person.prototype

不過,要明確的真正重要的一點就是,這個連線存在於例項(person1)與建構函式(Person)的原型物件(Person.prototype)之間,而不是存在於例項(person1)與建構函式(Person)之間。

4.構造器

熟悉 Javascript 的童鞋都知道,我們可以這樣建立一個物件:

var obj = {}

它等同於下面這樣:

var obj = new Object()

obj 是建構函式(Object)的一個例項。所以:

obj.constructor === Object
obj.__proto__ === Object.prototype

新物件 obj 是使用 new 操作符後跟一個建構函式來建立的。建構函式(Object)本身就是一個函式(就是上面說的函式物件),它和上面的建構函式 Person 差不多。只不過該函式是出於建立新物件的目的而定義的。所以不要被 Object 嚇倒。

同理,可以建立物件的構造器不僅僅有 Object,也可以是 Array,Date,Function等。
所以我們也可以建構函式來建立 Array、 Date、Function

var b = new Array();
b.constructor === Array;
b.__proto__ === Array.prototype;

var c = new Date(); 
c.constructor === Date;
c.__proto__ === Date.prototype;

var d = new Function();
d.constructor === Function;
d.__proto__ === Function.prototype;

這些構造器都是函式物件:

在這裡插入圖片描述

5.原型鏈測試

小測試來檢驗一下你理解的怎麼樣:

person1.__proto__是什麼?

Person.__proto__是什麼?

Person.prototype.__proto__是什麼?

Object.__proto__是什麼?

Object.prototype__proto__是什麼?

答案:
第一題:
因為 person1.__proto__ === person1 的建構函式.prototype
因為 person1的建構函式 === Person
所以 person1.__proto__ === Person.prototype

第二題:
因為 Person.__proto__ === Person的建構函式.prototype
因為 Person的建構函式 === Function
所以 Person.__proto__ === Function.prototype

第三題:
Person.prototype是一個普通物件,我們無需關注它有哪些屬性,只要記住它是一個普通物件。
因為一個普通物件的建構函式 === Object
所以 Person.prototype.__proto__ === Object.prototype

第四題,參照第二題,因為 Person 和 Object 一樣都是建構函式

第五題:
Object.prototype物件也有proto屬性,但它比較特殊,為 null 。因為 null 處於原型鏈的頂端,這個只能記住。
Object.prototype.__proto__ === null

6.函式物件

所有函式物件的proto都指向Function.prototype.

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

// 所有的構造器都來自於Function.prototype,甚至包括根構造器Object及Function自身
Object.__proto__ === Function.prototype  // true
Object.constructor == Function // true

// 所有的構造器都來自於Function.prototype,甚至包括根構造器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

JavaScript中有內建(build-in)構造器/物件共計12個(ES5中新加了JSON),這裡列舉了可訪問的8個構造器。剩下如Global不能直接訪問,Arguments僅在函式呼叫時由JS引擎建立,Math,JSON是以物件形式存在的,無需new。它們的proto是Object.prototype。如下

Math.__proto__ === Object.prototype  // true
Math.construrctor == Object // true

JSON.__proto__ === Object.prototype  // true
JSON.construrctor == Object //true

上面說的函式物件當然包括自定義的。如下

// 函式宣告
function Person() {}
// 函式表示式
var Perosn = function() {}
console.log(Person.__proto__ === Function.prototype) // true
console.log(Man.__proto__ === Function.prototype)    // true

這說明什麼呢?

所有的構造器都來自於 Function.prototype,甚至包括根構造器Object及Function自身。所有構造器都繼承了·Function.prototype·的屬性及方法。如length、call、apply、bind

Function.prototype也是唯一一個typeof XXX.prototype為 function的prototype。其它的構造器的prototype都是一個物件.

console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype)   // object
console.log(typeof Number.prototype)   // object
console.log(typeof Boolean.prototype)  // object
console.log(typeof String.prototype)   // object
console.log(typeof Array.prototype)    // object
console.log(typeof RegExp.prototype)   // object
console.log(typeof Error.prototype)    // object
console.log(typeof Date.prototype)     // object
console.log(typeof Object.prototype)   // object

知道了所有構造器(含內建及自定義)的__proto__都是Function.prototype,那Function.prototype的__proto__是誰呢?
相信都聽說過JavaScript中函式也是一等公民,那從哪能體現呢?如下
console.log(Function.prototype.__proto__ === Object.prototype) // true
這說明所有的構造器也都是一個普通 JS 物件,可以給構造器新增/刪除屬性等。同時它也繼承了Object.prototype上的所有方法:toString、valueOf、hasOwnProperty等。

最後Object.prototype的proto是誰?
Object.prototype.__proto__ === null // true
已經到頂了,為null。

參考文章:
https://segmentfault.com/a/1190000008959943
https://www.jianshu.com/p/dee9f8b14771
https://www.jianshu.com/p/652991a67186
https://www.jianshu.com/p/a4e1e7b6f4f8
https://juejin.im/entry/58f62135a22b9d006c0cee45