深入了解原型链,es5应该如何实现继承

什么是原型链?

原型链,简单理解就是原型组成的链,对象的__proto__是它的原型,而原型也是一个对象,也有__proto__属性,原型的__proto__又是原型的原型,就这样可以一直通过__proto_向上找,这就是原型链。
理解原型链只需要理解三个角色与三个等式
三个角色:
constructor 、prototype、实例
三个等式:
constructor.prototype == prototype
prototype.constructor == constructor
实例.__ proto __ == prototype
在这里插入图片描述

	function B(){
        
    }
    let b = new B();

我们来看一下这段代码的原型链是怎样的?
在这里插入图片描述

我们可以看到__proto__的终点最终指向null;一切构造函数的原型都指向Function.prototype,包括Function自身。

继承

原型链继承

通过指定子类构造函数的原型为父类的实例实现继承。

	function Animal() {
            this.name = "animal";
            this.friends =[];
        }
        Animal.prototype.addFriend=function (name){
            this.friends.push(name);
        }
        Animal.prototype.sayHello = function (){
            this.friends.forEach((item)=>{
                console.log(item + "hello");
            })
        }
        function Cat() {
            this.type = "1";
        }

        Cat.prototype = new Animal();
        Cat.prototype.constructor = Cat; //把构造函数指向改回来
        let cat = new Cat();
        let cat2 = new Cat();
        cat.addFriend("Bob");
        cat.addFriend("Jack");
        cat2.addFriend("Tom");
        cat.sayHello(); //Bob hello Jack hello Tom hello
        cat2.sayHello(); //Bob hello Jack hello Tom hello
缺点:
1、当父类中有引用类型的属性时,所有实例都会共享同一个属性,其中一个实例修改该属性,会影响其他实例。
2、在创建子类实例时,不能向父类构造函数传递参数。
3、子类构造函数指向是错误的,需要手动修改回来。
构造函数继承

在子类构造函数中指定上下文调用父类构造函数。相较于原型链继承,这种方式可以在子类构造函数中向父类构造函数传递参数。

	function Animal(name) {
            this.name = name;

        }
        Animal.prototype.sayName=function (){
            console.log("My name is " + this.name);
        }
        function Dog(name) {
            Animal.call(this,name);
        }
        let dog = new Dog("A");
        console.log(dog.name);
        dog.sayName(); //Uncaught TypeError: dog.sayName is not a function
缺点:
1、只能继承父类构造函数上的私有属性和方法,无法继承原型上的属性和方法。
2、子类实例上也会生成父类构造函数的私有方法的副本,无法实现函数复用。
组合继承

顾名思义,就是将原型链继承和构造函数继承组合在一起,发挥两者的长处。

		function SuperType(name){
            this.name = name;
            this.color=["red","blue"];
        }
        SuperType.prototype.sayName = function (){
            console.log("My name is " + this.name);
        }
        function SubType(name,age){
            SuperType.call(this,name);
            this.age =age;
        }
        SubType.prototype = new SuperType();
        SubType.prototype.constructor = SubType;
        SubType.prototype.sayAge = function (){
            console.log("My age is " + this.age);
        }
        var xm = new SubType("xiaoming",24);
        xm.sayName();
        xm.sayAge();
        console.log(xm.color);
缺点:
1、会执行两次父类的构造函数
2、子类的原型对象上会有冗余的属性(如上代码中的name,color)
原型式继承

不使用构造函数,基于一个已有对象使用Object.create()指定已有对象为原型创建新对象。

		var person = {
            name:"Bob",
            friends:["Jack","Tom"]
        }
        var otherPeron = Object.create(person);
        otherPeron.name = "Jim";
        otherPeron.friends.push("David");
        console.log(person.friends);
缺点:
1、和原型链继承相同,当有需要继承的对象引用类型的属性时,所有实例都会共享同一个属性,其中一个实例修改
该属性,会影响其他实例。
寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。

		function createOther(o) {
            var clone = Object(o);
            clone.sayHi = function (){
                console.log('Hi!');
            }
            return clone;
        }
        var person = {
            name:"Bob",
            friends:["Jack","Tom"]
        }
        var otherPerson = createOther(person);
        otherPerson.name = "Jack";
        console.log(otherPerson.friends);
        otherPerson.sayHi();
缺点:
1、无法实现函数复用。
寄生组合式继承

通过构造函数来继承属性,通过原型链来继承方法。

		function Person(name){
            this.friends =[];
            this.name = name;
        }
        Person.prototype.sayName = function (){
            console.log("My name is " + this.name);
        }
        function Student(name) {
            Person.call(this,name);//调用父类构造函数,继承属性
        }

        //改变子类的原型指向,继承父类方法
        function changeProto(superType,subType){
            var prototype = Object(superType.prototype);//复制一份父类原型
            prototype.constructor = subType;//修正构造函数指向
            subType.prototype = prototype;//指定子类原型
        }
        changeProto(Person,Student);
        Student.prototype.sayScore = function (){
            console.log(this.score);
        }
        var student1 = new Student("Bob");
        student1.score = 95;
        student1.sayName();
        student1.sayScore();
在组合继承的基础加入了寄生式继承,不用再生成父类实例,而是直接使用父类原型对象继承父类方法,避免了调用
两次父类构造函数,在子类原型对象上生成冗余属性的问题,也能实现方法复用,子类实例自己管理一份属性,不会
互相影响,可以说是目前最优最完善的继承方式了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值