JS高级程序设计之class继承重点详解

目录
  • 引言
  • 写法
    • constructor
    • 特性
  • 继承
    • 题外话

引言

前文已提过:在 class 出现之前,JavaScript 实现继承是件麻烦事,构造函数继承有加上原型上的函数不能复用的问题;原型链继承又存在引用值属性的修改不独立的问题;组合继承又存在两次调用构造函数的问题,寄生组合继承,写起来又太麻烦了,总之,在 class 出现前,JavaScipt 实现继承真是件麻烦事儿。

然而,class 的出现真的改变这一现状了吗?

不如往下看。

写法

与函数类型相似,定义类也有两种主要方式:类声明和类表达式。

// 类声明 class Person {}

// 类表达式 const Animal = class {};

不过,与函数定义不同的是,虽然函数声明可以提升,但类定义不能。

与函数构造函数一样,多数编程风格都建议类名的首字母要大写,以区别于通过它创建的实例。

类可以包含:

  • 构造函数方法
  • 实例方法
  • 获取函数
  • 设置函数
  • 静态类方法

这些项都是可选的

constructor

class Person {
    constructor(name) {
        this.name = name
        console.log('person ctor');
    }
}
let p1 = new Person("p1")

constructor 会告诉解释器 在使用 new 操作符创建类的新实例时,应该调用这个函数。

等同于

function Person(name){
    this.name = name
    console.log('person ctor')
}
let p1 = new Person("p1")

类构造函数与构造函数的主要区别是,这样写会报错:

class Animal {}
let a = Animal(); // TypeError: class constructor Animal cannot be invoked without 'new'

所以,new 操作符是强制要写的;

使用 new 时,原理与 new 一个对象也是一样的,因为太重要了,再强调一遍:

(1) 在内存中创建一个新对象。

(2) 这个新对象内部的[[Prototype]]指针被赋值为构造函数的 prototype 属性。

(3) 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。

(4) 执行构造函数内部的代码(给新对象添加属性)。

(5) 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。

特性

从各方面来看,ECMAScript 类就是一种特殊函数。

我们可以用 typeof 打印试试:

class Person {}
console.log(typeof Person); // function

也可以用 instanceof 检查它的原型链

class Person {}
let p = new Person()
console.log(p instanceof Person); // true

通过 class 构造的每个实例都对应一个唯一的成员对象,这意味着所有成员都不会在原型上共享;

class Person {
 constructor() {
 this.name = new String('Jack');
 this.sayName = () => console.log(this.name);
 }
}
let p1 = new Person();
let p2 = new Person();
console.log(p1.name === p2.name) // false
console.log(p1.sayName === p2.sayName) // false

如果想要共享,就改写成方法,写在 constructor 外面:

class Person {
 constructor() {
 this.name = new String('Jack');
 }
 sayName(){
      console.log(this.name);
 }
}
let p1 = new Person();
let p2 = new Person();
console.log(p1.sayName === p2.sayName) // true

我们可以在方法前面加 static 关键字,实现:静态类成员。我们不能在类的实例上调用静态方法,只能通过类本身调用。不做赘述。

继承

ECMAScript 6 新增特性中最出色的一个就是原生支持了类继承机制。虽然类继承使用的是新语法,但背后依旧使用的是原型链

让我们再回顾构造函数继承和原型链继承 2 个经典的问题:

① 构造函数继承的问题:构造函数外在原型上定义方法,不能重用

function SuperType(){}
SuperType.prototype.sayName = ()=>{console.log("bob")}
function SubType(){
    SuperType.call(this) // 构造函数继承
}
let p1 = new SubType()
console.log(p1.sayName()) // Uncaught TypeError: p1.sayName is not a function

而原型链继承可以解决这一点:

function SuperType(){}
SuperType.prototype.sayName = ()=>{console.log("bob")}
function SubType(){}
SubType.prototype = new SuperType()  // 原型链继承
let p1 = new SubType()
console.log(p1.sayName()) // bob

② 原型链继承的问题:原型中包含的引用值会在所有实例间共享。

function SuperType(){
    this.name = ["bob","tom"];
}
function SubType(){}
SubType.prototype = new SuperType()  // 原型链继承
let p1 = new SubType()
p1.name.push("jerry")
let p2 = new SubType()
console.log(p2.name) //  ['bob', 'tom', 'jerry']

而构造函数继承可以解决这一点:

function SuperType(){
    this.name = ["bob","tom"];
}
function SubType(){
    SuperType.call(this) // 构造函数继承
}
let p1 = new SubType()
p1.name.push("jerry")
let p2 = new SubType()
console.log(p2.name) //  ['bob', 'tom']

class 继承有这两个问题吗??

代码一试便知:

class SuperType{}
SuperType.prototype.sayName = ()=>{console.log("bob")}
class SubType extends SuperType{
}
let p1 = new SubType()
p1.sayName() // bob

问题①,没有问题,在构造函数外写的原型继承,公共方法还是能访问的!!

class SuperType{
    constructor(){
        this.name=["bob","tom"]
    }
}
class SubType extends SuperType{
}
let p1 = new SubType()
let p2 = new SubType()
p1.name.push("Jerry")
console.log(p2.name) //  ['bob', 'tom']

问题②,没有问题,在 constructor 的引用值属性,修改不会产生干涉!!

class 继承完美的解决了构造函数继承的问题,和原型链继承的问题,写起来也没有组合继承、寄生继承那么麻烦,如果非得用 JS 模拟面向对象编程,class 必不可少!!

题外话

其实写 Class C 和 C.prototype 一起写是很危险的:明明都在操作面向对象的类了,还要操作原型链。类操作和原型操作是两种不同的设计思路,有兴趣可见本瓜一年前的一篇文章:“类”设计模式和“原型”设计模式——“复制”和“委托”的差异

以上就是JS高级程序设计之class继承重点详解的详细内容,更多关于JS高级程序设计class继承的资料请关注我们其它相关文章!

(0)

相关推荐

  • JavaScript面向对象之class继承类案例讲解

    1. 面向对象class继承 在上面的章节中我们看到了JavaScript的对象模型是基于原型实现的,特点是简单,缺点是理解起来比传统的类-实例模型要困难,最大的缺点是继承的实现需要编写大量代码,并且需要正确实现原型链. 有没有更简单的写法?有! 我们先回顾用函数实现 Student 的方法: function Student(name) { this.name = name; } // 现在要给这个Student新增一个方法 Student.prototype.hello = function

  • JS面向对象编程——ES6 中class的继承用法详解

    本文实例讲述了 ES6 中class的继承用法.分享给大家供大家参考,具体如下: JS是一种基于对象的语言,要实现面向对象,写法跟传统的面向对象有很大的差异.ES6引入了Class语法糖,使得JS的继承更像面向对象语言的写法. 此篇博客,分为:基本介绍.Vue使用案例 基本介绍 Class可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多: class Father { } class Son extends Father { } 代码定义了一个Son 类

  • ES6 javascript中Class类继承用法实例详解

    本文实例讲述了ES6 javascript中Class类继承用法.分享给大家供大家参考,具体如下: 1. 基本用法 Class 之间可以通过extends关键字实现继承, 这比 ES5 的通过修改原型链实现继承, 要清晰和方便很多. class ColorPoint extends Point {} 上面代码定义了一个ColorPoint类, 该类通过extends关键字, 继承了Point类的所有属性和方法. 但是由于没有部署任何代码, 所以这两个类完全一样, 等于复制了一个Point类. 下

  • JavaScript之class继承_动力节点Java学院整理

    JavaScript的对象模型是基于原型实现的,特点是简单,缺点是理解起来比传统的类-实例模型要困难,最大的缺点是继承的实现需要编写大量代码,并且需要正确实现原型链. 有没有更简单的写法?有! 新的关键字class从ES6开始正式被引入到JavaScript中.class的目的就是让定义类更简单. 我们先回顾用函数实现Student的方法: function Student(name) { this.name = name; } Student.prototype.hello = functio

  • JS高级程序设计之class继承重点详解

    目录 引言 写法 constructor 特性 继承 题外话 引言 前文已提过:在 class 出现之前,JavaScript 实现继承是件麻烦事,构造函数继承有加上原型上的函数不能复用的问题:原型链继承又存在引用值属性的修改不独立的问题:组合继承又存在两次调用构造函数的问题,寄生组合继承,写起来又太麻烦了,总之,在 class 出现前,JavaScipt 实现继承真是件麻烦事儿. 然而,class 的出现真的改变这一现状了吗? 不如往下看. 写法 与函数类型相似,定义类也有两种主要方式:类声明

  • Js类的构建与继承案例详解

    JS里类的定义和继承实在五花八门,所以单独开一个笔记本记录. 定义 派生于Object的方式 1.new Object:在创建对象后动态定义属性.方法 var Car = new Object; Car.color = "red"; Car.showColor = function(){ console.log(this.color); } //想要继承就要先构造空对象然后用__proto__原型链来继承 var Car1 = new Object; //或者 = {} Car1.__

  • javascript 中的继承实例详解

    javascript 中的继承实例详解 阅读目录 原型链继承 借用构造函数 组合继承 寄生组合式继承 后记 继承有两种方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法. 由于函数没有签名,在ECMAScript中无法实现接口继承.ECMAScript只支持实现继承,而且实现继承主要依靠原型链来实现. 下面介绍几种js的继承: 原型链继承 原型链继承实现的本质是重写原型对象,代之以一个新类型的实例.代码如下: function SuperType() { this.pr

  • C++中继承(inheritance)详解及其作用介绍

    概述 面向对象程序设计中最重要的一个概念是继承 (inheritance). 继承允许我们依据另一个类来定义一个类, 这使得创建和维护一个应用程序变得更统一. 这样做也达到了重用代码功能和提高执行效率的效果. 类的概念 一个类中包含了若干数据成员和成员函数. 不同的类中的数据成员和成员函数各不相同. 但是有时两个类的内容基本相同. 例如: 继承的概念 继承 (inheritance) 就是在一个已存在的类的基础上建立一个新的类. 已存在的类: 基类 (base class) 或父类 (fathe

  • C++继承模式详解

    目录 继承 继承的概念 继承的定义 继承关系和访限定符 继承方式 父类和子类对象赋值转化 继承中的作用域 子类的默认成员函数 继承与友元 继承与静态成员 复杂的菱形继承 虚继承 继承的总结 继承 继承的概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能.继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程. 继承的定义 上面的基类也可以叫父类,派生类也可以叫子类. 继承关系和访限定符 继承方式 接下来用代码测试上面的

  • C++继承的赋值转换与菱形虚拟继承深入详解

    目录 一.继承的概念及定义 1.1.继承的概念 1.2.继承的定义 二.基类和派生类对象赋值转换 三.继承中的作用域 3.1.继承同名成员处理方式 3.2.继承同名静态成员处理方式 3.3.继承与友元 3.4.继承与静态成员 四.派生类的默认成员函数 五.复杂菱形继承及菱形虚拟继承 5.1.继承分类 5.2.虚拟继承解决菱形继承问题原理 一.继承的概念及定义 继承是面向对象三大特性之一. 1.1.继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许

  • 一篇Vue、React重点详解大全

    目录 Vue 生命周期 MVVM 双向绑定原理 虚拟dom v-for为什么要用key nextTick v-show.v-if的区别 vue单页面应用如何首页优化 Vue 的父组件和子组件生命周期钩子执行顺序是什么 Vue 中的 computed 是如何实现的 React 不同之处 vue和react的区别 Vue 生命周期 vue2: 总共分为8个阶段:创建前/后,载入前/后,更新前/后,销毁前/后. 创建前/后: 在beforeCreate阶段,由于还未初始化,vue实例的挂载元素el和数

  • js事件源window.event.srcElement兼容性写法(详解)

    如下所示: <html> <body> <p>一个好处就是 我想让body(或其他元素内)的某些对象响应事件 就不用挨个儿去写 只要在外层上写一个 然后检查event.srcElement.tagName或其他属性是不是你想要的来决定是否响应事件就可以了.不过好像event.srcElement在ff里不起作用 </p> <p>比如</p> <p> <input value="input">

  • 使用Node.js实现ORM的一种思路详解(图文)

    ORM是O和R的映射.O代表面向对象,R代表关系型数据库.二者有相似之处同时也各有特色.就是因为这种即是又非的情况,才需要做映射的. 理想情况是,根据关系型数据库(含业务需求)的特点来设计数据库.同时根据面向对象(含业务需求)的特点来设计模型(实体类).然后再去考虑如何做映射.但是理想很骨jian感dan,现实太丰fu满za. 没见哪个ORM是这么做的,也没见哪位高手会这么做设计.那么实际情况是什么样子的呢?以.net的Entity Framework为例. DB frist,就是先设计好数据库

  • node.js中grunt和gulp的区别详解

    node.js中grunt和gulp的区别详解 自nodeJS登上前端舞台,自动化构建变得越来越流行.目前最流行的当属grunt和gulp,这两个光看名字挺像,功能也差不多,不过gulp能在grunt这位大哥如日中天的境况下开辟出自己的一片天地,有着她独到的优点. 易用 Gulp相比Grunt更简洁,而且遵循代码优于配置策略,维护Gulp更像是写代码. 高效 Gulp相比Grunt更有设计感,核心设计基于Unix流的概念,通过管道连接,不需要写中间文件. 高质量 Gulp的每个插件只完成一个功能

随机推荐