原型继承

原型继承

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
posted @ 2022-02-02 00:23  前端销纸  阅读(29)  评论(0编辑  收藏  举报