【js】ES5,ES6继承是如何实现的
==详解 ES5 组合继承(构造函数+原型链)==
function Parent(){...} function Child(){ Parent.call(this) // ① } Child.prototype = new Parent() // ②
1. 先记住目的
我们要让:
- Child 实例 继承 Parent 的属性 + 方法
- 这是 ES5 没有 class/extends 时的标准写法
2. 逐句解释
① Parent.call(this)
function Child(){ Parent.call(this) }
- call(this) 把 Parent 里的 this 换成 Child 的实例
- 等于:在创建 Child 时,把 Parent 的属性“复制”一份给自己
- 作用:继承父构造函数里的实例属性
例子:
function Parent() { this.name = "父类" } function Child() { Parent.call(this) } const c = new Child() console.log(c.name) // "父类" 继承到了
--- ② Child.prototype = new Parent()
Child.prototype = new Parent()
- 把 Child 的原型对象,变成 Parent 的一个实例
- 原型链就通了:
Child实例 → Child.prototype → Parent.prototype
- 作用:继承父类原型上的方法
例子:
Parent.prototype.say = function(){ console.log('说话') } const c = new Child() c.say() // 能调用,因为原型链找到了
---
3. 合起来:组合继承 = 最常用的 ES5 继承
- Parent.call(this)
→ 继承实例属性
- Child.prototype = new Parent()
→ 继承原型方法
所以它叫 组合继承:
借用构造函数 + 原型链继承 合在一起。
4. 面试常问缺点
它有个小问题:
- Parent 会被执行两次
1. new Child() 时:Parent.call(this)
2. 挂原型时:new Parent()
后来就有了寄生组合式继承(优化版),但面试先把这个搞懂就稳了。
5. 一句话总结(面试背诵版)
这段代码是 ES5 组合继承:
1. Parent.call(this) 继承父类实例属性;
2. Child.prototype = new Parent() 继承父类原型方法;
3. 结合了借用构造函数和原型链两种方式,是 ES5 最常用继承方案。
===寄生组合继承==
一、最终最优代码(背它)
function Parent() { this.name = 'parent'; } Parent.prototype.say = function () { console.log('hello'); }; function Child() { Parent.call(this); // 继承属性(只调用1次父构造) } // 核心:寄生组合继承关键一句 Child.prototype = Object.create(Parent.prototype); // 修正 constructor Child.prototype.constructor = Child;
---
二、和你之前写法的区别
你之前:
Child.prototype = new Parent();
问题:
- 会调用两次 Parent 构造函数
- 会把 Parent 实例属性挂到原型上,多余、浪费
优化后:
Child.prototype = Object.create(Parent.prototype);
- 只继承原型,不调用 Parent 构造
- 干净、高效、标准
---
三、三句话背会
1. 用 Parent.call(this) 继承实例属性
2. 用 Object.create(Parent.prototype) 继承原型方法
3. 最后修正 constructor 指向
这就是 JS 最标准、最推荐的继承方式。
---
四、我再给你一句超简记忆版
call 拿属性,create 拿原型,constructor 指回去
---
题目1(基础组合继承)
function Parent() { this.name = "父亲"; } Parent.prototype.say = function () { console.log(this.name); }; function Child() { Parent.call(this); this.name = "儿子"; } Child.prototype = new Parent(); Child.prototype.constructor = Child; var child = new Child(); child.say();
问:输出什么?//儿子
执行过程 1. new Child() 2. 里面先跑: Parent.call(this) - 给当前 child 实例设置: this.name = "父亲" 3. 然后跑: this.name = "儿子" - 把上面的 name 覆盖了 4. 调用 child.say() - say 在原型上, this 指向 child - 所以输出: this.name → 儿子 答案:儿子
---
题目2(寄生组合继承 + 箭头函数)
function Parent() { this.age = 40; } Parent.prototype.getAge = () => { console.log(this.age); }; function Child() { Parent.call(this); this.age = 20; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; var child = new Child(); child.getAge();
问:输出什么?//undefined
关键知识点 箭头函数没有自己的 this 它的 this 是定义时外层作用域的 this,不是调用时的实例。 这里: - 箭头函数写在最外层(全局) - 所以 this = window / 全局对象 - 全局没有 age 属性 答案:undefined 两题核心总结 1. 普通函数: this 指向调用它的对象(实例) 2. 箭头函数: this 指向定义时的外层 this,不绑定实例
===ES6 Class 继承===
题目 1(基础 class + extends)
class Person { constructor() { this.name = "普通人"; } sayName() { console.log(this.name); } } class Student extends Person { constructor() { super(); // 先执行父类的 constructor this.name = "小学生"; // 覆盖掉前面的 name } } const stu = new Student(); stu.sayName();
问:输出什么?//小学生
- extends 就是继承
- super() = 调用父类构造方法
- 先设 name = 普通人,又改成 小学生
- 普通方法 this 指向实例
---
题目 2(super 传参)
class Animal { constructor(type) { this.type = type; } showType() { console.log(this.type); } } class Cat extends Animal { constructor() { super("猫"); // 传给父类构造:type = "猫" } } const cat = new Cat(); cat.showType();
问:输出什么?
- super("猫") 等于调用父类:new Animal("猫")
- 所以实例上 this.type = "猫"
---
超简口诀
- extends:继承
- super():调用父构造
- 后面赋值会覆盖前面
==JS 继承 · 终极总结==
一、ES5 组合继承(老写法)
function Parent() { this.name = '父'; } Parent.prototype.say = function () {}; function Child() { Parent.call(this); // 继承实例属性 } Child.prototype = new Parent(); // 继承原型方法 Child.prototype.constructor = Child;
- 优点:属性、方法都能继承
- 缺点:Parent 执行两次,冗余
二、寄生组合继承(最优 ES5)
function Child() { Parent.call(this); } // 核心:只继承原型,不执行构造 Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child;
- 面试必写:最标准、最高效
三、ES6 class 继承(最简单)
class Parent {} class Child extends Parent { constructor() { super(); // 必须写! } }
- extends:继承
- super():调用父类构造
- 语法清晰,日常开发用这个
四、this 必坑
普通函数在原型上
- this → 实例对象
箭头函数在原型上
- 没有自己的 this
- this → 定义时外层作用域(通常是全局)
- 千万别在原型上写箭头函数!
五、一句话串起所有继承
- ES5:call 拿属性,Object.create 拿原型
- ES6:extends + super 一把梭
- 普通函数 this 看调用,箭头函数 this 看定义
欢迎关注我,一起进步!扫描下方二维码即可加我QQ
浙公网安备 33010602011771号