JavaScript 中的 OOPS,包含易于理解的示例🔥🔥
介绍
在本文中,我们将学习和使用 JavaScript 中的面向对象编程 (OOP) 概念。
如果您来自面向对象编程语言(如 C++ 或 Java),您可能希望在 javascript 中使用基于类的语法。
如果您不了解 OOP,请不要担心,我将通过易于理解的示例向您解释所有概念。
很高兴有
在 ES6 发布之前,我们一直使用构造函数来运用 OOP 概念。但现在,借助 ES6 类,我们可以使用更类似于 C++ 或 Java 的语法来运用 OOP 概念。
(ES6 类在幕后使用了相同的构造函数)
太酷了
但是面向对象编程是什么?🤔
这是 OOP 的流行定义
面向对象编程(OOP)是一种基于对象概念的编程范例。
意思是?🙄
这意味着我们在 OOP 语言中实现的一切都是通过对象来实现的,这里的对象定义了现实世界的实体,如学生或汽车(更多详细信息即将推出)。
好的!但是我们为什么需要 OOP?
好问题!
开发 OOP 的主要目标是组织代码结构。使用 OOP,你可以编写更模块化、更易于维护的代码。你可以将代码与现实世界的实体关联起来。
通过使用 OOP,您可以确保只有一组代码的授权成员才能被其他成员访问。这使得您的代码完全安全,可以防止未经身份验证的访问(在代码内部)。
得到它??
现在,让我们逐步了解面向对象编程的主要概念。
目的
正如我上面提到的,对象就像现实生活中的实体一样。它们有自己的属性和方法。
假设一辆汽车是一个对象。它有很多特性,例如颜色、公司名称、模式名称和价格等等。我们可以在汽车上执行启动、刹车和停止等操作。在这里,汽车的特性是属性,而操作是方法。
如果您使用 javascript 有一段时间了,您可能会在代码中多次使用对象,但可能不是以 OOP 的方式。
让我在这里创建一个用户对象。
const user = {
name: 'Nehal Mahida',
userName: 'nehal_mahida',
password: 'password:)',
login: function(userName, password) {
if (userName === this.userName && password === this.password) {
console.log('Login Successfully');
} else {
console.log('Authentication Failed!!');
}
},
};
user.login('nehal', 'nehal');
user.login('nehal_mahida', 'password:)');
// Authentication Failed!!
// Login Successfully
上面的代码非常直观。我创建了一个用户对象,它拥有一些属性以及用户可以执行的操作。
没什么新鲜事吧?
让我们了解更多 OOP 概念。
班级
类是现实生活中实体的蓝图。它描述了对象的外观、特性以及我们可以对其执行的操作。
类只是一个模板。您无法对其执行任何操作。请将类视为您的网站用户体验设计(线框图)。您创建它是为了了解您的网站 UI 最终会是什么样子。用户无法像在实际网站上那样与您的线框图进行交互。
我们从一个类实例化一个对象。我们可以创建一个类的多个实例。
让我们举个例子。
class User {
#password;
constructor(name, userName, password) {
this.name = name;
this.userName = userName;
this.#password = password;
}
login(userName, password) {
if (userName === this.userName && password === this.#password) {
console.log('Login Successfully');
} else {
console.log('Authentication Failed!!');
}
}
setPassword(newPassword) {
this.#password = newPassword;
}
};
const nehal = new User('Nehal Mahida', 'nehal_mahida', 'password:)');
const js = new User('JavaScript', 'js', 'python:)');
nehal.login('nehal_mahida', 'password:)'); // Login Successfully
js.login('js', 'python:)'); // Login Successfully
console.log(nehal.name); // Nehal Mahida
console.log(nehal.password); // undefined
console.log(nehal.#password); // Syntax Error
nehal.setPassword('new_password:)');
nehal.login('nehal_mahida', 'password:)'); // Authentication Failed!!
nehal.login('nehal_mahida', 'new_password:)'); // Login Successfully
这里我创建了一个名为 的类User
,它有一些属性和方法。然后我使用new User()
并传递所需属性的值来创建该类的实例。
您是否看到constructor
我们在代码中从未调用过的一个方法?
事实上,该方法被称为🙄
当我们使用关键字 javascript 从类创建对象时,new
内部会调用构造函数方法,该方法初始化类的公共和私有属性。此处的对象可以访问类的所有公共属性和方法。
什么是public
和private
属性??
默认情况下,类中声明的所有属性都是公共的,这意味着您可以从类外部调用和修改它们。您可以在构造函数内部或外部声明公共属性。这里的name
和userName
是公共属性。
那么私人的呢?
再看一下代码。你注意到密码写在了构造函数方法的外面,并以 为前缀了吗#
?
Hash( #
) 表示此属性是类的私有属性,只有在类内部声明的方法才能访问它。私有属性应在使用前声明。
当我尝试打印密码时,我得到的undefined
结果是我没有任何名为“密码”的成员,然后我尝试使用“#password”,但出现语法错误,因为“#password”是私有的。
要打印/修改私有属性,我们需要getter/setter方法。这里我创建了一个设置新密码的方法。
以下概念是 OOP 语言的四大支柱。
封装
封装的定义是将数据和方法绑定到一个单元中,以防止外部访问。就像药丸的药衣内含有药物一样。
在类的上下文中,某些属性不能从类外部直接访问。您需要调用负责这些属性的方法。
听起来很熟悉?
是的,你猜对了。这就像为我们在类中声明的私有属性创建一个getter/setter方法。
在上面的例子中,我们已经使用了封装。我们将私有属性password
与公共方法(逻辑上)绑定setPassword()
。此外,还有一个 getter 方法,用于返回私有属性的当前值。
抽象
人们常常将封装与抽象相混淆。抽象比封装更进一步。抽象的定义是只展示本质的东西,隐藏内部实现。
让我们以汽车为例。在汽车上,我们可以执行一些操作,例如启动、刹车和停止。每当您调用其中一个操作时,它都会返回一些结果。这些操作包含一些对您隐藏的子操作,但您无需关心这些子操作。
这就是汽车公司如何使用功能抽象来为客户提供流畅的体验。
让我们再举一个抽象的例子。假设你在前端项目中使用某个第三方 React 组件。该组件提供了许多 props 和方法供你自定义。这个组件本身并没有什么特别之处,它内部使用相同的 HTML 标签、CSS 和 JavaScript。但现在你无需担心这些事情。你只需要根据需求设置 props 和调用方法即可。这就是抽象。
让我们开始编码吧🤩
class User {
name;
email;
#password;
constructor() {}
#validateEmail(email) {
// check email is valid or not.
return true;
}
#validatePassword(password) {
// check password is satisfying the minimum requirements or not.
return true;
}
signUp(name, email, password) {
let isValidated = false;
isValidated = this.#validateEmail(email);
isValidated &&= this.#validatePassword(password);
if (isValidated) {
this.name = name;
this.email = email;
this.#password = password;
// add user in your db.
console.log('User registered successfuly');
} else {
console.log('Please enter correct Details!!');
}
}
login(email, password) {
if (email === this.email && password === this.#password) {
console.log('Login Successfully');
} else {
console.log('Authentication Failed!!');
}
}
#isRegisteredUser(email) {
// check user is registered or not.
return true;
}
resetPassword(email, newPassword) {
if (this.#isRegisteredUser(email)) {
this.#password = newPassword;
console.log('Operation performed successfully');
}
else {
console.log('No account found!');
}
}
};
const nehal = new User();
nehal.signUp('Nehal Mahida', 'nm@gmail.com', 'password:)'); // User registered successfuly
nehal.#validateEmail('nm@gmail.com'); // Syntax Error.
nehal.login('nm@gmail.com', 'password:)'); // Login Successfully
nehal.resetPassword('nm@gmail.com', ''); // Operation performed successfully
在上面的例子中,我们引入了一些私有方法。这些方法执行一些操作,并且不会暴露给类的外部。
这些方法由公开可用的方法调用。
作为开发人员,我只需要提供从 UI 收到的详细信息并调用负责的方法。
在Java等 OOP 语言中,我们有抽象类和接口的概念。但这在 JavaScript 中是不可能的。
否则,我们可以创建一个抽象类,并且该类可以被另一个类使用来实现类似的功能。
所以基本上我们可以说我们正在使用封装来实现抽象。😊
遗产
当一个类继承了另一个类的属性和方法时,在 OOP 中称为继承。继承了属性的类称为子类,而被继承属性的类称为超类。
为什么需要继承?
继承是面向对象编程 (OOP) 中一个非常重要的概念。继承的主要优势在于可重用性。当子类继承父类时,我们无需重复编写相同的代码。当我们需要更改属性时,只需在父类中进行更改,所有子类都会自动继承该更改,从而提高代码的可靠性。继承还能提高代码的可读性。
让我们编码...
class User {
#password;
constructor(email, password) {
this.email = email;
this.#password = password;
}
login(email, password) {
if (email === this.email && password === this.#password) {
console.log('Login Successfully');
} else {
console.log('Authentication Failed!!');
}
}
resetPassword(newPassword) {
this.#password = newPassword;
}
logout() {
console.log('Logout Successfully');
}
}
class Author extends User {
#numOfPost;
constructor(email, password) {
super(email, password);
this.#numOfPost = 0;
}
createPost(content) {
// add content to your DB. :)
this.#numOfPost++;
}
getNumOfPost() {
return this.#numOfPost;
}
}
class Admin extends User {
constructor(email, password) {
super(email, password);
}
removeUser(userId) {
// remove this userId from your DB.
console.log('User Removed successfully.');
}
}
const nehal = new Author('nm@gmail.com', 'password:)');
nehal.login('nm@gmail.com', 'password:)');
nehal.createPost('I hope you are enjoying this article. Don\'t forget to leave your feedback. :)');
nehal.createPost('I am tired, Do you wanna buy me a coffee? :)');
console.log(nehal.getNumOfPost()); // 2
const json = new Admin('jason@gmail.com', '[Object] [object]');
json.login('jason@gmail.com', '[Object] [object]');
json.resetPassword('{id: 1}');
json.login('jason@gmail.com', '{id: 1}');
json.removeUser(12);
在上面的例子中,Author
和类使用和关键字Admin
继承了类的属性。User
extends
super
关键字extends
用于在两个类之间建立父子关系。在第一种情况下,Author
成为子类,User
成为父类。
子类可以访问超类的所有公共和受保护成员。此外,它还可以拥有自己的属性和方法。这就是我们通过继承实现可重用性的方式。
关键字super
是一个特殊的关键字。调用子类的构造函数会调用父类的构造函数。这就是我们在和类super
中初始化属性的方式。Author
Admin
子类也可以覆盖父类的方法。这就引入了多态的概念。
多态性
多态意味着“不止一种形态”。像我们一样,软件工程师可以同时处理前端、后端、DevOps 甚至测试。😅
多态性有两种类型。
- 编译时多态性
- 运行时多态性
函数重载是编译时多态的一种。在这里,我们创建多个具有相同名称但参数或类型不同的函数。
JavaScript 不支持函数重载,因为如果创建同名函数,JavaScript 将用前面的函数覆盖最后定义的函数。
方法覆盖是一种运行时多态性。还记得我说过,可以在子类中覆盖父类的方法吗?这就是方法覆盖。
我们来举个例子。
class User {
constructor(email, password) {
this.email = email;
this.password = password;
}
login(email, password) {
if (email === this.email && password === this.password) {
console.log('Login Successfully');
} else {
console.log('Authentication Failed!!');
}
}
}
class Author extends User {
#numOfPost;
constructor(email, password) {
super(email, password);
this.#numOfPost = 0;
}
createPost(content) {
// add content to your DB. :)
this.#numOfPost++;
}
getNumOfPost() {
return this.#numOfPost;
}
}
class Admin extends User {
constructor(email, password) {
super(email, password);
}
login(email, password) {
// add extra layer of security as this is an admin account.
const isValidAdmin = true; // we can have some 2FA type security check here.
if (email === this.email && password === this.password && isValidAdmin) {
console.log('Admin Login Successfully');
} else {
console.log('Authentication Failed!!');
}
}
removeUser(userId) {
// remove this userId from your DB.
console.log('User Removed successfully.');
}
}
const nehal = new Author('nm@gmail.com', 'password:)');
nehal.login('nm@gmail.com', 'password:)'); // Login Successfully
const json = new Admin('jason@gmail.com', '[Object] [object]');
json.login('jason@gmail.com', '[Object] [object]'); // Admin Login Successfully
这里,Author
和Admin
都继承了 UserUser
类。这两个类都包含login
User 类的方法。现在我需要对管理员帐户进行一些额外的验证,因此我在 Admin 类中创建了一个 login 方法。它将覆盖父类的login
方法。
当类的对象Admin
调用该方法时,它将引发对该类方法的login
函数调用。login
Admin
这就是我们如何通过方法覆盖实现多态性。
就这样。我们已经讲完了 JavaScript 中 OOP 的所有概念。🤩
注:以上所有信息均基于我的知识和研究。如果您发现任何错误,请在评论区指正。祝您学习愉快 🙂
如果您喜欢这篇文章,请分享并标记🔖这篇文章!
如果您使用 Twitter,请关注我,我会分享学习 Web 开发的精彩资源。🙏🏻
欢迎您提供反馈。🤗
🏃♂️ 让我们联系👇
🕊 Twitter(Twitter 上见😃)
Github