---恢复内容开始---
ECMAScript中两种开发模式:函数式(过程化)与面向对象。面向对象即为类的概念无法创建类,用函数来封装以特定的接口创建对象的细节。
一.创建对象
1:创建一个对象,然后给这个对象添加属性和方法。
var person=new Object(); //创建一个Object对象
person.name="Jack"; //创建一个name属性
person.walk=function(){return this.name+"你好!";}; //创建一个walk()方法并返回值
alert(person.wallk()); //输出属性和方法值。
这是一种最基本的创建对象的方法,但缺点是:想创建一个类似的对象,就会产生大量的代码。
2.工厂模式创建对象(解决实例化对象大量重复的问题):用函数来封装以特定接口创建对象的细节。
function creatObj(name,age){
var obj=new Object(); //集中实例化对象
obj.name=name;
obj.age=age;
obj.run=function(){return this.name+this.age+"你好";};
return obj; //每次返回包含两个属性和一个方法的对象
}
var person1=creatObj("lee",10); //第一个实例
var person2=creatObj("Jack",20); //第二个实例
alert(person1.run());
alert(person2.run()); //保持独立
存在的问题:对象识别问题,无法通过instanceof来判断它属于哪个类。
3.构造函数模式(用来创建特定类型的对象,类似于Object对象)解决对象识别问题:
function Box(name,age){ //构造函数模式
this.name=name;
this.age=age;
this.run=function(){return this.name+this.age+"你好";};
}
var box1=new Box("lee",100);
var box2=new Box("Jack",50);
alert(box1.run());
alert(box1 instanceof Box); // box1从属于Box
定义与使用构造函数的规范:
1.函数名和实例化构造名相同且大写。
2.通过构造函数实例化对象,必须使用new操作符。
疑问:通过构造函数可以创建对象,那么这个对象又从哪里来的,new Oject()在什么地方执行了?执行的过程如下:
1.当使用了构造函数,并且用new构造的函数(),那么就后台执行了new Object();
2.将构造函数的作用域给新对象,即new Object()创建出的新对象,而函数体内的this就代表new Object()出来的新对象;
3.执行构造函数内的代码(为这个新对象添加属性);
4.返回新对象(后台直接返回),不用return obj。
构造函数模式与工厂模式的区别:
1.构造函数方法没有显示的创建对象(new OBJECT());
2.直接将属性和方法赋值给this对象;
3.没有return返回语句。
构造函数与一般函数的区别:调用的方式不同。
构造函数存在的问题:不同的实例上的同名函数是不行等的,但是方法值是相同的,说明引用地址的唯一性,而创建完成同样任务的Function实例是没有必要的,因此可以把构造函数内部的方法通过全局来实现引用地址的一致性。
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=sayName; ||new Function("alert(this.name)");//new Functon()唯一性
}
function sayNane(){alert(this.name);}
3.原型:
我们创建的每一函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。逻辑上可以这么理解:prototype通过调用构造函数而创建的那个对象的原始对象。使用原型的好处可以让所有实例对象共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是直接将这些信息添加到原型里。
function Box(){} //声明一个构造函数
Box.prototype.name="lee";
Box.prototype.run=function(){return this.name+"你好";};
var box1=new Box();
var box2=new Box();
alert(box1.run==box2.run); //ture,方法的引用地址保持一致
在原型模式声明中,多了两个属性,这两个属性都是创建对象时自动产生的。_proto_属性是实例指向原型对象的一个指针,它的作用是指向构造函数的原型属性constructor.通过这两个属性,就可以访问原型里的属性和方法。
虽然所有实例都无法访问到proto属性,但可以通过isPrototyeof()方法来确定对象之间是否存在这种关系。
alert(Box.prototype.isPrototypeof(box));// 只要实例化对象,即都会指向。
原型模式的执行过程:
1.先查找构造函数实例里的属性或方法,若有返回会true;
2.如果构造函数实例里没有,则去他的原型里查找。
可以通过对象实例访问保存在原型里的值,但却不能访问通过对象实例重写的原型中的值。
为了让属性和方法更好的体现封装的效果,并且减少不必要的输入,原型的创建可以使用字面量的方法。字面量创建方式使用constructor属性不会指向实例,而会指向Object。所以手动指向。
function Person(){}
Person.prototype={
constructor:Person, //强制指向即可
name:"lee",
sayName:function(){return this.name;};
};
Box.prototype={};这种写法其实就是创建了一个新对象。而每创建一个函数就会同时创建它的prototype,这个对象也会自动获取constructor属性。所以,新对象的constructor重写了Person原来的const,因此会指向新对象,那个新对象没有指定构造函数,那么就会默认为Object.
原型的声明有先后顺序的,重写的原型会切断之前的原型。
function Person(){}
var friend=new Person();
Person.prototype={
contructor:Person,
name:'',
age:'',
sayName:function(){alert(this.name);};
};
friend.sayName(); //error
产生错误的原因:先创建一个实例,然后字面量的方式重写了实例的属性,切断了现有原型与任何之前已经存在的对象实例之间的联系。
原型对象的问题:(数据共享是它最大的优点,也是最大的缺点),针对的是引用类型值的属性存在突出问题。
function Box(){};
Box.prototype={
constructor:Box,
name:"lee",
age:30,
family:['father','mother','sister'],
run:function(){return this.name+this.age+this.family;}
};
var box1=new Box();
box.family.push("brother"); //在实例中添加brother
alert(box1.run());
var box2=new Box();
alert(box2.run()); //共享带来的麻烦,也有了brother,因为数组时引用类型值。
4.构造函数+原型模式(解决构造函数传参和共享的问题)
function Person(name,age){ //不共享的使用构造函数
this.name=name;
this.age=age;
this.family=['father','mother','sister'];
};
Person.prototype={
constructor:Person,
run:function(){return this.name+this.age+this.family;}
};
5.动态原型模式(把构造函数和原型封装到一起)
function Box(name,age){
this.name=name;
this.age=age;
if(typeof this.run!='function'){ //仅在第一次调用时初始化
Box.prototype.run=function(){return this.name+this.age;};
} }
当第一次调用构造函数时,run()方法不存在,然后初始化原型。当第二次调用,就不会初始化,并且第二次创建新对象,原型也不会再初始化。这样既可以封装,有实现了原型方法共享,并且属性都保持独立。
6.寄生构造函数模式(工厂模式+构造函数)
dunction Person(name,age){
var o=new Object();
o.name=name;
o.age=age;
o.sayName=function(){return this.name;};
return o;
}
这个模式在:假设要创建一个具有额外方法的引用类型。由于之前说明不建议直接String.prototype.addstring,可以通过寄生构造的方式添加。
function myString(string){
var str=new String(string);
str.addstring=function(){return this+"你好!";};
return str;
}
var string=new myString("Jack");
alert(string.addstring());
7.继承
基本思想:每一个构造函数都有一个原型对象,原型对象都包含一个只想构造函数的指针,而实例都包含一个指向原型的内部指针。利用一个引用类型继承另一个引用类型的属性和方法。
function Box(){this.name="lee";}
function Desk(){this.age=100;}
Desk.prototype=new Box(); //Desk继承了Box,通过原型,形成链条
var desk=new Desk();
alert(desk.age);
alert(desk.neme); //得到被继承的属性,超类型(父类)实例化的对象实例,赋值给子类型的原型属性
8.借用构造函数(解决引用共享和超类型无法穿惨的问题),对象冒充
function Box(){this.name=['Jack','Lee','NANA'];}
function Desk(age){Box.call(this,age);} //对象冒充,给超类型传参
var desk=new Desk(200);
alert(desk.age);
alert(desk.name);
desk.name.push("AAA"); //添加的新数据,只给desk
9.原型链+借用构造函数(组合继承)
function Box(age){ this.name=['Lee','Jack','Hello'],this.age=age;}Box.prototype.run=function(){ return this.name+this.age;};function Desk(age){ Box.call(this,age); //对象冒充}Desk.prototype=new Box(); //原型链继承var desk=new Desk(100);alert(desk.run());
10. 原型式继承;这种继承借助原型并基于已有的对象创建新对象,同时还不必因此创建自定义类型。function obj(o){ //传递一个字面量函数function F(){} //创建一个构造函数F.prototype=o; //把字面量函数赋值给构造函数的原型return new F(); //最终返回出实例化的构造函数}var box={ //字面量对象name:'Lee',arr:['哥哥','妹妹','姐姐']};var box1=obj(box); //传递alert(box1.name);box1.name='Jack';alert(box1.name);alert(box1.arr);box1.arr.push('父母');alert(box1.arr);var box2=obj(box); //传递alert(box2.name);alert(box2.arr); //引用类型共享了寄生式继承把原型式+工厂模式结合而来,目的是为了封装创建对象的过程。function create(o){ //封装创建过程var f=obj(o);f.run=function(){ return this.arr; //同样,会共享引用};return f;}组合式继承是 JavaScript 最常用的继承模式;但,组合式继承也有一点小问题,就是超类型在使用过程中会被调用两次:一次是创建子类型的时候,另一次是在子类型构造函数的内部。DL 20151226function Box(name){ this.name=name;this.arr=['哥哥','妹妹','父母'];}Box.prototype.run=function(){ return this.name;};function Desk(name,age){ Box.call(this,name); //第二次调用 Boxthis.age=age;}Desk.prototype=new Box(); //第一次调用 Box以上代码是之前的组合继承,那么寄生组合继承,解决了两次调用的问题。function obj(o){ function F(){}F.prototype=o;return new F();}function create(box,desk){ var f=obj(box.prototype);f.constructor=desk;desk.prototype=f;}function box(name){ this.name=name;this.arr=['哥哥','妹妹','父母'];}Box.prototype.run=function(){ return this.name;};function Desk(name,age){ Box.call(this,name);this.age=age;}inPrototype(Box,Desk); //通过这里实现继承var desk=new Desk('Lee',100);desk.arr.push('姐姐');alert(desk.arr);alert(desk.run()); //只共享了方法var desk2=new Desk('Jack',200);alert(desk2.arr); //引用问题解
---恢复内容结束---