【09类和模块】——2类和构造函数

在之前的博文”【09类和模块】——1类和原型“中有提到一种定义类的方法,但是那种方法并不常用,毕竟它没有定义构造函数,构造函数是用来初始化新创建的对象的。
使用new调用构造函数会自动创建一个新对象,因此构造函数本身只需要初始化这个新对象的状态即可。

调用构造函数有一个重要的特征——构造函数的prototype属性被用做新对象的原型
这就意味着通过同一个构造函数创建的所有实例对象都继承自同一个对象,因此他们是同一类的成员。

接下来我们用构造函数的方式来定义一个“范围类”,对【09类和模块】——1类和原型“中的那个例子做进一步的改进

定义构造函数

function Range(from,to){
    this.from = from;
    this.to = to;
}

定义原型对象属性,所有的实例化对象都继承自这个原型对象

//注意:原型对象的属性名字必须是prototype
Range.prototype={
    includes:function(x){return this.from <=x && x<=this.to;},
    foreach:function(f){
        for(var x=Math.ceil(this.from);x<this.to;x++) f(x)
    },
    toString:function(){return "("+this.from+"..."+this.to+")"}

}

实例化范围对象

var r=new Range(1,3)        //实例化一个范围对象
r.includes(2);             //true
r.foreach(console.log)     // 1  2  3

如果有看过【09类和模块】——1类和原型这篇文章的朋友应该会发现用工厂函数模式和使用构造函数模式定义类的技术差别

1、工厂函数range()转化为构造函数时被重命名为Rang(),这里遵循了一个常见的约定:从某种意义上来讲,定义 构造函数既是定义类,并且类名首字母要大写,而普通函数和方法都是首字母小写

2、Range()构造函数是通过new关键字调用的,因此不必使用类似【09类和模块】——1类和原型中的inherit()或者其他的逻辑来创建新对象,而工厂函数range()则不必使用new,所以需要使用其他逻辑来返回一个新的对象赋值给变量

这里解释一下2中的new关键字创建的新对象,其实在构造函数调用之前,javascript引擎就已经创建了一个新的对象,通过this关键字可以获取这个对象,Range()构造函数只不过是初始化this而已,构造函数甚至不需要返回这个新创建的对象,构造函数会自动创建对象,然后将构造函数作为这个对象的方法调用一次,最后返回这个新的对象;这里的解释可能有一点模糊,只要知道构造函数就是用来“构造新对象”的,它必须通过关键字new来调用

3、在【09类和模块】——1类和原型中示例代码的原型是range.methods,这种命名方式很方面同时具有很好语义,但是又过于随意,
在构造函数中使用Range.prototype作为原型,这是一个强制命名对Range()构造函数的调用会自动使用Range.prototype作为新的Range对象的原型

构造函数和类的标识
尽管构造函数不想原型那样基础,但构造函数是类的外在表现形式,很明显的就是构造函数的名字 往往就是用做类名,比如我们说Rang()构造函数创建Range对象。

比如用instanceof运算符来检测对象是否属于某个类时就会用到构造函数,假设我们有一个对象r,想知道r是否是Range类

r instanceof Range         //如果r继承自Range.prototype,则返回true

实际上instanceof运算符并不会检查r是否是由Range()构造函数来初始化的,而是检查r是否继承自Range.prototype。

constructor(构造函数)属性
每个函数都拥有一个prototype属性,这个属性的值是一个对象,这个对象包含唯一一个不可枚举属性——constructor,constructor属性的值是该函数对象

var F=function(){};  //这是一个函数对象
var p=F.prototype;   //这是F想关联的原型对象
var c=p.constructor  //这是与原型相关联的函数
c===F       //true,对任意韩式F.prototype.constructor=F

上面就是说明一个函数的原型的构造函数就是该函数这里就解释了用new实例化的对象的构造函数就是new引用的那一个函数,因为实例化对象继承子new引用函数的prototype对象

那么值得注意的是:在上面的构造函数示例中我们重写了Range.prototype对象的属性,这个新定义的原型对象并不包含constructor属性,因此我们要采取补救措施

Range.prototype={
    constructor:Range,//显示的设置构造函数的反向引用
    includes:function(x){return this.from <=x && x<=this.to;},
    foreach:function(f){
        for(var x=Math.ceil(this.from);x<this.to;x++) f(x)
    },
    toString:function(){return "("+this.from+"..."+this.to+")"}

}

还有一种避免重写原型对象的方式就是依次给原型对象增加属性

Range.prototype.includes=function(x){return this.from <=x && x<=this.to;}
Range.prototype.foreach=function(f){for(var x=Math.ceil(this.from);x<this.to;x++) f(x)}
Range.prototype.toString=function(){return "("+this.from+"..."+this.to+")"}

有没有觉得这样很麻烦,但是它保留了原型对象原有的属性,不需要手动的纠正


期待你阅读下一篇博文,【09类和模块】——3:javascript中java式的类继承

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值