js面向對象設計之function類
阿新 • • 發佈:2017-12-27
發現 文章 類實例化 執行過程 原型 js面向對象 靜態 java 執行
/*本文並非是介紹JavaScript的原型的原理的文章,
**僅僅討論function作為類來使用時如何近似傳統的面向對象設計
**/
/*function作為類的用法
**下面是一個最簡單的類
**實例將有自己的屬性val和pVal,也有方法printVal和pMethod
**/
function Class01( val, pVal )
{
this.val = val; /*實例可直接讀寫的屬性*/
var pVal = pVal; /*實例無法直接讀寫的屬性*/
}
Class01.prototype.printVal = function ()
{
var _this = this;
/*盡管此處並不會出現this丟失的情況,但推薦總是這麽做*/
console.log( _this.val );
};
/*實例可直接讀寫的屬性和方法不再多說
**對於不可直接讀寫的屬性pVal(這是傳統面向對象語言中的私有屬性)
**需要通過公有接口來訪問pVal,如下面的getpVal和setpVal
**這樣做的弊端十分明顯,每一個實例都將擁有getpVal和setpVal
**是否可以在原型上定義getpVal和setpVal,先來試試
**/
function Class01( val, pVal )
{
this.val = val; /*實例可直接讀寫的屬性*/
var pVal = pVal; /*實例無法直接讀寫的屬性*/
this.getpVal = function ()
{
return pVal;
};
this.setpVal = function ( v )
{
pVal = v;
return this;
}
}
/*下面的例子是通過原型方法讀取“私有”屬性pVal
**試過發現,運行報錯,pVal並不存在
**路子都被堵死了,采用這種方式定義“私有”屬性將只能通過實例方法讀寫
**這種方式顯然不能在工程中使用,下面介紹另外一種方法
**/
function Class01( val, pVal )
{
this.val = val; /*實例可直接讀寫的屬性*/
var pVal = pVal; /*實例無法直接讀寫的屬性*/
}
Class01.prototype.printVal = function ()
{
var _this = this;
/*盡管此處並不會出現this丟失的情況,但推薦總是這麽做*/
console.log( _this.val );
};
Class01.prototype.getpVal = function ()
{
console.log( pVal );
};
var ins01 = new Class01( 1, 2 );
ins01.getpVal(); /*此處報錯*/
/*采用高階函數的方式,return Class的方式
**在Class01的內部只定義可直接讀寫的屬性
**把“私有”屬性或方法放到Class作用域外部
**/
var Class01 = ( function ()
{
var pValue = ‘‘; /*實例無法直接讀寫的屬性*/
function hello ()
{
console.log( ‘歡迎來到nDos的博客‘ );
}
function Class( val, pVal )
{
this.val = val; /*實例可直接讀寫的屬性*/
pValue = pVal;
}
Class.prototype.printVal = function ()
{
var _this = this;
/*盡管此處並不會出現this丟失的情況,但推薦總是這麽做*/
console.log( _this.val );
};
Class.prototype.getpVal = function ()
{
console.log( pValue );
return pValue;
};
Class.prototype.setpVal = function ( v )
{
pValue = v;
return this;
};
Class.prototype.sayHello = function ()
{
hello();
return this;
};
return Class;
} )();
var ins01 = new Class01( 1, 2 );
ins01.getpVal();
ins01.setpVal( ‘2222‘ ).getpVal();
ins01.sayHello();
/*小問題:
**實例ins01是由Class實例化而來
**還是由Class01實例化而來
**讀者可先自行思考
**/
console.log( ins01.constructor.name ); /*此處是Class*/
/*Class01這個變量在ins01中找不到任何的痕跡
**想要搞清楚可能需要到JS引擎中去找
**本文目的顯然並不是要搞清楚這個問題,留給讀者研究或者持續關註本博客
**console.log( ins01 )
**在Google的開發者工具中找到如下的屬性
**Class.__proto__.constructor["[[Scopes]]"]
**Google工具顯示了與Class有關的閉包,在內部可以看到pValue和hello()
**顯然Class在全局作用域中也不存在
**/
/*該類已經實現的內容有
**公有、私有屬性和方法、原型方法
**至於公有方法,實例化之後在定義
**對於工程化來講,私有屬性和方法這麽寫維護會很困難
**可以將私有屬性和方法都放到object中方便維護
**var pV = { pVal:‘‘, hello:()=>console.log(‘ok‘) }
**/
/*類還有靜態方法和靜態屬性
**通過Class.staticVal和Class.staticMethod來定義
**原型上也可以定義屬性,該屬性被所有的實例所共享
**原型上的屬性只能是引用(array和object)時,才能被存儲
**/
var Class01 = ( function ()
{
/*代碼略*/
Class.prototype.nDos = {
name: ‘nDos‘,
sayHello: hello
};
Class.prototype.noChangeVal = ‘實例拿我沒辦法‘;
Class.staticVal = ‘Class的靜態屬性‘;
Class.staticMethod = function ()
{
console.log( ‘Class的靜態方法‘ );
};
return Class;
} )();
var ins01 = new Class01( 1, 2 ),
ins02 = new Class01( ‘a‘, ‘b‘ );
ins01.nDos.name = ‘ins01 say hello nDos‘; /*對於數組也是一樣的*/
console.log( ‘ins02中的name值:‘ + ins02.nDos.name );
try {
ins01.noChangeVal = ‘實例1改變原型屬性值‘;
console.log( ins01.noChangeVal );
console.log( ins02.noChangeVal );
ins01.prototype.noChangeVal = ‘我就是想改變它‘; /*報錯*/
} catch ( e ) {
console.Error( e );
}
/*Class的靜態屬性和方法都可以在Class01上找到
**原型上的屬性通過上述的了解也學習過了
**是時候來一波總結
**並不是總結學習了什麽,而是思考可以用來幹什麽
**以及其中可能存在的缺陷
**/
/*總結
**1、靜態屬性和原型屬性,都可以用來儲存實例化次數
** 同樣也可以用來儲存每個實例的引用
此處建議將實例化次數和對實例的引用都儲存在靜態屬性中
因為原型屬性在編程過程中
並不容易分清楚它究竟是原型屬性還是實例屬性
**2、顯然,function也是可以被當作函數而執行
** 避免這種情況,需要加入防禦性代碼
if ( this.constructor.name !== ‘Class‘ )
{
throw new Error( ‘類只能被實例化‘ );
}
** 就算這麽做了,還是可以繞過去,比如
function fakeClass () {
Class01.call(this, ‘1‘, ‘2‘);
}
fakeClass.prototype = Class01.prototype;
var ins = new fakeClass();
** 當然這也可以算是繼承的一種
**3、為避免2中出現的情況,就是加入以下代碼
if ( !new.target || new.target.name !== ‘Class‘ )
{
throw new Error( ‘類只能被實例化‘ );
}
**4、只有函數才能被實例化
實例化之後得到的變量是實例並不是函數或者說是object
**5、function類實例化過程,實際上是函數體的執行過程
這個執行過程也就是初始化的過程
在這種過程當中可以做相當多的事情
上述的防禦性代碼
實例(this)的傳遞與儲存
使用Object.create給this擴充功能(Mixin)
加入鉤子函數,讓類消費者自行決定如何實例化
**6、function類一般不會有return當然也可以存在
這也是實例化的變化所在
通過不同的鉤子從而決定返回什麽樣的實例
希望下一篇能學懂並講解這種方式
這種實例化方式與多態繼承等也有關系
**/
/*function與類的學習希望對你有幫助
**下篇文章介紹Class(ES2015新語法)與類
**最後附上最終的Class源代碼
**/
var Class01 = ( function ()
{
var pValue = ‘‘; /*實例無法直接讀寫的屬性*/
function hello ()
{
console.log( ‘歡迎來到nDos的博客‘ );
}
function Class( val, pVal )
{
if ( this.constructor.name !== ‘Class‘ )
{
throw new Error( ‘類只能被實例化‘ );
}
if ( !new.target || new.target.name !== ‘Class‘ )
{
throw new Error( ‘類只能被實例化‘ );
}
this.val = val; /*實例可直接讀寫的屬性*/
pValue = pVal;
}
Class.prototype.printVal = function ()
{
var _this = this;
/*盡管此處並不會出現this丟失的情況,但推薦總是這麽做*/
console.log( _this.val );
};
Class.prototype.getpVal = function ()
{
console.log( pValue );
return pValue;
};
Class.prototype.setpVal = function ( v )
{
pValue = v;
return this;
};
Class.prototype.sayHello = function ()
{
hello();
return this;
};
Class.prototype.nDos = {
name: ‘nDos‘,
sayHello: hello
};
Class.prototype.noChangeVal = ‘實例拿我沒辦法‘;
Class.staticVal = ‘Class的靜態屬性‘;
Class.staticMethod = function ()
{
console.log( ‘Class的靜態方法‘ );
};
return Class;
} )();
var ins01 = new Class01( 1, 2 ),
ins02 = new Class01( ‘a‘, ‘b‘ );
ins01.nDos.name = ‘ins01 say hello nDos‘; /*對於數組也是一樣的*/
console.log( ‘ins02中的name值:‘ + ins02.nDos.name );
try {
ins01.noChangeVal = ‘實例1改變原型屬性值‘;
console.log( ins01.noChangeVal );
console.log( ins02.noChangeVal );
/* ins01.prototype.noChangeVal = ‘我就是想改變它‘; 報錯*/
} catch ( e ) {
console.error( e );
}
function fakeClass()
{
Class01.call( this, ‘1‘, ‘2‘ );
}
fakeClass.prototype = Class01.prototype;
var ins = new fakeClass();
js面向對象設計之function類