本文共 5004 字,大约阅读时间需要 16 分钟。
1. 创建对象
1.1 ECMAScript中的对象
ECMAScript中没有类的概念,对象的定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。相当于对象是一组没有特定顺序的值,每个属性或方法都有一个名字。每个对象都是基于一个引用类型创建的。
创建自定义对象最简单方式就是创建一个Object实例,再为它添加属性和方法,例如:
var myobj = new Object();myobj.name = "object";myobj.getName = function() { alert(this.name);}
1.2 工厂模式
工厂模式是软件工程中广为人知的设计模式,这种模式抽象了创建具体对象的过程。因为在ECMAScript中无法创建类,因此可以用函数来封装以特定接口创建对象的细节,例如:
function createObject(name) { var myobj = new Object(); myobj.name = "object"; myobj.getName = function() { alert(this.name); } return myobj;}
1.3 构造函数模式
ECMAScript中的构造函数可用来创建特定类型的对象。比如Object和Array这样的原生构造函数,此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法,例如:
function Myobj(name) { this.name = name; this.getName = function() { alert(this.name); }}
构造函数与其他函数的唯一区别,就在于调用它们的方式不同,但是构造函数也是函数,不存在定义构造函数的特殊语法。任何函数,只要通过new操作符来调用,那它就可以作为构造函数,而任何函数,如果不通过new操作符来调用,那它跟普通函数没有区别,例如:
var myobj = new Myobj("object");
使用构造函数的主要问题,就是每个方法都要在每个实例上重新创建一遍,因为函数也是对象,因此每定义一个函数,也就是实例化了一个对象。要解决这个问题,可以把函数定义转移到外部,例如:
function Myobj(name) { this.name = name; this.getName = getName;}function getName() { alert(this.name);}
1.4 原型模式
每个函数都有一个prototype属性,这个属性是一个指向对象的指针,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法,例如:
function Myobj() {}Myobj.prototype.name = "object";Myobj.prototype.getName = function() { alert(this.name);}var myobj = new Myobj();myobj.getName();
每当代码读取某个对象的某个属性时,首先从对象实例本身开始搜索,如果在实例中找到具有给定名字的属性,则返回该属性的值,如果没有找到,则继续搜索原型对象。当为对象实例添加一个属性时,这个属性会屏蔽原型对象中保存的同名属性,但不会修改原型中那个属性的值,例如:
function Myobj() {}Myobj.prototype.name = "object";Myobj.prototype.getName = function() { alert(this.name);}var myobj1 = new Myobj();var myobj2 = new Myobj();myobj1.name = "new object";
实例中的属性即使设为null,也只会在实例中设置这个属性,而不会恢复其指向原型的连接,但delete操作符可以完全删除实例属性,使我们能够重新访问原型中的属性,例如:
function Myobj() {}Myobj.prototype.name = "object";Myobj.prototype.getName = function() { alert(this.name);}var myobj1 = new Myobj();var myobj2 = new Myobj();myobj1.name = "new object";delete myobj1.name;
使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中,只在给定属性存在于对象实例中,才会返回true,例如:
alert(myobj.hansOwnProperty("name"));
in操作符会在通过对象能够访问给定属性时返回true,无论访属性存在于实例还是原型中,例如:
alert("name" in myobj);
所有原生引用类型,都是采用原型模式创建的,都在其构造函数的原型上定义了方法,例如:
alert(typeof String.prototype.substring);
原型模式的缺点是,原型中所有属性被很多实例共享,对于包含引用类型值的属性,就会产生问题,例如:
function Myobj() {}Myobj.prototype = { list : ["1", "2"];}var myobj1 = new Myobj();var myobj2 = new Myobj();myobj1.list.push("3");alert(myobj2.list); //1,2,3
1.5 组合构造函数模式和原型模式
创建自定义类型的最常见方式,就是组合使用构造函数模式和原型模式,构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。每个实例都会有自己的一份实例属性的副本,但又共享着对方法的引用,例如:
function Myobj(name) { this.name = name;}Myobj.prototype = { constructor : Myobj; getName : function() { alert(this.name); }}
1.6 动态原型模式
动态原型模式把所有信息封装在了构造函数中,而通过在构造函数中初始化原型,又保持了同时使用构造函数和原型的优点,例如:
function Myobj(name) { this.name = name; if (typeof this.getName != "function") { Myobj.prototype.getName = function() { alert(this.name); } }}
1.7 寄生构造函数模式
在前几种模式都不适用的情况下,可以使用寄生构造函数模式,这种模式的思想是创建一个函数用以封装创建对象的代码,再返回新创建的对象,例如:
function Myobj(name) { var myobj = new Object(); myobj.name = name; myobj.getName = function() { alert(this.name); }}
1.8 稳妥构造函数模式
所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象,稳妥对象最适合在一些安全环境中使用,例如:
function Myobj(name) { var myobj = new Object(); myobj.getName = function() { alert(name); } return myobj;}
2. 继承
2.1 原型链
原型链实现继承的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例包含一个指向原型对象的指针,如果让原型对象等于另一个类型的实例,即原型对象将包含一个指向另一个原型的指针,相应的,另一个原型也包含一个指向另一个构造函数的指针。如此层层递进,就构成实例与原型的链条,就是所谓的原型链,例如:
function SuperType() { this.property = true;}SuperType.prototype.getSuperValue = function() { return this.property;}function SubType() { this.subproperty = false;}SubType.prototype = new SuperType();SubType.prototype.getSubValue = function() { return this.subproperty;}var instance = new SubType();alert(instance.getSuperValue());
2.2 借用构造函数
借用构造函数的基本思想即在子类型构造函数的内部调用超类型构造函数,例如:
function SuperType() { this.name = "object";}function SubType() { SuperType.call(this);}
借用构造函数的一个很大优势是可以在子类型构造函数中向超类型构造函数传递参数,例如:
function SuperType(name) { this.name = name;}function SubType() { SuperType.call(this, "object");}
2.3 组合继承
组合继承也叫伪经典继承,指将原型链和借用构造函数的技术组合的一种继承模式,思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数实现对实例属性的继承,例如:
function SuperType(name) { this.name = name;}SuperType.prototype.getName = function() { alert(this.name);}function SubType(name) { SuperType.call(this, name);}SubType.prototype = new SuperType();
2.4 原型式继承
借助原型可以基于已有的对象创建新对象,同时不必因此创建自定义类型,例如:
function object(o) { function F() { F.prototype = o; return new F(); }}
2.5 寄生式继承
寄生式继承与原型式继承紧密相关,它的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,例如:
function createObject(original) { var clone = object(original); clone.sayHi = function() { alert("Hi!"); } return clone;}
2.6 寄生组合式继承
组合继承最大的问题就是不论在什么情况下,都会调用两次超类型构造函数:创建子类型原型和子类型构造函数内部的时候。所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链接混成形式来继承方法。寄生组合式继承的基本模式如下:
function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); prototype.constructor = subType; subType.prototype = prototype;}
转载地址:http://dgqzx.baihongyu.com/