JavaScript 面向對象程序設計詳解【類的創(chuàng)建、實例對象、構造函數、原型等】
本文實例講述了JavaScript 面向對象程序設計。分享給大家供大家參考,具體如下:
類的創(chuàng)建于實例對象
工廠模型創(chuàng)建對象
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關鍵字, 傳遞參數, 執(zhí)行模板代碼, 返回對象。
var p1 = new Person('zf',20,'女'); //類的概念:根據模板創(chuàng)建出不同的實例對象
console.log( p1.name );
p1.sayName();
創(chuàng)建類的實例:
-
當作構造函數去使用
var p1 = new Person('a1',20);
-
作為普通函數去調用
Person('a2',20); //在全局環(huán)境中定義屬性并賦值, 直接定義在window上。
-
在另個一對象的作用域中調用
var o = new Object();
Person.call(o,'a3',23);
Object每個實例都會具有的屬性和方法:
Constructor: 保存著用于創(chuàng)建當前對象的函數。(構造函數)
hasOwnProperty(propertyName):用于檢測給定的屬性在當前對象實例中(而不是原型中)是否存在。
isPrototypeOf(Object): 用于檢查傳入的對象是否是另外一個對象的原型。
propertyIsEnumerable(propertyName):用于檢查給定的屬性是否能夠使用for-in語句來枚舉。
toLocaleString():返回對象的字符串表示。該字符串與執(zhí)行環(huán)境的地區(qū)對應.
toString():返回對象的字符串表示。
valueOf():返回對象的字符串、數值或布爾表示。
判斷一個對象是不是另一個對象的實例,通常使用的是 instanceof. 比較少使用constructor。
原型
創(chuàng)建每一個函數的時候,都有一個prototype屬性. 這個是屬性,是一個指針。而這個對象總是指向一個對象。
這個對象 的用途就是將特定的屬性和方法包含在內,是一個實例對象, 起到了一個所有實例所共享的作用。
屏蔽了,構造函數的缺點,new 一個對象,就把構造函數內的方法實例化一次。
function Person () {
}
var obj = Person.prototype;
console.log( obj ); //Person.prototype 就是一個對象
//Person.prototype 內部存在指針,指針指向一個對象。 這個對象稱之為:原型對象。原型對象,被所有的實例對象所共享。
console.log( obj.constructor ); //function Person(){} //obj這個對象的構造器就是 Person
原型圖例:

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

常用方法
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 操作符
無論是 原型的屬性, 還是實例對象的屬性, 都區(qū)分不開。 如果存在,返回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", "age", "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循環(huán)是無法枚舉. [eable = false];
//Object.getOwnPropertyNames(); //枚舉對象所有的屬性:不管該內部屬性能夠被枚舉.
var attr4 = Object.getOwnPropertyNames(Person.prototype); //["constructor", "name", "age", "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);
});
簡單原型
直接通過對象字面量來重寫整個原型對象(這種方法會改變原型對象的構造器[改變?yōu)镺bject])
//簡單原型
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 //值 構造器的 引用
});
原型的動態(tài)特性
注意原型和創(chuàng)建實例的前后順序
function Person () {
}
var p1 = new Person(); // {}
Person.prototype = {
constructor: Person,
name: 'zf',
age: 20,
say: function () {
console.log('原型');
}
}
//先把原型對象寫好,然后再實例化。
//p1.say(); //error 因為 原型對象里面沒有任何屬性和方法
var p2 = new Person();
p2.say();
//注意 簡單原型使用的順序(實例對象必須在原型對象之后創(chuàng)建)
原型對象的常用開發(fā)模式
組合構造函數式和原型模式
function Person( name,age,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',20,['zf']);
var p2 = new Person('zx',22,['z1']);
console.log( p1.firends ); //['zf']
console.log( p2.firends ); //['z1']
動態(tài)原型模式
就是把信息都封裝到函數中,這樣體現了封裝的概念。
//動態(tài)原型模式:(讓你的代碼 都封裝到一起)
function Person( name,age,firends ) {
this.name = name;
this.age = age;
this.firends = firends;
//動態(tài)原型方法
if ( typeof this.sayName !== 'function' ) {
Person.prototype.sayName = function () {
console.log(this.name);
}
}
}
穩(wěn)妥構造函數式
穩(wěn)妥模式就是沒有公共屬性,而且其他方法也不引用this對象,穩(wěn)妥模式最適合在安全的環(huán)境中使用。如果程序對于安全性要求很高,那么非常適合這種模式。
也不能使用new關鍵字。
//穩(wěn)妥構造函數式 durable object (穩(wěn)妥對象)
//1,沒有公共的屬性
//2,不能使用this對象
function Person ( name,age ) {
//創(chuàng)建一個要返回的對象。 利用工廠模式思維。
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(); //判斷是否 一個對象的 原型

//父類的構造函數 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
原型鏈繼承映射圖


繼承的三種方式
原型繼承
//原型繼承的特點:
//即繼承了父類的模板,又繼承了父類的原型對象。 (全方位的繼承)
//父類
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,age,sex ) {
//類繼承
Person.call(this,name,age);
this.sex = sex;
}
var b = new Boy('zf',20,'女');
console.log( b.name ); //zf
console.log( b.age ); //20
console.log( b.sex ); //女
console.log( b.id ); //父類的原型對象并沒有繼承過來.
混合繼承
原型繼承+類繼承
//父類 (關聯(lián)父類和子類的關系)
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,age,sex ) {
//1 類繼承
Person.call(this,name,age); //繼承父類的模板
this.sex = sex;
}
//2 原型繼承
//父類的實例 和 父類的 原型對象的關系.
Boy.prototype = new Person(); //繼承父類的原型對象
var b = new Boy('z1',20,'女');
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,age,sex ) {
//call 綁定父類的模板函數 實現 借用構造函數繼承 只復制了父類的模板
// Person.call(this,name,age);
Boy.superClass.constructor.call(this,name,age);
this.sex = sex;
}
//原型繼承的方式: 即繼承了父類的模板,又繼承了父類的原型對象。
// Boy.prototype = new Person();
//只繼承 父類的原型對象
extend(Boy,Person); // 目的 只繼承 父類的原型對象 , 需要那兩個類產生關聯(lián)關系.
//給子類加了一個原型對象的方法。
Boy.prototype.sayHello = function () {
console.log('hi,js');
}
var b = new Boy('zf',20,'男');
console.log( b.name );
console.log( b.sex );
b.sayHello();
Boy.superClass.sayHello.call(b);
//extend方法
//sub子類, sup 父類
function extend ( sub,sup ) {
//目的, 實現只繼承 父類的原型對象。 從原型對象入手
//1,創(chuàng)建一個空函數, 目的:空函數進行中轉
var F = new Function(); // 用一個空函數進行中轉。
// 把父類的模板屏蔽掉, 父類的原型取到。
F.prototype = sup.prototype; //2實現空函數的原型對象 和 超類的原型對象轉換
sub.prototype = new F(); //3原型繼承
//做善后處理。 還原構造器 ,
sub.prototype.constructor = sub; //4 ,還原子類的構造器
// 保存一下父類的原型對象 // 因為 ①方便解耦, 減低耦合性 ② 可以方便獲得父類的原型對象
sub.superClass = sup.prototype; //5 ,保存父類的原型對象。 //自定義一個子類的靜態(tài)屬性 , 接受父類的原型對象。
//判斷父類的原型對象的構造器, (防止簡單原型中給更改為 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程序設計有所幫助。

