對象分普通對象和函數對象,Object
、Function
是js自帶的函數對象。
function f1(){};var f2 = function(){};var f3 = new Function('str','console.log(str)');var o3 = new f1();var o1 = {};var o2 = new Object();console.log(typeof Object); //functionconsole.log(typeof Function); //functionconsole.log(typeof o1); //objectconsole.log(typeof o2); //objectconsole.log(typeof o3); //objectconsole.log(typeof f1); //functionconsole.log(typeof f2); //functionconsole.log(typeof f3); //function
以上例子說明:凡是通過 new Function()
創建的對象都是函數對象,其他的都是普通對象。 Function
、Object
也都是通過new Function()
創建的。
js中所有的__函數__都有一個prototype
屬性,這個屬性引用了一個對象,即原型對象,也簡稱原型。
這個_函數_包括構造函數和普通函數,我們講的更多是構造函數的原型,但是也不能否定普通函數也有原型。
註:普通對象沒有prototype
屬性,但有內置屬性:__proto__
(可以在chrome中查看該屬性,但不要依賴使用此屬性)。
譬如普通函數:
function f(){ //...}alert(f.prototype instanceof Object) //true
構造函數,也叫構造對象。首先瞭解下通過構造函數實例化對象的過程。
function A(x){ this.x=x;}var a = new A(1);
new
也就是實例化對象,過程有如下幾步:
o
;var o = {};
o
內部的__proto__
對象的引用指向o
的構造函數A
的原型對象(A.prototype
)。o
作為this
去調用構造函數A,從而設置o
的屬性和方法並初始化。傳送門:模擬new操作符的內部處理過程
當這3步完成,這個o
對象就與構造函數A
再無聯繫,這個時候即使構造函數A
再加任何成員,都不再影響已經實例化的o
對象了。
此時,o
對象具有了x
屬性,同時具有了構造函數A
的原型對象的所有成員,當然,此時該原型對象是沒有成員的。
原型對象初始是空的,也就是沒有一個成員(即原型屬性和原型方法)。可以通過如下方法驗證原型對象具有多少成員。
function A(name){ this.name = name;}var num = 0;A.prototype.say = function(){ alert("Hi")};for(i in A.prototype){ console.log(i); //say num++;}console.log("member: " + num); // 1
解釋了什麼是原型對象,我們再來看看它的作用是什麼,先看一段代碼:
function Person(name){ this.name = name;}Person.prototype.getName = function(){ return this.name;}var o = new Person('chan');o.getName(); // chan
可以看到,通過給Person.prototype
設置一個方法getName
,實例後的對象o
也會繼承這個方法。具體怎麼實現的繼承,需講到下面的原型鏈。
js在創建對象(無論是普通對象還是函數對象)的時候,都有一個叫做__proto__
的內置屬性,用於指向創建它的函數對象的原型對象prototype
。
原型鏈的基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。
簡單回顧下構造函數、原型和實例的關係:
每個構造函數都有一個原型對象,原型對象包含一個指向構造函數的指針(prototype
),而實例則包含一個指向原型對象的內部指針(__proto__
)。
以下面的例子為例:
function Person(name){ this.name = name;}Person.prototype.getName = function(){ return this.name;}var o = new Person('chan');o.getName(); // chanconsole.log(o.__proto__ === Person.prototype) //true
Person.prototype
對象也有__proto__
屬性,它指向創建它的函數對象(Object
)的 prototype
:
console.log(Person.prototype.__proto__ === Object.prototype) //true
繼續,Object.prototype
對象也有__proto__
屬性,但它比較特殊,為null
console.log(Object.prototype.__proto__) //null
原型鏈如下圖:
Object.__proto__ === Function.prototype // true Object是函數對象,是通過new Function()創建,所以Object.__proto__指向Function.prototype。Function.__proto__ === Function.prototype // true Function 也是對象函數,也是通過new Function()創建,所以Function.__proto__指向Function.prototype。Function.prototype.__proto__ === Object.prototype //true Function.prototype是個函數對象,理論上他的__proto__應該指向 Function.prototype,就是他自己,自己指向自己,沒有意義。
js一切皆為對象,原型鏈的最頂層為null,即Object.prototype.__proto__ === null
//demo[1,2] instanceof Array //truenew Object() instanceof Array //false[1,2] instanceof Object //true
左側一般是一個對象,右側一定是個函數對象,不是函數對象會報錯。
原理:右側函數的prototype
屬性是否出現在左側對象的原型鏈上。即:左側的原型鏈上是否有右側的原型。
原型對象prototype
中都有預定義的constructor
,用來指向它的構造函數。
o.prototype.constructor === Person //trueFunction.prototype.constructor === Function //trueObject.prototype.constructor === Object //true
因為ECMAscript的發明者為了簡化這門語言,同時又保持繼承性,採用了鏈式繼承的方法。
由構造函數創建的每個實例都有個__proto__
屬性,它指向構造函數的prototype
。那麼構造函數的prototype
上定義的屬性和方法都會被instance所繼承.
function Person(){ //...}function Student(){ //...}Student.prototype = Person.prototype; //不可以這樣繼承,改變Student的同時,也會改變Person,因為他們是引用Student.prototype = new Person(); //可以實現 new Person的時候得到了Person的實例,並且Person的實例指向了Person.prototype 並且調用了構造函數。不過因為調用了構造函數,在Person有參數時此方法不太好使//僅ES5+支持Student.prototype = Object.create(Person.prototype); //Object.create的作用:創建以個空對象,並且這個空對象的原型指向傳入的參數,即Person.prototypeStudent.prototype.constructor = Person;if(!Object.create){ //proto 為原型對象 Object.create = function(proto){ function F(){}; F.prototype = proto; return new F; }}
參考自:
联系客服