在编写软件时,你所做的大部分工作就是创立和连贯多个值和办法,让他们一起工作,以便提供应用程序的性能。面向对象编程能够帮忙你更容易地,并且是申明式地实现这些性能。

在这篇文章中,你将理解到在JavaScript中开始应用类和面向对象编程办法所须要的所有。

前置常识

在浏览本篇文章之前,你须要把握JavaScript的基础知识。

面向对象编程

面向对象编程(OOP)是一种编程范式,在大型、简单和踊跃保护的我的项目中,OOP每天都在被应用。

OOP使你更容易创立和治理你应用程序的许多局部,并在不使它们相互依赖的状况下将它们连接起来。接下来,让咱们看看OOP的四个次要概念。

形象

OOP中的形象是指只向用户公开必要的性能,同时暗藏简单的外部工作,使程序更容易应用和了解。

举例来说,当你在手机上发送音讯时,所有将你的信息传递给对方的函数和逻辑都是暗藏的,因为你不须要晓得它们是如何工作的。

同样地,在编程中,如果你正在构建一个帮忙金融app验证用户身份和银行信息的API,应用你API的开发者不须要晓得你应用的是哪种数据库系统,也不须要晓得你如何调用你的数据库。他们惟一须要晓得的是要调用的函数,以及他们须要提供的参数。

形象有助于升高复杂性,减少可用性,并使应用程序的变动不那么具备破坏性。

封装

封装是将相干代码捆绑在一个独立单元中的过程。封装使代码的其余局部无奈扭转应用程序捆绑局部的工作形式,除非你显式地进入该单元并扭转它们。

举例来说,如果你正在建设一个航班预订的API,把搜寻航班的代码和预订航班的代码离开是有意义的。这样一来,两个不同的开发者就能够无缝地在每个局部工作而不发生冲突,因为每个开发者都没有理由间接操作对方的代码。

封装有助你升高复杂性,减少代码可用性。

继承

OOP中的继承升高了代码重复性,使你可能通过继承应用程序局部的属性和办法,在其余中央构建你的应用程序。

OOP中继承的一个重要劣势是升高了代码重复性。

多态

在编程中,多态是一个术语,用来形容一个代码或程序,它能够通过依据给定的数据返回一个响应或后果来解决多个类型的数据。

举例来说,你有一个用来向产品目录增加产品的表单,并有三种不同类型的产品。通过多态,你能够创立一个单一类办法来格式化所有品种的产品,而后再将它们增加到数据库中。

多态能够帮忙你打消复杂性和不必要的ifswitch语句,因为在编写简单的程序时,这些语句会变得简短。

接下来咱们来看看JavaScript对象。

JavaScript对象

JavaScript中的对象是一个无序的键值对汇合,也就是属性和值。对象的键能够是字符串,值能够是任何类型。

接下来,咱们来看看如何在JavaScript中创建对象。

创建对象

在JavaScript中创立一个对象相当容易:

const car = {    name: 'Ford',    year: 2015,    color: 'red',    description: function () {    return `${this.name} - ${this.year} - ${this.color}`;    }}

上述代码申明了一个car对象,对象属性包含nameyearcolor以及description函数。

拜访对象属性

在JavaScript中有两种形式拜访对象属性。咱们接着往下看:

应用点符号

上面的例子展现了如何应用点合乎来拜访对象属性。

const country = {    name: 'Spain',    population: 4000000,    description: function () {    return `${this.name} - ${this.population}`;    }}

如果你有一个像下面所示的对象,你能够应用objectName.keyName的格局,它应该返回给定键的值:

console.log(country.name); // returns 'Spain'

应用数组符号

上面的例子展现了如何应用数组符号来拜访对象属性。

const job = {  role: "Software Engineer",  'salary': 200000,  applicationLink: "meta.com/careers/SWE-role/apply",  isRemote: true,};

如果你有一个像下面所示的对象,你能够应用objectName[keyName]的格局,它应该返回给定键的值:

console.log(job[role]); // returns 'Software Engineer'

此外,你只能用数组符号来拜访salary属性。试图用点符号来获取它将返回一个谬误:

console.log(job.'salary'); // SyntaxError: Unexpected string

接下来,咱们来看看如何批改对象属性。

批改对象属性

你能够在JavaScript动静增加、编辑和删除对象属性。

编辑属性

你能够应用赋值=操作符来批改对象的值。这里一个例子:

const person = {  name: "John",  age: 30,  job: "Software Developer",  country: "Nigeria",  car: "Ford",  description: function () {    return `${this.name} - ${this.age} - ${this.job.role} - ${this.country.name} - ${this.car.name}`;  },};

你还能够更改上述对象中name的值:

person.name = "Smith Doe";console.log(person.name); // returns "Smith Doe"

增加新属性

其余语言中的对象与JavaScript中的对象之间的一个显著区别是,在创立后能够向对象增加新的属性。

为了向对象中增加新属性,你能够应用点符号:

// adding a new `race` propertyperson.race = "Asian";console.log(person.race); // returns "Asian"

删除对象属性

JavaScript容许你通过应用delete关键字从一个对象中删除属性:

delete person.race;console.log(person.race); // returns 'undefined'
留神:你只能删除现有的对象属性。

查看属性

在向一个对象增加或删除属性之前,确定该对象上是否存在该属性是一个很好的主见。这个看似简略的查看将为你节俭几个小时的工夫来调试一个由反复值引起的谬误。

要确定一个属性是否存在于一个对象上,你能够应用in关键字:

console.log('name' in person) // returns trueconsole.log('race' in person) // returns false

下面的代码对于name的查看返回true,因为name存在,而对于被删除的race属性返回false

当初你晓得了什么是对象以及如何应用它们,让咱们通过学习类来迈出JavaScript的OOP的下一步。

在编程中,类是由程序员定义的一种构造,而后用来创立同一类型的多个对象。例如,如果你正在建设一个解决各种汽车的应用程序,你能够创立一个Car类,它具备实用于所有车辆的基本功能和属性。而后,你能够用这个类来制作其余的汽车,并为它们增加每种汽车所特有的属性和办法。

为了扩大你在后面的例子中看到的对象,如果你想创立另一个job对象,你须要创立这样的货色:

const job2 = {  role: "Head of Design",  salary: 175000,  applicationLink: "amazon.com/careers/hod-role",  isRemote: false,};

然而,正如你所看到的,下面创立多个对象的形式容易出错,并且不可扩大。因为你不可能在每次须要创立一个job时都写出这些,从而产生100个job对象。这时类就派上用场了。

创立类

你能够创立一个Job类来简化创立多个job

class Job {  constructor(role, salary, applicationLink, isRemote) {    this.role = role;    this.salary = salary;    this.applicationLink = applicationLink;    this.isRemote = isRemote;  }}

上述代码创立了一个Job类,具备rolesalaryapplicationLink以及isRemote属性。当初你能够应用new关键字创立不同的job

let job1 = new Job(  "Software Engineer",  200000,  "meta.com/careers/SWE-role/apply",  true);let job2 = new Job(  "Head of Design",  175000,  "amazon.com/careers/hod-role",  false);

下面的代码创立了两个不同的job,蕴含所有的必填字段。让咱们通过在控制台中打印出两个job来看看这是否见效:

console.log(job1);console.log(job2);

打印后果为:

图中显示了两个job和它们在控制台中的所有属性。

this关键字

this关键字被认为是最令人蛊惑的关键字,因为它蕴含有不同的含意,这取决于在代码中什么地位呈现。

在下面的例子中,this关键词指的是用Job类创立的任何对象。因而,constructor办法外部的this.role = role; 意味着,将你正在创立的这个对象的role属性定义为给该构造函数的任何值。

另外请留神,在创立一个新的Job对象时,你给出的初始值必须是按程序的。例如,你像这样创立一个job3对象:

let job3 = new Job(  "netflix.com/careers/HOE-role",  true,  "Head of Engineering");console.log(job3)

上述代码创立了一个新的job3对象,该对象有着谬误的属性程序,并缺失了isRemote属性。你会在控制台中失去以下后果:

下面的图片显示了job3对象在控制台中打印进去的样子。请留神,isRemoteundefined

接下来,咱们来看看如何给类增加办法。

类办法

当创立类时,你能够增加多个属性。为了在类外部增加办法,你能够在constructor函数前面增加:

class Vehicle {  constructor(type, color, brand, year) {    this.type = type;    this.color = color;    this.brand = brand;    this.year = year;  }  start() {    return "Vroom! Vroom!! Vehicle started";  }  stop() {    return "Vehicle stopped";  }  pickUpPassengers() {    return "Passengers picked up";  }  dropOffPassengers() {    return "Passengers dropped off";  }}

上述代码定义了一个具备typecolorbrandyear属性的Vehicle类,同时具备startstoppickUpPassengersdropOffPassengers办法。

为了运行对象中的办法,能够应用点符号:

const vehicle1 = new Vehicle("car", "red", "Ford", 2015);const vehicle2 = new Vehicle("motorbike", "blue", "Honda", 2018);console.log(vehicle1.start()); // returns 'Vroom! Vroom!! Vehicle started'console.log(vehicle2.pickUpPassengers()); // returns "Passengers picked up"

接下来,让咱们看看计算属性。

计算属性

编程在很大水平上取决于值的变动,相似于你不想硬编码类属性的大部分值,你可能会有一些基于某些值而变动的动静属性名称。你能够应用计算属性来做到这一点;让咱们看看是如何做到的。

例如,在创立工作列表API时,你可能心愿开发者可能将applyThrough函数名称改为另一个词,如applyThroughLinkedInapplyThroughIndeed,这取决于他们的平台。要应用计算属性,你须要用方括号把属性名称包起来:

let applyThrough = "applyThroughIndeed";class Job {  constructor(role, salary, applicationLink, isRemote) {    this.role = role;    this.salary = salary;    this.applicationLink = applicationLink;    this.isRemote = isRemote;  }  [applyThrough]() {    if (applyThrough === "applyThroughLinkedin") {      return `Apply through Linkedin`;    } else if (applyThrough === "applyThroughIndeed") {      return `Apply through Indeed`;    }  }}

下面的代码申明了applyThrough变量的字符串值为 "applyThroughIndeed",以及一个能够调用job1.applyThroughIndeed()的计算方法[applyThrough]

计算属性使其余开发者更容易定制他们的代码。

Getters and Setters

在团队中编写代码时,你想限度谁能够扭转代码库的某些局部,以避免出现bug。倡议在OOP中,某些变量和属性在必要时应该被暗藏。

接下来,让咱们学习getset关键字是如何工作的。

Getters

当构建热衷于确保用户隐衷的应用程序时,例如,法律问题管理应用程序,你要管制谁能够拜访用户的数据,如姓名、电子邮件和地址。get关键字能够帮忙你实现这一指标。你能够限度谁能够访问信息:

class Client{  constructor(name, age) {    this._name = name;    this._age = age;  }  get name() {    if (userType === "Lawyer") {      return this._name;    } else {      return "You are not authorized to view this information";    }  }}

下面的代码申明了一个Client类,其属性是nameage,还有一个getter,只在用户是律师时返回姓名。如果你试图以助理的身份拜访名字,你会失去一个谬误:

let userType = "Assistant";const person = new Client("Benjamin Adeleye", 24);console.log(person.name); // returns 'You are not authorized to view this information'
留神:将this.name改为this._name以防止命名抵触。

Setters

set关键字是get关键字的背面。get关键字用于管制谁能够拜访属性,而set关键字管制谁能够扭转属性的值。

为了理解set关键字如何工作,让咱们通过增加一个setter来扩大后面的例子:

set name(newName) {  if (userType === "Lawyer" && verifiedData === true) {    this._name = newName;  } else {    console.log("You are not authorized to change this information");  }}

下面的代码申明了一个set办法,只有在用户是律师并且文件曾经被验证的状况下,才容许对名字进行更改:

let userType = "Lawyer";let verifiedData = false;let client = new Client("Benjamin Adeleye", 30);client.name = "Adeleye Benjamin";console.log(client.name); // returns 'You are not authorized to change this information'
留神:以getset办法为前缀的办法别离称为gettersetter函数。

接下来,让咱们看看动态属性和办法。

动态值

有时你想创立绑定到类而不是类的实例的属性和办法。例如,你可能想要一个计算数据库中客户数量的属性,但你不心愿这个值绑定到类的实例上。

动态属性

为了追踪数据库客户数量,你能够应用static关键字:

static clientCount = 0;

上述代码申明了一个动态clientCount属性,其值为0。你能够像这样来拜访动态属性:

let cl = new Client("Benjamin Adeleye", 30);console.log(Client.clientCount); // returns 0
留神:试图应用console.log(cl.clientCount);拜访动态属性会返回undefined,因为动态属性被绑定到类而不是实例上。

接下来,让咱们看看静态方法。

静态方法

创立静态方法跟创立动态属性非常相似,因为你只须要在办法名称前加上static关键字:

static increaseClientCount() {  this.clientCount++;}

下面的代码申明了一个动态的increateClientCount办法,该办法每次被调用时都会减少clientCount

静态方法和属性使得创立能够间接用于类而不是实例的辅助函数变得容易。

公有值

ECMAScript2022的更新反对JavaScript类中的公有值。

公有字段和办法将类的封装晋升到了一个新的程度,因为你当初能够创立只能在类申明的大括号内应用的属性和办法,而在这些大括号外的任何代码都无法访问它们。

接下来让咱们看看公有属性。

公有属性

你能够通过在变量前加上#号来申明类中的公有属性。让咱们通过为每个客户增加一个惟一的ID来改良Client类的局部:

class Client {  #client_unique_id = "";  constructor(name, age, id) {    this._name = name;    this._age = age;    this.#client_unique_id = id;  // same as Client class...  }

下面的代码申明了一个公有的#client_unique_id变量,只能在类申明中应用和拜访。试图在类外拜访它将返回一个谬误:

let cl = new Client("Benjamin Adeleye", 30, "##34505833404494");console.log(cl.name);console.log(cl.#client_unique_id); // returns Uncaught SyntaxError: Private field '#client_unique_id' must be declared in an enclosing class

公有办法

如前所述,公有办法只能在类申明中拜访。为了学习公有办法的工作原理,咱们将增加一个公有办法,从数据库中获取客户的案件文件文档:

#fetchClientDocs(id) {   return "Fetching client with id: " + id;}

上述代码当初能够在一个公共函数中应用,用户将调用该函数来获取客户端的文件。公有函数的实质是将所有的底层认证和对数据库的调用从应用该代码的用户或开发人员那里暗藏起来。

留神:你也能够创立公有动态、gettersetter函数。

接下来,咱们来理解如何链式类办法。

办法链

作为一个开发者,你可能最喜爱做的事件之一就是用尽可能少的代码实现一个性能。你能够在JavaScript中通过链式办法来实现这一点。例如,当一个用户登录到你的应用程序时,你想把用户的状态改为"在线",当他们退出时,你再把它改回"离线":

class Twita {  constructor(username, offlineStatus) {    this.username = username;    this.offlineStatus = offlineStatus;  }  login() {    console.log(`${this.username} is logged in`);    return this;  }  setOnlineStatus() {    // set the online status to true    this.offlineStatus = false;    console.log(`${this.username} is online`);    return this;  }  setOfflineStatus() {    // set the offline status to true    this.offlineStatus = true;    console.log(`${this.username} is offline`);    return this;  }  logout() {    console.log(`${this.username} is logged out`);    return this;  }}

上述代码申明了一个Twita类,具备usernameofflineStatus属性,并具备loginlogoutsetOnlineStatussetOfflineStatus办法。为了链式应用这些办法,你能够应用点符号:

const user = new Twita("Adeleye", true);user.login().setOnlineStatus().logout().setOfflineStatus();

上述代码将在user对象上顺次运行所有的函数并返回一个响应:

留神:你须要在每个函数的开端通过返回this来返回以后对象。

接下来,咱们来看看如何通过继承来建设在现有的类上。

类继承

在解决对象时,你很可能会遇到这样的状况:你须要创立一个与你代码中曾经存在的对象十分类似的对象,但你晓得它们不可能是一样的。例如,当建设一个电子商务应用程序时,你会有一个Product类,它有namepricedescription以及image等属性,以及formatPriceaddToCart等办法。

然而,如果你有多种不同规格的产品,怎么办?

  • 带有作者、分量和页面详情的书籍。
  • 家具的长度、宽度、高度和木材类型的详细信息。
  • 电影光盘的尺寸、工夫和内容详情。

在这种状况下,为每个产品创立一个类将导致大量的代码反复,这是OOP和个别编程的次要规定之一:don't repeat yourself(DRY)。

类继承容许你在其余对象的根底上创建对象。例如,你能够通过创立一个Product类来解决下面提到的问题:

class Product {  constructor(name, price, description, image) {    this.name = name;    this.price = price;    this.description = description;    this.image = image;  }  formatprice() {    return `${this.price}$`;  }  addToCart() {    cart.add(this);  }}

接着,应用extends关键字为每个产品类型创立一个子类:

class Book extends Product {  constructor(name, price, description, image, weight, pages, author) {    super(name, price, description, image);    this.author = author;    this.weight = weight;    this.pages = pages;  }}class Movie extends Product {  constructor(    name,    price,    description,    image,    size,    contentDescription,    duration  ) {    super(name, price, description, image);    this.size = size;    this.contentDescription = contentDescription;    this.duration = duration;  }}class Furniture extends Product {  constructor(    name,    price,    description,    image,    woodType,    length,    height,    width  ) {    super(name, price, description, image);    this.woodType = woodType;    this.length = length;    this.height = height;    this.width = width;  }}

上述代码通过扩大Product类来申明BookMovieFurniture产品类型。

在下面的代码中,有两个新的关键字:extendssuper。接下来,让咱们来看一下它们。

extends关键字

extends关键字是不言自明的;它被用来扩大另一个类的能力。在咱们的例子中,咱们用它通过扩大Product类来创立BookMovieFurniture类。

super关键字

super关键字打消了你须要为每个新的子类反复进行的屡次申明。例如,上述代码中调用的super函数取代了以下代码:

this.name = name;this.price = price;this.description = description;this.image = image;

还记得DRY吗?这样做的起因是为了不反复上述代码,因为它曾经写在了Product类外面。

如果子类不须要构造函数,super函数能够被疏忽:

class Animal {  constructor(name, species, color) {    this.name = name;    this.species = species;    this.color = color;  }  makeSound() {    console.log(`${this.name} makes a noise.`);  }}class Bird extends Animal {  fly() {    console.log(`${this.name} flies.`);  }}

上述代码申明了一个父类Animal和一个子类Bird,该类不须要构造函数来运行,因为它没有在构造函数中申明任何新变量。因而,上面的代码应该能够运行:

const bird = new Bird('Chloe', 'Parrot', 'Green'); console.log(`${bird.name} is a ${bird.color} ${bird.species}`);

上述代码能够运行,即便Bird类外面没有namecolor或者species

如果你只须要给子类增加办法的话,你不须要调用super或者反复雷同的构造函数。

总结

在本教程中,你理解了JavaScript中的面向对象编程和类,以及它如何帮忙你放弃代码的清洁、干燥和可重用。咱们涵盖了面向对象编程的四个外围概念,包含形象、封装、继承和多态。

你还理解到,在你的代码中应用面向对象的编程范式和类有很多长处,从改善利用构造和代码的可重用性到升高代码的复杂性。

以上就是本文的全部内容,如果对你有所帮忙,欢送珍藏、点赞、转发~