JavaScript 是基于原型的语言,在es6 之前并没有包含内置类的实现。但是这并不意味着类在JavaScript中毫无必要 。实际上类是一种很有用的工具,像其他编程语言中一样,类在JavaScript中同样可以起到重要的作用 。
下面我们手动封装一个类库,实现一个简单的类的创建。
预期实现的功能:
1. 可以对新创建出来的类进行初始化
2. 可以扩展类的静态属性与方法
3. 可以扩展类的实例的属性与方法
4. 在创建类时可以继承其他类的属性与方法。
代码实现:
varClass=function (parent) {
// 初始化类库
var _class =function () {
this.init.apply(this,arguments); //将原型init方法上的属性同步到当前类
}
_class.prototype.init=function () { }
// 动态扩展
// 定义别名
_class.fn=_class.prototype;
_class.fn.parent=_class;
// 给类添加属性和方法
_class.extend=function (obj) {
var extended = obj.extended;
for(var i in obj)
{
_class[i] = obj[i]
}
if(extended) extended(_class); //回调,用于类自身扩展成功后的提示
}
// 给类的实例添加属性和方法
_class.include=function (obj) {
var included = obj.included;
for(var i in obj)
{
_class.fn[i]=obj[i];
}
if(included) included(_class);// 回调,用于类实例扩展后的提示
}
// 给类添加继承
if(parent)
{
var subclass=function () { };
subclass.prototype =parent.prototype;
_class.prototype = new subclass;
}
return _class;
}
// 使用“类”库
var Cat = new Class();
Cat.prototype.init=function(name,color) { // 对类进行初始化,修改为带有参数的类
this.name = name;
this.color = color;
this.like = function () {
return "fish";
}
}
var cat = new Cat("mini","yellow");
console.log( cat.name + " is"+ cat.color + " it like "+ cat.like());
var Animal = new Class();
Animal.prototype.init=function () {
this.feed = "nurse";// 动物的喂养方式是哺乳
}
Animal.extend({ // 扩展类的静态属性和方法
extended:function (_class){ //设置回调函数,用于扩展成功后的显示
console.log(_class+"is extended");
},
ClassName:"Animal"
})
Animal.include({ // 扩展类实例的属性和方法
walk:"crawl",
birth:function () {
console.log("胎生哺乳");
}})
console.log(Animal.ClassName);
var Dog = new Class(Animal);
var dog = new Dog();
console.log(dog.feed);
console.log(dog.walk);
在开始分析上边的代码之前先和大家讨论一下原型的概念,关于原型有几个重要的点是我们需要了解的:
1. 我们将原型也称为“模板对象”,它上边的属性用来初始化一个新对象 。
2. 任何对象都可以作为另一个对象的原型对象,以此来实现属性共享 。
3. 我们也可以将原型理解为某种形式的继承。
代码详解:
首先因为我们的最终目的是想通过 Class函数 ,返回给我们一个构造函数 用于表示我们新创建的类 。所以只需要在Class 函数定义另一个构造函数(_class ),最终将其返回出来就好了。
在函数_class 的原型上定义一个init方法,并运用apply方法使_class继承自身prototype.init 方法。这样,当我们在新类的prototype.init 方法上进行初始化设置时,就会将内容同步到当前的类中。
关于属性和方法的扩展 。当我们要扩展的是类的静态属性和方法,只需要将传入的对象上的属性都复制到当前对象就可以了 。而如果我们需要对当前类的实例也进行属性和方法的扩展,就需要将传入的对象上的属性都复制到当前类的原型上 。 当然在这里我们加了一个回调函数的功能,用于扩展成功后的显示 (实际上如果你不能很好地理解,去除这一部分同样是可以的)。
给“类”库添加继承,我们传入的参数是一个可选的父类。注意如果将parent直接传入Class构造函数则所有子类都必然共享一个原型,这不是我们想要的结果。当然也不可以直接将parent的prototype与_class的prototype进行绑定,因为一旦这样做了,任何一方发生改变,另一个也将随之发生改变,这同样不是我们想要的结果。这里还有非常重要的一点需要我们记住:只有实例的属性才会被继承 。在这里我们通过创建匿名函数的小技巧,避免了在继承parent类的时候创建parent类的实例 。