js通过对象原型和原型链实现面向对象的三个核心特性:封装、继承和多态。我们可以逐步理解这几个概念以及它们在JavaScript中的实现。

1. 封装(Encapsulation)

封装是面向对象编程中的一个核心概念,指的是将数据(属性)和操作数据的行为(方法)封装到对象中,隐藏对象的内部细节,只暴露公共接口给外部使用。

在JavaScript中,封装可以通过对象来实现。我们把相关的属性和方法定义在一个对象中,然后通过访问权限来控制哪些属性和方法可以被外部访问,哪些则不能。

JavaScript中的封装例子:
function Person(name, age) {
  let privateAge = age; // 私有属性,通过封装隐藏
  this.name = name; // 公有属性

  this.getAge = function() { // 公有方法
    return privateAge;
  }

  this.setAge = function(newAge) { // 公有方法
    if (newAge > 0) {
      privateAge = newAge;
    }
  }
}

const john = new Person('John', 25);
console.log(john.name); // 输出 'John'
console.log(john.getAge()); // 输出 25
john.setAge(30); 
console.log(john.getAge()); // 输出 30

// 尝试直接访问 privateAge 会失败
console.log(john.privateAge); // undefined

在这个例子中,privateAge 是一个私有变量,不能被外部直接访问。通过封装,我们只暴露了 getAgesetAge 方法,控制了对 privateAge 的访问。这就是封装的作用——保护数据和隐藏实现细节。

2. 继承(Inheritance)

继承是面向对象编程中的另一重要概念,指的是一个对象可以继承另一个对象的属性和方法。继承的主要目的是为了重用代码,避免重复定义相同的功能。

JavaScript通过**原型继承(prototype inheritance)**实现继承,每个对象都有一个内部的原型(__proto__),通过原型可以访问父对象的属性和方法。

  JavaScript中的继承例子:

function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  console.log(this.name + ' makes a noise.');
}

function Dog(name) {
  Animal.call(this, name); // 调用父类构造函数,继承父类属性
}

Dog.prototype = Object.create(Animal.prototype); // 继承父类方法
Dog.prototype.constructor = Dog;

Dog.prototype.speak = function() { // 重写父类方法
  console.log(this.name + ' barks.');
}

const dog = new Dog('Buddy');
dog.speak(); // 输出 'Buddy barks.'

在这个例子中,Dog 类继承了 Animal 类,使用 Animal.call(this, name) 来继承 Animal 的属性(即 name),并通过 Object.create(Animal.prototype) 继承 Animal 的方法(即 speak())。通过继承,Dog 可以访问并重用 Animal 的属性和方法。

3. 多态(Polymorphism)

多态允许子类在继承父类的基础上,对父类的某些方法进行重写,从而提供不同的实现。简单来说,多态是指“同样的方法在不同对象上表现出不同的行为”。

在JavaScript中,方法重写是实现多态的方式。子类可以重写父类的方法,调用时会根据实际的对象类型执行对应的实现。

JavaScript中的多态例子:
const animal = new Animal('generic animal');
const dog = new Dog('Buddy');

animal.speak(); // 输出 'generic animal makes a noise.'
dog.speak();    // 输出 'Buddy barks.'

在这个例子中,animal.speak() 调用了 Animal 类的 speak() 方法,而 dog.speak() 调用了 Dog 类重写的 speak() 方法。这就是多态——同样的 speak() 方法,针对不同的对象类型表现出不同的行为。

4. 对象原型(Prototype)

在JavaScript中,对象原型是实现继承的基础。每个对象都有一个隐藏的属性 [[Prototype]],通常可以通过 __proto__ 来访问。通过原型,JavaScript对象可以继承另一个对象的属性和方法。

当我们访问一个对象的属性或方法时,JavaScript引擎会先在对象本身查找该属性或方法。如果没有找到,它会沿着原型链查找,直到找到或走到原型链的尽头。

通过对象原型实现继承:
function Parent() {
  this.name = 'parent';
}

Parent.prototype.sayHello = function() {
  console.log('Hello from ' + this.name);
}

const child = new Parent();
child.sayHello(); // 输出 'Hello from parent'

在这个例子中,Parent 的实例 child 继承了 Parent.prototype 上的方法 sayHello()。这就是通过原型实现的继承。

5. 原型链(Prototype Chain)

原型链是指对象通过 __proto__ 链接到另一个对象(通常是构造函数的 prototype),并且这个被链接的对象也可以有它自己的原型,形成一条原型链。原型链的顶端是 Object.prototype,它没有原型。

当我们访问一个对象的属性或方法时,如果该属性或方法不在对象本身,它会沿着原型链依次向上查找,直到找到该属性或方法,或者查找到 Object.prototype 为止。

原型链的例子:
function A() {}
A.prototype.sayHi = function() {
  console.log('Hi from A');
}

function B() {}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
B.prototype.sayHi = function() {
  console.log('Hi from B');
}

const b = new B();
b.sayHi(); // 输出 'Hi from B'

在这个例子中,B.prototype 继承了 A.prototype,因此 B 的实例 b 有一个原型链:b -> B.prototype -> A.prototype -> Object.prototype。当我们调用 b.sayHi() 时,首先在 B.prototype 中找到方法 sayHi,因为它被重写了,所以多态地执行 B 类的 sayHi() 方法。

总结:

  • 封装:将数据和行为封装在对象中,通过接口与外部交互,隐藏内部细节。
  • 继承:子类可以继承父类的属性和方法,避免重复代码。
  • 多态:子类可以重写父类的方法,同样的方法在不同对象上表现出不同的行为。
  • 对象原型:对象通过原型实现继承,每个对象都有一个原型,方法和属性通过原型链共享。
  • 原型链:对象通过原型链进行属性和方法的查找,沿着链条直到 Object.prototype
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐