原型继承
原型继承
1.原型链继承
以数组作为例子
var a = [];
console.log(a.__proto__ === Array.prototype); //true
结论1:数组a是Array的实例对象
console.log(Array.prototype.__proto__ === Object.prototype); //true
结论2:Array.prototype是Object的实例对象
因此可以得出以下关系即原型链
[] -> Array.prototype -> Object.prototype
2.原型链继承存在的问题
依旧采取举一个销例子
第一个构造函数
function Super(){
this.a = 111;
}
第二个构造函数
function Sub(){
this.b = 222;
}
实例对象
var sub = new Sub()
需要实现的原型链
sub -> Sub.prototype -> Super.prototype
Sub.prototype = new Super()
解释:Sub.prototype被赋值为Super的实例即继承于Super.prototype
对super的原型做些手脚来检验原型链的正确性
添加say方法
Super.prototype.say = function () {
console.log('hello world');
}
验证
var sub1 = new Sub()
var sub2 = new Sub()
Sub1.say() //"hello world"
Sub2.say() //"hello world"
当我们期望对原型上的属性就行修改时,并尝试打印它
sub.a = 345
// Sub {b: 222, a: 345}
// a: 345
// b: 222
// [[Prototype]]: Super
// a: 111
// [[Prototype]]: Object
结论:给sub添加了一个a属性而不是修改了原型上的a属性
再次验证结论
console.log(sub.a); // 345
console.log(sub.__proto__.a); // 111
当我们尝试对原型上的属性进行修改时,并期望只有一个实例对象被修改
var sub1 = new Sub()
var sub2 = new Sub()
Sub.prototype.a = '000'
console.log(sub1.a); // 000
console.log(sub2.a); // 000
结论(个人理解):sub1和sub2的实例原型(sub1.proto)或者说Sub.prototype均指向同一个Super的实例对象。对sub1的原型进行修改其他所有由Sub产生的实例对象的原型均被修改
3.构造函数继承
解决原型链继承值共享的问题
function Super(){
this.a = 111;
}
Super.prototype.say = function(){
console.log("hello world");
}
function Sub(){
Super.call(this) //注意如果不改变this指向即指向window(典型独立调用)
}
var sub1 = new Sub()
var sub2 = new Sub()
sub1.a = 222
console.log(sub1.a); //222
console.log(sub2.a); //111
存在的问题:父级原型上的方法丢失
Super.prototype.say = function(){
console.log("hello world");
}
4.组合继承(伪经典继承)
function Super(){
this.a = 111;
}
Super.prototype.say = function(){
console.log("hello world");
}
function Sub(){
Super.call(this)
}
Sub.prototype = new Super()
var sub1 = new Sub()
var sub2 = new Sub()
sub1.a = 222
console.log(sub1.a); //222
console.log(sub2.a); //111
sub1.say() //hello world
sub2.say() //hello world
显然组合继承能够比较完全的实现理想中的继承
需要注意的是
console.log(sub1)
//Sub {a: 222}
// a: 222
// [[Prototype]]: Super
// a: 111
// [[Prototype]]: Object
console.log(sub2)
//Sub {a: 222}
// a: 111
// [[Prototype]]: Super
// a: 111
// [[Prototype]]: Object
组合继承存在原型上属性冗余的问题
5.寄生组合继承(经典继承)
function Super(){
this.a = 111;
}
Super.prototype.say = function(){
console.log("hello world");
}
function Sub(){
Super.call(this)
}
if(!Object.create){ //浏览器兼容性判断
Object.create = function (proto) {
function F(){}
F.prototype = proto
return new F()
}
}
Sub.prototype = Object.create(Super.prototype)
Sub.constructor = Sub //刷新构造函数
//Sub.prototype.__proto__ = Super.prototype
var sub1 = new Sub()
var sub2 = new Sub()
sub1.a = 222
console.log(sub1); //222
console.log(sub2); //111
sub1.say();
sub2.say()
Sub.prototype = Object.create(Super.prototype)
指定sub的原型的原型是Super的原型,利用Object.create()(es5)方法
以下纯属个人理解:
Sub.prototype.__proto__ = Super.prototype
上述一行代码即可实现Object.create()的效果,且对构造函数没有影响
6.经典题目
function Foo(){
getName = function(){
console.log(1);
}
return this
}
Foo.getName = function(){
console.log(2);
}
Foo.prototype.getName = function(){
console.log(3);
}
var getName = function(){
console.log(4);
}
function getName(){
console.log(5);
}
Foo.getName() //2
getName() //4
Foo().getName() //1
getName() //1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName() //3