返回

JavaScript面向对象-既熟悉又陌生的它

前端

关于对象,之前学过的java就是一门纯面向对象的语言,在更早之前JavaScript只能说是基于对象的语言,随着ES6的出现,JavaScript的面向对象的思想也变得更加明确。

理解JavaScript中的面向对象

JavaScript是一种基于原型的语言,这意味着对象不是通过类创建的,而是通过原型创建的。原型是对象的模板,它定义了对象可以拥有的属性和方法。

在JavaScript中,每个对象都有一个原型,原型对象也是一个对象,它也有一个原型,依此类推,直到到达Object.prototype。Object.prototype是所有JavaScript对象的根原型。

JavaScript中的类

ES6中引入了class,使JavaScript更像一门面向对象的语言。class关键字允许你创建类,类是对象的模板,它定义了对象的属性和方法。

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

const person = new Person('John Doe', 30);
person.greet(); // Hello, my name is John Doe and I am 30 years old.

继承

在JavaScript中,继承是通过原型链实现的。每个对象都有一个原型,原型对象也是一个对象,它也有一个原型,依此类推,直到到达Object.prototype。

当一个对象访问一个不存在的属性或方法时,JavaScript会沿着原型链向上查找,直到找到该属性或方法。

例如,以下代码演示了如何创建一个Person对象并继承自Object.prototype:

const person = {
  name: 'John Doe',
  age: 30
};

person.greet(); // TypeError: person.greet is not a function

因为Person对象没有greet方法,JavaScript会沿着原型链向上查找,直到找到Object.prototype。Object.prototype没有greet方法,因此抛出TypeError异常。

为了解决这个问题,我们可以将greet方法添加到Person对象的原型上:

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

person.greet(); // Hello, my name is John Doe and I am 30 years old.

现在,当Person对象访问greet方法时,JavaScript会沿着原型链向上查找,找到Person.prototype上的greet方法,并执行它。

封装

封装是指将对象的属性和方法隐藏起来,只允许通过特定的方法来访问它们。这有助于保护对象的数据免遭意外更改,并使对象更容易维护。

在JavaScript中,封装可以通过使用闭包来实现。闭包是指能够访问其创建时的变量的函数。

例如,以下代码演示了如何使用闭包来封装Person对象的数据:

const Person = (function() {
  const privateData = {};

  function Person(name, age) {
    privateData[this] = {
      name: name,
      age: age
    };
  }

  Person.prototype.greet = function() {
    console.log(`Hello, my name is ${privateData[this].name} and I am ${privateData[this].age} years old.`);
  };

  return Person;
})();

const person = new Person('John Doe', 30);
person.greet(); // Hello, my name is John Doe and I am 30 years old.

console.log(person.name); // undefined
console.log(person.age); // undefined

因为Person对象的数据被封装在闭包中,因此无法直接访问它们。只能通过Person对象的greet方法来访问这些数据。

多态

多态是指能够以不同的方式响应相同的消息。这有助于使代码更灵活和可重用。

在JavaScript中,多态可以通过使用继承和接口来实现。

例如,以下代码演示了如何使用继承来实现多态:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

class Student extends Person {
  constructor(name, age, school) {
    super(name, age);
    this.school = school;
  }

  study() {
    console.log(`I am studying at ${this.school}.`);
  }
}

class Teacher extends Person {
  constructor(name, age, subject) {
    super(name, age);
    this.subject = subject;
  }

  teach() {
    console.log(`I am teaching ${this.subject}.`);
  }
}

const person = new Person('John Doe', 30);
person.greet(); // Hello, my name is John Doe and I am 30 years old.

const student = new Student('Jane Doe', 20, 'Harvard University');
student.greet(); // Hello, my name is Jane Doe and I am 20 years old.
student.study(); // I am studying at Harvard University.

const teacher = new Teacher('Michael Jones', 40, 'Mathematics');
teacher.greet(); // Hello, my name is Michael Jones and I am 40 years old.
teacher.teach(); // I am teaching Mathematics.

因为Student类和Teacher类都继承自Person类,因此它们都可以响应greet消息。但是,它们以不同的方式响应greet消息。Student类以“I am studying at {this.school}.”的方式响应greet消息,而Teacher类以“I am teaching {this.subject}.”的方式响应greet消息。

这使得我们可以使用相同的代码来处理不同类型的对象,从而使代码更灵活和可重用。