js面向对象的程序设计笔记

chap6:面向对象的程序设计

面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。前面提到过,ECMAScript 中没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。
ECMA-262 把对象定义为: “无序属性的集合, 其属性可以包含基本值、 对象或者函数。 ” 严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。正因为这样(以及其他将要讨论的原因) ,我们可以把 ECMAScript 的对象想象成散列表:无非就是一组名值对,其中值可以是数据或函数。

6.1 理解对象

上一章曾经介绍过,创建自定义对象的最简单方式就是创建一个 Object 的实例,然后再为它添加属性和方法,如下所示。

1 var person = new Object();
2 person.name = "Nicholas";
3 person.age = 29;
4 person.job = "Software Engineer";
5 person.sayName = function(){
6     alert(this.name);
7 };

上面的例子创建了一个名为 person 的对象,并为它添加了三个属性( name 、 age 和 job )和一个方法( sayName() ) 。其中, sayName() 方法用于显示 this.name (将被解析为 person.name )的值。早期的JavaScript开发人员经常使用这个模式创建新对象。几年后,对象字面量成为创建这种对象的首选模式。前面的例子用对象字面量语法可以写成这样:

1 var person = {
2     name: "Nicholas",
3     age: 29,
4     job: "Software Engineer",
5     sayName: function(){
6         alert(this.name);
7     }
8 };

这个例子中的 person 对象与前面例子中的person对象是一样的,都有相同的属性和方法。这些属性在创建时都带有一些特征值(characteristic) ,JavaScript通过这些特征值来定义它们的行为。

6.2 创建对象

6.2.1 工厂模式

工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程(本书后面还将讨论其他设计模式及其在 JavaScript 中的实现) 。考虑到在 ECMAScript 中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节,如下面的例子所示。

 1 function createPerson(name, age, job){
 2     var o = new Object();
 3     o.name = name;
 4     o.age = age;
 5     o.job = job;
 6     o.sayName = function(){
 7         alert(this.name);
 8     };
 9     return o;
10 }
11 var person1 = createPerson("Nicholas", 29, "Software Engineer");
12 var person2 = createPerson("Greg", 27, "Doctor");

函数 createPerson() 能够根据接受的参数来构建一个包含所有必要信息的 Person 对象。 可以无数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型) 。随着 JavaScript的发展,又一个新模式出现了。

6.2.2 构造函数模式

前几章介绍过,ECMAScript 中的构造函数可用来创建特定类型的对象。像 Object 和 Array 这样的原生构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。例如,可以使用构造函数模式将前面的例子重写如下。

 1 function Person(name, age, job){
 2     this.name = name;
 3     this.age = age;
 4     this.job = job;
 5     this.sayName = function(){
 6         alert(this.name);
 7     };
 8 }
 9 var person1 = new Person("Nicholas", 29, "Software Engineer");
10 var person2 = new Person("Greg", 27, "Doctor");

在这个例子中, Person() 函数取代了 createPerson() 函数。我们注意到, Person() 中的代码除了与 createPerson() 中相同的部分外,还存在以下不同之处:
 (1)没有显式地创建对象;
 (2)直接将属性和方法赋给了 this 对象;
 (3)没有 return 语句。
此外,还应该注意到函数名 Person 使用的是大写字母 P。按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。这个做法借鉴自其他 OO 语言,主要是为了区别于 ECMAScript 中的其他函数;因为构造函数本身也是函数,只不过可以用来创建对象而已。
要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4 个步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
(3) 执行构造函数中的代码(为这个新对象添加属性) ;
(4) 返回新对象。
在前面例子的最后, person1 和 person2 分别保存着 Person 的一个不同的实例。这两个对象都有一个 constructor (构造函数)属性,该属性指向 Person ,如下所示。

1 alert(person1.constructor == Person); //true
2 alert(person2.constructor == Person); //true

对象的 constructor 属性最初是用来标识对象类型的。但是,提到检测对象类型,还是 instanceof 操作符要更可靠一些。 我们在这个例子中创建的所有对象既是 Object 的实例, 同时也是 Person的实例,这一点通过 instanceof 操作符可以得到验证。

1 alert(person1 instanceof Object); //true
2 alert(person1 instanceof Person); //true
3 alert(person2 instanceof Object); //true
4 alert(person2 instanceof Person); //true

创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型; 而这正是构造函数模式胜过工厂模式的地方。在这个例子中, person1 和 person2 之所以同时是 Object 的实例,是因为所有对象均继承自 Object (详细内容稍后讨论) 。

6.2.4 组合使用构造函数模式和原型模式

创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存。另外,这种混成模式还支持向构造函数传递参数;可谓是集两种模式之长。下面的代码重写了前面的例子。

 1 function Person(name, age, job){
 2     this.name = name;
 3     this.age = age;
 4     this.job = job;
 5     this.friends = ["Shelby", "Court"];
 6 }
 7 Person.prototype = {
 8     constructor : Person,
 9     sayName : function(){
10         alert(this.name);
11     }
12 }
13 var person1 = new Person("Nicholas", 29, "Software Engineer");
14 var person2 = new Person("Greg", 27, "Doctor");
15 person1.friends.push("Van");
16 alert(person1.friends); //"Shelby,Count,Van"
17 alert(person2.friends); //"Shelby,Count"
18 alert(person1.friends === person2.friends); //false
19 alert(person1.sayName === person2.sayName); //true

在这个例子中,实例属性都是在构造函数中定义的,而由所有实例共享的属性 constructor 和方法 sayName() 则是在原型中定义的。而修改了 person1.friends (向其中添加一个新字符串) ,并不会影响到 person2.friends ,因为它们分别引用了不同的数组。这种构造函数与原型混成的模式,是目前在 ECMAScript中使用最广泛、认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式。

时间: 2024-10-10 15:47:00

js面向对象的程序设计笔记的相关文章

JS面向对象的程序设计 - 高设

1 //工厂模式 2 function createPerson (name, age, job){ 3 var o = new Object(); 4 o.name = name; 5 o.age = age; 6 o.job = job; 7 8 o.sayName = function (){ 9 alert(this.name); 10 } 11 return o; 12 } 13 14 var sam = createPerson("sam", 18, "web f

JS面向对象的程序设计

面向对象的语言有一个标志,即拥有类的概念,抽象实例对象的公共属性与方法,基于类可以创建任意多个实例对象,一般具有封装.继承.多态的特性!但JS中对象与纯面向对象语言中的对象是不同的,ECMA标准定义JS中对象:无序属性的集合,其属性可以包含基本值.对象或者函数.可以简单理解为JS的对象是一组无序的值,其中的属性或方法都有一个名字,根据这个名字可以访问相映射的值(值可以是基本值/对象/方法). 一.理解对象: 第一种:基于Object对象 var person = new Object(); pe

继承[第6章-面向对象的程序设计 笔记3]

许多 OO 语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.如前所述,由于函数没有签名,在 ECMAScript 中无法实现接口继承. ECMAScript 只支持实现继承, 而且其实现继承主要是依靠原型链来实现的. 原型链 ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法.其基本思想是利用原 型让一个引用类型继承另一个引用类型的属性和方法. 简单回顾一下构造函数.原型和实例的关系:每个构造函数都有一个原型对象,原型对象

原型模式[第6章-面向对象的程序设计 笔记2]

我们创建的每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含特定类型的所有实例共享的属性和方法.按照字面意思来理解,那么 prototype 就是通过调用构造函数而创建的那个对象实例的原型对象.(有点类似于java中的类) 因此不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中,如下所示: function Person(){ } Person.prototype.name = "Nicholas"; P

javascript对象[第6章-面向对象的程序设计 笔记1]

ECMA-262 把对象定义为: “无序属性的集合, 其属性可以包含基本值. 对象或者函数. ” 严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性或方法都有一个名字,而每个名字都映射到一个值.正因为这样(以及其他将要讨论的原因) ,我们可以把 ECMAScript 的对象想象成散列表:无非就是一组名值对,其中值可以是数据或函数. 对象 创建自定义对象的最简单方式就是创建一个 Object 的实例,然后再为它添加属性和方法,如下所示. var person = new Object

JS面向对象笔记二

菜单导航,<JS面向对象笔记一>,  参考书籍:阮一峰之<JavaScript标准参考教程> 一.构造函数和new命令 二.this关键字 三.构造函数和new命令 四.构造函数和new命令 五.构造函数和new命令 六.构造函数和new命令 七.构造函数和new命令 八.构造函数和new命令 一.构造函数和new命令 1.构造函数 JavaScript语言的对象体系,不是基于"类"的,而是基于构造函数(constructor)和原型链(prototype) 为

JavaScript 面向对象的程序设计记录笔记

以下为JavaScript高级程序设计第六章面向对象的程序设计读书笔记记录. 对象存在两个属性: 数据属性/ 访问器属性. 数据属性包括四个值: Configurable: 默认值为true, 表示能否通过delete删除属性重新定义:能否修改属性特性,或者能否吧属性修改为访问器属性. Enumable:  表示能否通过for in循环返回属性.默认值为true. writable: 表示能否修改属性,默认值为true. Value:包含这个属性的数据值. 访问器属性包括四个值:Configur

js高级程序设计笔记之-addEventListener()与removeEventListener(),事件解除与绑定

js高级程序设计笔记之-addEventListener()与removeEventListener(),事件解除与绑定 addEventListener()与removeEventListener()用于处理指定和删除事件处理程序操作.所有的DOM节点中都包含这两种方法,并且它们都接受3个参数:要处理的事件名.作为事件处理程序的函数和一个布尔值.最有这个布尔值参数是true,表示在捕获阶段调用事件处理程序:如果是false,表示在冒泡阶段调用事件处理程序. 要在按钮上为click事件添加事件处

JavaScript 面向对象的程序设计记录笔记2(设计模式)

以下为JavaScript高级程序设计 第六章面向对象的程序设计6.2节 创建对象(设计模式部分)读书记录. 1)工厂模式: function createPerson(name, age, sex) { var o = new Obejct(); o.age = age; o.sex = sex; o.name = name; return o; } var person = createPerson('Tom', 12, 'male'); var person = createPerson(