js面向对象设计之function类

Posted nDos

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js面向对象设计之function类相关的知识,希望对你有一定的参考价值。


/*本文并非是介绍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类的主要内容,如果未能解决你的问题,请参考以下文章

js面向对象的程序设计

VSCode自定义代码片段——JS中的面向对象编程

VSCode自定义代码片段9——JS中的面向对象编程

js补充之面向对象&&this

js 设计模式 oop 面向对象编程

Class类和function函数的面向对象设计以及用法区别