JavaScript类与原型--组织JavaScript代码
潜心研究JavaScript,了解使用类和原型的主要区别,以及两者对Lightning Web组件的影响。
关于JavaScript语言的故事是相当有趣的。对于那些不了解的人来说,下面是这种流行的多范式语言的一些亮点:
-
Brendan Eich(@BrendanEich)是网景通信公司的一名程序员,他在1995年仅用10天时间就创建了Mocha。
-
Mocha很快就被命名为JavaScript--它与Java完全没有关系--很可能是一种营销方式,以利用Java的流行。
-
在网站大多是静态的时候,JavaScript是作为一种提供动态编程体验的选择被引入的。
-
自1996年以来,ECMAScript语言规范(ECMA-262)定义并规范了JavaScript语言,现在已经是第11版。
-
JavaScript在全世界大约97%的浏览器上运行。
-
JavaScript已经成为Angular、React和Vue.js等客户端框架的主要选择,加上Node.js的运行时间。
自从JavaScript成为主流以来,我一直是以这种或那种方式使用JavaScript的项目的一部分。在那些早期的日子里,JavaScript被HTML文件引用,在向后端服务发送请求之前进行简单验证。现在,我在过去7年里所做的每一个基于网络的项目都使用了完全由JavaScript构建的客户端框架。
然而,JavaScript并非没有设计上的挑战,我在2017年6月发表的《JavaScript能否通过时间的考验?"中指出,早在2017年6月,我就曾发表过一篇文章。
当时没有提到的一个项目是关于在JavaScript中何时使用类,何时使用原型的讨论。我这篇文章的目标是关注这些概念--即使在利用现有的框架,如Salesforce Lightning Web Components(LWC)。
JavaScript中的原型概念
为了本文的目的,最好先谈一下JavaScript中的原型概念。
在JavaScript中,所有对象都从原型中继承属性和方法。让我们来看看下面这个原型的例子。
JavaScript
function Vehicle(vinNumber, manufacturer, productionDate, fuelType) {
this.manufacturer = manufacturer;
this.vinNumber = vinNumber;
this.productionDate = productionDate;
this.fuelType = fuelType;
}
Vehicle.prototype.vehicleInformation = function() {
var productionDate = new Date(this.productionDate * 1000);
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var year = productionDate.getFullYear();
var month = months[productionDate.getMonth()];
var day = productionDate.getDate();
var friendlyDate = month + ' ' + day + ', ' + year;
return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' was produced on ' + friendlyDate + ' using a fuel type of ' + this.fuelType;
}
作为这段代码的结果,有一个Vehicle 对象,可以用以下代码创建一个新的实例。
JavaScript
let rogue = new Vehicle('5N1FD4YXN11111111', 'Nissan', 1389675600, 'gasoline');
有了这些信息,可以用下面的方法调用vehicleInformation() 函数。
JavaScript
alert(rogue.vehicleInformation());
这将产生一个警报对话框,包含以下信息:
"日产汽车的VIN号码=5N1FD4YXN111111,于2014年1月14日生产,使用的燃料类型为汽油"
正如人们所期望的那样,可以引入第二个原型,即SportUtilityVehicle ,以进一步定义一个给定的车辆类型。
JavaScript
function SportUtilityVehicle(vinNumber, manufacturer, productionDate, fuelType, drivetrain) {
Vehicle.call(this, vinNumber, manufacturer, productionDate, fuelType);
this.drivetrain = drivetrain;
}
现在,我们可以新建一个SportUtilityVehicle ,而不是一个简单的Vehicle
JavaScript
let rogue = new SportUtilityVehicle('5N1FD4YXN11111111', 'Nissan', 1389675600, 'gasoline', 'AWD');
我们还可以用SportUtilityVehicle 原型定义一个新的版本。
JavaScript
SportUtilityVehicle.prototype.vehicleInformation = function() {
return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' utilizes drivetrain = ' + this.drivetrain + ' and runs on ' + this.fuelType;
}
现在,当使用以下方法调用vehicleInformation() 函数时。
JavaScript
alert(rogue.vehicleInformation());
出现一个警报对话框,包含以下信息:
"日产汽车的VIN号=5N1FD4YXN111111,利用动力系统=AWS,以汽油为燃料"
JavaScript类
从ECMAScript 2015(2015年6月作为第6版发布)开始,JavaScript引入了类的概念。虽然这可能会引起使用Java、C#和C++等语言的开发者的兴趣,但引入类选项的目的是为了让类使用更简单、更干净的语法来创建。事实上,该文档继续指出,类只是 "语法糖",使开发者更容易操作。
将之前的例子从原型转换为类,会出现如下图所示。
JavaScript
class Vehicle {
constructor(vinNumber, manufacturer, productionDate, fuelType) {
this.manufacturer = manufacturer;
this.vinNumber = vinNumber;
this.productionDate = productionDate;
this.fuelType = fuelType;
}
vehicleInformation() {
var productionDate = new Date(this.productionDate * 1000);
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var year = productionDate.getFullYear();
var month = months[productionDate.getMonth()];
var day = productionDate.getDate();
var friendlyDate = month + ' ' + day + ', ' + year;
return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' was produced on ' + friendlyDate + ' using a fuel type of ' + this.fuelType;
}
}
class SportUtilityVehicle extends Vehicle {
constructor(vinNumber, manufacturer, productionDate, fuelType, drivetrain) {
super(vinNumber, manufacturer, productionDate, fuelType);
this.drivetrain = drivetrain;
}
vehicleInformation() {
return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' utilizes drivetrain = ' + this.drivetrain + ' and runs on ' + this.fuelType;
}
}
如果我们需要在SportUtilityVehicle 类中添加getters和setters,可以将该类更新为如下所示。
JavaScript
class SportUtilityVehicle extends Vehicle {
constructor(vinNumber, manufacturer, productionDate, fuelType, drivetrain) {
super(vinNumber, manufacturer, productionDate, fuelType);
this.drivetrain = drivetrain;
}
vehicleInformation() {
return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' utilizes drivetrain = ' + this.drivetrain + ' and runs on ' + this.fuelType;
}
get drivetrain() {
return this._drivetrain;
}
set drivetrain(newDrivetrain) {
this._drivetrain = newDrivetrain;
}
}
正如你所看到的,其语法与Java或C#等语言相似。类的方法也允许属于原型链的函数和属性不引用Object.prototype的语法。唯一的要求是,构造函数总是被称为 "constructor"。
JavaScript类与原型
如上所述,JavaScript中的类只是语法上的糖,使在JavaScript中工作的功能开发者更容易。虽然这种方法允许那些来自Java、C#或C++等语言的人采用更常见的设计,但许多Javascript纯粹主义者建议根本就不要使用类。
事实上,Michael Krasnov在《请停止在JavaScript中使用类》一文中提到了一个令人担忧的问题。
绑定问题。由于类的构造函数与这个关键词密切相关,它可以引入潜在的绑定问题,特别是当你试图将你的类方法作为回调传递给外部例程时。
迈克尔接着提出了避免使用Javascript类的其他四个理由,但类选项的倡导者们很快就减轻了他的想法的分量。
从2021年开始,我一直坚持以下任何IT专业人士的使命宣言。
"把你的时间集中在提供特性/功能上,这将扩展你的知识产权的价值。在其他方面利用框架、产品和服务"。
当谈到在JavaScript中使用类或原型时,我觉得这应该由支持和维护代码库的团队来决定。如果他们的舒适度对遵循原型方法没有问题,那么他们应该相应地设计他们的组件。然而,如果他们倾向于利用类的概念,那么该团队的开发人员应该了解上述的绑定挑战,但应该继续前进并保持在他们的舒适区。
对Lightning Web组件的影响
几年前,Salesforce引入了Lightning Web Components(LWC),我在《Salesforce提供JavaScript编程模型》一文中谈到了这一点。近三年后,我发现自己在谈论使用类和原型方法对Salesforce开发人员的影响。
快速的答案是......这并不重要。Salesforce允许Lightning Web组件利用原型或类。JavaScript的典型继承模式是通过原型。但为了吸引那些习惯于经典继承的开发者,有这种语法糖来帮助开发者通过使用一种看起来非常像经典继承的方法来实现原型继承。
因此,当涉及到LWC时--因为LWC已经建立了一个很棒的基类组件供你扩展,所以它就是关于继承的--你也可以利用这个语法糖。
你不需要担心原型继承,尽管这一切都在引擎盖下发生。只要做经典的继承就可以了。
下面是一个例子,说明这可能是怎么回事。
JavaScript
import { LightningElement } from 'lwc';
export default class VehicleComponent extends LightningElement {
// properties go here
vehicleInformation() {
return this.manufacturer + ' vehicle with VIN Number = ' + this.vinNumber + ' utilizes drivetrain = ' + this.drivetrain + ' and runs on ' + this.fuelType;
}
}
看到了吗?LWC知道JavaScript给了你这种语法上的糖,让你变得如此简单。
结论
我承认,JavaScript并不是我在开发功能方面花费了大部分时间的语言。除了Angular的客户端开发和使用Node.js的小型努力之外,我作为服务开发者的主要工作往往集中在其他语言选项上。
同时,使用类方法而不是原型方法为Java、C#和C++开发者提供了类似的桥梁。虽然这里没有一个正确的答案,但了解类和原型在JavaScript中的工作原理是很重要的。
归根结底,我们的角色是能够支持你的代码库,解决缺陷,并快速实现特性和功能。实施的方法应该总是纯粹由功能团队的理解和能力来驱动,以保持选定的标准。
祝你今天过得非常愉快