面向对象与原型

---恢复内容开始---

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 20151226
function Box(name){
this.name=name;
this.arr=['哥哥','妹妹','父母'];
}
Box.prototype.run=function(){
return this.name;
};
function Desk(name,age){
Box.call(this,name); //第二次调用 Box
this.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); //引用问题解

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    

---恢复内容结束---

posted @ 2016-11-02 16:06  睿夏之霞  阅读(747)  评论(0编辑  收藏  举报