javascript中的constructor&&prototype

Object.constructor,prototype

對象的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屬性,則哪些預約義的方法就消失了),可是咱們能夠向其中添加本身的擴展方法。

Object.constructor遇到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

Object的經常使用方法

javascript中的一切皆對象,而這些對象的有一個最父層的類就是Object,經常使用的一些屬性方法彙總一下,這些方法在判斷上述問題以及其餘方面頗有用。

Object.constructor //對象的構造函數

Object.hasOwnProperty() //檢查對象屬性是否被繼承

Object.isPrototypeOf() //檢查一個對象是不是另一個對象的原型

Object.propertyIsEnumerable() //是否能夠經過for/in 循環看到屬性

Object.toLocaleString() //返回對象的本地字符串表示

Object.toString() //定義一個對象的字符串表示

Object.valueOf() //制定對象的原始值

最近從圖書館把那邊厚厚的《javascript高級程序設計》借過來了,看看單單事件就能將講那麼厚厚一章,打算搞一個基礎知識系列~~求監督和共勉

相關文章
相關標籤/搜索