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( , 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( , 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( , 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( , 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();
js面向对象设计之function类的更多相关文章
- js面向对象设计之class类
class 相对 function 是后出来的,既然 class 出来了,显然是为了解决 function 在处理面向对象设计中的缺陷而来.下面通过对比,来看看 class 作为 ES6 中的重大升级 ...
- js面向对象设计之class继承
EcmaScript 2015 (又称ES6)通过一些新的关键字,使类成为了JS中一个新的一等公民.但是目前为止,这些关于类的新关键字仅仅是建立在旧的原型系统上的语法糖,所以它们并没有带来任何的新特性 ...
- javascript 面向对象设计之 Function 普通类
var test = "Class01"; function Class01(privateValue, publicValue) { var _this = this; if ( ...
- JS面向对象设计-理解对象
不同于其他面向对象语言(OO,Object-Oriented),JS的ECMAScript没有类的概念, 它把对象定义为"无序属性(基本值.对象.函数)的集合",类似于散列表. 每 ...
- JS面向对象设计-创建对象
Object构造函数和对象字面量都可以用来创建单个对象,但是在创建多个对象时,会产生大量重复代码. 1.工厂模式 工厂模式抽象了创建具体对象的过程.由于ECMAScript无法创建类,我们用函数来封装 ...
- js面向对象设计之class中一些坑和技巧
this的指向 super 类工厂,类中定义方法名时,可以使用字符串,这就可以创建工厂函数(类似模板类) Generator 函数 静态属性和私有属性.私有方法 new.target
- 【JavaScript】 JS面向对象的模式与实践 (重点整治原型这个熊孩子 (/= _ =)/~┴┴ )
参考书籍 <JavaScript高级语言程序设计>—— Nicholas C.Zakas <你不知道的JavaScript> —— KYLE SIMPSON 在JS的面向 ...
- 第十五节 JS面向对象实例及高级
实例:面向对象的选项卡 把面向过程的程序,改写成面向对象的形式 原则:不能有函数套函数,但可以有全局变量 过程: onload —— 改写成 构造函数,其中window.onload的功能是在页面加载 ...
- js面向对象编程 ---- 系列教程
原 js面向对象编程:数据的缓存 原 js面向对象编程:如何检测对象类型 原 js面向对象编程:if中可以使用那些作为判断条件呢? 原 js面向对象编程:this到底代表什么?第二篇 原 js面向对象 ...
随机推荐
- linux文件系统底层原理
Linux文件系统中的文件是数据的集合,文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有Linux 用户和程序看到的文件.目录.软连接及文件保护信息等都存储在其中. 底层原理图: 在讲解各个 ...
- ngRoute 与ui.router区别
angular路由 路由 (route) ,几乎所有的 MVC(VM) 框架都应该具有的特性,因为它是前端构建单页面应用 (SPA) 必不可少的组成部分. 那么,对于 angular 而言,它自然也有 ...
- [题解]luogu P4116 Qtree3
终于来到了Qtree3, 其实这是Qtree系列中最简单的一道题,并不需要线段树, 只要树链剖分的一点思想就吼了. 对于树链剖分剖出来的每一根重链,在重链上维护一个Set就好了, 每一个Set里存的都 ...
- cobbler 自定义安装系统
1.自定义安装系统(根据mac地址)--name=定义名称--mac=客户端的mac地址--ip-address=需求的ip--subnet=掩码 --gateway=网关--interface=网口 ...
- #阿里云#云服务器搭建git服务器
前言:大家都知道,git是非常方便的版本控制工具,目前网上有很多免费的git仓库可以给我们使用,但是有些时候我们并不放心将我们的项目寄放在别人的服务器上,这个时候就需要自己搭建一个git服务器,十分的 ...
- LODOP内嵌挡住浏览器的div弹出层
首先,做一个简单的div弹出层用于测试,该弹出层的介绍可查看本博客另一篇博文:[JS新手教程]浏览器弹出div层 然后加入LODOP内嵌,LODOP可以内嵌,C-LODOP不能内嵌,可以在IE等浏览器 ...
- php 常见图片处理函数封装
<?php /** * 常见图像处理函数的封装 */ class Image{ private $info=[]; private $width;//原始图片宽度 private $height ...
- string的七种用法
以下是string的七种用法,注意哦,记得要时常去查看java的API文档,那个里面也有很详细的介绍 1>获取 1.1:字符串中包含的字符数,也就是字符串的长度. int length():获 ...
- SpringMVC3.2+Spring3.2+Mybatis3.1(SSM~Demo)
SpringMVC+Spring+Mybatis 框架搭建 整个Demo的视图结构: JAR: 下载地址:http://download.csdn.net/detail/li1669852599/85 ...
- Liunx安装Git
环境:CentOS 6.5 min 安装git yum -y install gcc curl curl-devel zlib-devel openssl-devel perl cpio expat- ...