JavaScript面向对象的程序设计1—基础篇
1. 理解JavaScript的对象
??? 面向对象的的语言都有类的概念,类抽象了对象共有的属性和行为,根据类可以创建不同的对象。
??? 可是Javascript中没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。
??? ECMA-262把对象定义为:"无序属性的集合",其属性可以是基本值,对象,或者函数。(严格来讲,这就相当于说对象是一组没有特定顺序的值)
??? JavaScript中每个对象都是基于一个引用类型创建的,包括JavaScript的原生类型(如Array,Date等),也可以是开发人员自定义的类型。
?
2. 创建对象
??? 1) 利用Object类型
??????? 创建自定义对象最简单的方法就是创建一个Object的实力,然后再为它添加属性和行为,如下:
???????????
var person = new Object();person.name = "anser"; //属性person.age = 22;person.job = "Software Engineer";person.sayName = function() { //行为 alert(this.name);}??? ?2) 工厂模式
???????? 使用Object类型创建不同类型的对象的缺点:使用同一个接口创建很多对象,会产生很多重复的代码。如上面,分别创建person1,person2......
??????? 因此使用工厂模式,封装特定接口创建对象的细节,上面示例修改如下:
?
function createPerson(name, age, job) { var person = new Object(); person.name = name; person.age = age; person.job = job; person.sayName = function() { alert(this.name); } return person; }??????? 使用工厂模式虽然解决了创建多个相似对象的问题,可是却没有解决对象识别的问题(及怎么样知道一个对象的类型:通过 instanceof 操作符)。
? 3) 构造函数模式
??????? ECMAScript中的构造函数可用来创建特定类型的对象。像Object,Array这样的原生构造函数,在运行时会自动出现在执行环境中。
??????? 此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。改写以上的例子如下:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function() { alert(this.name); } } Person person1 = new Person("anser", 22, "Software Engineer"); Person person2 = new Person("xhc", 22, "Player");????????注意到构造函数模式和工厂模式的不同:
??????????? a. 没有显示地创建对象,在工厂模式中创建了新的Object对象;
??????????? b. 直接将属性和方法赋给了this对象,在工厂模式中赋给了新创建的Object对象;
??????????? c. 没有return语句, 在工厂模式中在工厂方法中返回显示创建的Object对象。
??????
??????? 要创建新的Person对象,使用new操作符,以这种方式调用构造函数实际上会经历一下4个步骤:
??????????? a. 创建一个新对象;
??????????? b. 将构造函数的作用域赋给新对象(因此this就指向了这个新对象);
??????????? c. 执行构造函数中的代码(为这个新对象添加属性);
??????????? d. 返回新对象。
??????? 通过构造函数创建的对象都有一个constructor(构造函数)属性,指向Person构造函数:
????????
alert(person1.constructor == Person); //true;alert(person2.consturcotr == Person); //ture;
?
??????? 但是检测对象类型还是使用instanceof操作符更可靠一些(因为在后面会介绍到,对象的构造函数不一定是这个类型的构造函数)。
alert(person1 instanceof Object); //true; 所有的对象均继承自Objec alert(person1 instanceof Person); //true;
?
??????? 使用构造函数,可以将它的实例标识为一种特定的类型,这正是构造函数模式胜过工厂模式的地方。
?理解构造函数:
??????? 1. 构造函数名首字母尽量大写,主要为了区分ECMAScript中的其他函数,因为构造函数本身也是函数,只不过可以用来创建对象而已。
??????? 2. 构造函数与其他函数的唯一区别,就在于调用它们的方式不同。任何函数通过new操作符来调用,那它就可以作为构造函数。
??????????? 如上面的构造函数Person:
//当作构造函数使用Person person = new Person("anser", 22, "Software Engineer");person.sayName(); //anser//作为普通函数使用Person("name1", 27, "job"); //添加到window,此时调用函数Person的作用域为window对象window.sayName(); //name1//在另一个对象的作用域中调用Object o = new Object();o.Person("object", 1, "test");o.sayName(); //object?
??????? 3. 构造函数的问题
??????????? 使用构造函数的缺点:就是每个方法都要在每个对象的实例上重新创建一遍。
??????????? 如前面例子中的person1和person2对象都有一个名为sayName()的方法,但那两个方法不是同一个Function的实例。
??????????? ECMAScript中函数也是对象,因此每定义一个函数,也就是实例化了一个对象。
??????????? 因此上面的构造函数实际相当于:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = new Function(" alert(this.name);") ; //与声明函数在逻辑上是等价的}alert(person1.sayName == person2.sayName); //false?
??????????? 因此创建两个完成相同功能的函数完全没有必要,况且有this对象在,根本不需要在执行代码前就把函数绑定到特定对象上面。
??????????? 所以,可以想下面这样,把函数转移到构造函数外部:
function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = sayName;}//函数移到构造函数外部function sayName() { alert(this.name);}?
??????????? 可是,这样做似乎有违封装的原则:在全局作用域中定义的函数,实际上只是被某个对象调用,这让全局作用域有点名不副实。
??????????? 更让人无法接受的是,如果对象需要定义很多方法,那么就需要定义很多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了(通过原型模式解决)。
????????? 这部分内容整理自《JavaScript高级程序设计》第二版,这本书真的还不错,虽然自己以前不喜欢写js,但是看到这么经典的书,还是让自己有兴趣研究下。
????????? 下一部分整理下这部分内容引出的利用函数原型,来实现JavaScript面向对象的程序设计,这也是框架js开发的基础。
?