/*本文并非是介绍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类
Posted nDos
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js面向对象设计之function类相关的知识,希望对你有一定的参考价值。
以上是关于js面向对象设计之function类的主要内容,如果未能解决你的问题,请参考以下文章