ECMAScript有两种开发模式:
1、函数式(过程化)
2、面向对象(OOP)
但是ECMAScript没有类的概念,因此与基于类的语言的对象也有所不同。
一、创建对象
var box=new Object();
box.name=‘lee‘; //属性
box.run=function(){ //方法
returnthis.name+‘运行中。。。‘;
}
alert(box.run());
关于工厂模式方法
因为对象是引用类型值,所以,每次更新这个对象,对象的源值都会改变,要想多整出几个对象,就要再写一个新的对象,从而产生2个不同的对象引用值,但是这样每次创建一个新的对象,要写很多代码,所以用到工厂模式方法。
代码:
function createObject(name,age){
var obj=new Object();
obj.name=name;
obj.age=age;
obj.run=function(){
returnthis.name+this.age+‘运行中。。‘;
};
return obj;
}
var box1=createObject(‘lee‘,100);
var box2=createObject(‘kee‘,200);
alert(box1.run());
alert(box2.run());
工厂模式解决了重复实例化的问题。
关于构造函数
因为工厂模式解决了重复实例化的问题,但是还有一个问题就是识别问题,因为根本无法搞清楚他们到底是那个对象的实例,所以就采用构造函数方法来创建特定对象。
代码:
function Box(name,age){
this.name=name;
this.age=age;
this.run=function(){
return this.name+this.age+‘运行中‘;
}
}
var box1=newBox(‘lee‘,100);
//new Box()即可
var box2=newBox(‘kee‘, 200);
alert(box1.run());
alert( box1 instantof Box); //很清晰的识别他从属于Box.
采用构造方法就解决了上面两个问题,第一,重复实例化,第二,对象识别的问题,但是这里了并没有new Object(),为什么可以实例化Box()?
采用构造方法,和使用工厂模式的方法不同之处:
1、构造函数方法没有显示的创建对象(new Object());
2、直接将属性和方法赋值给this对象。
3、
没有return语句。
构造函数方法有一些规范:
1、函数名和实例化构造名相同且大写(PS:非强制,但这么写有助于区分构造函数和普通函数)。
2、通过构造函数创建对象,必须使用new运算符。
上面说了,在构造函数方法里面没有new Object(),没有创建对象,那么它在什么地方创建了一个新的对象?
1、使用了构造函数,并且new
构造函数(),那么后台就执行了new Object();
2、将构造函数的作用域给新对象,(即new Object()创建出来的对象),而函数体内的this就代表newObject()出来的对象。
3、返回新对象(后台直接返回)
对象冒充调用:
为了改变作用域,就用到了对象冒出调用。
var 0=new Object();
Box.call(o,‘jack‘,200)
alert(0.run());
这样对象0就可以调用box的方法了。
二、原型
我们创建的每一个函数都有一个prototype()属性,这个属性是一个对象,用途是包含可以由特定类型的所有实例共享的属性和方法。
原型的创建:
方法一:
functionBox(){} //声明一个构造函数
Box.prototype.name=‘lee‘;
Box.prototype.age=100;
Box.prototype.run=function(){
return.this.name+this.age+‘运行中。。‘;
}
方法二:
function Box(){};
Box.prototype={
name:‘lee‘,
age:100,
run:function(){
return this.name+this.age+‘运行中。。‘;
}
};
进一步了解构造函数的声明方式和原型模式的声明方式,下面是图示;
判断一个对象是否指向了该构造函数的原型对象,可以使用isPrototypeOf()方法来测试。
alert(Box.prototype.isPrototypeOf(box));
原型模式的执行流程:
1、先查找构造函数实例里的属性或方法,如果有,立即返回。
2、如果构造函数实例里没有,就去它的原型对象里找,如果有,就返回。
对象实例仅可以访问原型中的值,不可更改。
判断属性是在构造函数还是在原型里?
alert(box.hasOwnProperty(‘name‘)); //在实例里返回true.
原型对象不仅可以在自定义对象的情况下使用,而ECMAScipt内置的引用类型都可以使用这种方式,并且内置的引用类型本身也使用了原型。
因为原型模式的最大优点:共享,也是它的最大的缺点,所以,当我们要保持数据的特性,而不能共享时,我们就用到下面:
组合构造函数+原型模式:
function Box(name,age){ //不共享的使用构造函数
this.name=name;
this.age=age;
this.family=[‘father‘];
};
Box.prototype={
//共享时使用原型模式
constructor:Box,
run:function(){
returnthis.name+this.age+this.family;
}
};
为了封装性更好,我们把构造函数和原型封装在一起,叫做:
动态原型模式:
functionBox(name,age){ //将所有的写成封装到函数体内
this.name=name;
this.age=age;
if(typeofthis.run!=‘function‘){ //仅在第一次调用的初始化
Box.prototype.run=function(){
return this.name+this.age+‘运行中。。。‘;
};
}
}
Varbox=new Box(‘lee‘,100);
Alert(box.run());
三、继承
继承是面向对象中的一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继承,一个是接口实现,一个是继承,而ECMAScript只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。
在JavaScript里,被继承的函数称为超类型(父类,基类),继承的函数称为子类型(子类,派生类)。继承也有之前的问题,比如字面量重写原型会中断关系,使用引用类型的原型,子类还无法给超类型传递参数。
借用构造函数:
目的:
为了解决引用共享和超类型无法传参的问题。
组合继承:
目的:
在上面的基础上,做到复用。
原型链+借用构造函数
组合继承是JavaScript最常用的继承模式,但有些小问题,就是超类型在使用过程中会被调用2次,一次是创建子类型的时候,另一次是在子类型构造函数的内部。
寄生组合继承:
解决了组合继承的2次调用的问题:
最后再来一张图镇楼~~