天天看點

js構造函數全面解析(面向對象程式設計基礎)

面向對象程式設計(OOP)

面向對象程式設計(Object Oriented Programming,OOP,面向對象程式設計)是一種計算機程式設計架構。

OOP 的一條基本原則是計算機程式是由單個能夠起到子程式作用的單元或對象組合而成。

OOP達到了軟體工程的三個主要目标:重用性、靈活性和擴充性。為了實作整體運算,每個對象都能夠接收資訊、處理資料和向其它對象發送資訊。

面向對象與面向過程的區分

面向對象是注重于對象自身(誰去做的)

面向過程是注重于函數過程(做了什麼)

構造函數方式

所謂”構造函數”,其實就是一個普通函數,但是内部使用了this變量。對構造函數使用new運算符,就能生成執行個體,并且this變量會綁定在執行個體對象上。

例如:

var Storages = function () {
        this.version = 'Lsy storage-group 1.0.1';
    };
    var sto = new Storages();
    console.log(sto.version); //Lsy storage-group 1.0.1
           

prototype 原型介紹

簡單來說就是:

prototype是javascript中的函數(function)的一個保留屬性,并且它的值是一個對象(我們可以稱這個對象為”prototype對象”)。

Prototype模式

Javascript規定,每一個構造函數都有一個prototype屬性,指向另一個對象。這個對象的所有屬性和方法,都會被構造函數的執行個體繼承。

…… 此處省略理論知識一萬字。

來幾個案例才是王道:

例一:初識構造函數

//通常把Storage的集合叫做類class
  var Storage = function () {
       //私有屬性
       var author = 'loushengyue'; 
       //私有方法(函數表達式)
       var getAuthor = function () {
           return author;
       };
       getAuthor(); //調用私有函數表達式
       showAuthor(); //調用私有方法
       //私有方法
       function showAuthor() {
           console.log(author);
       }
       //共有屬性
       this.version = '1.0.1';
       //特權方法
       this.create = function () {
           console.log('create fn');
           this.other(); //可以調用共有方法
           showAuthor(); //可以調用私有方法
       }
   };
   //共有方法
   Storage.prototype.other = function () {
       console.log('other fn');
   };
   //共有屬性
   Storage.prototype.firstName = 'lou';
   //靜态方法
   Storage.init = function () {
       new this();
   };
   //靜态屬性
   Storage.createTime = '20180204';
           

例二:初識構造函數原型

var Storages = function () {
        this.version = 'Lsy storage-group 1.0.1';
    };
    Storages.prototype.set = function () {
        console.log('set function');
    };
    Storages.prototype.get = function () {
        console.log('get function');
    };
    //構造函數的顯示原型指向set函數和get函數(記住構造函數的原型屬性可以是字元串,數組,對象;不過通常是函數居多)
    console.log(Storages.prototype);
    //構造函數的原型屬性constructor指向構造函數本身
    console.log(Storages.prototype.constructor);
    //構造函數的隐式原型執行構造函數Function ()
    console.log(Storages.__proto__.constructor);
           

例三:初識執行個體對象

var Storages = function () {
        this.version = 'Lsy storage-group 1.0.1';
    };
    Storages.prototype.set = function () {
        console.log('set function');
    };
    Storages.prototype.get = function () {
        console.log('get function');
    };
    var sto = new Storages(); //執行個體化構造函數Storages(),并指派給變量sto
    //通過執行個體化得到的sto對象具有構造函數一切共有屬性及原型屬性
    console.log(sto.version); //Lsy storage-group 1.0.1
    sto.set(); //具有構造函數Storages()的set方法
    sto.get(); //具有構造函數Storages()的get方法
           

例四:初識執行個體對象原型與構造函數原型之間的關系

var Storages = function () {
        this.version = 'Lsy storage-group 1.0.1';
    };
    Storages.prototype.set = function () {
        console.log('set function');
    };
    var sto = new Storages(); //執行個體化構造函數Storages(),并指派給變量sto
    //因為prototype不是一個構造函數,是以他的顯示原型是undefined
    console.log(sto.prototype); //undefined
    //執行個體對象sto的隐式原型是構造函數Storages()的顯示原型
    console.log(sto.__proto__ === Storages.prototype); // true
    //同樣的執行個體對象sto的隐式原型的構造函數===構造函數Storages()本身
    console.log(sto.__proto__.constructor === Storages); // true
    //另外,由例二可知“構造函數的原型屬性constructor指向構造函數本身”
    console.log(sto.__proto__.constructor === Storages.prototype.constructor); // true
           

例五:構造函數多次執行個體化

var Storages = function () {
        this.version = 'version: 1.0.1';
    };
    Storages.prototype.set = function () {
        console.log('set function');
    };
    var sto1 = new Storages();
    var sto2 = new Storages();
    var sto3 = new Storages();
    //因為執行個體對象繼承了構造函數的所有共有資訊,是以以下結果都是相同的
    console.log(sto1.version); //version: 1.0.1
    console.log(sto2.version); //version: 1.0.1
    console.log(sto3.version); //version: 1.0.1
    sto1.set();// set function
    sto2.set();// set function
    sto3.set();// set function
           

例六:執行個體對象自定義屬性與構造函數屬性

var Storages = function () {
        this.version = 'version: 1.0.1';
    };
    Storages.prototype.set = function () {
        console.log('set function');
    };
    var sto = new Storages();
    sto.pluginName = 'Lsy storage';
    sto.name = 'loushengyue';
    //執行個體對象跟普通對象一樣,可以定義自己的屬性
    console.log(sto.pluginName); //Lsy storage
    //給執行個體對象定義的屬性并不會被回報給構造函數,是以構造函數沒有pluginName屬性
    console.log(Storages.pluginName); //undefined
    console.log(sto.name); //loushengyue
    //不得不提的是: 構造函數自帶一個name屬性,并且指向構造函數名稱
    console.log(Storages.name); //Storages
           

例七:執行個體對象讀取屬性的先後順序

var Storages = function () {
        this.version = 'version: 1.0.1';
        this.type = 'javascript';
    };
    Storages.prototype.version = 'prototype version';
    Storages.prototype.type = 'prototype javascript';
    Storages.prototype.str = 'prototype string';

    var sto = new Storages();
    sto.version = 'sto version: 10.0.1';
    console.log(sto.version); //sto version: 10.0.1
    console.log(sto.type); //javascript
    console.log(sto.str); //prototype string
    console.log(sto.icon); //undefined
           

通過上面的例子,很顯然得到這樣的結論:

執行個體對象優先讀取自身定義的屬性,然後讀取構造函數共有屬性,其次讀取構造函數原型屬性,……(這裡其實還有讀取構造函數的隐式原型鍊上的屬性)……,如果都讀取不到結果才為“

undefined

例八:執行個體對象調用方法的先後順序

var Storages = function () {
        this.test = function () {console.log('constructor testFn');};
        this.set = function () {console.log('constructor setFn');}
    };
    Storages.prototype.test = function () {console.log('prototype testFn');};
    Storages.prototype.set = function () {console.log('prototype setFn');};
    Storages.prototype.get = function () {console.log('prototype getFn');};
    var sto = new Storages();
    sto.test = function () {console.log('sto testFn');};
    sto.test(); //sto testFn
    sto.set(); //constructor setFn
    sto.get(); //constructor getFn
    sto.toString(); //
    sto.hehe(); //error:sto.hehe is not a function
           

執行個體對象調用方法跟讀取屬性一樣,都是同樣的順序。

不過,這裡與例七不同的是

sto.toString()

沒有任何結果,也沒有報錯(原因:

toString()

是執行個體對象sto的構造函數的隐式原型的構造函數的屬性方法,即構造函數

Function()

的屬性方法)。

hehe()

表明在執行個體對象的所有被繼承對象中都未找到該方法,故而報錯。

例九:this屬性繼承

//父類
    var Storage = function () {
        var author = 'loushengyue'; //私有屬性不支援繼承
        this.version = '1.0.2';
        this.create = function () {
            console.log('create fn');
        };
        function showAuthor() { //私有方法不支援繼承
            console.log(author);
        }
    };
    //原型上的方法需要采用其他方式繼承
    Storage.prototype.getAuthor = function () {
        console.log('get author fn');
    };
    //子類
    var LsyStorage = function () {
        this.son = 'LsyStorage plugin';
        //apply與call用法類似,隻是有傳遞參數的差別
        Storage.apply(this); //将繼承父類Storage所有this屬性與方法(不包括原型prototype)
        // Storage.call(this);//将繼承父類Storage所有this屬性與方法(不包括原型prototype)
    };
           

例十:類的完整繼承

//父類
    var Parent = function () {
        //handle
    };
    Parent.prototype.test = function () {
        //handle
    };
    //子類
    var Child = function () {
        Parent.call(this); //繼承Parent的this屬性與方法(不包括原型)
    };
    Child.prototype = Object.create(Parent.prototype); //繼承Parent的顯示原型
    Child.prototype.constructor = Child; //恢複Child的constructor指向本身
           

歡迎點贊,謝謝關注:)

繼續閱讀