/*本文并非是介绍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类
原文地址:https://www.cnblogs.com/ndos/p/8127571.html