本文深入解析JavaScript基于原型的面向对象实现机制。详细阐述原型对象在内存共享中的作用,区分实例属性与方法与类属性、类方法的差异。通过代码示例演示如何定义构造函数、添加原型方法,并重点讲解子类化的实现步骤及constructor属性的修复技巧,帮助开发者掌握JS动态继承的核心概念。
引言
一、引言
在C#和Java等语言中,面向对象编程主要依赖类(Class)来实现,尤其是继承机制,其功能强大且易于理解。然而,JavaScript并非纯粹的面向对象语言,而是基于对象的语言。它的继承机制通过原型(Prototype)函数实现,这对许多初学者来说可能较为晦涩。尽管如此,这种基于原型的面向对象实现方式不仅可行,还赋予了JavaScript动态继承的能力。本文将重点探讨JavaScript中的面向对象技术。
原型对象概述
二、原型对象概述
JavaScript中的每个对象都关联着一个原型对象,并继承该原型对象的所有属性。对象的原型由创建它的构造函数定义。JavaScript中的所有函数都拥有一个名为prototype的属性,该属性引用了原型对象。在原型对象初始化时,它仅包含一个constructor属性,用于引用创建该原型的构造函数。JavaScript中没有Class关键字来定义类,而是通过构造函数来隐式定义类,并初始化类中的属性。每个类的成员都会从原型对象中继承相同的属性,这意味着原型对象提供了实例间共享属性和方法的机制,从而有效节省内存空间。
当访问对象的属性时,JavaScript引擎会首先在对象自身中查找;若未找到,则继续向原型对象中查找(包括方法)。因此,建议将方法保存在原型对象中以实现共享并节省内存。此外,原型对象还有一个重要特性:如果在通过构造函数实例化对象后,向构造函数的原型对象添加新的属性或方法,之前已实例化的对象也将自动继承这些新增的内容。
对象属性与方法分类
三、对象属性、对象方法、类属性、类方法
每个对象都拥有独立的实例属性和实例方法副本。如果实例化5个对象,就会产生5份实例属性和方法的副本。this关键字用于引用当前操作的实例对象:谁调用了实例方法,this就指向谁;访问了哪个实例对象的属性,this就指向该实例对象。
类方法和类属性仅存在一个副本。调用类方法时,必须通过类的名称进行引用,例如:Date.setHours();
下面通过一个程序示例来展示实例属性、实例方法、类属性和类方法的具体应用。
代码如下:
function Mobile(kind,brand) {
this.kind=kind;//定义手机的种类,例如GSM/CDMA
this.brand=brand;//定义手机的品牌,this关键字表示用该构造函数实例化之后的对象
}
/**//*
定义类的第二步是在构造函数的原型对象中定义它的实例方法或其他属性
该对象定义的任何属性都将这个类的所有实例继承。
*/
//拨号,这里只是返回电话号码
Mobile.prototype.dial = function(phoneNo) {
return phoneNo;
};
/**//*
定义类的第三步是定义类方法,常量和其他必要的类属性,作为构造函数自身的属性,而不是构造函数
原型对象的属性,注意,类方法没有使用关键字this,因为他们只对他们的实际参数进行操作。
*/
//开机关机方法
Mobile.turnOn=function() {
return "The power of mobile is on";
}
Mobile.turnOff=function() {
return "The power of mobile is off";
}
//类属性,这样他们就可以被用作常量,注意实际上他们并不是只读的
Mobile.screenColor=64K;//假设该类手机的屏幕颜色都是64K彩屏的
子类化实现
四、子类化
JavaScript支持子类化,只需将子类的原型对象设置为超类的实例即可。但需注意,这样做会覆盖子类原本由JavaScript提供的constructor属性。为了确保constructor属性的正确性,需要重新指定它。以下是子类化的程序示例:
/***** 子类化 *****/
//下面是子类构造函数智能型手机
function SmartPhone(os)
{
this.os=os;
}
//我们将Mobile对象作为它的原型
//这意味着新类的实例将继承SmartPhone.prototype,
//后者由Mobile.prototype继承而来
//Mobile.prototype又由Object.prototype继承而来
SmartPhone.prototype=new Mobile(GSM,Nokia);
//下面给子类添加一个新方法,发送电子邮件,这里只是返回Email地址
SmartPhone.prototype.sendEmail=function(emailAddress) {
return this.emailAddress
}
//上面的子类化方法有一点缺陷,由于我们明确把SmartPhone.prototype设成了我们所创建的一个对象,所以就覆盖了JS提供
//的原型对象,而且丢弃了给定的Constructor属性。该属性引用的是创建这个对象的构造函数。但是SmartPhone对象集成了它的
//父类的constructor,它自己没有这个属性,明确设置着一个属性可以解决这个问题:
SmartPhone.prototype.constructor=SmartPhone;
var objSmartPhone=new SmartPhone();//实例化子类