继承,继承,继承,哪里有家产可以继承

核心概念

如果说继承到底是怎么实现的,其实很简单-JavaScript的原型链的机制。想象一下,每一个对象之间都有一个隐形的链条🔗到了一起,我们可以顺着这个⛓️看到甚至使用另一个对象的一些东西。 但是知道这个⛓️并不能保证学会继承,我们通过⛓️连接到了一起,但是你那里的一个属性或者方法是你的呢,还是我的呢,我如果用了,你还有吗,你如果还想要怎么办,这才是理解JavaScript继承的必须要掌握的内容,通过这些继承可以分为

  1. 原型链继承
  2. 构造函数继承
  3. 组合继承
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合继承
  7. class继承

关键原理

原型链继承

将子类的原型对象prototype指向父类的一个实例

js 复制代码
	function Parent() {
	  this.parentProperty = true;
	  this.colors = ['red', 'blue', 'green']; // 引用类型属性
	}
	Parent.prototype.getParentProperty = function() {
	  return this.parentProperty;
	};
	
	function Child() {
	  this.childProperty = false;
	}
	
	// 实现继承的关键:将 Child 的原型指向 Parent 的实例
	Child.prototype = new Parent();
	
	// 修复 constructor 指向,这一步很重要
	Child.prototype.constructor = Child;
	
	const instance1 = new Child();
	const instance2 = new Child();
	
	instance1.colors.push('black');
	
	console.log(instance1.getParentProperty()); // true
	console.log(instance1.colors); // ['red', 'blue', 'green', 'black']
	console.log(instance2.colors); // ['red', 'blue', 'green', 'black'] (注意:引用类型被共享了!)

缺点:

  • 引用类型的属性被所有实例共享
  • 无法在创建子类实例时向父类构造函数传参

逻辑图表

flowchart TD subgraph G1 [原型链继承结构] A[实例 instance] B[子类构造函数 SubType] C[子类原型对象 SubType.prototype] D[父类实例 parentInstance] E[父类原型对象 SuperType.prototype] F[Object.prototype] G[null] A -- __proto__ --> C B -- prototype --> C C -- __proto__ --> E D -- __proto__ --> E E -- __proto__ --> F F -- __proto__ --> G end subgraph G2 [属性/方法查找顺序] H[访问 instance.someProperty] I{实例自身存在?} J{子类原型存在?} K{父类原型存在?} L[返回属性值] M[返回 undefined] H --> I I -- 否 --> J J -- 否 --> K K -- 是 --> L I -- 是 --> L K -- 否 --> M end

构造函数继承

在子类构造函数内部调用父类构造函数(使用 callapply方法改变执行上下文)

js 复制代码
	function Parent(name) {
	  this.name = name;
	  this.colors = ['red', 'blue', 'green'];
	}
	Parent.prototype.sayName = function() {
	  console.log(this.name);
	};
	
	function Child(name, age) {
	  Parent.call(this, name); // 在子类构造函数中调用父类构造函数,并传递参数
	  this.age = age;
	}
	
	const instance1 = new Child('Alice', 10);
	const instance2 = new Child('Bob', 12);
	
	instance1.colors.push('black');
	
	console.log(instance1.name); // 'Alice'
	console.log(instance1.colors); // ['red', 'blue', 'green', 'black']
	console.log(instance2.colors); // ['red', 'blue', 'green'] (引用类型属性是独立的)
	console.log(instance1.sayName); // undefined (无法继承父类原型上的方法)

缺点:

  • 无法继承父类原型上的方法和属性

逻辑图表

flowchart TD subgraph Parent构造函数 Parent["Parent(name)"] ParentProto["Parent.prototype"] ParentMethod["sayName: function()"] end subgraph Child构造函数 Child["Child(name, age)"] ChildProto["Child.prototype"] end subgraph instance1实例 instance1["instance1: Child"] instance1Props["name: 'Alice'
age: 10
colors: ['red','blue','green','black']"] end subgraph instance2实例 instance2["instance2: Child"] instance2Props["name: 'Bob'
age: 12
colors: ['red','blue','green']"] end Parent -.->|prototype| ParentProto ParentProto -.->|constructor| Parent ParentProto --> ParentMethod Child -.->|prototype| ChildProto ChildProto -.->|constructor| Child instance1 --> |__proto__| ChildProto instance2 --> |__proto__| ChildProto Child --> |new 操作符创建| instance1 Child --> |new 操作符创建| instance2 Child --> |Parent.callthis, name| Parent

组合继承

结合了原型链继承和构造函数继承。使用构造函数继承实例属性,使用原型链继承原型方法。

js 复制代码
	function Parent(name) {
	  this.name = name;
	  this.colors = ['red', 'blue', 'green'];
	}
	Parent.prototype.sayName = function() {
	  console.log(this.name);
	};
	
	function Child(name, age) {
	  Parent.call(this, name); // 第二次调用 Parent,继承实例属性
	  this.age = age;
	}
	
	Child.prototype = new Parent(); // 第一次调用 Parent,继承原型方法
	Child.prototype.constructor = Child; // 修复 constructor 指向
	Child.prototype.sayAge = function() {
	  console.log(this.age);
	};
	
	const instance1 = new Child('Nicholas', 29);
	const instance2 = new Child('Greg', 27);
	
	instance1.colors.push('black');
	
	console.log(instance1.colors); // ['red', 'blue', 'green', 'black']
	instance1.sayName(); // 'Nicholas'
	instance1.sayAge(); // 29
	
	console.log(instance2.colors); // ['red', 'blue', 'green']
	instance2.sayName(); // 'Greg'

缺点:

  • 父类构造函数被调用了两次

逻辑图表

flowchart TD subgraph Parent构造函数 ParentCTOR["Parent(name)"] ParentProto["Parent.prototype"] ParentMethod["sayName: function()"] end subgraph Child构造函数 ChildCTOR["Child(name, age)"] ChildProto["Child.prototype"] ChildMethod["sayAge: function()"] end subgraph instance1实例 instance1["instance1: Child"] instance1Props["name: 'Alice'
age: 10
colors: ['red','blue','green','black']"] end subgraph instance2实例 instance2["instance2: Child"] instance2Props["name: 'Bob'
age: 12
colors: ['red','blue','green']"] end ParentCTOR -.->|prototype| ParentProto ParentProto -.->|constructor| ParentCTOR ParentProto --> ParentMethod ChildCTOR -.->|prototype| ChildProto ChildProto -.->|constructor| ChildCTOR ChildProto --> ChildMethod %% 关键继承关系 ChildCTOR --> |Parent.callthis, name| ParentCTOR ChildProto --> |Prototype| ParentProto instance1 --> |__proto__| ChildProto instance2 --> |__proto__| ChildProto ChildCTOR --> |new 操作符创建| instance1 ChildCTOR --> |new 操作符创建| instance2

原型式继承

基于一个现有对象创建一个新对象,而不必创建自定义类型。

js 复制代码
	function object(o) {
	  function F() {}
	  F.prototype = o;
	  return new F();
	}
	
	const person = {
	  name: 'Nicholas',
	  friends: ['Shelby', 'Court', 'Van']
	};
	
	const anotherPerson = object(person);
	anotherPerson.name = 'Greg';
	anotherPerson.friends.push('Rob'); // 修改会影响到原对象
	
	const yetAnotherPerson = object(person);
	yetAnotherPerson.name = 'Linda';
	yetAnotherPerson.friends.push('Barbie'); // 修改会影响到原对象和其他实例
	
	console.log(person.friends); // ['Shelby', 'Court', 'Van', 'Rob', 'Barbie']

其实就是Object.create()

js 复制代码
	const person = {
	  name: 'Nicholas',
	  friends: ['Shelby', 'Court', 'Van']
	};
	
	const anotherPerson = Object.create(person); // 本质上与上面的 object 函数相同
	anotherPerson.name = 'Greg';
	anotherPerson.friends.push('Rob');

缺点:

  • 与原型链继承类似,引用类型的属性值始终会被所有实例共享

逻辑图表

flowchart TD subgraph "现有对象 (parentObj)" parent["parentObj"] parentProps["name: 'Nicholas'
friends: ['Shelby', 'Court', 'Van']"] end subgraph "新对象 (childObj)" child["childObj"] childProps["新增的属性..."] end parent --> |Prototype| ObjectProto["Object.prototype"] ObjectProto --> |Prototype| null child --> |Prototype| parent child --> |自身属性| childProps

寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象

js 复制代码
	function createAnother(original) {
	  const clone = Object.create(original); // 通过调用函数创建一个新对象
	  clone.sayHi = function() { // 以某种方式来增强这个对象
	    console.log('hi');
	  };
	  return clone;
	}
	
	const person = {
	  name: 'Nicholas',
	  friends: ['Shelby', 'Court', 'Van']
	};
	
	const anotherPerson = createAnother(person);
	anotherPerson.sayHi(); // 'hi'

缺点 ​:与原型式继承相同,​引用类型属性被共享 ;同时,为对象添加函数会导致函数难以复用

逻辑图表

flowchart TD subgraph "原始对象 (originalObj)" original["originalObj"] originalProps["name: '李白'
friends: ['杜甫','陆游']"] end subgraph "寄生函数 (createEnhancedObj)" direction TB create["createEnhancedObj(original)"] createStep1["以 original 为原型
创建新对象 (clone)"] createStep2["增强 clone 对象
添加新方法/属性"] createStep3["返回增强后的 clone 对象"] create --> createStep1 --> createStep2 --> createStep3 end subgraph "新对象 (enhancedObj)" enhanced["enhancedObj"] enhancedProps["新增的属性或方法..."] end original --作为原型--> enhanced createStep3 --返回--> enhanced enhanced --自身属性--> enhancedProps

寄生组合式继承

通过借用构造函数继承属性,但使用一种更高效的方式继承方法(不调用父类构造函数为子类原型赋值,而是直接获取父类原型的一个副本)

js 复制代码
	function inheritPrototype(child, parent) {
	  const prototype = Object.create(parent.prototype); // 创建父类原型的副本
	  prototype.constructor = child; // 修复副本的 constructor 指向
	  child.prototype = prototype; // 将副本赋值给子类的原型
	}
	
	function Parent(name) {
	  this.name = name;
	  this.colors = ['red', 'blue', 'green'];
	}
	Parent.prototype.sayName = function() {
	  console.log(this.name);
	};
	
	function Child(name, age) {
	  Parent.call(this, name); // 只调用一次 Parent 构造函数
	  this.age = age;
	}
	
	// 实现继承:不再需要 new Parent()
	inheritPrototype(Child, Parent);
	
	Child.prototype.sayAge = function() {
	  console.log(this.age);
	};
	
	const instance = new Child('Nicholas', 29);
	instance.sayName(); // 'Nicholas'
	instance.sayAge(); // 29

这种方式只调用了一次父类构造函数 ,避免了在子类原型上创建不必要的属性,同时保持了原型链不变。被认为是引用类型最理想的继承范式

逻辑图表

flowchart TD subgraph "父类构造函数 (Parent)" ParentCTOR["Parent(name)"] ParentProto["Parent.prototype"] ParentMethod["sayName: function()"] end subgraph "子类构造函数 (Child)" ChildCTOR["Child(name, age)"] ChildProto["Child.prototype"] ChildMethod["sayAge: function()"] end subgraph "实例 (instance)" instance1["instance: Child"] instance1Props["name: 'Nicholas'
age: 29
colors: ['red','blue','green','black']"] end ParentCTOR -.->|prototype| ParentProto ParentProto -.->|constructor| ParentCTOR ParentProto --> ParentMethod ChildCTOR -.->|prototype| ChildProto ChildProto -.->|constructor| ChildCTOR ChildProto --> ChildMethod %% 关键继承关系 ChildProto --> |Object.create
Prototype| ParentProto ChildCTOR --> |Parent.call
继承实例属性| ParentCTOR instance1 --> |__proto__| ChildProto ChildCTOR --> |new 操作符创建| instance1

Class继承

es6提供的语法糖,引入classextendssuper关键字

js 复制代码
	class Parent {
	  constructor(name) {
	    this.name = name;
	    this.colors = ['red', 'blue', 'green'];
	  }
	
	  sayName() {
	    console.log(this.name);
	  }
	}
	
	class Child extends Parent { // 使用 extends 继承
	  constructor(name, age) {
	    super(name); // 必须在构造函数中首先调用 super(),它相当于 Parent.call(this, name)
	    this.age = age;
	  }
	
	  sayAge() {
	    console.log(this.age);
	  }
	}
	
	const instance1 = new Child('Tom', 18);
	const instance2 = new Child('Jerry', 20);
	
	instance1.colors.push('black');
	
	console.log(instance1.colors); // ['red', 'blue', 'green', 'black']
	instance1.sayName(); // 'Tom'
	instance1.sayAge(); // 18
	
	console.log(instance2.colors); // ['red', 'blue', 'green']
	instance2.sayName(); // 'Jerry'

如果把这段语法糖换成底层的代码,类似于寄生组合继承

相关推荐
司宸3 小时前
Cursor 编辑器高效使用与配置全指南
前端
维维酱3 小时前
为什么说 useCallback 实际上是 useMemo 的特例
前端·react.js
王六岁3 小时前
Vue 3 表单验证组合式 API,提供类似 Ant Design Vue Form 的强大表单验证功能
前端·vue.js
机构师3 小时前
<uniapp><日期组件>基于uniapp,编写一个自定义的日期组件
前端·javascript
lypzcgf3 小时前
Coze源码分析-资源库-创建提示词-前端源码
前端·人工智能·typescript·系统架构·开源软件·react·安全架构
fury_1233 小时前
vue3:el-date-picker三十天改成第二十九天的23:59:59
前端·javascript·vue.js
小周同学@3 小时前
DOM常见的操作有哪些?
前端·javascript
文心快码BaiduComate3 小时前
5句话让文心快码实现一个大模型MBTI测试器
前端·后端·llm
橙某人3 小时前
💫分享一个CSS技巧:用径向渐变实现弯曲框缺口效果
前端·css