乐趣区

关于javascript:面向对象编程

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

在这篇文章中,你将理解到在 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` property
person.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 true
console.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 中的面向对象编程和类,以及它如何帮忙你放弃代码的清洁、干燥和可重用。咱们涵盖了面向对象编程的四个外围概念,包含形象、封装、继承和多态。

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

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

退出移动版