ES5 的构造函数和 ES6 的类有什么区别

文章目录

    • 语法不同
    • 方法定义方式不同
    • 继承方式不同
    • 类内部的this指向不同
    • 静态成员定义方式不同
    • 访问器属性
    • 类的类型检查

在JavaScript中,类和构造函数都被用来创建对象,接下来会从以下几点说说两者的区别:

语法不同

  • 构造函数使用函数来定义
  • 类使用class关键字来定义

ES6 的class可以看作是一个语法糖,这种写法只是让对象原型的写法更加清晰、更像面向对象编程的语法。

比如这是一个构造函数生成实例对象的例子,

function Point(x, y) {this.x = x;this.y = y;
}
Point.prototype.toString = function () {return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
console.log(p.toString()) // (1, 2)

上面的代码用类来改写是这样的,

class Point {constructor(x, y) {this.x = x;this.y = y;}toString() {return '(' + this.x + ', ' + this.y + ')';}
}
var p = new Point(1, 2);
console.log(p.toString()) // (1, 2)

所以类完全可以看作构造函数的另一种写法,

class Point {// ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true

类的数据类型就是函数,类本身就指向构造函数。

方法定义方式不同

  • 构造函数的方法都是定义在构造函数的原型上,即原型上的方法被所有实例共享
  • 类的方法可以直接定义在类中,也可以定义在类的原型上,而且定义在类中的方法是不可枚举的。

构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。

class Point {constructor() {// ...}toString() {// ...}toValue() {// ...}
}
// 等同于
Point.prototype = {constructor() {},toString() {},toValue() {},
};

以下代码表明类的内部所有定义的方法,都是不可枚举的(non-enumerable)

class Point {constructor(x, y) {// ...}toString() {// ...}
}
Object.keys(Point.prototype) // []
Object.getOwnPropertyNames(Point.prototype) // ["constructor","toString"]

而直接定义在原型上的都是可枚举的

class Point {constructor(x, y) {this.x = x;this.y = y;}toString() {return '(' + this.x + ', ' + this.y + ')';}
}
Point.prototype.toString1 = function() {// ...
};console.log(Object.keys(Point.prototype)) // [ 'toString1' ]
console.log(Object.getOwnPropertyNames(Point.prototype)) // [ 'constructor', 'toString', 'toString1' ]

继承方式不同

  • 构造函数通过将一个构造函数作为另一个构造函数的原型,实现原型链继承,可以实现单继承和多继承。
  • 类通过extends关键字来实现继承,支持单继承,子类继承父类的属性和方法,可以调用父类的构造函数。

构造函数的继承需要手动处理原型链,并确保构造函数和原型的正确指向,以下示例可看出

function Animal(name) {this.name = name;
}Animal.prototype.eat = function() {console.log(this.name + ' is eating.');
};function Dog(name, breed) {Animal.call(this, name); // 调用父类构造函数this.breed = breed;
}Dog.prototype = Object.create(Animal.prototype); // 设置子类的原型对象
Dog.prototype.constructor = Dog; // 重设构造函数指向自身Dog.prototype.bark = function() {console.log(this.name + ' is barking.');
};const dog = new Dog('Buddy', 'Golden Retriever');
dog.eat(); // 输出: Buddy is eating.
dog.bark(); // 输出: Buddy is barking.

类的继承示例

class Animal {constructor(name) {this.name = name;}eat() {console.log(`${this.name} is eating.`);}
}class Dog extends Animal {constructor(name, breed) {super(name); // 调用父类构造函数this.breed = breed;}bark() {console.log(`${this.name} is barking.`);}
}const dog = new Dog('Buddy', 'Golden Retriever');
dog.eat(); // Buddy is eating.
dog.bark(); // Buddy is barking.

需要注意的是,子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。

class Dog extends Animal {
}
// 等同于
class Dog extends Animal {constructor(...args) {super(...args);}
}

另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。

class Animal {constructor(name) {this.name = name;}
}
class Cat extends Animal {constructor(name, color) {this.color = color; // ReferenceErrorsuper(name);this.color = color; // 正确}
}

类内部的this指向不同

  • ES5中,类内部函数中的this指向调用函数的对象
  • ES6中,类内部方法中的this指向实例对象
    注意:如果静态方法中包含this关键字,这个this指的是类,而不是实例。
class Foo {static classMethod() {return 'hello';}}Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function

静态成员定义方式不同

  • ES5中,静态成员需要通过给构造函数手动添加属性或方法来实现
function Person(name, age) {this.name = name;this.age = age;
}Person.staticMethod = function() {console.log("This is a static method.");
};
  • ES6中,可以使用static关键字定义静态属性或方法
class Person {constructor(name, age) {this.name = name;this.age = age;}static staticMethod() {console.log("This is a static method.");}
}

访问器属性

  • ES6中引入了getter和setter方法,可以使用get和set关键字定义访问器属性。
class Person {constructor(name, age) {this._name = name;this._age = age;}get name() {return this._name;}set name(value) {this._name = value;}
}var person = new Person("Tom", 20);
console.log(person.name); // 输出 "Tom"
person.name = "Jerry";
console.log(person.name); // 输出 "Jerry"

在上述例子中,通过getter和setter方法定义了name访问器属性,可以通过person.name进行属性的访问和修改。

类的类型检查

  • ES5中,可以使用instanceof运算符来判断对象的类型

instanceof 运算符通过检查对象的原型链来确定对象是否是某个构造函数的实例。它会逐级向上查找对象的 __proto__(或 prototype)属性,直到找到与指定构造函数的 prototype 属性相等的对象或到达原型链的末尾。如果找到了相等的对象,则返回 true,否则返回 false

function Person() {}
var person = new Person();
console.log(person instanceof Person); // 输出 true
  • ES6中,可以使用typeof运算符来判断类的类型
class Person {}
var person = new Person();
console.log(typeof person); // 输出 "object"
console.log(typeof Person); // 输出 "function"

在上述例子中,typeof person返回objecttypeof Person返回function,可以看出,在ES6中类的类型也是object,类本身的类型是function

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/48533.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

# Lua与C++交互(二)———— 交互

C 调用lua 基础调用 再来温习一下 myName “beauty girl” C想要获取myName的值,根据规则,它需要把myName压入栈中,这样lua就能看到;lua从堆栈中获取myName的值,此时栈顶为空;lua拿着myName去全局表中查…

合宙Air724UG LuatOS-Air LVGL API-概念

概念 在 LVGL 中,用户界面的基本构建块是对象。例如,按钮,标签,图像,列表,图表或文本区域。 属性 基本属性 所有对象类型都共享一些基本属性: Position (位置) Size (尺寸) Parent (父母) Cli…

【Jenkins】rpm方式安装Jenkins(2.401,jdk版本17)

目录 【Jenkins】rpm方式安装Jenkins 1、主机初始化 2、软件要求 RPM包安装的内容 配置文件说明 3、web操作 【Jenkins】rpm方式安装Jenkins 1、主机初始化 [rootlocalhost ~]# hostname jenkins[rootlocalhost ~]# bash[rootjenkins ~]# systemctl stop firewalld[roo…

YOLOv8教程系列:三、K折交叉验证——让你的每一份标注数据都物尽其用(yolov8目标检测+k折交叉验证法)

YOLOv8教程系列:三、K折交叉验证——让你的每一份标注数据都物尽其用(yolov8目标检测k折交叉验证法) 0.引言 k折交叉验证(K-Fold Cross-Validation)是一种在机器学习中常用的模型评估技术,用于估计模型的性…

Java详解编译型和解释型语言

在计算机的高级编程语言类型分为两种,分别是编译型和解释型,而Java既有编译型又有解释型 什么是编译型?什么是解释型? 字面上来说编译和解释都有‘翻译’的意思,而她们两个的区别是‘翻译’的时机不同,什…

自然语言处理从入门到应用——LangChain:链(Chains)-[通用功能:链的保存(序列化)与加载(反序列化)]

分类目录:《自然语言处理从入门到应用》总目录 本文介绍了如何将链保存(序列化)到磁盘和从磁盘加载(反序列化)。我们使用的序列化格式是json或yaml。目前,只有一些链支持这种类型的序列化。随着时间的推移&…

Python采集电商平台泳衣数据进行可视化分析

前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 环境使用: python 3.8 解释器 pycharm 编辑器 模块使用: 第三方模块 需要安装 requests —> 发送 HTTP请求 内置模块 不需要安装 csv —> 数据处理中经常会用到的一种文件格式 第三方模块安装&#xff1a…

实验五 Linux 内核的安装与加载

【实验目的】 掌握 uboot 的使用方法,能够使用 uboot 安装和加载内核 【实验环境】 ubuntu 14.04 发行版FS4412 实验平台 【注意事项】 实验步骤中以“$”开头的命令表示在 ubuntu 环境下执行,以“#”开头的命令表 示在开发板下执行 【实验步骤】 …

计算机视觉 -- 图像分割

文章目录 1. 图像分割2. FCN2.1 语义分割– FCN (Fully Convolutional Networks)2.2 FCN--deconv2.3 Unpool2.4 拓展–DeconvNet 3. 实例分割3.1 实例分割--Mask R-CNN3.2 Mask R-CNN3.3 Faster R-CNN与 Mask R-CNN3.4 Mask R-CNN:Resnet1013…

ES搭建集群

一、创建 elasticsearch-cluster 文件夹 创建 elasticsearch-7.8.0-cluster 文件夹,在内部复制三个 elasticsearch 服务。 然后每个文件目录中每个节点的 config/elasticsearch.yml 配置文件 node-1001 节点 #节点 1 的配置信息: #集群名称&#xff0…

【数据备份、恢复、迁移与容灾】上海道宁与云祺科技为企业用户提供云数据中心容灾备份解决方案

云祺容灾备份系统支持 主流虚拟化环境下的虚拟机备份 提供对云基础设施 云架构平台以及 应用系统的全方位数据保护 云祺容灾备份系统规范功能 增强决策能力 高效恢复数据至可用状态 有效降低恢复成本 更大限度减少业务中断时间 保障业务可访问性 开发商介绍 成都云祺…

Hadoop、Spark与Flink的基础架构及其关系和优异

Hadoop、Spark与Flink的基础架构及其关系和优异 前言Hadoop基础架构优点不足 Spark基础架构优点不足 Flink基础架构优点不足 结语:大数据框架的选择 前言 Hadoop、Spark和Flink是目前重要的三大分布式计算系统。它们都可以用于大数据处理,但在处理方式和…

LSTM数学计算公式

LSTM(长短期记忆网络)是一种循环神经网络(RNN)的变体,常用于处理时间序列相关的任务。下面将简要介绍LSTM的数学推导和公式模型。 在训练一般神经网络模型时,通常用,其中W为权重,X为输入&#…

算法通关村第九关——中序遍历与搜索树

1 中序遍历和搜索树原理 二叉搜索树按照中序遍历正好是一个递增序列。其比较规范的定义是: 若它的左子树不为空,则左子树上所有节点的值均小于它的根节点的值;若它的右子树不为空,则右子树所有节点的值均大于它的根节点的值&…

代码随想录算法训练营day42 | LeetCode 416. 分割等和子集

今天主要学习了一下01背包的二维数组和一维数组的做法,二维数组比较好理解,根据每次将不将物品i放入背包来充分的用到前面的数据遍历数组。两层for循环,物品个数放外边,背包重量放里边。二维数组遍历的时候,里层for循环…

【网络层协议】ARP攻击与欺骗常见的手段以及工作原理

个人主页:insist--个人主页​​​​​​ 本文专栏:网络基础——带你走进网络世界 本专栏会持续更新网络基础知识,希望大家多多支持,让我们一起探索这个神奇而广阔的网络世界。 目录 一、ARP攻击的常见手段 第一种:IP…

【健康医疗】Axure用药提醒小程序原型图,健康管理用药助手原型模板

作品概况 页面数量:共 20 页 兼容软件:Axure RP 9/10,不支持低版本 应用领域:健康管理,用药助手 作品申明:页面内容仅用于功能演示,无实际功能 作品特色 本作品为「用药提醒」小程序原型图…

QT使用QXlsx实现对Excel sheet的相关操作 QT基础入门【Excel的操作】

准备:搭建环境引用头文件QT中使用QtXlsx库的三种方法 QT基础入门【Excel的操作】_吻等离子的博客-CSDN博客 #include "xlsxdocument.h"QTXLSX_USE_NAMESPACE // 添加Xlsx命名空间(https://github.com/dbzhang800/QtXlsxWriter) or QXLSX_USE_NAMESPACE // 添加Xl…

Spring Boot 知识集锦之actuator监控端点详解

文章目录 0.前言1.参考文档2.基础介绍默认支持的端点 3.步骤3.1. 引入依赖3.2. 配置文件3.3. 核心源码 4.示例项目5.总结 0.前言 背景: 一直零散的使用着Spring Boot 的各种组件和特性,从未系统性的学习和总结,本次借着这个机会搞一波。共同学…

Android NDK JNI与Java的相互调用

一、Jni调用Java代码 jni可以调用java中的方法和java中的成员变量,因此JNIEnv定义了一系列的方法来帮助我们调用java的方法和成员变量。 以上就是jni调用java类的大部分方法,如果是静态的成员变量和静态方法,可以使用***GetStaticMethodID、CallStaticObjectMethod等***。就…