js物件導向深入理解

來源:互聯網
上載者:User

js物件導向深入理解
ECMAScript 有兩種開發模式:1.函數式(過程化),2.物件導向(OOP)。物件導向的語言有一個標誌,那就是類的概念,而通過類可以建立任意多個具有相同屬性和方法的對象。但是,ECMAScript 沒有類的概念,因此它的對象也與基於類的語言中的對象有所不同。 一.建立對象建立一個對象,然後給這個對象建立屬性和方法。  

var box = new Object(); //建立一個Object 對象box.name = 'Lee'; //建立一個name 屬性並賦值box.age = 100; //建立一個age 屬性並賦值box.run = function () { //建立一個run()方法並傳回值return this.name + this.age + '運行中...';};alert(box.run()); //輸出屬性和方法的值

 

 上面建立了一個對象,並且建立屬性和方法,在run()方法裡的this,就是代表box 對象本身。這種是JavaScript 建立對象最基本的方法,但有個缺點,想建立多個類似的對象,就會產生大量的代碼。為瞭解決多個類似對象聲明的問題,我們可以使用一種叫做原廠模式的方法,這種方法就是為瞭解決執行個體化對象產生大量重複的問題。  
function createObject(name, age) { //集中執行個體化的函數var obj = new Object();obj.name = name;obj.age = age;obj.run = function () {return this.name + this.age + '運行中...';};return obj;}var box1 = createObject('Lee', 100); //第一個執行個體var box2 = createObject('Jack', 200); //第二個執行個體alert(box1.run());alert(box2.run()); //保持獨立

 

  原廠模式解決了重複執行個體化的問題,但是它有許多問題,建立不同對象其中屬性和方法都會重複建立,消耗記憶體;還有函數識別問題等等。 二.建構函式的方法建構函式的方法有一些規範:  1)函數名和執行個體化構造名相同且大寫,(PS:非強制,但這麼寫有助於區分建構函式和普通函數);2)通過建構函式建立對象,必須使用new 運算子。  
function Box(name, age) { //建構函式模式this.name = name;this.age = age;this.run = function () {return this.name + this.age + '運行中...';};}var box1 = new Box('Lee', 100); //new Box()即可var box2 = new Box('Jack', 200);alert(box1.run());alert(box1 instanceof Box); //很清晰的識別他從屬於Box

 

建構函式可以建立對象執行的過程:  1)當使用了建構函式,並且new 建構函式(),那麼就後台執行了new Object();2)將建構函式的範圍給新對象,(即new Object()建立出的對象),而函數體內的this 就代表new Object()出來的對象。3)執行建構函式內的代碼;4)返回新對象(後台直接返回)。 註: 1)建構函式和普通函數的唯一區別,就是他們調用的方式不同。只不過,建構函式也是函數,必須用new 運算子來調用,否則就是普通函數。 2)this就是代表當前範圍對象的引用。如果在全域範圍this 就代表window 對象,如果在建構函式體內,就代表當前的建構函式所聲明的對象。 這種方法解決了函數識別問題,但消耗記憶體問題沒有解決。同時又帶來了一個新的問題,全域中的this 在對象調用的時候是Box 本身,而當作普通函數調用的時候,this 又代表window。即this範圍的問題。 三.原型我們建立的每個函數都有一個prototype(原型)屬性,這個屬性是一個對象,它的用途是包含可以由特定類型的所有執行個體共用的屬性和方法。邏輯上可以這麼理解:prototype 通過調用建構函式而建立的那個對象的原型對象。使用原型的好處可以讓所有對象執行個體共用它所包含的屬性和方法。也就是說,不必在建構函式中定義對象資訊,而是可以直接將這些資訊添加到原型中。  
function Box() {} //聲明一個建構函式Box.prototype.name = 'Lee'; //在原型裡添加屬性Box.prototype.age = 100;Box.prototype.run = function () { //在原型裡添加方法return this.name + this.age + '運行中...';};

 

 建構函式的聲明方式和原型模式的聲明方式儲存情況如下: image 所以,它解決了消耗記憶體問題。當然它也可以解決this範圍等問題。 我們經常把屬性(一些在執行個體化對象時屬性值改變的),定義在建構函式內;把公用的方法添加在原型上面,也就是混合方式構造對象(構造方法+原型方式):  
var person = function(name){   this.name = name  };  person.prototype.getName = function(){     return this.name;   }  var zjh = new person(‘zhangjiahao’);  zjh.getName(); //zhangjiahao

 

 下面詳細介紹原型: 1.原型對象  每個javascript對象都有一個原型對象,這個對象在不同的解譯器下的實現不同。比如在firefox下,每個對象都有一個隱藏的__proto__屬性,這個屬性就是“原型對象”的引用。 2.原型鏈  由於原型對象本身也是對象,根據上邊的定義,它也有自己的原型,而它自己的原型對象又可以有自己的原型,這樣就組成了一條鏈,這個就是原型鏈,JavaScritp引擎在訪問對象的屬性時,如果在對象本身中沒有找到,則會去原型鏈中尋找,如果找到,直接傳回值,如果整個鏈都遍曆且沒有找到屬性,則返回undefined.原型鏈一般實現為一個鏈表,這樣就可以按照一定的順序來尋找。 1)__proto__和prototypeJS在建立對象(不論是普通對象還是函數對象)的時候,都有一個叫做__proto__的內建屬性,用於指向建立它的函數對象的原型對象prototype。以上面的例子為例: console.log(zjh.__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我們把這個有__proto__串起來的直到Object.prototype.__proto__為null的鏈叫做原型鏈。如: 2dbd5870d84a471896d69f7d1980ae63 2)constructor  原型對象prototype中都有個預定義的constructor屬性,用來引用它的函數對象。這是一種循環參考  person.prototype.constructor === person //trueFunction.prototype.constructor === Function //trueObject.prototype.constructor === Object //true3)為加深對理解,我們再舉一個例子:  
function Task(id){      this.id = id;  }      Task.prototype.status = "STOPPED";  Task.prototype.execute = function(args){      return "execute task_"+this.id+"["+this.status+"]:"+args;  }      var task1 = new Task(1);  var task2 = new Task(2);      task1.status = "ACTIVE";  task2.status = "STARTING";      print(task1.execute("task1"));  

 

  結果:execute task_1[ACTIVE]:task1execute task_2[STARTING]:task2構造器會自動為task1,task2兩個對象設定原型對象Task.prototype,這個對象被Task(在此最為構造器)的prototype屬性引用,參看中的箭頭指向。 image 由於Task本身仍舊是函數,因此其”__proto__”屬性為Function.prototype, 而內建的函數原型對象的”__proto__”屬性則為Object.prototype對象。最後Obejct.prototype的”__proto__”值為null。 總結: 執行個體對象的__proto__指向,其建構函式的原型;建構函式原型的constructor指向對應的建構函式。建構函式的prototype獲得建構函式的原型。 有時某種原因constructor指向有問題,可以通過 constructor:建構函式名;//constructor : Task重新指向。 四.繼承繼承是物件導向中一個比較核心的概念。其他正統物件導向語言都會用兩種方式實現繼承:一個是介面實現,一個是繼承。而ECMAScript 只支援繼承,不支援介面實現,而實現繼承的方式依靠原型鏈完成。 在JavaScript 裡,被繼承的函數稱為超類型(父類,基類也行,其他語言叫法),繼承的函數稱為子類型(子類,衍生類別) 1.call+遍曆屬性使用對象冒充(call)(實質上是改變了this指標的指向)繼承基類,方法用遍曆基類原型。 
function A(){    this.abc=12;}A.prototype.show=function (){    alert(this.abc);};//繼承Afunction B(){    //繼承屬性;this->new B()    A.call(this);   //有參數可以傳參數A.call(this,name,age)}//繼承方法;B.prototype=A.prototype;for(var i in A.prototype){    B.prototype[i]=A.prototype[i];}//添加自己的方法B.prototype.fn=function (){    alert('abc');};var objB=new B();var objA=new A();objB.show();

 

 可以實現多繼承。 2.寄生組合繼承主要是Desk.prototype = new Box(); Desk 繼承了Box,通過原型,形成鏈條。主要通過臨時中轉函數和寄生函數實現。 臨時中轉函數:基於已有的對象建立新對象,同時還不必因此建立自訂類型寄生函數:目的是為了封裝建立對象的過程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.