1. 程式人生 > 程式設計 >JavaScript 面向物件程式設計詳解【類的建立、例項物件、建構函式、原型等】

JavaScript 面向物件程式設計詳解【類的建立、例項物件、建構函式、原型等】

本文例項講述了JavaScript 面向物件程式設計。分享給大家供大家參考,具體如下:

類的創建於例項物件

工廠模型建立物件

function CreatePerson ( name,sex,age ) {
 
 var obj = new Object();
 
 obj.name = name;
 obj.sex = sex;
 obj.age = age;
 
 obj.sayName = function () {
  
  console.log( this.name );
  
 }
 
 return obj;
 
}

var p1 = CreatePerson('zf','女',22);

p1.sayName(); //zf

console.log( p1.name ); //zf

建構函式式

//函式的第一個字母大寫(類的模板)
function Person ( name,age,sex ) {
 
 this.name = name;
 this.age = age;
 this.sex =sex;
 
 this.sayName = function () {
  
  alert(this.name);
  
 }
 
}

//構造一個物件, 使用new關鍵字, 傳遞引數, 執行模板程式碼, 返回物件。

var p1 = new Person('zf',20,'女'); //類的概念:根據模板創建出不同的例項物件
 
console.log( p1.name );

p1.sayName();

建立類的例項:

  • 當作建構函式去使用

    var p1 = new Person('a1',20);

  • 作為普通函式去呼叫

    Person('a2',20); //在全域性環境中定義屬性並賦值, 直接定義在window上。

  • 在另個一物件的作用域中呼叫

    var o = new Object();
    Person.call(o,'a3',23);

Object每個例項都會具有的屬性和方法:

Constructor: 儲存著用於建立當前物件的函式。(建構函式)
hasOwnProperty(propertyName):用於檢測給定的屬性在當前物件例項中(而不是原型中)是否存在。

isPrototypeOf(Object): 用於檢查傳入的物件是否是另外一個物件的原型。
propertyIsEnumerable(propertyName):用於檢查給定的屬性是否能夠使用for-in語句來列舉。
toLocaleString():返回物件的字串表示。該字串與執行環境的地區對應.
toString():返回物件的字串表示。
valueOf():返回物件的字串、數值或布林表示。

判斷一個物件是不是另一個物件的例項,通常使用的是 instanceof. 比較少使用constructor。

原型

建立每一個函式的時候,都有一個prototype屬性. 這個是屬性,是一個指標。而這個物件總是指向一個物件。
這個物件 的用途就是將特定的屬性和方法包含在內,是一個例項物件, 起到了一個所有例項所共享的作用。
遮蔽了,建構函式的缺點,new 一個物件,就把建構函式內的方法例項化一次。

function Person () {
    
}

var obj = Person.prototype;

console.log( obj ); //Person.prototype 就是一個物件
//Person.prototype 內部存在指標,指標指向一個物件。 這個物件稱之為:原型物件。原型物件,被所有的例項物件所共享。

console.log( obj.constructor ); //function Person(){} //obj這個物件的構造器就是 Person

原型圖例:

JavaScript 面向物件程式設計詳解【類的建立、例項物件、建構函式、原型等】

console.log(Person.prototype) 的結果:

JavaScript 面向物件程式設計詳解【類的建立、例項物件、建構函式、原型等】

常用方法

Object.getPrototypeOf()

根據例項物件獲得原型物件

每次程式碼讀取一個物件的屬性的時候:首先會進行一次搜尋,搜尋例項物件裡,看看是否存在,如果沒有,再去例項所對的原型中尋找屬性.如果有則返回,如果兩次都沒有則返回undefined

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型物件方法' );
 
}

var p1 = new Person();

console.log( p1.name ); //z1

console.log( Object.getPrototypeOf(p1) ); 
console.log( Object.getPrototypeOf(p1) == Person.prototype ); //true

hasOwnProperty()

判斷是否是 例項物件自己的屬性

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型物件方法' );
 
}


// 判斷一個物件屬性 是屬於 原型屬性 還是屬性 例項屬性

var p3 = new Person(); 
console.log( p3.name ); //zf 是原型上的

//hasOwnProperty() 是否是 例項物件自己的屬性 
console.log( p3.hasOwnProperty('name') ); //false 

in 操作符

無論是 原型的屬性, 還是例項物件的屬性, 都區分不開。 如果存在,返回true

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型物件方法' );
 
}


//判斷屬性是否存在 例項物件 和 原型物件中. 

var p1 = new Person();
console.log('name' in p1); //true //表示,name的屬性到底在不在p1的屬性中 true
var p2 = new Person();

p1.name = 'zzz';

console.log('name' in p1); //true

判斷一個屬性是否在原型中

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型物件方法' );
 
}


//判斷屬性是否存在 例項物件 和 原型物件中. 

var p1 = new Person();
p1.name = '123';

//在原型物件中,是否存在這個值
//@obj 當前物件
//@判斷的屬性
function hasPrototypeProtoperty ( obj,attrName ) {
 
 return !obj.hasOwnProperty(attrName) && (attrName in obj);
 
}

console.log( hasPrototypeProtoperty(p1,'name') ); //false

Object.keys()

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型物件方法' );
 
}

//ECMA5新特性 Object.keys(); 
//拿到當前物件中的所有keys, 返回一個數組

var p1 = new Person();
p1.name = 'zz';
p1.age = 20;

var attr = Object.keys(p1);
console.log( attr ); //["name","age"]

var attr2 = Object.keys(p1.__proto__); 

console.log( attr2 ); //["name","age","sayName"]

var attr3 = Object.keys(Person.prototype); 

console.log( attr3 ); //["name","sayName"]

Object.getOwnPropertyNames()

function Person () {
}

Person.prototype.name = 'z1';
Person.prototype.age = 20;

Person.prototype.sayName = function () {
 
 console.log( '我是原型物件方法' );
 
}

var p1 = new Person();
p1.name = 'zz';
p1.age = 20;

//ECMA5 
//constructor屬性,是無法被列舉的. 正常的for-in迴圈是無法列舉. [eable = false];

//Object.getOwnPropertyNames(); //列舉物件所有的屬性:不管該內部屬效能夠被列舉.

var attr4 = Object.getOwnPropertyNames(Person.prototype); //["constructor","name","sayName"]

console.log( attr3 );

isPrototypeOf()

判斷原型的方法

原型物件.isPrototypeOf(new instance);

實現each方法

原型的另外一個作用就是擴充套件物件中的屬性和方法

//遍歷多維陣列
var arr = [1,2,4,5,[455,[456,[345345]]]];

Array.prototype.each = function ( cb ) {

 try {

  //計數器
  this.i || (this.i = 0);

  //核心程式碼
  if ( this.length > 0 && cb.constructor === Function ) {

   while ( this.i < this.length ) { //計數器 大於 陣列長度跳出

    //獲得每一項值 
    var e = this[this.i];

    //判斷是否是 陣列
    if ( e && e.constructor === Array ) {

     //遞迴
     e.each(cb);

    } else {

     cb.call(null,e);

    }

    this.i++; 

   }

   //使用完之後,釋放變數
   this.i = null;

  }

 } catch (e) {
  //do someting
  
 }

 return this;

};

arr.each(function( val ){

 console.log(val);

});

簡單原型

直接通過物件字面量來重寫整個原型物件(這種方法會改變原型物件的構造器[改變為Object])

//簡單原型
   
function Person () {
 
}

Person.prototype = {
 
 constructor: Person,//原型的構造器改變
 
 name: 'zz',age: 20,say: function () {
  console.log( this.age );
 }
 
}

var p1 = new Person();

console.log( p1.name );
p1.say();

存在的問題,constructor屬性是無法被列舉的。加在原型物件上,可以被列舉,被列舉。不符合要求。

ECMA5中的Object.defineProperty()方法可以為原型物件重新加入構造器。constructor問題可以被避免。

//3個引數, 引數1:重新設定構造的物件 (給什麼物件設定)  引數2:設定什麼屬性  引數3:options配置項 (要怎麼去設定)
Object.defineProperty(Person.prototype,'constructor',{
 enumerable: false,//是否是 能夠 被列舉
 value: Person //值 構造器的 引用
});

原型的動態特性

注意原型和建立例項的前後順序

function Person () {
      
}

var p1 = new Person(); // {}
 
Person.prototype = {
 constructor: Person,name: 'zf',say: function () {
  console.log('原型');
 }
}

//先把原型物件寫好,然後再例項化。

//p1.say(); //error 因為  原型物件裡面沒有任何屬性和方法

var p2 = new Person();

p2.say();

//注意 簡單原型使用的順序(例項物件必須在原型物件之後建立)

原型物件的常用開發模式

組合建構函式式和原型模式

function Person( name,firends ) {
 
 this.name = name;
 this.age = age;
 this.firends = firends;
 
}

Person.prototype = {
 constructor: Person,sayName: function () {
  console.log( this.name );
 }
}

var p1 = new Person('zz',['zf']);
var p2 = new Person('zx',22,['z1']);

console.log( p1.firends ); //['zf']    
console.log( p2.firends ); //['z1']

動態原型模式

就是把資訊都封裝到函式中,這樣體現了封裝的概念。

//動態原型模式:(讓你的程式碼 都封裝到一起)
function Person( name,firends ) {
 
 this.name = name;
 this.age = age;
 this.firends = firends;
 
 //動態原型方法
 if ( typeof this.sayName !== 'function' ) {
  
  Person.prototype.sayName = function () {
   console.log(this.name);
  }
  
 }
 
}

穩妥建構函式式

穩妥模式就是沒有公共屬性,而且其他方法也不引用this物件,穩妥模式最適合在安全的環境中使用。如果程式對於安全性要求很高,那麼非常適合這種模式。
也不能使用new關鍵字。

//穩妥建構函式式 durable object (穩妥物件)
//1,沒有公共的屬性
//2,不能使用this物件

function Person ( name,age ) {
 
 //建立一個要返回的物件。 利用工廠模式思維。
 var obj = new Object();
 
 //可以定義一下是有的變數和函式 private
 
 var name = name || 'zf';
 
//      var sex = '女';
//      var sayName = function () {
//      }

  
 //新增一個對外的方法
 obj.sayName = function () {
  console.log(name);
 }
 
 return obj;
 
}

var p1 = Person('xixi',20);

p1.sayName(); 

深入原型繼承的概念

如果讓原型物件等於另一個型別的例項,結果會怎麼樣呢?顯然此時的原型物件將包含一個指向另一個原型的指標,相應的另一個原型中也包含著一個指向另一個建構函式的指標。

原型鏈: 利用原型讓一個引用型別繼承另外一個引用型別的屬性和方法。

建構函式 原型物件 例項物件

  • 建構函式.prototype = 原型物件

  • 原型物件.constructor = 建構函式

  • 例項物件.__proto__ = 原型物件

  • 原型物件.isPrototypeOf(例項物件)

  • 建構函式 例項物件 (類和例項)

isPrototypeOf(); //判斷是否 一個物件的 原型

JavaScript 面向物件程式設計詳解【類的建立、例項物件、建構函式、原型等】

//父類的建構函式 Sup
function Sup ( name ) {
 
 this.name = name;
 
}

//父類的原型物件
Sup.prototype = {
 constructor: Sup,sayName: function () {
  console.log(this.name);
 }
}

//子類的建構函式 Sub
function Sub ( age ) {
 
 this.age = age;
 
}

//如果子類的原型物件 等於 父類的 例項

//1,顯然此時的原型物件將包含一個指向另一個原型的指標
//2,相應的另一個原型中也包含著一個指向另一個建構函式的指標。

//   例項物件.__proto__ = 原型物件
//    Sup的例項物件 和 Sup的原型物件 有一個關係

Sub.prototype = new Sup('zf');

//   console.log( Sub.prototype.constructor ); //function Sup () {}
//   
//   console.log( Sub.prototype.__proto__ ); //Sup 的 原型物件


var sub1 = new Sub(20);

console.log( sub1.name ); //zf
sub1.sayName(); //zf

原型鏈繼承對映圖

JavaScript 面向物件程式設計詳解【類的建立、例項物件、建構函式、原型等】


JavaScript 面向物件程式設計詳解【類的建立、例項物件、建構函式、原型等】

繼承的三種方式

原型繼承

//原型繼承的特點:
//即繼承了父類的模板,又繼承了父類的原型物件。 (全方位的繼承)
//父類
function Person ( name,age ) {
 
 this.name = name;
 this.age = age;
 
}

Person.prototype.id = 10;

//子類
function Boy ( sex ) {
 
 this.sex = sex;  
 
}

//原型繼承
Boy.prototype = new Person('zz');

var b = new Boy();

console.log( b.name ); //zz
console.log( b.id ); //10

類繼承

類繼承 (只繼承模板) 不繼承原型物件 (借用建構函式的方式繼承)

//父類
function Person ( name,age ) {
 
 this.name = name;
 this.age = age;
 
}

Person.prototype.id = 10;

//子類
function Boy ( name,sex ) {
 
 //類繼承
 Person.call(this,name,age);
 
 this.sex = sex;  
 
}

var b = new Boy('zf','女');

console.log( b.name ); //zf
console.log( b.age ); //20
console.log( b.sex ); //女
console.log( b.id ); //父類的原型物件並沒有繼承過來.

 

混合繼承

原型繼承+類繼承

 
 //父類  (關聯父類和子類的關係)
function Person ( name,age ) {
 
 this.name = name;
 this.age = age;
 
}

Person.prototype.id = 10;
Person.prototype.sayName = function () {
 console.log( this.name );
}

//子類
function Boy ( name,sex ) {
 
 //1 類繼承
 Person.call(this,age); //繼承父類的模板
 
 this.sex = sex;  
 
}    

//2 原型繼承
//父類的例項 和 父類的 原型物件的關係.
Boy.prototype = new Person(); //繼承父類的原型物件

var b = new Boy('z1','女');

console.log( b.name );//z1
console.log( b.sex ); //女
console.log( b.id ); //10

b.sayName(); //z1 

ExtJs底層繼承方式

模擬ExtJs底層繼承一部分程式碼

//ExtJs 繼承
//2件事: 繼承了1次父類的模板,繼承了一次父類的原型物件


function Person ( name,age ) {
 
 this.name = name;
 this.age = age;
 
}

Person.prototype = {
 
 constructor: Person,sayHello: function () {

  console.log('hello world!');

 }
 
}

function Boy ( name,sex ) {
 
 //call 繫結父類的模板函式 實現 借用建構函式繼承 只複製了父類的模板
 
//    Person.call(this,age);

  Boy.superClass.constructor.call(this,age);
 
 this.sex = sex;  
 
}

//原型繼承的方式: 即繼承了父類的模板,又繼承了父類的原型物件。
//   Boy.prototype = new Person();
//只繼承 父類的原型物件
 extend(Boy,Person); // 目的 只繼承 父類的原型物件,需要那兩個類產生關聯關係.

//給子類加了一個原型物件的方法。
Boy.prototype.sayHello = function () {
 
 console.log('hi,js');
 
}

var b = new Boy('zf','男');

console.log( b.name );
console.log( b.sex );
b.sayHello(); 

Boy.superClass.sayHello.call(b);

//extend方法

//sub子類, sup 父類
function extend ( sub,sup ) {
 
 //目的,實現只繼承 父類的原型物件。 從原型物件入手
 
 //1,建立一個空函式, 目的:空函式進行中轉
 var F = new Function(); // 用一個空函式進行中轉。
 
//    把父類的模板遮蔽掉, 父類的原型取到。
 
 F.prototype = sup.prototype; //2實現空函式的原型物件 和 超類的原型物件轉換
 
 sub.prototype = new F(); //3原型繼承
 
 //做善後處理。 還原構造器 ,
 sub.prototype.constructor = sub; //4 ,還原子類的構造器
 
//    儲存一下父類的原型物件 // 因為 ①方便解耦, 減低耦合性  ② 可以方便獲得父類的原型物件
 sub.superClass = sup.prototype; //5 ,儲存父類的原型物件。 //自定義一個子類的靜態屬性 , 接受父類的原型物件。
 
 //判斷父類的原型物件的構造器, (防止簡單原型中給更改為 Object)
 if ( sup.prototype.constructor == Object.prototype.constructor ) {
  
  sup.prototype.constructor = sup; //還原父類原型物件的構造器
  
 }
 
}

感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。

更多關於JavaScript相關內容感興趣的讀者可檢視本站專題:《javascript面向物件入門教程》、《JavaScript錯誤與除錯技巧總結》、《JavaScript資料結構與演算法技巧總結》、《JavaScript遍歷演算法與技巧總結》及《JavaScript數學運算用法總結》

希望本文所述對大家JavaScript程式設計有所幫助。