封装
- 最简单的封装就是将属性封装在对象中,这是生成对象的原始模式;
- 缺点在于写起来麻烦,而且原型与实例之间没有联系;
//原型
var Cat={
name:"",
color:""
}
//实例
var cat1={};
cat1.name="Tom";
cat1.color="yellow";
var cat2={};
cat1.name="Peter";
cat1.color="black";
- 用函数生成实例:原型与实例之间仍然没有关联
//用函数生成实例
function Cat(name,color){
return{
name:name,
color:color
}
}
var cat1=Cat("Tom", "yellow");
var cat2=Cat("Peter", "black");
- 构造函数模式:构造函数其实是个普通函数,但内部使用this变量;
- 实例的constructor属性指向构造函数
- 构造函数模式的问题在于浪费内存:当为构造函数添加固定值的属性和方法时,在所有实例中这些属性和方法时一样的,但
//构造函数
function Cat(name,color){
this.name=name;
this.color=color;
}
//生成实例
var cat1=new Cat("Tom","yellow");
var cat2=new Cat("Peter","black");
继承
//animal对象的构造函数,使cat对象继承animalfunction(){this.species="动物";}
- 构造函数绑定:使用call()或apply()方法将要继承的对象的构造函数绑定到子对象的构造函数中;
function Cat(name,color){//将Animal构造函数作为cat对象的方法调用,使cat对象继承animal的属性和方法Animal.apply(this,arguments);this.name=name;this.color=color;}
- prototype模式
- Cat.prototype.constructor默认是指向Cat的,但是重写了Cat.prototype之后,该属性就指向Animal()了,而且实例将继承该属性,cat1的构造函数也变成了Animal(), 所以必须手动纠正,将Cat.prototype.constructor重新指向Cat, 这在重写prototype是必须的;
- Cat.prototype.constructor默认是指向Cat的,但是重写了Cat.prototype之后,该属性就指向Animal()了,而且实例将继承该属性,cat1的构造函数也变成了Animal(), 所以必须手动纠正,将Cat.prototype.constructor重新指向Cat, 这在重写prototype是必须的;
Cat.prototype=new Animal(); //将cat的原型指向Animal的实例Cat.prototype.constructor=Cat; //将cat原型原型的constructor设置为Cat
- 直接继承原型
- Animal的属性可以直接写到Animal.prototype中,所以可以直接让Cat.prototype继承Animal.prototype;
- 优点是效率高,不需要创建Animal的实例;
- 缺点是Cat.prototype和Animal.prototype指向同一个对象,对Cat.prototype的修改同样会反映到Animal.prototype;
- 所以现在Animal.prototype.constructor也指向Cat();
function Animal(){}Animal.prototype.species="动物";Cat.prototype=Animal.prototype;Cat.prototype.constructor=Cat;
- 利用空对象作为中介,然后继承原型;
- F是空对象,几乎不占内存;
- 通过F()作为中介,修改cat的原型就不会影响animal的原型了;
var F=function(){};F.prototype=Animal.prototype;Cat.prototype=new F();Cat.prototype.constructor=Cat;
- 将以上方法封装为函数:child对象将继承parent对象
function extend(Child, Parent){ //Child是子类,Parent是父类var F=function(0{};F.prototype=Parent.prototype;Child.prototype=new F();Child.prototype.constructor.=Child;Child.uber=Parent.prototype;}
- uber属性直接指向Parent.prototype, 是为了使对象可以直接调用父对象的方法,纯属备用性质;
- 复制继承:将父类的属性和方法都复制到子类中
//复制继承function extend2(Child,Parent){var p=Parent.prototype;var c=Child.prototype;for(var prop in p){c[prop]=p[prop]; //将父类的属性复制给子类}c.uber=p;}
不使用构造函数的继承
- 不经过构造函数,直接实现实例对象之间的继承;
- object()方法:该函数返回的对象继承了传入的对象;
//object()函数:实现实例对象之间的继承function object(parent){function F(){};F.prototype=parent;return new F(); //返回的对象不包含自有属性}
- 浅复制:将父对象的属性复制给子对象
- 当对象的属性值是引用类型时,子对象获得是该属性值一个引用,当修改子对象的属性只时,父对象的属性也会被修改
- 所以浅复制只适用于复制基本类型属性值;
//浅复制function extendCopy(parent){var child={};for(prop in parent){child[prop]=parent[prop];}c.uber=p;return c;}
- 深复制:真正实现对对象的复制,而不是引用
//深复制function deepCopy(parent,child){var c=c || {};for(prop in parent){if(parent[prop]==="object"){child[prop]=(p[prop].constructor===Array) ? []:{};deepCopy(parent[prop],child[prop]); //递归,将prop对象的属性值复制给child[prop]对象}else{child[prop]=parent[prop];}}return child;}