js面向對象理解

js面向對象理解

ECMAScript 有兩種開發模式:1.函數式(過程化),2.面向對象(OOP)。面向對象的語言有一個標誌,那就是類的概念,而經過類能夠建立任意多個具備相同屬性和方法的對象。可是,ECMAScript 沒有類的概念,所以它的對象也與基於類的語言中的對象有所不一樣。javascript

js(若是沒有做特殊說明,本文中的js僅包含ES5之內的內容)自己是沒有class類型的,可是每一個函數都有一個prototype屬性。prototype指向一個對象,當函數做爲構造函數時,prototype則起到相似class的做用。html

一.建立對象

建立一個對象,而後給這個對象新建屬性和方法。
java

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 建立對象最基本的方法,但有個缺點,想建立多個相似的對象,就會產生大量的代碼。

爲了解決多個相似對象聲明的問題,咱們可使用一種叫作工廠模式的方法,這種方法就是爲了解決實例化對象產生大量重複的問題。
app

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()); //保持獨立

工廠模式解決了重複實例化的問題,可是它有許多問題,建立不一樣對象其中屬性和方法都會重複創建,消耗內存;還有函數識別問題等等。函數

二.構造函數的方法

構造函數的方法有一些規範:this


1)函數名和實例化構造名相同且大寫,(PS:非強制,但這麼寫有助於區分構造函數和
普通函數);
2)經過構造函數建立對象,必須使用new 運算符。spa

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

構造函數能夠建立對象執行的過程:firefox


1)當使用了構造函數,而且new 構造函數(),那麼就後臺執行了new Object();
2)將構造函數的做用域給新對象,(即new Object()建立出的對象),而函數體內的this 就
表明new Object()出來的對象。
3)執行構造函數內的代碼;
4)返回新對象(後臺直接返回)。prototype

注:3d

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__和prototype
JS在建立對象(不管是普通對象仍是函數對象)的時候,都有一個叫作__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 //true
Function.prototype.constructor === Function //true
Object.prototype.constructor === Object //true

3)爲加深對理解,咱們再舉一個例子:

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"));  
print(task2.execute("task2"));
結果:
execute task_1[ACTIVE]:task1
execute 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);
};

//繼承A
function 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,經過原型,造成鏈條。主要經過臨時中轉函數和寄生函數實現。

臨時中轉函數:基於已有的對象建立新對象,同時還沒必要所以建立自定義類型
寄生函數:目的是爲了封裝建立對象的過程

//臨時中轉函數
function obj(o) { //o表示將要傳遞進入的一個對象
function F() {}   //F構造是一個臨時新建的對象,用來存儲傳遞過來的對象
F.prototype = o;  //將o對象實例賦值給F構造的原型對象
return new F();   //最後返回這個獲得傳遞過來對象的對象實例
}
//寄生函數
function create(box, desk) {
var f = obj(box.prototype);
f.constructor = desk;  //調整原型構造指針
desk.prototype = f;
}
function Box(name) {
this.name = name;
this.arr = ['apple','pear','orange'];
}
Box.prototype.run = function () {
return this.name;
};
function Desk(name, age) {
Box.call(this, name);
this.age = age;
}
//經過寄生組合繼承實現繼承
create(Box, Desk); //這句話用來替代Desk.prototype = new Box();
var desk = new Desk('Lee',100);
desk.arr.push('peach');
alert(desk.arr);
alert(desk.run());

臨時中轉函數和寄生函數主要作的工做流程:

臨時中轉函數:返回的是基類的實例對象函數
寄生函數:將返回的基類的實例對象函數的constructor指向派生類,派生類的prototype指向基類的實例對象函數(是一個函數原型),從而實現繼承。

-------------------------------------------------------------------------------------------------------------------------------------

轉載需註明轉載字樣,標註原做者和原博文地址。

更多閱讀:

http://www.108js.com/article/article1/10201.html?id=1092

https://msdn.microsoft.com/zh-cn/library/bb397568.aspx

相關文章
相關標籤/搜索