《javascript高级程序设计(第二版)》学习原型与继承
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《javascript高级程序设计(第二版)》学习原型与继承相关的知识,希望对你有一定的参考价值。
声明:这类属于学习笔记,主要是摘录书中内容,比较少的整理。内容经常是跳跃的,建议您阅读书本,收益更大。
function Person(){}
Person.prototype.name="tom";
//这里等于重写了原型对象
//切断了与构造函数之间的联系
Person.prototype={
name:"mike",
age:22
};
//得到的不是Person了,而是Object
console.log(Person.prototype.constructor);
console.log(Person.prototype);
function Person(){}
//这里如果实例化在重写原型对象之后,是可以的
//因为实例对象[[prototype]]指向的是最初的原型对象
Person.prototype={
name:"mike",
sayname:function(){
console.log(this.name);
}
};
var person1=new Person();
person1.sayname();
//再次重写但是顺序不同
Person.prototype={
name:"tom",
sayname:function(){
console.log(this.name+"hello"); //我们期望的结果应该是mike hello
}
};
person1.sayname(); //person1是在第二次重写前声明的,但是这里得到的是mike,指向了最初的原型对象
var person2=new Person();
person2.sayname(); //这里我们得到的是 mike hello 对于他来说,重写的这个是他的最初
//总结起来就是:重写原型对象切断了现有原型与之前已经存在对象实例之间的联系;他们引用仍然是最初的原型。
原型有一个问题
function Person(){} Person.prototype={ constructor:Person, name:"mike", age:22, job:"doctor", friend:["Joe","Tom"], sayName:function(){ console.log(this.name); } }; var person1=new Person(); var person2=new Person(); person1.friend.push("Van"); console.log(person1.friend); //["Joe", "Tom", "Van"] console.log(person2.friend); //["Joe", "Tom", "Van"] console.log(person1.friend===person2.friend); //true
一般我们为某个实例添加某个属性仅仅是想添加在其身上的
并不想其他实例也有这个方法,然后因为Person中没有friend属性
只有原型中有,所以就直接向原型添加了,导致其他的实例也
自动获取了新的属性
对此我们会采用构造函数模式和原型模式来自定义类型
//创建自定义类型常用方式是结合构造函数模式和原型函数模式 //构造函数用于定义实例属性 function Person(name, age, job){ this.name=name; this.age=age; this.job=job; this.friend=["Joe","Tom"]; } //原型模式用于定义方法和共享属性 Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); } }; var person1=new Person("mike", 29, "software engineer"); var person2=new Person("pom", 26, "doctor"); person1.friend.push("Van"); console.log(person1.friend); //["Joe", "Tom", "Van"] console.log(person2.friend); //["Joe", "Tom"] console.log(person1.friend===person2.friend); //false console.log(person1.sayName===person2.sayName); //true
与之相似的还有动态原型模式
//动态原型模式 //把所有信息封装在构造函数中,通过在构造函数中初始化原型(在需要的情况下) //又保持二者的优点 function Person(name, age, job){ this.name=name; this.age=age; this.job=job; this.friend=["Joe","Tom"]; //方法 if(typeof this.sayName != "function"){ //这里不能使用对象字面量来写原型 //会切断现有实例和原型之间的联系 /* 这种就不行的 Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); } }; */ Person.prototype.sayName=function(){ alert(this.name); }; } }
寄生构造模式
function SpecialArray(){ var values=new Array(); values.push.apply(values, arguments); values.tojoin=function(){ return this.join("|"); }; return values; } var colors=new SpecialArray("red","blue","yellow"); console.log(colors instanceof SpecialArray); //false console.log(colors instanceof Array); //true //返回相对于构造函数和其原型属性之间没有关系,这与直接在外部构建对象没有什么不同
稳妥构造函数模式
//稳妥对象durable Objects 时没有公共属性,方法也不引用this的对象。 //最适合在一些安全的环境中(环境会禁止使用this和new),或者防止数据被其他应用程序改动。 //其与寄生构造函数类似,但又有所不同 不使用new调用构造函数 function Person(name, age, job){ var o=new Object(); //可以在这里定义私有变量和函数 o.age=age; //添加方法 o.sayName=function(){ console.log(name); }; return o; } var friend=Person("Tom",22,"doctor"); friend.sayName(); //tom console.log(friend.age); //22
在原型中药谨慎的定义其方法
function SuperP(){ this.property=true; } SuperP.prototype.getsupvalue=function(){ return this.property; }; function subP(){ this.subproperty=false; } //继承superP subP.prototype=new SuperP(); //添加新方法 subP.prototype.getsubvalue=function(){ return this.subproperty; }; //重写超类型中的方法 subP.prototype.getsupvalue=function(){ return false; }; var instance2=new SuperP(); var instance=new subP(); console.log(instance2.getsupvalue()); //true console.log(instance.getsupvalue()); //false /* 当通过subP的实例调用getsupervalue时,调用的是重写的方法 而通过superP的实例,则还是原来的方法 */
继承
1 包含引用类型值得原型属性会被所有实例共享,通过原型来实现继承时,原型实际上会变成另一个类型的实例,于是原来的实例属性也就变为了原型属性了
2 创建子类的时候,不能向超类的构造函数中传递参数,实际上,是无法再不影响所有对象实例的情况下,给超类的构造函数传递参数。
鉴于上面2个问题,很少单独使用原型链
//问题1的例子 function SuperType(){ this.colors=["red","yellow","blue"]; } function subType(){} //继承SuperType subType.prototype=new SuperType(); var instance1=new subType(); instance1.colors.push("black"); console.log(instance1.colors); //["red", "yellow", "blue", "black"] var instance2=new subType(); console.log(instance2.colors); //["red", "yellow", "blue", "black"] /* 这个例子中超类定义了一个colors的属性,包含一个数组。超类各个实例度会有格子包含自己数组的colors属性。
当子类通过原型链继承了超类后,子类原型就变为超类的一个实例,也拥有了colors,与专门创建子类原型的一个color
s属性一样。其实例也会共享,即便实例想单独增加其一个属性,其他实例也会得到。 */
比其相对好的继承方法是使用借用构造函数
在子类构造函数的内部调用超类构造函数,这样又可以传递参数。
function SuperType(){ this.colors=["red","yellow","blue"]; } function subType(){ //调用超类构造函数 SuperType.call(this); } //继承SuperType subType.prototype=new SuperType(); var instance1=new subType(); instance1.colors.push("black"); console.log(instance1.colors); //["red", "yellow", "blue", "black"] var instance2=new subType(); console.log(instance2.colors); //["red", "yellow", "blue"]
function SuperType(name){ this.name=name; } function subType(){ //调用超类构造函数,并传递参数 SuperType.call(this, "mike"); this.age=29; } var instance=new subType(); console.log(instance.name); console.log(instance.age); /* 仅仅是使用借用构造函数,同样会有构造函数的问题,方法都在构造函数中定义,因此函数复用无从谈起,
而且在超类原型中定义的方法,对于子类来说是不可见的,结果所有类型只能用构造函数模式,因此 借用构造函数也很少单独使用。 */
最普遍的做法是使用 组合继承
使用原型链和借用构造函数结合。使用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实力属性的继承。
function SuperType(name){ this.name=name; this.colors=["red","yellow","blue"]; } SuperType.prototype.sayName=function(){ console.log(this.name); }; function subType(name, age){ //调用超类构造函数 SuperType.call(this, name); this.age=age; } subType.prototype=new SuperType(); subType.prototype.sayAge=function(){ console.log(this.age); }; var instance1=new subType("mike",22); instance1.colors.push("black"); console.log(instance1.colors); instance1.sayAge(); instance1.sayName(); var instance2=new subType("gery",24); console.log(instance2.colors); instance2.sayAge(); instance2.sayName(); /* 这样的组合继承避免了原型链和借用构造函数的缺点,融合了他们的各自优点,为了javascript常用的继承模式,
instanceof和isPrototypeOf也能够识别基于组合继承创建的对象. */
原生式的继承(浅复制)
var person={ name:"mike", friends:["kobe","lebron","Tim"] }; function obj(o){ function F(){} F.prototype=o; return new F(); } //根据具体需求做修改 var anotherperson=obj(person); anotherperson.name="jack"; anotherperson.friends.push("rose"); var yetanotherperson=obj(person); yetanotherperson.name="lin"; yetanotherperson.friends.push("west"); console.log(person.name); /* Object.create()方法规范了原型继承。 第一个参数:一个用作新对象原型 第二个参数:为新对象增加额外属性的对象(与原属性名相同会覆盖) 在没有必要兴师动众创建构造函数,而指向让一个对象与另一个对象相似的情况下,原型式继承时可以胜任的,不过,与使用原型模式一样,包含引用类型值得属性都会共享 */
寄生组合式的继承
这种继承主要是客服 组合继承需要两次调用超类的问题
function SuperType(name){ this.name=name; this.colors=["red","blue","green"]; } SuperType.prototype.sayName=function(){ alert(this.name); }; function subType(name, age){ SuperType.call(this, name); this.age=age; } function obj(o){ function F(){} F.prototype=o; return new F(); } function inheritPrototype(subType, SuperType){ var prototype=obj(SuperType.prototype); prototype.constructor=subType; subType.prototype=prototype; } inheritPrototype(subType, SuperType); subType.prototype.sayAge=function(){ alert(this.age); };
YUI的Yahoo.lang.extend()就是采用寄生组合继承
以上是关于《javascript高级程序设计(第二版)》学习原型与继承的主要内容,如果未能解决你的问题,请参考以下文章
《javascript高级程序设计(第二版)》学习原型与继承