【前端基礎進階】JS原型、原型鏈、物件詳解
一. 普通物件與函式物件
JavaScript 中,萬物皆物件!但物件也是有區別的。分為普通物件和函式物件,Object 、Function 是 JS 自帶的函式物件。下面舉例說明
var o1 = {}; var o2 =new Object(); var o3 = new f1(); function f1(){}; var f2 = function(){}; var f3 = new Function('str','console.log(str)'); console.log(typeof Object); //function console.log(typeof Function); //function console.log(typeof f1); //function console.log(typeof f2); //function console.log(typeof f3); //function console.log(typeof o1); //object console.log(typeof o2); //object console.log(typeof o3); //object
在上面的例子中 o1 o2 o3 為普通物件,
f1 f2 f3 為函式物件。
怎麼區分,其實很簡單,凡是通過 new Function() 建立的物件都是函式物件,其他的都是普通物件。
f1,f2,歸根結底都是通過 new Function()的方式進行建立的。
Function Object 也都是通過 New Function()建立的。
二. 建構函式
我們先複習一下建構函式的知識:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name) } } var person1 = new Person('Zaxlct', 28, 'Software Engineer'); var person2 = new Person('Mick', 23, 'Doctor');
上面的例子中 person1 和 person2 都是 Person 的例項。這兩個例項都有一個 constructor (建構函式)屬性,該屬性(是一個指標)指向 Person。 即:
console.log(person1.constructor == Person); //true console.log(person2.constructor == Person); //true
我們要記住兩個概念(建構函式,例項):
person1 和 person2 都是 建構函式 Person 的例項
一個公式:
例項的建構函式屬性(constructor)指向建構函式。
三. 原型物件
在 JavaScript 中,每當定義一個物件(函式也是物件)時候,物件中都會包含一些預定義的屬性。其中每個函式物件都有一個prototype 屬性,這個屬性指向函式的原型物件。
function Person() {} Person.prototype.name = 'Zaxlct'; Person.prototype.age= 28; Person.prototype.job= 'Software Engineer'; Person.prototype.sayName = function() { alert(this.name); } var person1 = new Person(); person1.sayName(); // 'Zaxlct' var person2 = new Person(); person2.sayName(); // 'Zaxlct' console.log(person1.sayName == person2.sayName); //true
我們得到了本文第「定律」:
2.每個建構函式(建構函式標準為大寫開頭,如Function(),Object()等等JS中自帶的建構函式,以及自己建立的)都具有一個名為prototype的方法(注意:既然是方法,那麼就是一個物件(JS中函式同樣是物件),所以prototype同樣帶有__proto__屬性); 3.每個物件的__proto__屬性指向自身建構函式的prototype; 4.每個物件都有 、__proto__ 屬性,但只有函式物件才有 prototype 屬性
四. 原型鏈
五、原型鏈
原型物件其實也是普通的物件。幾乎所有的物件都可能是原型物件,也可能是例項物件,而且還可以同時是原型物件與例項物件。這樣的一個物件,正是構成原型鏈的一個節點。因此理解了原型,那麼原型鏈並不是一個多麼複雜的概念。
我們知道所有的函式都有一個叫做toString的方法。那麼這個方法到底是在哪裡的呢?
先隨意宣告一個函式:
function add() {}
那麼我們可以用如下的圖來表示這個函式的原型鏈。
原型鏈
其中add是Function物件的例項。而Function的原型物件同時又是Object原型的例項。這樣就構成了一條原型鏈。原型鏈的訪問,其實跟作用域鏈有很大的相似之處,他們都是一次單向的查詢過程。因此例項物件能夠通過原型鏈,訪問到處於原型鏈上物件的所有屬性與方法。這也是foo最終能夠訪問到處於Object原型物件上的toString方法的原因。
基於原型鏈的特性,我們可以很輕鬆的實現繼承。
具體基本知識瞭解後,我們分析下圖
第一
先從例項add()分析
例項add() 為 Function的例項
所以
add()的__proto__指向其建構函式的原型即 Function.prototype
var add = function () {} add.__proto__ === Function.prototype//true
特別注意 建構函式Funciton的__proto__指向自身的Function.prototype
Function.__proto__ === Function.prototype//true
所以建構函式Function的__proto__和prototype都指向Function.prototype
第二
因為Function和Object都是js自帶函式
且 Object 也是由 new Function建立而來
typeof Function "function" typeof Object "function"
所以 Object的__proto__指向Function的原型物件即 Function.prototype
Object.__proto__ === Function.prototype true
因此 Object 的 prototype 和 Function.prototype的 __proto__都指向 Object.prototype
第三
Object.prototype被稱為原型鏈的E端,因為它的__proto__為null