金色小芝麻

vuePress-theme-reco 金色小芝麻    2021 - 2023
金色小芝麻 金色小芝麻

Choose mode

  • dark
  • auto
  • light
主页
分类
  • JavaScript
  • BUG复盘
  • SVG笔记
  • TypeScript
  • 个人总结
  • CSS笔记
  • 开发工具
  • 前端入门
  • Vue2.0
  • 性能优化
  • 架构学习
  • 每日一题
标签
时间轴
社交
  • 掘金 (opens new window)
author-avatar

金色小芝麻

83

文章

27

标签

主页
分类
  • JavaScript
  • BUG复盘
  • SVG笔记
  • TypeScript
  • 个人总结
  • CSS笔记
  • 开发工具
  • 前端入门
  • Vue2.0
  • 性能优化
  • 架构学习
  • 每日一题
标签
时间轴
社交
  • 掘金 (opens new window)
  • 前端入门

    • 博客搭建过程及相关配置
    • 数组中的16种常用方法
    • JS中的数据类型简析——基本数据类型值
    • JS中的数据类型object基础
    • JS中三种常见的判断
    • 数据类型之间的区别(堆内存Heap VS 栈内存Stack)
    • JS基础练习题及解析
    • 开关灯的小案例
    • JS中数据类型检测方法——typeof
    • 条件判断几个小练习
    • JS循环——for循环
    • 自定义属性实现选项卡小案例
    • 字符串中的12种常用方法
    • DOM操作的简单原理
    • JS实现隔行变色鼠标跟随小案例
    • JS中function的基础知识
    • JS中数组去重的三种方法
    • JS中时间格式化的三种方法
    • JS中URL参数处理的三种方法
    • JS小案例——获取随机验证码
    • DOM操作之——获取DOM标签的9种方式
    • DOM操作之——DOM节点类型及属性
    • DOM操作之——元素的增删改、样式修改、插入内容
    • JS中THIS相关问题梳理
    • JS中的变量提升机制
    • 在全局:私有上下文中:带VAR和不带VAR的区别
    • 作用域和作用域链查找机制
    • JS中堆栈内存的释放问题
    • JS中的闭包机制
    • ES3和ES6定义变量的区别
    • JS中的单例和工厂设计模式
    • JS中的面向对象OOP理论基础
    • 构造函数创建自定义类
    • JS中的原型和原型链
    • Math中常用的9种方法
    • 从一道阿里经典面试题剖析函数三种角色
    • 重写内置call
    • 重写一个内置new
    • 浏览器渲染页面的主体流程
    • 减少DOM的回流和重绘
    • JS中的多种继承方式
    • JS中数据类型检测四种方式的优缺点
    • JS中的正则表达式&&全面梳理
    • 非严格模式 🆚 严格模式的区别
    • 数组方法reduce、filter、flat
    • 获取数组中最大值/最小值的三种基础方法
    • 轮播图——渐隐渐显版
    • 深克隆 VS 浅克隆|深比较 VS 浅比较|回调函数
    • 轮播图——左右切换版
    • 事件及事件绑定、事件对象及事件传播
    • 从在地址栏输入网址到看到页面的过程&&AJAX基础
    • 想自学JS吗?想提升JS底层原理吗?76张脑图带你彻底搞懂原生JS
    • 面试手写API系列
    • 30张脑图带你从零开始学VUE
    • JS中的盒子模型
    • 初识JS-基础中的基础
    • 前端发展简史
    • JS中的三大类输出方式

JS中的多种继承方式

vuePress-theme-reco 金色小芝麻    2021 - 2023

JS中的多种继承方式

金色小芝麻 2020-06-18

JS本身是基于面向对象开发的编程语言:所以我们学习JS主要就是学习类,以及类的实例,在学习类的原型上提供的方法;

类:

  • 封装、继承、多态

# 一、类的封装

  • 类也是一个函数,把实现一个功能的代码进行封装,以此实现“低耦合高内聚”

# 二、多态:重载、重写

  • 重写:子类重写父类上的方法(伴随着继承运行的)
  • 重载:相同的方法,由于参数或者返回值不同,具备了不同的功能(JS中不具备严格意义上的重载,JS中的重载:同一个方法内,根据传参不同实现不同的功能)

# 1、在后台语言当中:

  • 重载定义:
    • 指的是在方法名字相同,都叫fn,但是参数的类型或者参数的个数不同,所以导致这两个方法不是一个方法,而变成了两个方法;(我们认为这就是方法的重载)
  • 重载的目的:(为了分担压力的)
    • 正常情况下,客户端向服务器发请求,一台服务器允许多个客户端同时发请求,所以服务器要有很强的抗压能力,
    • 如果说我们把所有的功能(比如传两个参数干什么,传一个参数干什么)都放在一个方法中,那这个方法承载的压力就会比较大,
    • 所以我们一般通过重载的方式来分发出这个方法承载的压力;
//=> 在后台语言中,
public void fn(int x,init y){

}
public void fn(int x){

}
fn(10,20);  执行第一个FN
fn(10);  执行第二个FN
fn('小芝麻');  报错  

# 2、在JS语言中:这种方式并不是重载

  • 由于JS中的变量提升机制,所以只执行第二个
  • 所以说JS中不具备严格意义上的重载,
function fn(x, y) {

}

function fn(x) {

}
fn(10, 20);  执行第二个FN
fn(10); 执行第二个FN 
  • JS中的重载:同一个方法内,根据传参不同实现不同的功能
function fn(x, y) {
    if(y===undefined){
        // ...
        return;
    }
    // ....
}
fn(10);
fn(10, 20); 

# 三、继承:子类继承父类中的方法

这里想到了一个笑话:

A去医院检查,被查出是“类风湿性关节炎”
A:好郁闷,得了…
B:这个病注意点就好了啊
A:不行,因为我们打算要孩子
B:你这个病和孩子有啥关系啊
….
10min  A的脑子中 "类风湿性关节炎” => 类是继承的

在生物学上的继承类似于:

父亲:基因
   AAA  BBB  CCC

儿子:继承了父亲的基因(把父亲的部分基因,直接拷贝到自身上)
   AAA  BBB 
   儿子基因突变  AAB  BBA,但是对父亲是没有影响的

正常的后台语言中的继承,就是这种拷贝式的继承,但是我们JS中的继承并不是这种继承;

# 1、继承的定义:

  • 在JS语言中,它的继承和其它编程语言还是不太一样的

# 2、继承的目的:

  • 让子类的实例同时也具备父类中私有的属性和公共的方法

# 3、JS中继承方案

# 第一种:原型继承(让子类的原型等于父类的实例即可)

function Parent() {
	this.x = 100;
}
Parent.prototype.getX = function getX() {
	return this.x;
};

function Child() {
	this.y = 200;
}
//=> 让子类的原型等于父类的实例
Child.prototype = new Parent; //=>原型继承

Child.prototype.getY = function getY() {
	return this.y;
};

let c1 = new Child;
console.log(c1); 

现在c1 能用的方法是:私有的 y 和公有的 getY方法,以及Object上的公有方法;

# 1.原理

  • 子类的实例,能够用子类私有的和原型上公有的
  • 父类的实例,可以使用 父类私有和原型上公有的

所以我们只要让子类的原型等于父类的实例即可

  • Child.prototype = new Parent

# 2.特点

  • 1、父类中私有和公有的属性方法,最后都变为子类实例公有的
  • 2、和其他语言不同的是
    • 原型继承并不会把父类的属性方法“拷贝”给子类,
    • 而是让子类实例基于__proto__原型链找到自己定义的属性和方法“指向/查找”方式的

# 3.优缺点

  • c1.__proto__.xxx = xxx 修改子类原型(原有父类的一个实例)中的内容,内容被修改后,对子类的其他实例有影响,但是对父类的实例不会有影响

  • c1.__proto__.__proto__.xxx = xxx直接修改的是父类原型,这样不仅会影响其它父类的实例,也影响其他子类的实例

  • JS中的重写影响很大

# 第二种:CALL继承(只能继承父类中私有的,不能继承父类中公共的)

function Parent() {
	this.x = 100;
}
Parent.prototype.getX = function getX() {
	return this.x;
};

function Child() {
	// 在子类构造函数中,把父类当做普通方法执行(没有父类实例,父类原型上的那些东西也就和它没关系了)
	// this -> Child的实例c1
	Parent.call(this); // this.x=100 相当于强制给c1这个实例设置一个私有的属性x,属性值100,相当于让子类的实例继承了父类的私有的属性,并且也变为了子类私有的属性 “拷贝式”
	this.y = 200;
}
Child.prototype.getY = function getY() {
	return this.y;
};

let c1 = new Child;
console.log(c1); 

# 1.原理

  • 在子类构造函数中,把父类当作普通函数执行(没有父类实例,父类原型上的那些东西也就和他没有关系了)
    • 此时 parent(this) 这个 this 是 window
  • 通过 call 强制改变 this 的指向为 Child 中的 this
    • Parent.call(this)
    • 此时 this 是当前 Child 中的 this

相当于强制给实例设置了一个私有的属性,相当于让子类的实例继承了父类的私有的属性,并且也变为了子类私有的属性“拷贝式”

# 2.特点

  • 只能继承父类中私有的,公有的不行(并且是把父类私有的变成子类私有的)

我们满意的继承方式应该是:父类私有变为子类私有 父类公有变为子类公有

# 第三种:寄生组合式继承(CALL继承 + 另类原型继承)

function Parent() {
	this.x = 100;
}
Parent.prototype.getX = function getX() {
	return this.x;
};

function Child() {
	Parent.call(this);
	this.y = 200;
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

Child.prototype.getY = function getY() {
	return this.y;
};

let c1 = new Child;
console.log(c1); 
  • 上面我们说了 call 继承有个好处:能把父类私有的变成子类私有的;所以 call 继承我们留下来

那现在我们只要想办法让父类中公有的也能变成子类中公有的即可;

# 1.原理

我们刚才的原型继承,大体的实现了这个功能;

  • 原型继承是创建了父类的一个实例,从而达到子类继承父类的效果;它最终实现的核心点是:Child.prototype.__proto__ === Parent.prototype;
  • 如果说我们在原型继承时不想要父类私有的内容,我们只需要:Child.prototype.__proto__ = Parent.prototype;

这样虽然能实现效果但是IE浏览器中不允许我们操作__proto__,那我们有什么可以替换它呢?

// Object.create();创建一个空对象,让其原型链指向obj
let obj = {
	xxx: 'xxx'
};
console.log(Object.create(obj));

所以可以写成:Child.prototype = Object.create(Parent.prototype);即可

# 2.缺点:

  • 由于新创建的原型没有constructor属性,所以我们自己手动加一个默认的;

# 四、ES6中的类和继承

  • class类
class Parent {
	constructor() {
		this.x = 100;
	}
	// Parent.prototype.getX=function...
	getX() {
		return this.x;
	}
}
  • 继承
// 继承: extends Parent(类似于寄生组合继承)
// 注意:继承后一定要在CONSTRUCTOR第一行加上SUPER
class Child extends Parent {
	constructor() {
		super(); //=>类似于我们之前的CALL继承  super(100,200):相当于把Parent中的constructor执行,传递了100和200
		this.y = 200;
	}
	getY() {
		return this.y;
	}
}
let c1 = new Child;
console.log(c1);

// Child(); //=>Uncaught TypeError: Class constructor Child cannot be invoked without 'new'  ES6中创建的就是类,不能当做普通函数执行,只能new执行

# 思维导图

欢迎来到 金色小芝麻
看板娘