`

JavaScript定义“类”的几种方式

阅读更多
之所以给“类”加上引号,是因为书上说JavaScript中其实没有类这么个概念。
注:文中代码及一些文字摘自《JavaScript高级程序设计》,有些地方稍有修改。

1、工厂方式:
function createCar(sColor, iDoors, iMpg){
	var oTempCar = new Object
	oTempCar.color = sColor
	oTempCar.doors = iDoors
	oTempCar.mpg = iMpg
	oTempCar.showColor = function(){
		alert(this.color)
	}
	return oTempCar
}

书上说,这种方式有人反对,原因有2:首先是语义上的原因,它看起来不像使用带有构造函数的new运算符那么正规。第二是功能上的原因,以上代码每创建一个Car时,都要创建一个新的function:showColor。
var car1 = createCar("red", 2, 23)
var car2 = createCar("blue", 4, 30)
alert(car1.showColor==car2.showColor) //显示“false”,表示这2个function不是同一个实例。


第二个问题有个解决办法,就是把showColor这个function定义在工厂函数createCar的外面,然后在createCar里面给oTempCar添加一个showColor属性,指向外面的showColor函数。但这种方式不太好,理由是:该函数看起来不像对象的方法。

2、构造函数方式:
function Car(sColor, iDoors, iMpg){
	this.color = sColor
	this.doors = iDoors
	this.mpg = iMpg
	this.showColor = function(){
		alert(this.color)
	}
}

这种方式通过new运算符调用Car这个构造函数,构造之前会先隐式的创建一个对象,只有用this才能访问这个对象,然后可以直接给this添加属性并赋值,构造函数默认返回这个被新建出来的对象,不用显式使用return。

但以上方式和工厂方式存在同样的一个问题:每创建一个Car时,都要创建一个新的function:showColor。当然也可以用上面提到的方法解决。

3、原型方式
function Car(){
}

Car.prototype.color = "red"
Car.prototype.doors = 4
Car.prototype.mpg = 23
Car.prototype.showColor = function(){
	alert(this.color)
}

prototype就是所谓的原型,可以看作是一个模板吧,对象的模板,跟“类”的概念差不多。这里解决了前面2种方式的问题,但新的问题随之而来:首先这种方式不能给构造函数传递参数,必须给属性一个一个的赋值,否则得到的对象都是一样的。还有个更糟糕的问题是,假如某个属性指向一个对象,那么这个属性将被所有对象共享:
function Car(){
}

Car.prototype.color = "red"
Car.prototype.doors = 4
Car.prototype.mpg = 23
Car.prototype.drivers = new Array("Mike", "Sue");
Car.prototype.showColor = function(){
	alert(this.color)
}

var car1 = new Car
var car2 = new Car
car1.drivers.push("Matt")
alert(car2.drivers)

如上,给car1添加了一个driver,car2中也多出了这么个driver,这是个大问题。
原因在于:给prototype添加的属性是一个对象,构造Car时便会把这个对象的引用复制给每个car。而之前的构造函数方式不存在这种问题,是因为构造函数是在每次创建对象时单独给每个添加属性,而不是一开始在prototyp这样的“对象模板”中写死了。

4、混合方式(构造函数&原型)
很明显,解决以上所有问题的方法产生了:把构造函数方式和原型方式混合,取长补短。
既然方法必须共享,那么就采用原型方式定义方法;既然属性不能共享,那么就采用构造函数方式定义属性。Perfect!
function Car(sColor, iDoors, iMpg){
	this.color = sColor
	this.doors = iDoors
	this.mpg = iMpg
	this.drivers = new Array("Mike", "Sue")
}
Car.prototype.showColor = function(){
	alert(this.color)
}

不过仍有些开发者觉得这种方法不够perfect。

5、动态原型方式
有开发者认为,在构造函数内部找属性,在外部找方法的做法不合逻辑,因此他们设计出了动态原型方式,这种方式把对象的方法给定义到了构造函数内部,但是是在内部使用prototype定义方法。由于构造函数本身在每构造一个对象时都会执行一次,所以对象的方法会在每构造一次时重新创建,覆盖掉之前的方法。为了防止每次都给prototype创建新的function,可以给Car这个对象(Car本身也是个对象,一个function对象)添加一个标记,用于判断prototype是否被初始化过:
function Car(sColor, iDoors, iMpg){
	this.color = sColor
	this.doors = iDoors
	this.mpg = iMpg
	this.drivers = new Array("Mike", "Sue")
	
	if(!Car._initialized){
		Car.prototype.showColor = function(){
			alert(this.color)
		}
		Car._initialized = true
	}
}


还有第6种方式,不过第6种方式存在着和工厂方式、构造函数方式相同的问题,而且还有第15章才能明白的原因,所以这里不做笔记了,15章再说。

OK,回头看一下,其实我觉得混合方式挺好的,动态原型写起来反倒有些麻烦。嗯,看来我跟大多数人想法一样,书上说使用最广泛的是混合构造函数/原型方式。

这篇笔记是我JavaScript的OOP入门篇。
分享到:
评论
5 楼 reyesyang 2012-05-28  
上面的四位同学讨论的好激烈!
4 楼 yuan 2009-04-27  
看来我还是比较聪明的。搜索到一篇笔记:关于JavaScript的prototype和instanceof

引用
同时,每个对象都会有一个内部的属性_proto_(不同的javascript虚拟机实现用的名字可能不同),这个属性对js开发人员不可见,只在虚拟机内部使用。每当创建一个对象的时候,这个对象的_proto_就会被赋值为这个对象的构造函数的prototype,这样对象的_proto_属性和构造函数的prototype引用相同的对象,并且一旦对象创建完成,_proto_属性就不会改变。这样通过对象的_proto_属性,以及_proto_所引用的对象的_proto_属性,就构成了一个_proto_链。当访问一个对象的属性和方法的时候,js虚拟机正是通过这个_proto_链来查找的。

关于instanceof:
     假设有一条这样的语句:
     o instanceof c;
     在上面的语句执行过程中,虚拟机会把c.prototype和o的_proto_链上的节点逐个进行比较,如果找到相等的节点,则返回true,否则返回false。
3 楼 yuan 2009-04-27  
那instanceof是根据什么判断的?constructor?还是每个对象都有一个prototype的引用?
2 楼 yuan 2009-04-27  
一点感悟:

JavaScript中没有类。

new是个特殊的操作符,用来创建一个新对象。function是一种特殊对象,可以被new操作。new function X的时候,将创建一个新对象,同时把X function的上下文设置为这个新的对象,接着运行X function中的每一行代码。

function这个特殊对象有一个特殊的属性叫做prototype,这个属性估计是为new而生的,每次执行new操作,prototype属性的每个属性将被复制到新生成的对象上。

JavaScript如此简单,没必要给它扣上“类”、“继承”之类的名字。
1 楼 yuan 2009-04-20  
回头想想有点奇怪ho..都说JavaScript不存在类的概念,那为什么有个instanceof关键字呢?

相关推荐

    JavaScript定义类的几种方式总结

    JavaScript定义类的几种方式包括工厂方式、构造函数和原型方式,每种方法都有其特点和适用场景,接下来我们将详细探讨。 工厂方式是最早期的面向对象编程在JavaScript中的实践之一。使用工厂方式定义类,本质上就是...

    Javascript对象定义的几种方式

    Javascript对象定义的几种方式

    JavaScript定义类或函数的几种方式小结

    JavaScript中定义类或函数,主要有以下几种方式: 1. 工厂方式 工厂方式是JavaScript中创建对象的一种模式,它通过一个工厂函数来封装创建对象的代码。通过工厂函数,我们可以创建多个具有相同属性和方法的对象实例...

    JAVASCRIPT中定义对象的几种方式.pdf

    在讨论JavaScript中定义对象的几种方式之前,我们先理解一下JavaScript中的对象到底是什么。JavaScript是一种基于原型的编程语言,对象是其核心概念之一。对象可以被视为一个容器,存储各种键值对集合,键为属性名,...

    关于JavaScript定义类和对象的几种方式

    综上所述,在JavaScript中定义类和对象的几种方式各有优劣。直接量方式适用于简单对象的快速定义;工厂方式适用于需要大量重复创建类似对象的场景;而原型方式则提供了代码复用和性能优化的可能性。开发者可以根据...

    在JavaScript中实现类的方式探讨

    在JavaScript中实现类的方式主要有几种,包括对象字面量(Object Literal)、构造函数(Constructor)、原型链(Prototype Chain)以及ES6引入的类语法。每种方式都有其适用的场景和特点,理解它们有助于我们更好地...

    javascript中定义类的方法汇总

    在JavaScript中,定义类的方法多种多样,这使得开发者可以根据需求选择最合适的方式来创建对象。以下是几种主要的方法: 1. **工厂方式**: 工厂模式是一种创建对象的模式,通过函数来模拟类的概念。在JavaScript...

    JavaScript学习之二 — JavaScript创建对象的8种方式

    JavaScript是Web开发中不可或缺的一部分,尤其在前端领域更是发挥着...以上就是JavaScript创建对象的8种常见方式,每种方式都有其适用场景和优缺点。理解并熟练运用这些技巧,可以帮助你在JavaScript开发中游刃有余。

    JavaScript中定义类的方式详解

    以上就是JavaScript中定义类的几种常见方式,虽然它们没有像其他面向对象语言那样提供显式的`class`关键字,但JavaScript的灵活性使得开发者可以通过这些技巧实现面向对象编程的需求。在实际开发中,随着ES6的引入,...

    CKevens#-#05-02.面向对象:类的定义和继承的几种方式1

    前言类与实例:类的声明生成实例类与继承:如何实现继承:继承的本质就是原型链继承的几种方式类的定义、实例化类的定义/类的声明方式一:用构造函数模拟类(传统写法)/

    aspnet操作javascript类

    在ASP.NET中,我们可以通过以下几种方式使用JavaScript类: 1. **定义和实例化JavaScript对象**:在HTML或者JavaScript代码中,可以定义一个函数构造器,通过new关键字来创建对象实例。例如,定义一个名为Person的...

    haoyi2015#Web-1#05-02.面向对象:类的定义和继承的几种方式1

    前言类与实例:类的声明生成实例类与继承:如何实现继承:继承的本质就是原型链继承的几种方式类的定义、实例化类的定义/类的声明方式一:用构造函数模拟类(传统写法)/

    javascript面向对象编程的几种模式详解

    以下是对标题和描述中提到的几种JavaScript面向对象编程模式的详细解释: 1. **构造函数与字面量结合模式** 在JavaScript中,我们可以通过构造函数和字面量语法来创建对象。构造函数用于定义对象的类型,而字面量...

    JS函数的几种定义方式分析

    函数的定义方式多种多样,每种方式都具有特定的用途和特点。本文将对这些函数定义方式及其区别和使用技巧进行分析。 首先,我们来看第一种也是最常用的函数定义方式——函数声明: ```javascript function func1(...

    【JavaScript源代码】详解js创建对象的几种方式和对象方法.docx

    本文将深入探讨JavaScript中创建对象的几种常见方式以及对象方法。 首先,我们来看“工厂模式”。工厂模式是一种创建对象的抽象方式,通过一个函数来返回一个新的对象。例如: ```javascript function getObj(name...

    javascript数组定义的几种方法

    数组在JavaScript中的定义有多种方式,下面将详细讲解。 1. **隐式创建**: ```javascript var arr1 = [1, 3, 5, 7, 9]; ``` 这种方式直接通过方括号 `[]` 创建数组,并在括号内列出数组的初始元素。 2. **...

Global site tag (gtag.js) - Google Analytics