JavaScript原型和原型链:一文掌握JavaScript的继承机制

## 什么是原型?

在JavaScript中,每个函数都有一个属性叫做prototype,它指向一个对象,这个对象就是该函数的**原型对象**。
原型对象中有一个属性叫做constructor,它指向该函数本身。这样就形成了一个循环引用的关系。
function Foo() {
  // ...
}

console.log(Foo.prototype); // Foo 的原型对象
console.log(Foo.prototype.constructor); // Foo
console.log(Foo.prototype.constructor === Foo); // true

那么为什么要有原型对象呢?原型对象的作用是存放一些公共的方法或属性,供该函数创建的实例对象共享使用。例如:
function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayHello = function() {
  console.log("Hello, I'm " + this.name);
};

var p1 = new Person("Alice", 20);
var p2 = new Person("Bob", 22);

p1.sayHello(); // Hello, I'm Alice
p2.sayHello(); // Hello, I'm Bob

console.log(p1.sayHello === p2.sayHello); // true

在上面的例子中,我们定义了一个Person构造函数,并给它的原型对象添加了一个sayHello方法。然后我们用new操作符创建了两个Person实例对象p1和p2。这两个实例对象都可以调用sayHello方法,并且这个方法其实是同一个函数,在内存中只存在一份。

什么是原型链?

我们已经知道了每个函数都有一个原型对象,那么每个对象呢?其实每个对象也有一个属性叫做__proto__(注意前后都有两个下划线),它指向该对象的原型。那么这个原型又是什么呢?其实就是该对象所属构造函数的prototype属性所指向的那个原型对象。

function Foo() {
  // ...
}

var f = new Foo();

console.log(f.__proto__); // f 的原型
console.log(f.__proto__ === Foo.prototype); // true

在上面的例子中,我们用Foo构造函数创建了一个f实例对象。f.__proto__就指向了Foo.prototype,也就是f的原型。
那么为什么要有__proto__属性呢?__proto__属性的作用是在访问一个对象的某个属性或方法时,
如果在该对象本身没有找到,则会沿着__proto__属性向上查找,直到找到或者到达null为止。
这样就形成了一条链式结构,称为原型链。
function Person(name) {
  this.name = name;
}

Person.prototype.sayName = function() {
  console.log(this.name);
};

var p = new Person("Tom");

p.sayName(); // Tom

// 实际上执行了以下步骤:
// 1. 查找 p 对象是否有 sayName 属性,没有则继续;
// 2. 查找 p.__proto__ 即 Person.prototype 是否有 sayName 属性,
//    找到则调用并返回结果;
//    如果没有则继续;
// 3. 查找 p.__proto__.__proto__ 即 Object.prototype 是否有 sayName 属性,
//    如果没有则继续;
// 4. 查找 p.__proto__.__proto__.__proto__ 即 null 是否有 sayName 属性,
//    因为 null 没有任何属性,所以查找结束,返回 undefined。

原型链的作用是实现JavaScript的继承。继承是指一个对象可以拥有另一个对象的属性或方法。例如:

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

Animal.prototype.eat = function() {
  console.log(this.name + " is eating.");
};

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

Dog.prototype = Object.create(Animal.prototype); // 设置子类原型为父类原型的副本,实现方法继承
Dog.prototype.constructor = Dog; // 修正子类原型的构造函数指向

Dog.prototype.bark = function() {
  console.log(this.name + " is barking.");
};

var d = new Dog("Spot");

d.eat(); // Spot is eating.
d.bark(); // Spot is barking.

console.log(d instanceof Dog); // true
console.log(d instanceof Animal); // true

在上面的例子中,我们定义了一个Animal构造函数,并给它的原型对象添加了一个eat方法。然后我们定义了一个Dog构造函数,并让它继承自Animal。这样我们就可以创建一个Dog实例对象d,并让它拥有Animal的属性和方法,以及自己特有的bark方法。

总结

  • 原型是一种设计模式,用于创建重复的对象,并保证性能;

  • 原型对象是每个函数创建时自动生成的一个对象,存放一些公共的方法或属性;

  • 原型链是每个对象通过__proto__属性向上查找自己原型的过程,用于实现继承;

  • 继承是一种机制,让一个对象可以拥有另一个对象的属性或方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值