面向对象(Object-Oriendted,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。ECMAScript中没有类的概念【类是面向对象程序设计(OOP,面向对象编程)实现信息封装的基础。类是一种用户定义类型,也称类类型。每个类包含数据说明和一组操作数据或传递消息的函数。的类实例称为对象】 ,因此它的对象也与基于类的语言中的对象有所不同。
ECMA-262把对象定义为:“ 无序属性的集合,其属性可以包含基本值,对象或者函数”严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值,正因为这样,我们可以把ECMAScript中的对象想象为散列表:无非就是一组名值对,其中值可以是数据或函数(这或许有点难理解〜 一组是指一个对象对应属性和方法。名是指属性或函数的名。值是指属性的值或函数的内容)
理解对象
创建自定义对象的最简单方式就是创建一个对象的实例,然后再为它添加属性和方法,早期的JavaScript开发人员经常这样写,如下所示:
var person = new Object(); //直接用new操作符创建一个实例化对象
person.name =“yao”;
person.age = 19;
person.job =“Student”;
person .sayName = function(){
alert(this .name); //将被解析为person.name
}; //注意不要少了分号
这个例子可以用对象字面量语法这样写:
var person = { //(还是用new操作符创建了一个对象) 也相当于直接创建对象,只不过换了一种更好的方式。
name:“yao”,
age:19
job:“Student”, //注意不要写成了分号,是逗号
sayName:function(){
alert(this.name);
}
}; //注意不要少了分号
创建对象
工厂模式
这种模式抽象了创建具体对象的过程。考虑到在ECMAScript中无法创建类,开发人员就发明了一种函数,用函数来封装(函数封装开发人员所写的内容)以特定接口(如此,我在想,接口是不是就相当于函数调用!)创建对象的细节,(你想想,函数中包含属性,属性值和函数不就相当于java中的类吗!),工厂模式写法如下所示:
function createPerson(name,age,job){ //所谓的用函数来封装,作为一个特定的接口,如果要用到它,就用它来创建对象
var A=new Object(); //还是用new操作符创建了一个对象A
A.name=name; //定义A的属性和方法
A.age=age;
A.sayName=function(){
alert(this.name); //函数中不仅包含属性,而且还包含这个函数
};
return 0;
}
var person1=createPerson("yao",19,"Student");
var person2=createPerson("xiyao",29,"Teacher"); //这个时候就相当于调用createPerson()函数了,传递对象参数进去即可
函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的Person对象。可以无数次地调用这个函数,而每次它都会返回一个包含三个属性和一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎么知道一个对象的类型。这就要说到对象的类型概念啦~ 对象是引用类型的一个实例,引用类型的值就是对象。引用类型包含Object类型、Array类型、Date类型、RegExp类型、Function类型、(基本包装类型:Boolean类型,Number对象,String类型)、单体内置对象:Global对象,Math对象)。
一个问题的出现,就会有解决之案,所以随着JavaScript的发展,又一个新模式出现了。
构造模式
ECNAScript中的构造函数可用来创建特定类型的对象。像Object和Array这样的原生构造函数,在运行时会自动出现在执行环境中。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。例如,可以使用构造函数模式将前面的例子重写如下:
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
alert(this.name);
};
}
var person1=new Person("yao",19,"Student"); //这个时候Person()就相当于一个特定对象类型了,这个类型是我们自定义的
var person2=new Person("xiyao",29,"Teacher");//该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数->由构造函数完成成员的初始化工作
在这个例子中,Person()函数取代了createPerson()函数。我们注意到,Person()中的代码除了与createPerson()中相同的部分外,还存在以下不同之处:
1、没有显示地创建对象;(即没有在函数中用new操作符创建对象了)
2、直接将属性和方法赋给了this对象;
3、没有return语句;(即没有返回值类型)
此外,还应该注意到函数名Person使用的是大写字母P。按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该一个小写字母开头。这个做法借鉴自其他OO语言,主要是为了区别于ECMAScript中的其他函数;因为构造函数本身也是函数,只不过可以用来创建对象而已。
要创建Person的新实例,必须使用new操作符。以这种方式调用构造函数实际上会经历以下4个步骤:
1、创建一个新对象;
2、将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
3、执行构造函数中的代码(为这个新对象添加属性和方法)
4、返回新对象
注意:1、我们在这个例子中创建的所有对象既是Object的实例(因为所有对象均继承自Object),同时也是Person的实例。
2、构造函数与其他函数的唯一区别,就在于调用它们的方式不同。任何函数,只要通过new操作符来调用,那它就可以作为构造函数,而不通过new操作符来调用,那它跟普通函数也不会有什么两样。例如,举个栗子:
//当作构造函数来创建对象
var person=new Person("yao",19,"Student");
person.sayName();
//作为普通函数调用
Person("xiyao",29,"Teacher"); //添加到window
widnow.sayName();
//在另一个对象的作用域中调用
var A=new Object();
Person.call(A,"yao",19,"Student");
A.sayName();
3、ECMAScript中的函数是对象,是Function的一个实例化对象,因此当定义了一个对象,也就是实例化了一个对象。所以在之前的构造函数中,sayName方法原来是这样写的:
this.sayName=function(){
alert(this.name);
};
现在可以这么写:
this.sayName=new Function("alert(this.name)"); //与声明函数在逻辑上是等价的
4、其实,我们可以将原来的构造函数这么写也是正确的:
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=sayName;
}
function sayName(){
alert(this.name);
} //无分号
var person1=new Person("yao",19,"Student");
var person2=new Person("xiyao",29,"Teacher");