当前位置: 首页 > news >正文

js面向对象设计之function类

本文仅探讨如何合理的使用 function 在 javascript中实现一个面向对象设计的类。总所周知,javascript 并不能实现一个真正意义上的类,比如 protect 比如 函数重载。
下面开始由浅入深的讨论 function 作为类来使用如何尽可能的模拟传统的面向对象设计。
还有一篇相关博文(关于 class)可对比阅读:js面向对象设计之class类。
下面的 Class01 一个最简单的类。
function Class01( val, pVal ) { this.val = val; /*实例可直接读写的属性*/ var pVal = pVal; /*实例无法直接读写的属性*/ } Class01.prototype.printVal = function () { var _this = this; /*此处并不会出现this丢失,但推荐这么做*/ console.log( _this.val ); };

对于实例无法直接读写的属性,需要提供接口,首先想到的是提供实例方法,如下面这个例子的 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; console.log( _this.val ); }; Class01.prototype.getpVal = function (){ console.log( pVal ); }; var ins01 = new Class01( 1, 2 ); ins01.getpVal(); /*此处报错*/
采用高阶函数,return function的方式,在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; 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 */ console.log( ins01 instanceof Class01 ); /* 此处是 true */ 显然在这里会在类的使用过程中造成疑惑,也会使得项目的实例的归属产生问题。可以把 Class01 直接改为 Class 即可。

小tips:在 Google 开发者工具中找到 Class.__proto__.constructor["[[Scopes]]"]
在这里可以看到闭包、全局作用域等信息。

至此,function 类中已经可以实现实例属性、实例私有属性和方法、原型方法。
实例方法一般不会在类中定义,而是在实例化之后有客户自行添加。
原型上也一般不会定义属性,原因在于原型属性并不能读写(对于数值字符串和布尔值),若可读写会影响所有实例。

小tips:对于私有属性和方法,建议将它们写入到一个对象当中,进行块状管理,在大型项目中代码结构清晰一些。
var pV = { pVal:'', hello:() => console.log('ok') }

传统面向对象设计中类还存在静态方法和静态属性,在 javascript 中也可以轻松实现。
静态方法只能通过类调用,静态属性也只能如此使用。静态属性和方法,类的实例无法获取。
var Class = ( 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 Class( 1, 2 ), ins02 = new Class( '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 ); } 总结:
1、静态属性和原型属性,都可以用来储存实例化次数,同样也可以用来储存每个实例的引用。
此处建议尽量使用静态属性,不使用原型属性的原因在于原型属性容易被实例属性覆盖。
2、显然 function 也可以被当作函数执行,在实际项目中为了防止这种情况发生,需要加入防御性代码:
if ( this.constructor.name !== 'Class' ) {
throw new Error( '类只能被实例化' );
}
就算这么做了,还是可以绕过去:
function fakeClass () {
Class01.call(this, '1', '2');
}
fakeClass.prototype = Class.prototype;
var ins = new fakeClass();
当然这算是继承里边的内容。
3、为避免2中出现的情况,就是加入以下代码:
if ( !new.target || new.target.name !== 'Class' ) {
throw new Error( '类只能被实例化' );
}
4、只有函数才能被实例化,实例化之后得到的变量是实例并不是函数或者说是 object。
5、function 类实例化过程,实际上是函数体的执行过程。这个执行过程也就是初始化的过程。
在这种过程当中可以做相当多的事情,比如 上述的防御性代码、实例(this)的传递与储存、使用 Object.assign 给this扩充功能(Mixin)
以及加入钩子函数,让类消费者自行决定如何实例化等
6、function 类一般不会有 return 当然也可以存在。若没有 return,函数会自动返回 this 做为类的实例。
若存在 return,该类生成的内容便可比较灵活丰富,读者可自行想象,比如通过不同的钩子函数返回不同的内容。

结尾是学习用源代码:
var Class = ( 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 Class( 1, 2 ),
    ins02 = new Class( '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(){
    Class.call( this, '1', '2' );
}
fakeClass.prototype = Class.prototype;
var ins = new fakeClass();

转载于:https://www.cnblogs.com/ndos/p/8127571.html

相关文章:

  • linux链路聚合
  • 无状态服务(stateless service)
  • kubectl delete pod Terminating 删不掉
  • spring zuul Ribbon 配置
  • selenium 代理
  • 在Ubuntu 10.04上安装java 1.5 java 1.6
  • 超级简单:共享两个自动生成存储过程的工具
  • 拍照黑科技上线 用2000万四镜头定格跨年瞬间
  • Easyui layout设置满屏效果
  • 用算法撩妹都不会,别跟我说你是程序员
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 设计模式系列之「责任链模式」
  • 20 个常用的 CSS 技巧
  • 为什么要使用Gradle?
  • Docker CE 安装
  • [deviceone开发]-do_Webview的基本示例
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • Bytom交易说明(账户管理模式)
  • Laravel Telescope:优雅的应用调试工具
  • React的组件模式
  • Transformer-XL: Unleashing the Potential of Attention Models
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • 前端存储 - localStorage
  • 一文看透浏览器架构
  • 责任链模式的两种实现
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • # 飞书APP集成平台-数字化落地
  • #pragma once
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (实战篇)如何缓存数据
  • (原創) 人會胖會瘦,都是自我要求的結果 (日記)
  • (转)Sublime Text3配置Lua运行环境
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • *p++,*(p++),*++p,(*p)++区别?
  • .NET 8 编写 LiteDB vs SQLite 数据库 CRUD 接口性能测试(准备篇)
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .net6Api后台+uniapp导出Excel
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • .net通用权限框架B/S (三)--MODEL层(2)
  • .NET中使用Redis (二)
  • .w文件怎么转成html文件,使用pandoc进行Word与Markdown文件转化
  • .考试倒计时43天!来提分啦!
  • /etc/motd and /etc/issue
  • @Autowired @Resource @Qualifier的区别
  • @for /l %i in (1,1,10) do md %i 批处理自动建立目录
  • @ModelAttribute使用详解
  • []使用 Tortoise SVN 创建 Externals 外部引用目录
  • [100天算法】-目标和(day 79)
  • [Angular] 笔记 6:ngStyle
  • [autojs]autojs开关按钮的简单使用
  • [C#]winform制作仪表盘好用的表盘控件和使用方法
  • [C/C++] C/C++中数字与字符串之间的转换
  • [C++][基础]1_变量、常量和基本类型
  • [CSS]盒子模型
  • [Django 0-1] Core.Email 模块