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

核心概念

如果说继承到底是怎么实现的,其实很简单-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'

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

相关推荐
WeiXiao_Hyy几秒前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡17 分钟前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone23 分钟前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_090142 分钟前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农1 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king1 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳1 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵2 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星2 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js