《现代JavaScript高级教程》面向对象编程与Class

113 阅读8分钟
点击在线阅读,体验更好链接
现代JavaScript高级小册链接
深入浅出Dart链接
现代TypeScript高级小册链接

面向对象编程与Class

引言

随着JavaScript的发展,ECMAScript 6(ES6)引入了许多新的语言特性和语法糖,其中包括了面向对象编程的Class(类)机制。Class提供了一种更简洁、更直观的方式来定义对象和操作对象的行为。本文将介绍ES6中Class的概念、语法和特性,并通过示例代码来说明其实际应用。

1. 什么是面向对象编程?

面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它将程序中的对象作为基本单元,通过封装、继承和多态等机制来组织和管理代码。面向对象编程将现实世界中的实体抽象为代码中的对象,对象拥有自己的状态(属性)和行为(方法),并与其他对象进行交互。

面向对象编程有以下几个核心概念:

  • 封装(Encapsulation):将数据和操作数据的方法封装在一个对象中,使其成为一个独立的实体,外部无法直接访问对象的内部实现细节。
  • 继承(Inheritance):通过定义一个基类(父类),其他类可以继承该基类的属性和方法,并可以在此基础上进行扩展或覆盖。
  • 多态(Polymorphism):不同对象可以对相同的方法做出不同的响应,即同一个方法可以根据调用对象的不同而具有不同的行为。

面向对象编程的优势包括代码的可重用性、可维护性、扩展性和灵活性等。

2. Class的基本概念

在ES6之前,JavaScript中的对象和面向对象编程的概念相对比较模糊。ES6引入了Class机制,使得JavaScript可以更加直观地定义和使用类。Class是一种特殊的函数,通过Class关键字定义。Class中可以定义构造函数、属性和方法等。

一个简单的Class示例如下:

class Rectangle {
  constructor(width, height) {
      this.width = width;
          this.height = height;
            }

          area() {
              return this.width * this.height;
                }
                
                  perimeter() {
                      return 2 * (this.width + this.height);
                        }
                        }</code></pre><p>在上述示例中,我们定义了一个名为<code>Rectangle</code>的类,</p><p>它具有<code>width</code><code>height</code>两个属性,以及<code>area()</code><code>perimeter()</code>两个方法。通过Class定义的类可以通过实例化来创建具体的对象,并调用其属性和方法。</p><pre><code class="javascript">const rect = new Rectangle(5, 3);
                        console.log(rect.area());       // 输出:15
                        console.log(rect.perimeter());  // 输出:16</code></pre><h3 id="item-0-5">3. Class的语法</h3><p>ES6中Class的语法相对简洁明了。一个Class可以包含构造函数、属性和方法等。下面介绍一些常用的语法规则:</p><h4>3.1 构造函数</h4><p>在Class中使用<code>constructor</code>关键字定义构造函数。构造函数用于创建对象时进行初始化操作,通过<code>new</code>关键字实例化类时会自动调用构造函数。</p><pre><code class="javascript">class Rectangle {
                          constructor(width, height) {
                              this.width = width;
                                  this.height = height;
                                    }
                                    }</code></pre><p>构造函数中的<code>this</code>关键字表示当前实例化的对象。</p><h4>3.2 属性</h4><p>在Class中可以定义各种属性。属性可以直接定义在Class的内部,也可以在构造函数中通过<code>this</code>关键字进行定义。</p><pre><code class="javascript">class Rectangle {
                                      width = 0;    // 直接定义属性
                                        height = 0;
                                        
                                          constructor(width, height) {
                                              this.width = width;    // 在构造函数中定义属性
                                                  this.height = height;
                                                    }
                                                    }</code></pre><h4>3.3 方法</h4><p>在Class中定义的函数称为方法。可以直接在Class的内部定义方法,也可以使用ES6的简写形式。</p><pre><code class="javascript">class Rectangle {
                                                      constructor(width, height) {
                                                          this.width = width;
                                                              this.height = height;
                                                                }
                                                                
                                                                  area() {            // 定义方法
                                                                      return this.width * this.height;
                                                                        }
                                                                        
                                                                          perimeter() {
                                                                              return 2 * (this.width + this.height);
                                                                                }
                                                                                }</code></pre><h4>3.4 方法的访问修饰符</h4><p>在Class中,可以使用访问修饰符来限制方法的访问权限。ES6中的Class默认所有方法都是公共的,可以被外部调用。但我们可以使用<code>static</code><code>get</code><code>set</code><code>private</code><code>protected</code>等修饰符来控制方法的访问。</p><ul><li><code>static</code>:定义静态方法,只能通过类本身调用,不能通过类的实例调用。</li><li><code>get</code><code>set</code>:定义属性的读取和设置方法,使用类似访问属性的语法进行调用。</li><li><code>private</code>:定义私有方法,只能在类的内部被访问,外部无法访问。</li><li><code>protected</code>:定义受保护方法,只能在类的内部和子类中被访问,外部无法访问。</li></ul><pre><code class="javascript">class Rectangle {
                                                                                  static description = 'This is a rectangle';  // 静态属性
                                                                                  
                                                                                    constructor(width, height) {
                                                                                        this.width = width;
                                                                                            this.height = height;
                                                                                              }
                                                                                              
                                                                                                static createSquare(side) {     // 静态方法
                                                                                                    return new Rectangle(side, side);
                                                                                                      }
                                                                                                      
                                                                                                      
                                                                                                      
                                                                                                        get area() {           // Getter方法
                                                                                                            return this.width * this.height;
                                                                                                              }
                                                                                                              
                                                                                                                set area(value) {     // Setter方法
                                                                                                                    this.width = Math.sqrt(value);
                                                                                                                        this.height = Math.sqrt(value);
                                                                                                                          }
                                                                                                                          
                                                                                                                            #privateMethod() {    // 私有方法
                                                                                                                                console.log('This is a private method');
                                                                                                                                  }
                                                                                                                                  
                                                                                                                                    protectedMethod() {   // 受保护方法
                                                                                                                                        console.log('This is a protected method');
                                                                                                                                          }
                                                                                                                                          
                                                                                                                                            publicMethod() {      // 公共方法
                                                                                                                                                console.log('This is a public method');
                                                                                                                                                    this.#privateMethod();
                                                                                                                                                        this.protectedMethod();
                                                                                                                                                          }
                                                                                                                                                          }</code></pre><p>在上述示例中,我们定义了一个<code>Square</code>类,它继承自<code>Rectangle</code>类。通过<code>super</code>关键字调用父类的构造函数,确保父类的属性被正确初始化。子类可以新增或覆盖父类的方法。</p><pre><code class="javascript">const square = new Square(5);
                                                                                                                                                          console.log(square.area());       // 输出:25
                                                                                                                                                          console.log(square.perimeter());  // 输出:20</code></pre><h3 id="item-0-6">4. 类的静态方法和属性</h3><p>静态方法和属性属于类本身,而不是类的实例。静态方法和属性可以通过类名直接访问,无需实例化类。</p><pre><code class="javascript">class MathUtil {
                                                                                                                                                            static PI = 3.14159;    // 静态属性
                                                                                                                                                            
                                                                                                                                                              static square(number) {    // 静态方法
                                                                                                                                                                  return number * number;
                                                                                                                                                                    }
                                                                                                                                                                    }</code></pre><p>在上述示例中,我们定义了一个<code>MathUtil</code>类,它具有一个静态属性<code>PI</code>和一个静态方法<code>square()</code>。可以通过类名直接访问静态属性和方法。</p><pre><code class="javascript">console.log(MathUtil.PI);        // 输出:3.14159
                                                                                                                                                                    console.log(MathUtil.square(5)); // 输出:25</code></pre><h3 id="item-0-7">5. Getter和Setter方法</h3><p>Getter和Setter方法用于对类的属性进行读取和设置操作,可以通过类似访问属性的语法进行调用。</p><pre><code class="javascript">class Circle {
                                                                                                                                                                      constructor(radius) {
                                                                                                                                                                          this.radius = radius;
                                                                                                                                                                            }
                                                                                                                                                                            
                                                                                                                                                                              get diameter() {
                                                                                                                                                                                  return 2 * this.radius;
                                                                                                                                                                                    }
                                                                                                                                                                                    
                                                                                                                                                                                      set diameter(value) {
                                                                                                                                                                                          this.radius = value / 2;
                                                                                                                                                                                            }
                                                                                                                                                                                            }</code></pre><p>在上述示例中,我们定义了一个<code>Circle</code>类,它具有一个属性<code>radius</code>。通过定义<code>get diameter()</code>方法和<code>set diameter()</code>方法,我们可以通过类似访问属性的方式来读取和设置直径(<code>diameter</code>)属性,而不需要直接访问<code>radius</code>属性。</p><pre><code class="javascript">const circle = new Circle(5);
                                                                                                                                                                                            console.log(circle.diameter);     //
                                                                                                                                                                                            
                                                                                                                                                                                             输出:10
                                                                                                                                                                                             circle.diameter = 12;
                                                                                                                                                                                             console.log(circle.radius);       // 输出:6</code></pre><h3 id="item-0-8">6. 类的私有属性和方法</h3><p>在ES6中,可以使用<code>#</code>作为前缀来定义私有属性和方法。私有属性和方法只能在类的内部被访问,外部无法访问。</p><pre><code class="javascript">class Person {
                                                                                                                                                                                               #name;   // 私有属性
                                                                                                                                                                                               
                                                                                                                                                                                                 constructor(name) {
                                                                                                                                                                                                     this.#name = name;
                                                                                                                                                                                                       }
                                                                                                                                                                                                       
                                                                                                                                                                                                         #privateMethod() {   // 私有方法
                                                                                                                                                                                                             console.log('This is a private method');
                                                                                                                                                                                                               }
                                                                                                                                                                                                               
                                                                                                                                                                                                                 publicMethod() {      // 公共方法
                                                                                                                                                                                                                     console.log(`Hello, my name is ${this.#name}`);
                                                                                                                                                                                                                         this.#privateMethod();
                                                                                                                                                                                                                           }
                                                                                                                                                                                                                           }</code></pre><p>在上述示例中,我们定义了一个<code>Person</code>类,它具有一个私有属性<code>#name</code>和一个私有方法<code>#privateMethod()</code>。私有属性和方法只能在类的内部访问。</p><pre><code class="javascript">const person = new Person('John');
                                                                                                                                                                                                                           person.publicMethod();   // 输出:Hello, my name is John
                                                                                                                                                                                                                           person.#name;            // 报错:SyntaxError: Private field '#name' must be declared in an enclosing class
                                                                                                                                                                                                                           person.#privateMethod(); // 报错:SyntaxError: Private field '#privateMethod' must be declared in an enclosing class</code></pre><h3 id="item-0-9">7. 类的实例和构造函数</h3><p>在ES6中,类的实例通过<code>new</code>关键字进行创建,并自动调用类的构造函数进行初始化。</p><pre><code class="javascript">const rect = new Rectangle(5, 3);
                                                                                                                                                                                                                           console.log(rect.area());       // 输出:15
                                                                                                                                                                                                                           console.log(rect.perimeter());  // 输出:16</code></pre><p>可以使用<code>instanceof</code>运算符来判断一个对象是否是某个类的实例。</p><pre><code class="javascript">console.log(rect instanceof Rectangle);  // 输出:true
                                                                                                                                                                                                                           console.log(rect instanceof Object);     // 输出:true</code></pre><h3 id="item-0-10">8. 类的继承</h3><p>继承是面向对象编程中的重要概念之一,它允许我们创建一个基类(父类),其他类可以继承该基类并扩展或覆盖其中的属性和方法。ES6中使用<code>extends</code>关键字实现类的继承。</p><pre><code class="javascript">class Square extends Rectangle {
                                                                                                                                                                                                                             constructor(side) {
                                                                                                                                                                                                                                 super(side, side);    // 调用父类的构造函数
                                                                                                                                                                                                                                   }
                                                                                                                                                                                                                                   }</code></pre><h3 id="item-0-11">9. 类的封装</h3><p>封装通过将数据和操作数据的方法封装在一个对象中,实现了数据的保护和访问的控制。类的属性和方法可以使用不同的访问修饰符来控制其可见性。</p><pre><code class="js">class Rectangle {
                                                                                                                                                                                                                                     #width;  // 私有属性
                                                                                                                                                                                                                                       #height;
                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                         constructor(width, height) {
                                                                                                                                                                                                                                             this.#width = width;
                                                                                                                                                                                                                                                 this.#height = height;
                                                                                                                                                                                                                                                   }
                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                     getArea() {    // 公共方法
                                                                                                                                                                                                                                                         return this.#width * this.#height;
                                                                                                                                                                                                                                                           }
                                                                                                                                                                                                                                                           }
                                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                           const rect = new Rectangle(5, 3);
                                                                                                                                                                                                                                                           console.log(rect.#width);  // 报错:SyntaxError: Private field '#width' must be declared in an enclosing class
                                                                                                                                                                                                                                                           console.log(rect.getArea());  // 输出:15
                                                                                                                                                                                                                                                           </code></pre><p>在上述示例中,Rectangle类具有私有属性#width和#height,只能在类的内部被访问。通过定义公共方法getArea()来访问私有属性,从而实现了封装。</p><h3 id="item-0-12">10. 类的多态</h3><p>多态允许不同的对象对相同的消息作出不同的响应。通过继承和方法的覆盖,不同的子类可以对父类的方法进行不同的实现,从而实现多态性。</p><pre><code class="js">class Animal {
                                                                                                                                                                                                                                                             makeSound() {
                                                                                                                                                                                                                                                                 console.log('Animal makes sound');
                                                                                                                                                                                                                                                                   }
                                                                                                                                                                                                                                                                   }
                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                   class Dog extends Animal {
                                                                                                                                                                                                                                                                     makeSound() {
                                                                                                                                                                                                                                                                         console.log('Dog barks');
                                                                                                                                                                                                                                                                           }
                                                                                                                                                                                                                                                                           }
                                                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                                           class Cat extends Animal {
                                                                                                                                                                                                                                                                             makeSound() {
                                                                                                                                                                                                                                                                                 console.log('Cat meows');
                                                                                                                                                                                                                                                                                   }
                                                                                                                                                                                                                                                                                   }
                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                   const animal = new Animal();
                                                                                                                                                                                                                                                                                   const dog = new Dog();
                                                                                                                                                                                                                                                                                   const cat = new Cat();
                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                   animal.makeSound();  // 输出:Animal makes sound
                                                                                                                                                                                                                                                                                   dog.makeSound();     // 输出:Dog barks
                                                                                                                                                                                                                                                                                   cat.makeSound();     // 输出:Cat meows
                                                                                                                                                                                                                                                                                   </code></pre><p>在上述示例中,Animal类是基类,Dog和Cat类是子类。它们都具有makeSound()方法,但不同的子类对该方法进行了不同的实现,实现了多态性。</p><p>通过封装、继承和多态,面向对象编程提供了一种更加灵活和可扩展的编程方式,使得代码的组织和管理更加直观和高效。</p><h3 id="item-0-13">11. 结语</h3><p>ES6引入的Class机制为JavaScript提供了一种更直观、更简洁的面向对象编程方式。通过Class,我们可以更方便地定义和使用类,实现封装、继承和多态等面向对象编程的基本原理。同时,ES6还提供了许多其他的语法糖和特性,使得JavaScript在面向对象编程方面更加强大和灵活。</p><h3 id="item-0-14">12. 参考资料</h3><ul><li><a target="_blank" href="https://link.segmentfault.com/?enc=B6g425RWS68VQGDWBhlbNA%3D%3D.14hg1369rTC4G1KXAjDAxEMe0jIbpg6AfFml2H%2FxwIzudC4EwJSS4V3gNyRl9ofF0cBwD5c6gCCEHP39O6H2L08thMJy2dBY3eoxdAa6MYY%3D">MDN Web Docs - Classes</a></li><li><a target="_blank" href="https://link.segmentfault.com/?enc=faInhM7ZYujysiOY7Lkh%2FQ%3D%3D.LBHlRwK0y%2Fe1hyOtqdMUuLCyExO7VWEVoMadRKL%2BJV4kxE6%2FhmccbLi8hj2hQC%2Fi">ECMAScript 6 入门 - Class</a></li><li><a target="_blank" href="https://link.segmentfault.com/?enc=QWxjHUC0jmGMTTPHXMrrhg%3D%3D.H6FowF5vqjL2WFea4hr4pWO%2BTUDtM7%2BnVaHLH9g7dOtzrtkJVo7kMrrOgP2ymw4O67QIXHkmtzmx4bBkp4ThHg%3D%3D">Understanding ECMAScript 6 - Classes</a></li><li><a target="_blank" href="https://link.segmentfault.com/?enc=0BOs3cf8qvArT%2BNhr%2FxcUw%3D%3D.hHApBGn%2BJSGNoufpDs7yElpeDedzmk7iMfpXuWwGcrH2ooXDQNwzcG6Wq0tM%2FaRl">Exploring ES6 - Classes</a></li></ul>