面对对象的程序设计

时间:2023-02-13 14:30:08

面向对象(Object-Oriented, OO)的语言有一个标志,那就是它们都有类的概念,而通过类可
以创建任意多个具有相同属性和方法的对象。(有相同属性和方法的对象属于一个类,在java中面向对象编程是从类的创建开始)

 一、理解对象:

         第一种:基于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 };

        第二种:对象字面量方式(比较清楚的查找对象包含的属性及方法)

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

二、对象属性类型

ECMA-262 第 5 版在定义只有内部才用的特性(attribute)时,描述了属性(property)的各种特征 。ECMAScript中有两种属性:数据属性和访问器属性。

 1、数据属性:
      数据属性指包含一个数据值的位置,可在该位置读取或写入值,该属性有4个供述其行为的特性:
      [[configurable]]:表示能否使用delete操作符删除从而重新定义,或能否修改为访问器属性。默认为true;
      [[Enumberable]]:表示是否可通过for-in循环返回属性。默认true;
      [[Writable]]:表示是否可修改属性的值。默认true;
      [[Value]]:包含该属性的数据值。读取/写入都是该值。默认为undefined;如上面实例对象person中定义了name属性,其值为“Nicholas”,对该值的修改都反正在这个位置
      要修改对象属性的默认特征(默认都为true),可调用Object.defineProperty()方法,它接收三个参数:属性所在对象,属性名和一个描述符对象(必须是:configurable、enumberable、writable和value,可设置一个或多个值)。
1 var person = {};
2 Object.defineProperty(person, "name", {
3 configurable: false,
4 value: "Nicholas"
5 });
6 alert(person.name); //"Nicholas"
7 delete person.name;
8 alert(person.name); //"Nicholas"

把 configurable 设置为 false,表示不能从对象中删除属性。如果对这个属性调用 delete,则在非严格模式下什么也不会发生,而在严格模式下会导致错误。而且,一旦把属性定义为不可配置的,就不能再把它变回可配置了。此时,再调用 Object.defineProperty()方法修改除 writable 之外的特性,都会导致错误.

在调用 Object.defineProperty()方法时,如果不指定, configurable、 enumerable 和
writable 特性的默认值都是 false。

  2、访问器属性:

      它主要包括一对getter和setter函数,在读取访问器属性时,会调用getter返回有效值;写入访问器属性时,调用setter,写入新值;该属性有以下4个特征:
      [[Configurable]]:是否可通过delete操作符删除重新定义属性;
      [[Numberable]]:是否可通过for-in循环查找该属性;
      [[Get]]:读取属性时调用,默认:undefined;
      [[Set]]:写入属性时调用,默认:undefined;
      访问器属性不能直接定义,必须使用defineProperty()来定义,如下:
 1 var book = {
 2     _year: 2004,
 3     edition: 1
 4 };
 5 Object.defineProperty(book, "year", {
 6     get: function() {
 7         return this._year;
 8     },
 9     set: function(newValue) {
10         if(newValue > 2004) {
11             this._year = newValue;
12             this.edition += newValue - 2004;
13         }
14     }
15 });
16 book.year = 2005;
17 alert(book.edition); //2

以上代码创建了一个 book 对象,并给它定义两个默认的属性: _year 和 edition。 _year 前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性。而访问器属性 year 则包含一个getter 函数和一个 setter 函数。 getter 函数返回_year 的值, setter 函数通过计算来确定正确的版本。因此,把 year 属性修改为 2005 会导致_year 变成 2005,而 edition 变为 2。这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。

三、创建对象

虽然 Object 构造函数或对象字面量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为解决这个问题,人们开始使用工厂模式的一种变体。

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");

工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

 2、构造函数模式

 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)的属性和方法。它与工厂方法区别在于:

  • 没有显式地创建对象
  • 直接将属性和方法赋值给this对象;
  • 没有return语句;

      此外,要创建Person的实例,必须使用new关键字,以Person函数为构造函数,传递参数完成对象创建;实际创建经过以下4个过程:

  1. 创建一个对象
  2. 将函数的作用域赋给新对象(因此this指向这个新对象,如:person1)
  3. 执行构造函数的代码
  4. 返回该对象

在前面例子的最后, person1 和 person2 分别保存着 Person 的一个不同的实例。这两个对象都有一个 constructor(构造函数)属性,该属性指向 Person,如下所示。

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

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

alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true

创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。

构造函数模式的问题:每个方法都要在每个实例上重新创建一遍,没有封装性。

3.原型模式

四、继承

继承:多个构造函数如果有相同的属性和方法,可以将相同的属性和方法定义在一个父类型的构造函数中,需要包含这些相同属性和方法的构造函数通过继承就可以直接使用
代码模式:
//1.创建父类型构造函数
function Parent(a,b,c){//a,b,c就是子类型都需要的私有的属性
this.a = a;
this.b = b;
this.c = c;
}

Parent.prototype={
constructor:Parent,
fn:function(){}//这是子类型共有的方法
}
//2.创建子类型构造函数
function A(a,b,c,d){
Parent.call(this,a,b,c);
this.d = d;//子类型相对其他父类型特有的属性
}
A.prototype = new Parent();
A.prototype.constructor = A;
A.prototype.fnA=function(){};//子类型特有的方法

//通过这个模式创建的类型A的实例就会拥有Parent中声明的a,b,c,fn这些属性,这就叫继承

//难点
1.构造函数需要使用new运算符来调用,使用new运算符调用构造方法实际经历的步骤,构造函数使用new调用和不使用new调用的区别
2.面向对象编程中的this:一般来说,在构造方法中,this就是当前创建的那个对象,如果这个构造方法不是通过new来调用,此时的this是Window。方法中的this指向调用当前方法的对象,
除非使用call,apply,bind方法来改变了this的指向。要注意在所有的异步方法(setTimeout,setInterval,事件处理程序)中,this的指向都会发生改变
3.原型链
4.静态方法和实例方法
定义在构造函数上的方法是静态方法
定义在实例上的方法是实例方法,实例方法必须要通过实例来调用

未完待续,后期再补

 注:以上内容参考《JavaScript 高级程序设计》第3版