JavaScript的六种继承

    技术2025-12-28  12

    继承

    简介

    继承就是子类继承父类的属性和方法。要继承必须有父类。 JavaScript里面的继承分为六种:1、原型链继承;2、构造继承;3、实例继承;4、拷贝继承;5、组合继承;6、寄生组合继承。

    继承的核心

    proto: 对象特有的,指向上层(创建自己的那个构造函数)的原型对象(prototype),即父元素。对象从prototype处继承属性和方法。 prototype: 函数特有,用于存储要共享的属性和方法。 constructor: 函数特有的,定义在prototype里面,通过new创建实例时,该实例便继承了prototype的属性和方法。

    对象

    对象是由构造函数创建的,下面这些都既是对象又是构造函数且所有对象的原型对象均指向function.prototype。且所有对象(构造函数)的prototype,均继承自Object.prototype。 Object: 作为函数时,Object.prototype是原型链的顶端,其中Object.prototype.__proto__=null。 Function: 作为对象时:Function.__proto__=Function.prototype。作为函数时,Function.prototype用于共享,而Function.prototype.__proto__继承自Object.prototype Array(Date): 作为对象时,Array.__proto__=Function.prototype。作为函数时Array.prototype用于共享,Array.prototype.__proto__继承自Object.prototype。 普通对象person: 作为对象:person.__proto__=Function.prototype。作为函数:person.prototype用于共享。person.prototype.__proto__继承自Object.prototype。

    六种继承方式

    1.原型链继承

    var arr=new Array(); console.log(arr);

    通过上述例子可知,js底层是通过原型链继承而来的,通过__proto__属性连接形成链式结构。这就叫原型链。原型链继承是通过prototype属性实现继承的。 由外到内继承是指在寻找过程中先从对象自身中找,没找到时就在对象的prototype中寻找,没找到继续往上推找对象的prototype中的_ proto _中寻找,直到原型链顶端Object.prototype里,要是还没找到,就会报错:ERROE:not a function。 特点: 子类实例继承自身的实例属性,业绩呈父类的构造属性和方法,父类的原型属性和方法。缺点是:不能继承子类的的原型属性和方法,只能进行单继承,不能进行多继承,不然会发生覆盖。继承之后,原型对象上的属性会被所有实例共享,所有实例用的是同一份数据,会相互影响。且子类不能直接向父类传递参数。 注意事项:通过原型链实现继承后,不能再使用字面量的方式创建原型对象,因为会覆盖原型链。 原型链继承的核心是让子类的原型等于父类的实例。原型链继承不能直接继承子类的原型属性和方法,所以要进行实例化对象,用Child.prototype = new Person("zhang"); 原型链继承之后,子类的原型对象指向父类。给子类添加原型属性和方法, 时直接添加给子类的原型对象的。如果没有原型链继承,那么自列的原型对象指向自身的prototype。 实例化对象 默认继承Object,原型对象的构造函数(用 new 关键字来调用的函数,称为构造函数。构造函数首字母一般大写,下面实例中的function那个就是构造函数)指向自身constructor,构造函数在类被实例化的时候直接执行。原型链继承之后,子类对象的实例既是自身也是父类。 构造函数 主要是为了方便的创建同一类型的多个对象,它会有以下几个执行过程: 1)new调用时,创建一个新的内存空间,标记为实例。2)函数体内部的this指向该内存。创建哪个函数体内部的this指向哪个内存空间。3)执行函数体内的代码,给this添加属性,相当于给实例添加属性。4)默认返回this,即默认返回该内存空间。此时,这个变量被标记为构造函数的实例。

    //没有手动添加返回值时,默认返回this。 function Person(){ this.name="李白"; } var p=new Person(); /*这段代码实际执行过程是:首先使用了new关键字调用了构造函数Person,并产生了一个新的内存空间#p1, 并标记这段内存空间为Person的实例。接着函数体内部的this指向了这个内存空间,并且执行函数内部的代码, 由于函数内部的this指向该内存空间,而该内存空间又被变量p所接收,所以p中就会有一个name属性,属性值为李白。*/ //手动添加了一个我基本数据类型的返回值,最终返回的还是this。 function Person1(){ this.age=26; return 50; } var p1=new Person1(); console.log(p1.age);//26,如果没用new来调用函数,那么输出的结果就是50。 //添加了一个复杂数据类型即对象的返回值,最终返回该对象。 function Person2() { this.height = '50'; return ['a', 'b']; } var p2 = new Person2(); console.log(p2.height); // undefined console.log(p2.length); // 2 console.log(p2[0]); // 'a'

    原型链继承实例:

    function Cat(c,age){ this.name="小猫"; this.color=c; this.age=age; this.active=function(){ return this.age+"岁的"+this.color+this.name+"吃鱼"; } } Cat.prototype.big="abc";//设置父元素的原型对象属性 var cat=new Cat("橘色",3); console.log(Cat.prototype);//打印父元素的原型对象属性 console.log(cat);//打印父元素的所有内容 console.log(cat.active());//3岁的橘色小猫吃鱼 function Child(){ this.place=null; } Child.prototype=new Cat("黑色",5);//构造函数的调用传参,同时使得子元素的原型对象继承父元素的属性 var child=new Child(); console.log(child); console.log(child.__proto__.active());//5岁的黑色小猫吃鱼

    2.构造继承

    使用call()和apply()实现继承。直接在子类的内部写call和apply即可实现。 call(this,参数); call的第二个参数可以时任意类型,也可为多个参数。在使用时,先让this指向第一个参数中的对象,然后把第二个及以后所有参数依次传递给函数作为实参。他调用一个对象的一个方法,以另一个对象替换当前的对象。 apply(this,参数); apply的第二个参数必须时数组或是arguments(即为伪数组)。使用时,让函数中的this指向第一个参数中的对象,把第二个参数中的数组或者维数组拆解开来,依次传递给函数作为形参。

    注意事项:当call和apply的第一个参数传入的是一个值类型的数据的时候,这个时候函数中的this会指向值类型对应的引用类型的数据。当第一个时undefined或者null或者不传的时候,函数中的this指向window对象。 优点:可以实现多继承,可以向父类传递参数。 缺点:构造继承,无法继承父类的原型属性和方法,子类的实例时本身不是父类。

    function Animal() { this.name = arguments[0]; this.sleep = function () { return "睡觉"; } } Animal.prototype.color = null; function Type() { this.type = arguments[0]; } function Dog(n, s, t) { this.sex = s; this.eat = function () { return "吃饭"; } //写构造继承 Animal.call(this, n); Type.apply(this, [t]); } Dog.prototype = { constructor: Dog, sleep: function () { } } var dog = new Dog("小花", "公", "犬科"); console.log(dog); //子类对象的实例是本身不是父类 console.log(dog instanceof Dog);//true console.log(dog instanceof Animal);//false //子类的原型对象 console.log(Dog.prototype);//Dog的原型对象 console.log(dog.__proto__); console.log(typeof dog.__proto__.__proto__);//Object function Cat(){ this.name=arguments[0]; } function Cats(){ this.sex=arguments[0]; } function Eat(a,b,c){ this.active=c; this.eat=function(){ return this.sex+this.name+this.active; } Cat.call(this,a); Cats.apply(this,[b]); } var cat=new Eat("橘猫","公","吃鱼"); console.log(cat.eat()); console.log(cat);

    3.实例继承(原型式继承)

    在子类内部直接构造父类的实例。优点是:不限制调用方式,可以向父类传递参数。缺点是:不能实现多继承,不能拿到子类构造属性和方法。子类的实例不是本身而是父类。

    function Person(n,s){ this.name=n; this.sex=s; this.sleep=function (){ return "睡觉"; } } function Child(n,s){ this.eat=function (){ } return new Person(n,s); } //子类实例有两种写法 var child=Child("小米","男"); console.log(child); var child1=new Child("小米","男"); console.log(child1); //子类的实例不是本身而是父类。 console.log(child instanceof Child);//false console.log(child instanceof Person);//true

    4.拷贝继承

    将父类里面的方法和属性拷贝给子类。优点:支持多继承,子类对象实例是本身不是父类,可以向父类传递参数。缺点是:在继承的时候,不断的使用new调用构造函数,比较占内存。

    function Animale(){ this.name=arguments[0]; this.sleep=function (){ return "睡觉"; } } function Type(){ this.type=arguments[0]; } function Cat(n,s,t){ this.sex=s; this.eat=function (){ return "吃饭"; } //拷贝父类里面的属性和方法,给prototype //先实例化父类对象 var animale=new Animale(n); //遍历属性加到animale里面 for(var key in animale) { Cat.prototype[key]=animale[key]; } var type=new Type(t); //遍历属性加到type里面 for(var key in type) { Cat.prototype[key]=type[key]; } } var cat=new Cat("小花","公","猫科"); console.log(cat); //子类对象的实例是本身 不是父类 console.log(cat instanceof Cat);//true console.log(cat instanceof Animale);//false console.log(cat instanceof Type);//false

    5.组合继承

    原型链继承和构造继承的结合,相互弥补自己的缺点。子类的实例既是本身也是父类。它可以实现多继承,可以向父类传递参数,没有实现原型对象属性共享,减少了相互之间的影响,但是它调用了两次父类构造函数,子类的构造函数会代替原型上的那个父类构造函数。

    function Person(n){ this.name=n; this.eat=function (){ return this.name+"吃饭"; } } Person.prototype={ constructor:Person, color:null } function Child(a,s,n){ this.age=a; this.sex=s; this.sleep=function (){ return "睡觉"; } Person.call(this,n); //构造继承只能获取到父类的属性和方法拿不到原型属性和方法 } //Child.prototype.work="学生"; Child.prototype=new Person("heihei");//这里没传值 var child=new Child(12,"男","花花"); console.log(child.eat()); var child1=new Child(12,"女","小红"); console.log(child1); console.log(child instanceof Child);//true console.log(child instanceof Person);//true

    6.寄生组合继承

    处理组合继承的缺点,避免调用两次父类的构造函数。原理是将父类的原型对象给一个空对象的prototype,再把空对象和子类的原型对象关联。

    function Person(){ this.name=null; this.sleep=function (){ return this.name+"睡觉"; } } Person.prototype={ constructor:Person, color:null } function Child(){ this.age=null; this.eat=function (){ return this.name+"吃饭"; } Person.call(this); } //开始寄生 (function (){ var fn=function (){}; fn.prototype=Person.prototype; Child.prototype=new fn(); })(); var child=new Child(); console.log(child); console.log(child instanceof Child);//true console.log(child instanceof Person);//true
    Processed: 0.034, SQL: 9