js继承有哪些方法

答案:

  • 原型继承

  • 构造继承

  • 组合继承(组合原型+构造)

  • 实例继承

  • 寄生组合继承

  • ES6 使用class extends继承

 

详细解析:

首先定义 父类Animal

//父类
function Animal(name){
    this.name = name;
    this.sum = function() {
        alert(this.name);
    }
}
Animal.prototype.age = 10;  //父类原型属性

一、原型链继承:子类的原型等于父类的实例

instanceof 判断对象的具体类型(判断元素是否在另一个元素的原型链上),这里 dog1 继承了 父类Animal 的属性,返回true。

实例是子类的实例,也是父类的实例。

//原型链实现继承
function Dog() {
    this.name = "xu";
}
Dog.prototype = new Animal();  //关键语句,子类型的原型指向父类型的实例
// 这里实例化一个 Animal 时, 实际上执行了两步
// 1,新创建的对象复制了父类构造函数内的所有属性及方法
// 2,并将原型 __proto__ 指向了父类的原型对象
var dog1 = new Dog();
console.log(dog1.age);  //10
console.log(dog1 instanceof Animal);  //true
console.log(dog1 instanceof Dog);  //true

结果:

特点:

  1. 实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(父类新增原型方法/原型属性,子类都能访问的到)

缺点:

  1. 新实例无法向父类构造函数传参。
  2. 继承单一,无法实现多继承。
  3. 所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)

 

二、构造函数继承:用 .call() 在子类的构造函数内部调用父类构造函数

实例并不是父类的实例,只是子类的实例,这里具体为 cat1 不是 父类Animal 的实例,因此返回false。

相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类的构造函数中向父类构造函数传递参数。

//构造函数实现继承
function Cat() {
    //调用 Animal 构造函数
    Animal.call(this, "animalType");  //关键语句,使用call()方法继承父类构造函数中的属性
    //为了保证父类的构造函数不会重写子类的属性,需要在调用父类构造函数后,定义子类的属性
    this.age = "5";  //子类属性
}
//子类实例
var cat1 = new Cat();
console.log(cat1.name);  //animalType
console.log(cat1.age);  //5
console.log(cat1 instanceof Animal);  //false
console.log(cat1 instanceof Cat);  //true

结果:

特点:

  1. 只继承了父类构造函数的属性,没有继承父类原型的属性。
  2. 解决了 原型继承 缺点1、2、3。不会造成原型属性共享的问题。
  3. 可以继承多个构造函数属性(call多个)。
  4. 在子实例中可向父实例传参。

缺点:

  1. 只能继承父类构造函数的属性/方法,不能继承原型属性/方法。
  2. 无法实现构造函数的复用。(每次用每次都要重新调用)
  3. 实例并不是父类的实例,只是子类的实例。
  4. 每个新实例都有父类构造函数的副本,影响性能,臃肿。

 

三、组合继承:结合了(原型链+构造)两种模式的优点,传参和复用

主要思想:使用原型继承使用对原型属性和方法的继承,通过构造函数继承来实现对实例属性的继承。这样既能通过在原型上定义方法实现函数复用,又能保证每个实例都有自己的属性。

通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用。

//组合继承
function Cat(name) {
	Animal.call(this, name);  //构造继承 ----第二次调用 父类Animal----
}
// ----第一次调用 父类Animal----
Cat.prototype = new Animal();  //原型继承
Cat.prototype.constructor = Cat;  //组合继承需要修复构造函数的指向

var cat = new Cat("miao");
console.log(cat.name);	//miao 继承了构造函数属性
console.log(cat.age);	//10 继承了父类原型的属性
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true

结果:

特点:

  1. 可以继承实例属性/方法,也可以继承原型属性/方法。不存在引用属性共享问题,可传参,可复用。
  2. 每个新实例引入的构造函数属性是私有的。

缺点:

调用了两次父类构造函数(耗内存),生成了两份实例。子类的构造函数会代替原型上的那个父类构造函数。

 

四、实例继承:为父类实例添加新特性,作为子类实例返回

实例是父类的实例,不是子类的实例,这里具体为 cat1 不是 子类Cat 的实例,因此返回false

//实例继承核心:为父类实例添加新特性,作为子类实例返回
function Cat(name) {
    var instance = new Animal();
    instance.name = 'cc';
    return instance;
}
var cat = new Cat();
console.log(cat.name);	//cc
console.log(cat instanceof Animal);  //true
console.log(cat instanceof Cat);  //false

结果:

特点:

  1. 不限制调用方式,不管是new子类()还是子类(),返回的对象都具有相同的效果

缺点:

  1. 实例是父类的实例,不是子类的实例
  2. 不支持多继承

 

五、寄生组合继承

核心:在组合继承中,调用了两次父类构造函数,这里 通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点。

 

主要思想:借用 构造函数 继承 属性 ,通过 原型链的混成形式 来继承 方法。

// 父类
function SuperType (name) {
  this.colors = ["red", "blue", "green"];
  this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

// 子类
function SubType (name, subName) {
  // 调用 SuperType 构造函数
  SuperType.call(this, name); // ----第二次调用 SuperType,继承实例属性----
  this.subName = subName;
};

// ----第一次调用 SuperType,继承原型属性----
SubType.prototype = Object.create(SuperType.prototype)

SubType.prototype.constructor = SubType; // 注意:增强对象

let instance = new SubType('An', 'sisterAn')

 

六、ES6 使用 class extends 继承

//ES6 class extends 继承
class Person{
    constructor(name, age){ //constructor构造函数
        this.name=name;
        this.age=age;
    }
    show(){
        console.log(this.name);
        console.log(this.age);
    }
}

class Worker extends Person{  //子类继承父类
    constructor(name, age, job){
        super(name, age);
        this.job=job;
    }
    showJob(){
        console.log(this.job);
    }
}
let worker1 = new Worker('jia', 21, '班长');
worker1.show();
worker1.showJob();

结果:

通过class创建对象看起来就比较简洁,不用通过原型去实现继承。

在class的继承中,子类的_proto_属性表示构造函数的继承,指向父类,子类prototype属性的_proto_属性,表示方法的继承,总是指向父类的prototype属性

class调用必须通过new 关键字。

class中的static 属性:用于定义类中的静态方法和静态属性关键字。
该方式声明的方法与属性,只能通过类名调用,可被继承,并且定义方法的 this 指向类 而不是实例。

ES中的function 可以用call apply bind 的方式 来改变他的执行上下文,但是 class 不可以 。

<think>好的,用户想了解JavaScript中的继承实现方式。首先我要回忆一下JavaScript的继承机制,因为和传统的面向对象语言不同,JS使用原型链来实现继承。用户可能已经知道原型链,但需要详细解释各种方法。 首先,我应该列出常见的几种继承方式,比如原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合式继承,以及ES6的class语法。需要确保每个方法都有清晰的解释,优缺点,以及代码示例。 然后,要检查是否有遗漏的方法,比如Object.create的使用是否属于原型式继承的一部分。要注意每个方法的实现细节,比如在构造函数继承中,如何在子类中调用父类构造函数,并传递参数。 接下来,代码示例需要正确,符合语法,并且注释清楚。例如,原型链继承中,子类的原型指向父类的实例,这可能导致引用类型属性的共享问题。这时候需要举例说明问题,比如修改一个实例的属性会影响其他实例。 组合继承结合了原型链和构造函数,解决了引用类型的问题,但会有原型上重复属性的问题。而寄生组合式继承则解决了这个问题,被认为是理想的继承方式。这部分需要详细说明,因为用户可能对优化方法感兴趣。 还要提到ES6的class和extends关键字,这是语法糖,底层还是基于原型链的。需要指出它更简洁,推荐使用。 在解释过程中,要确保术语准确,比如“原型”、“构造函数”、“实例”等。同时,引用用户提供的参考内容,但用户给出的引用是关于JS继承的,但引用标记是[^1],可能在回答中需要正确引用。 最后,生成相关问题时要考虑用户可能的后续疑问,比如各种方法的优缺点比较,如何选择,或者ES6继承与传统方法的区别等。 检查是否有错误,比如原型链继承中,子类原型是父类实例,而构造函数继承则是复制父类属性。组合继承两者结合,但可能有两次调用父类构造函数的问题。寄生组合式通过创建一个空函数来避免重复。 确保每个部分结构清晰,代码正确,并且用中文自然表达。现在组织这些内容,按照用户要求的格式,分步骤解释,并添加相关问题。</think># JavaScript继承实现方式详解 JavaScript的继承主要通过原型链实现,以下是7种常见实现方式及演进过程: ## 一、原型链继承(Prototype Chaining) ```javascript function Parent() { this.name = 'Parent'; } Parent.prototype.say = function() { return this.name; }; function Child() {} Child.prototype = new Parent(); // 核心实现 const c = new Child(); console.log(c.say()); // "Parent" ``` **特点**: - 子类实例共享父类引用类型属性 - 无法向父类构造函数传参 - 原型方法定义在父类实例上 ## 二、构造函数继承(Constructor Stealing) ```javascript function Parent(name) { this.name = name; } function Child(name) { Parent.call(this, name); // 核心实现 } const c = new Child('Child'); console.log(c.name); // "Child" ``` **特点**: - 解决原型链继承的共享问题 - 无法继承父类原型方法 - 实例方法需在构造函数中定义 ## 三、组合继承(Combination Inheritance) ```javascript function Parent(name) { this.name = name; } Parent.prototype.say = function() { return this.name; }; function Child(name) { Parent.call(this, name); // 第二次调用父类构造函数 } Child.prototype = new Parent(); // 第一次调用父类构造函数 const c = new Child('Child'); console.log(c.say()); // "Child" ``` **特点**: - 综合原型链和构造函数的优点 - 父类构造函数被调用两次 - 子类原型包含冗余属性 ## 四、原型式继承(Prototypal Inheritance) ```javascript function objectCreate(o) { function F() {} F.prototype = o; return new F(); } const parent = { name: 'Parent' }; const child = objectCreate(parent); console.log(child.name); // "Parent" ``` **特点**: - ES5 `Object.create()` 的底层实现 - 适合不需要构造函数的场景 - 共享引用类型属性的问题依然存在 ## 五、寄生式继承(Parasitic Inheritance) ```javascript function createChild(original) { const clone = Object.create(original); clone.sayHi = function() { // 增强对象 return 'Hi!'; }; return clone; } const parent = { name: 'Parent' }; const child = createChild(parent); console.log(child.sayHi()); // "Hi!" ``` ## 六、寄生组合式继承(Parasitic Combination) ```javascript function inheritPrototype(child, parent) { const prototype = Object.create(parent.prototype); // 创建父类原型副本 prototype.constructor = child; // 修复constructor child.prototype = prototype; } function Parent(name) { this.name = name; } Parent.prototype.say = function() { return this.name; }; function Child(name) { Parent.call(this, name); } inheritPrototype(Child, Parent); // 核心实现 const c = new Child('Child'); console.log(c.say()); // "Child" ``` **特点**: - 被广泛认为是理想的继承方式 - 只调用一次父类构造函数 - 保持原型链完整 ## 七、ES6 Class继承 ```javascript class Parent { constructor(name) { this.name = name; } say() { return this.name; } } class Child extends Parent { constructor(name) { super(name); // 必须调用super } } const c = new Child('Child'); console.log(c.say()); // "Child" ``` **特点**: - 语法糖,底层仍是原型链实现 - 通过`super`关键字访问父类 - 支持静态方法和`extends`继承 # 演进对比表 | 继承方式 | 优点 | 缺点 | 原型方法继承 | 实例属性继承 | |-------------------|------------------------------|------------------------------|--------------|--------------| | 原型链继承 | 简单 | 共享引用类型属性 | ✔️ | ❌ | | 构造函数继承 | 独立实例属性 | 无法继承原型方法 | ❌ | ✔️ | | 组合继承 | 综合两种方式 | 父类构造函数被调用两次 | ✔️ | ✔️ | | 寄生组合式继承 | 最理想的继承方式 | 实现相对复杂 | ✔️ | ✔️ | | ES6 Class继承 | 语法简洁,现代推荐方式 | 需要转译器支持旧浏览器 | ✔️ | ✔️ | : 原型链是JavaScript实现继承的基础机制,通过`__proto__`属性形成链式结构实现属性和方法继承
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值