JavaScript 是一门典型的面向对象编程语言,其中 prototype 和 class 是两个重要的概念。ECMAScript 2021 引入了一些新特性来优化它们的使用。本文将详细介绍 prototype 和 class 的概念及其在 JavaScript 中的应用。
prototype
JavaScript 中的每个对象都包含一个隐式的 prototype 属性。它指向另一个对象,称为原型对象。该原型对象也可能具有自己的原型对象,形成一个 “原型链” 用于继承性质。对象可以访问其原型对象上的所有属性和方法。
假设我们有如下的一个对象:
// javascriptcn.com 代码示例 const Person = function(name, age) { this.name = name; this.age = age; }; Person.prototype.sayHello = function() { console.log(`Hello, my name is ${this.name}`); }; const jack = new Person('Jack', 20); jack.sayHello(); // Hello, my name is Jack
在上面的例子中,我们使用了构造函数来创建一个名为 Person 的对象,并将其原型对象添加了一个名为 sayHello 的方法。在创建实例 jack 后,我们可以调用其原型对象中的 sayHello 方法。
console.log(Object.getPrototypeOf(jack) === Person.prototype); // true
此时我们还可以修改原型对象中的属性和方法:
Person.prototype = { eat() { console.log(`${this.name} is eating.`); } }; jack.eat(); // TypeError: jack.eat is not a function
因为我们修改了 Person 的原型对象,导致 jack 不再关联到其原型对象,从而无法调用原型上的 eat 方法。这种变化通常应该在创建实例之前进行。
class
class 是 ECMAScript 2015 引入的一种语法糖,以更自然的方式定义构造函数并创建对象,可以视为 prototype 的一种替代方式。
使用 class 定义 Person 对象等价于使用构造函数定义:
// javascriptcn.com 代码示例 class Person { constructor(name, age) { this.name = name; this.age = age; } sayHello() { console.log(`Hello, my name is ${this.name}`); } }
它们的实例创建方式也相同:
const jack = new Person('Jack', 20); jack.sayHello(); // Hello, my name is Jack
class 还支持 extends 和 super 关键字,让我们可以方便地实现继承:
// javascriptcn.com 代码示例 class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a noise.`); } } class Dog extends Animal { speak() { console.log(`${this.name} barks.`); } } const dog = new Dog('Husky'); dog.speak(); // Husky barks.
ECMAScript 2021 新特性
ECMAScript 2021 引入了一些新特性,用于简化面向对象编程中的一些操作:
简化原型对象
我们可以使用 Object.setPrototypeOf 方法以及 proto 属性来修改对象的原型链。但是这样做会影响代码性能,因为它会导致运行时进行属性查找、方法调用时的性能开销。在 ECMAScript 2021 中,我们可以使用 Object.create 方法来构建对象的原型,并指定对象的属性和方法:
// javascriptcn.com 代码示例 const personProto = { sayHello() { console.log(`Hello, my name is ${this.name}`); } }; const jack = Object.create(personProto, { name: { value: 'Jack' }, age: { value: 20 } }); jack.sayHello(); // Hello, my name is Jack
这样做的好处是,在对象原型中添加新属性或方法时,无需修改其他对象,同时使代码更具可读性。
私有字段
常见的面向对象编程方法将对象的状态保留在对象本身中。但是在 JavaScript 中,我们通常使用外部变量来实现这一点。ECMAScript 2021 引入了私有字段,使得状态可以完全封装在对象内部。它们可以在类中使用 # 开头的标识符来声明:
// javascriptcn.com 代码示例 class Person { #name; constructor(name) { this.#name = name; } getName() { return this.#name; } setName(name) { this.#name = name; } } const jack = new Person('Jack'); console.log(jack.getName()); // Jack jack.setName('Tom'); console.log(jack.getName()); // Tom console.log(jack.#name); // Uncaught SyntaxError: Private field '#name' must be declared in an enclosing class
此时,我们无法直接访问 #name 属性,因为它是私有的。只能通过 getName 和 setName 方法来获取和修改其值。
总结
prototype 和 class 是 JavaScript 中非常重要的概念,它们实现了面向对象编程的封装、继承和多态等特性。ECMAScript 2021 引入了新特性以简化这些操作。在使用面向对象编程时,我们应该充分理解这些概念的本质,灵活应用它们,以实现更优雅、更高效的代码。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6542371c7d4982a6ebbe253f