JavaScript中的类

JavaScript实际上是一种弱类型语言,与C++和Java等语言不同。因此,在JavaScript中,没有强调类(class)这一概念,但实际运用中,类还是很重要的,比如写一款游戏,如果我们不停地调用函数来完成创建角色,移动角色的话,那会是什么样的呢?可能会出现非常多的重复代码,因此我们需要一个类来统一这些代码。所谓的类,就是把程序中的代码分类,比如说游戏中的关于角色的代码算作一类,游戏背景算作一类,游戏特效又是一类。这样一来,我们对类进行操作,就不会使代码显得很凌乱,冗杂。虽然Js是弱类型语言,但是也提供了类这一概率。
定义Js中的类,实际上用的是function,总所周知,这个语法其实是用来定义函数的。不用于定义函数的是,我们可以在function中通过this.xxx的方式来定义属性和方法。比如说:

function People () {
this.name = "Yorhom"; this.getName = function () {
return this.name
};
}

使用的时候使用new

var yorhom = new People();
// "Yorhom"
alert(yorhom.getName());

可以看到,这样就可以使用到我们定义的类和类中的方法了。
也许你会问this.xxx只能定义公有属性和方法,那私有属性和方法怎么办呢?这个可以用到js闭包的知识来解决:

function People () {
this.name = "Yorhom"; var age = 16; this.getName = function () {
return this.name
}; this.getAge = function () {
return age;
};
} var yorhom = new People();
// undefined
alert(yorhom.age);
//
alert(yorhom.getAge());

可以看到,这里的age就是一个私有属性了。

JavaScript中的prototype

上面的代码美中不足的地方就是,如果一个类有很多方法,同时用到这个类的地方又有很多(也就是new出来的对象有很多),那么用上面的代码就会出现内存占用过剩的问题。问题的根本原因在于,每次实例化一个对象,这个类就会执行构造器里的代码(以People类为例就是function People () {…}执行的代码),因此每当这个类被实例化的时候,这些方法和属性就会被拷贝到实例化出来的对象中。这样一来,就会造成“吃”内存的现象。
于是js中的prototype就诞生了。prototype的作用通常是给一个类添加一系列常量或者方法。 每当一个类被实例化之后,实例化出来的对象会自动获取类的prototype中定义的方法和属性。只不过这里的获取类似于C++里面的引用,不会在内存里对这些方法和属性进行复制,而是指向这些方法和属性。示例:

function People () {
this.name = "Yorhom";
} People.prototype.getName = function () {
return this.name;
}; var yorhom = new People();
// "Yorhom"
alert(yorhom.getName());

这种方法虽然可以节约内存,但是,美中不足的是,无法定义私有属性。

类的继承

Javascript没有提供继承的函数,所以只有自己写了。这里借用lufylegend.js中的继承方法向大家展示如何实现继承:

function base (d, b, a) {
var p = null, o = d.constructor.prototype, h = {}; for (p in o) {
h[p] = 1;
}
for (p in b.prototype) {
if (!h[p]) {
o[p] = b.prototype[p];
}
} b.apply(d, a);
}

这里的base就是继承函数了。继承函数的原理莫过于复制类的方法和属性。因此,只要做到这点,就可以实现类的继承了。可以在上面的代码中看见,我们通过遍历prototype来获取原型链中定义的方法和属性。通过apply调用父类的构造器进行构造器中属性和方法的复制。使用示例:

function People () {
this.name = "Yorhom";
} People.prototype.getName = function () {
return this.name;
}; function Student () {
base(this, People, []);
} var yorhom = new Student();
// "Yorhom"
alert(yorhom.getName());

静态属性和方法的定义

静态属性和方法以及静态类在js中的定义非常简单,先来看静态类:

var StaticClass = {};

这么写不是在定义一个Object吗?是的,不错,不过js中的静态类也是可以这样定义的。如果要添加静态类中的方法和属性,就可以这么写:

var StaticClass = {
id : 5,
sayHello : function () {
alert("Hello");
}
};

如果是要向类中添加静态属性或者方法,可以采用这种写法:

function People () {
this.name = "Yorhom";
} People.prototype.getName = function () {
return this.name;
}; People.TYPE = "people";
People.sayHello = function () {
alert("Hello");
};

实现一个功能丰富的类

我们在上文中提到了,节省内存和定义私有属性两者无法兼得,是啊,和“鱼和熊掌不可兼得”是一个道理,在通常的使用过程中,我们需要对这两项进行取舍。但是现在这个年代,哪有不可兼得的呢?鱼和熊掌不能同时吃?当然不行……因为吃熊掌是违法的(有待考证)?不过至少鸡和鱼是可以同时吃的吧。
由于js没有实现私有属性的定义,所以这其实是一个没有头绪的工作,因为在标准的做法中,我们除了闭包可以阻止外部访问,没有别的办法了。所以这里我们要用点歪门邪道的方法了。

JavaScript Set/Get访问器

什么是set/get访问器呢?如果你熟悉python,那么你可以理解为@property@xxx.setter,但是简陋的js里也有?当然有,只不过是ES5的标准,可以采用这种写法:

Object.defineProperty(this, "name", {
get : funtion () {
return name;
}, set : function (v) {
name = v;
}
});

具体有什么用呢?大致就是this.name属性在被获取的时候调用get访问器,在被更改值的时候调用set
你可以从上面的代码了解大致的写法,不过如果你想深究,可以参考这篇文章:http://blog.csdn.net/teajs/article/details/22738851

注意以上的这种用法会有兼容性问题,浏览器支持情况如下:

PC端

Firefox Google Chrome Internet Explorer Opera Safari
4.0 5 9 11.6 5.1

移动端

Firefox Mobile Android IE Mobile Opera Mobile Safari Mobile
4.0 Yes 9 11.5 Yes

来自: https://developer.mozilla.org/…/defineProperty#Browser_compatibility

如何“歪门邪道”地做到禁止访问私有和保护属性?

这是个比较头疼的问题,正如本节开篇所说,我们在常规开发下,只能通过闭包来阻止某变量的访问。可是如果你使用了prototype,那么闭包这条路就走不通了。在这种情况下,我们的Object.defineProperty就出场了。我们知道,通过这个函数可以设定获取属性时返回的值,也可以设定更改属性时设置的值。有了这个函数,我们可以随时跟踪到某个属性是不是在被获取,或者是不是在被更改。我们还需要一个开关,我们在类内部的方法调用时,把这个开关打开,表明是在内部运行,方法调用结束后将开关关闭,表明回到外部运行状态。有了这两个状态,我们就可以跟踪privateprotected属性和方法了,一旦他们在开关关闭的时候被使用,就终止这个属性或方法的获取或设置。
于是乎,大难题就快解决了。

开源库件jpp.js

秉着这个歪门邪道的思想,我把这个功能封装到jpp.js这个库件中,库件的github地址如下:
https://github.com/yuehaowang/jpp.js
当然这个库件不限于创建一个类,还可以实现函数的重载等。目前库件还处于开发阶段,欢迎各位提交建议。

使用jpp.js创建一个类

var People = jpp.class({
extends : null,
private : {
id : null,
hobby : null
},
protected : {
money : null,
phoneNumber : null
},
public : {
firstName : null,
lastName : null,
age : null,
birthday : null,
occupation : null, constructor : function (name, id) {
if (name) {
var nameArray = name.split(" "); this.firstName = nameArray[0];
this.lastName = nameArray[1];
} if (id) {
this.id = id;
}
}, setBirthday : function (date) {
if (date) {
this.birthday = date;
}
}, getBirthday : function () {
return this.birthday;
}, askForId : function () {
return this.id;
}, findHobby : function () {
return this.hobby;
}
},
static : {
OCCUPATION_PROGRAMMER : "programmer",
OCCUPATION_ARTIST : "artist",
OCCUPATION_MUSICIAN : "musician",
OCCUPATION_STUDENT : "student"
}
}); var peter = new People("Peter Wong", 543232123565);
peter.occupation = People.OCCUPATION_PROGRAMMER; peter.setBirthday("19980727"); // result: Peter
alert(peter.firstName);
// result: 19990727
alert(peter.getBirthday());
// result: 51092028
alert(peter.askForId());
// result: null
alert(peter.findHobby());
// result: programmer
alert(peter.occupation);
// error
alert(peter.id);

对上面的代码进行分析:
使用jpp.class函数创建一个类,函数的参数是一个Object,这个Object可添加的属性如下:

  • extends 继承时的父类
  • private 装载私有属性,里面定义的成员外部不可使用且不能继承给子类
  • protected 装载保护属性,里面定义的成员外部不可使用但可以继承给子类
  • public 装载公有属性
  • static 装载静态方法和属性

在创建类的过程中,在public中添加constructor方法初始化构造器,this.super可访问父类构造器。

运行代码,可以看到浏览器正常运行前5个alert,而最后一个运行的时候浏览器报错:

具体的实现过程有点复杂,不过原理在上文已经详细讲述了。代码可以在github里参看,欢迎各位研究。

 

JavaScript实现类的private、protected、public、static以及继承的更多相关文章

  1. private,protected,public和default的区别

    private,protected,public和default的区别 除了default以外,其他都是Java语言的关键字.default代表的是对类成员没有进行修饰的情况.它本身也代表了一种访问控 ...

  2. @private@protected@public@package

    @private@protected@public@package 为了强制一个对象隐藏其数据,编译器限制实例变量范围以限制其在程序中的可见性 但是为了提供灵活性,苹果也让开发者显式设置范围(四选一) ...

  3. Java 基础 enum枚举类 的创建/使用/接口继承 ,以及手动创建枚举类的对象为:public static final

    笔记: import java.lang.*; /**一:枚举类 : enum Season implements info { s1(),s2(),s3(),s4() }; //s1--s4 放在S ...

  4. C++类:private、public、friend、protected的区别

           private和public的作用是让编译器帮你检查某些模块是否使用了他没权限使用的模块,也就是生成可执行代码的时候做权限检查.比如,公司里各个部门有自己私有的信息,财务部可以看所有员工 ...

  5. 2.匿名类,匿名类对象,private/protected/public关键字、abstract抽象类,抽象方法、final关键字的使用,多线程Thread类start方法原理

    package com.bawei.multithread; //注意:模板方法我们通常使用抽象类或者抽象方法!这里我们为了方便在本类中使用就没有使用抽象类/抽象方法 public class Tem ...

  6. oc 中四种实例变量的范围类型@private@protected@public@package

    To enforce the ability of an object to hide its data, the compiler limits the scope of instance vari ...

  7. iOS中四种实例变量的范围类型@private@protected@public@package

    文档上记录是这样的 The Scope of Instance Variables Toenforce the ability of an object to hide its data, the c ...

  8. 对private protected public的详解:

    #include <iostream> #include <stack> #include <queue> #include <exception> # ...

  9. java: private, protected, public

    这三个 「可访问修饰符」,是一个老生常谈的话题了.在 C++ 中也有类似的概念. 按其修饰对象的不同,分为几种用法小记一下: 用于类 只有 public 可以修饰类:private 和 protect ...

  10. 17.Java基础_初探类的private和public关键字

    package pack1; public class Student { // 成员变量 private String name; private int age; // get/set方法 pub ...

随机推荐

  1. php大力力 [046节] 兄弟连高洛峰 PHP教程 2015年[最新最新最新最新最新]

    兄弟连高洛峰老师新版PHP视频教程列表[每日更新] http://bbs.lampbrother.net/read-htm-tid-160506.html HTML部分1.[2015]兄弟连高洛峰 H ...

  2. unity5.3.4之no android module loaded

    参考http://www.cnblogs.com/shenggege/p/5165616.html 最近从unity5.1.3升级到5.3.4的时候,发现有个问题: system.io.file' d ...

  3. qt QMetaObject::connectSlotsByName()自动关联失效问题解决

    自己编写qt程序的时候,想使用qt on_objectName_signalName()命名规则自动关联信号和槽,老是发现失效.多方求解,答案事实上很简单就是没有理解objectName的含义. on ...

  4. springmvc工作原理以及源码分析(基于spring3.1.0)

    springmvc是一个基于spring的web框架.本篇文章对它的工作原理以及源码进行深入分析. 一.springmvc请求处理流程 二.springmvc的工作机制 三.springmvc核心源码 ...

  5. HTML5实战——svg学习

    百度百科: SVG可缩放矢量图形(Scalable Vector Graphics)是基于可扩展标记语言(XML),用于描述二维矢量图形的一种图形格式.SVG是W3C制定的一种新的二维矢量图形格式,也 ...

  6. 关于配置服务器(IIS7)(二)

    然 ,你以为这样就解决问题了? 图样 图森破(too yong too simply).......... HTTP 错误 500.21 - Internal Server Error 处理程序“Pa ...

  7. Oracle 10g提权测试

    一直想摸索一下orcl提权的方式,今天测试了一下10g,可以成功提权. C:\wmpub>sqlplus scott/tiger@orcl SQL*Plus: Release 10.2.0.1. ...

  8. redis 集群环境搭建-redis集群管理

    集群架构 (1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽. (2)节点的fail是通过集群中超过半数的节点检测失效时才生效. (3)客户端与redi ...

  9. 20151217JS便签

    JS便签: 根据一个数值来改变Repeater行数的颜色 <script type="text/javascript"> var query = document.ge ...

  10. C#中get和set的写法

    class program{ private string name; public string XXXXXX{ get{return name;} set{name=value;} } } 在C# ...