對象的prototype和constructor是兩個重要的屬性,他們老是成對出現,提到constructor的地方,不得不涉及到另一個很是重要的屬性prototype,它是js中基於原型繼承的一個基礎。所謂的成對出現,是由於function的prototype屬性指向了一個prototype對象,在prototype對象中又有一個constructor屬性,這個constructor屬性一樣指向一個constructor對象,而這個constructor對象偏偏就是這個function函數自己。javascript
function Person(name) { this.name=name; this.showMe=function() { alert(this.name); } }; var one=new Person('JavaScript'); one.showMe();//JavaScript
不少人見到了久違的new操做符,因而就叫Person爲「類」,但是又沒有關鍵字class的出現,以爲叫「類」有點勉強。因而退而求其次叫Person爲類的構造函數。這些概念好像都沒有錯,之因此出現這樣的狀況,多是由於你們都學習了傳統的面嚮對象語言(c++,c#,java等),還有一種思惟定勢吧。爲了讓javascript也面向對象,要在javascript中找到與傳統面嚮對象語言的影子。但是按照javascript的說法,function定義的這個Person就是一個Object(對象),並且仍是一個很特殊的對象,這個使用function定義的對象與使用new操做符生成的對象之間有一個重要的區別。
這個區別就是function定義的對象有一個prototype屬性,使用new生成的對象就沒有這個prototype屬性。(這個以前沒有注意過這個區別,代碼測試下居然還真是)java
function Person(name) { this.name=name; this.showMe=function() { alert(this.name); } }; var one=new Person('js'); alert(one.prototype)//undefined alert(typeof Person.prototype);//object alert(Person.prototype.constructor);//function Person(name) {...};
one這個對象居然沒有prototype屬性。。。c++
function Person(name) { this.name=name; this.showMe=function() { alert(this.name); } }; Person.prototype.from=function() { alert('I come from prototype.'); } var one=new Person('js'); one.showMe();//js,這個結果正常 one.from();//I come from prototype.,這個結果有一點奇怪
要解釋這個結果就要仔細研究一下new這個操做符了c#
var one=new Person('js');
這個語句執行的過程能夠分紅下面的語句:segmentfault
var one={}; Person.call(one,'js');
按照《悟透javascript》書中說的,new形式建立對象的過程實際上能夠分爲三步:數組
第一步是創建一個新對象(叫A吧);函數
第二步將該對象(A)內置的原型對象設置爲構造函數(就是Person)prototype 屬性引用的那個原型對象;學習
第三步就是將該對象(A)做爲this 參數調用構造函數(就是Person),完成成員設置等初始化工做。測試
其中第二步中出現了一個新名詞就是內置的原型對象__prop__,注意這個新名詞跟prototype對象不是一回事,__prop__(圖中標記的inobj)就指向了函數Person的prototype對象。在person的prototype對象中出現的任何屬性或者函數均可以在one對象中直接使用,這個就是javascript中的原型繼承了。示意圖以下所示:this
每一個函數都有一個默認的prototype屬性。
若是這個函數被用在建立自定義對象的場景中,咱們稱這個函數爲構造函數。 好比下面一個簡單的例子:
// 構造函數 function Person(name) { this.name = name; } // 定義Person的原型,原型中的屬性能夠被自定義對象引用 Person.prototype = { getName: function() { return this.name; } } var zhang = new Person("ZhangSan"); console.log(zhang.getName()); // "ZhangSan"
做爲類比,咱們考慮下JavaScript中的數據類型 - 字符串(String)、數字(Number)、數組(Array)、對象(Object)、日期(Date)等。
咱們有理由相信,在JavaScript內部這些類型都是做爲構造函數來實現的;
同時對數組操做的不少方法(好比concat、join、push)應該也是在prototype屬性中定義的。
實際上,JavaScript全部的固有數據類型都具備只讀的prototype屬性(由於若是修改了這些類型的prototype屬性,則哪些預約義的方法就消失了),可是咱們能夠向其中添加本身的擴展方法。
constructor始終指向建立當前對象的構造函數。
var arr = [1, 56, 34, 12];// 等價於 var foo = new Array(1, 56, 34, 12); console.log(arr.constructor === Array); // true var Foo = function() { }; // 等價於 var foo = new Function(); console.log(Foo.constructor === Function); // true // 由構造函數實例化一個obj對象 var obj = new Foo(); console.log(obj.constructor === Foo); // true // 將上面兩段代碼合起來,就獲得下面的結論 console.log(obj.constructor.constructor === Function); // true
可是當constructor遇到prototype時,有趣的事情就發生了。 這個現象在個人這篇博客裏基於原型建立對象的時候也提到過。連接描述
咱們知道每一個函數都有一個默認的屬性prototype,而這個prototype的constructor默認指向這個函數。以下例所示:
function Person(name) { this.name = name; }; Person.prototype.getName = function() { return this.name; }; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true // 將上兩行代碼合併就獲得以下結果 console.log(p.constructor.prototype.constructor === Person); // true
當時當咱們從新定義函數的prototype時(這裏不是修改而是覆蓋),或者成爲原型重寫,constructor的行爲就有點奇怪了,以下示例:
function Person(name) { this.name = name; }; Person.prototype = { getName: function() { return this.name; } }; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // false console.log(Person.prototype.constructor === Person); // false console.log(p.constructor.prototype.constructor === Person); // false
是由於覆蓋Person.prototype時,等價於進行以下代碼操做:
Person.prototype = new Object({ getName: function() { return this.name; } });
而constructor始終指向建立自身的構造函數,因此此時Person.prototype.constructor === Object,即:
function Person(name) { this.name = name; }; Person.prototype = { getName: function() { return this.name; } }; var p = new Person("ZhangSan"); console.log(p.constructor === Object); // true console.log(Person.prototype.constructor === Object); // true console.log(p.constructor.prototype.constructor === Object); // true
如何修正過來,只須要從新覆蓋Person.prototype.constructor便可:
function Person(name) { this.name = name; }; Person.prototype = new Object({ getName: function() { return this.name; } }); Person.prototype.constructor = Person; var p = new Person("ZhangSan"); console.log(p.constructor === Person); // true console.log(Person.prototype.constructor === Person); // true console.log(p.constructor.prototype.constructor === Person); // true
javascript中的一切皆對象,而這些對象的有一個最父層的類就是Object,經常使用的一些屬性方法彙總一下,這些方法在判斷上述問題以及其餘方面頗有用。
Object.constructor //對象的構造函數 Object.hasOwnProperty() //檢查對象屬性是否被繼承 Object.isPrototypeOf() //檢查一個對象是不是另一個對象的原型 Object.propertyIsEnumerable() //是否能夠經過for/in 循環看到屬性 Object.toLocaleString() //返回對象的本地字符串表示 Object.toString() //定義一個對象的字符串表示 Object.valueOf() //制定對象的原始值
最近從圖書館把那邊厚厚的《javascript高級程序設計》借過來了,看看單單事件就能將講那麼厚厚一章,打算搞一個基礎知識系列~~求監督和共勉