JavaScript 原型与原型链详情

JavaScript 原型与原型链详情

在 JavaScript 中,每个对象都拥有一个原型(prototype)属性。原型是一个对象,它包含了创建当前对象的构造函数的原型。当你访问一个对象的属性时,JavaScript 引擎会先在该对象本身中查找是否有这个属性,如果没有,它会去该对象原型(也就是构造函数的原型)中查找是否有这个属性,如果还没有,就会继续在原型的原型中查找,直到找到该属性或者到达 Object 的原型为止。这种属性查找的链式结构就是原型链。

构造函数与实例

在 JavaScript 中,每个函数既可以作为普通函数调用,也可以作为构造函数使用。当函数作为构造函数使用时,它们会返回一个新创建的对象。我们可以使用 new 运算符来创建对象,例如:

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

var person1 = new Person('小明', 18);
var person2 = new Person('小红', 20);
console.log(person1.name); // 小明
console.log(person2.age); // 20

在上面的代码中,我们通过构造函数 Person 创建了两个对象 person1 和 person2。由于 person1 和 person2 都是使用 Person 构造函数创建的,所以它们都具有 name 和 age 两个属性。

原型和原型链

当我们使用构造函数创建对象时,JavaScript 引擎会自动为这个对象创建一个原型属性 prototype,它指向该对象的构造函数的原型属性。假设我们现在有两个对象 person1 和 person2,它们都是使用 Person 构造函数创建的,那么它们的原型属性 prototype 就会指向如下对象:

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

所以,当我们在 person1 或者 person2 对象中查找 name 属性时,如果该对象本身没有这个属性的话,JavaScript 引擎就会在该对象原型(也就是 Person.prototype 对象)中查找是否有该属性。如果该属性在 Person.prototype 对象中存在,JavaScript 引擎就会把该属性返回给我们。

如果 person1 或者 person2 对象中仍然没有找到该属性,JavaScript 引擎就会在 Person.prototype 的原型中继续查找。由于 Person.prototype 是一个对象,它也有一个原型属性,这个原型属性指向它的构造函数 Object.prototype。最后,如果在 Object.prototype 中还没有找到该属性,JavaScript 引擎就会返回 undefined。

实例与原型的关系

我们知道,当我们使用 new 运算符创建对象时,JavaScript 引擎会为该对象创建一个原型属性 prototype,这个属性与构造函数的原型属性相等(Person.prototype)。同时,JavaScript 引擎还会把该对象的 proto 属性指向该对象原型属性(也就是 Person.prototype)。例如:

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

var person1 = new Person('小明', 18);

console.log(person1.__proto__ === Person.prototype); // true

上面的代码中,我们创建了一个 Person 对象 person1,然后我们可以通过 person1.proto 访问该对象的原型属性。由于 person1 是通过 Person 构造函数创建的,所以 person1.proto 指向的就是 Person.prototype。

同时,我们也知道,当我们在 person1 对象中查找 name 属性时,如果该对象本身没有这个属性的话,JavaScript 引擎就会在该对象原型(也就是 Person.prototype 对象)中查找是否有该属性。

示例

以下是使用构造函数创建对象、原型和原型链的两个示例:

示例一

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

Animal.prototype.eat = function() {
  console.log(this.name + '正在吃东西');
};

function Cat(name) {
  Animal.call(this, name);
  this.type = 'cat';
}

Cat.prototype = Object.create(Animal.prototype, {
  constructor: {
    value: Cat,
    enumerable: false,
    writable: true,
    configurable: true
  }
});

Cat.prototype.miao = function() {
  console.log(this.name + '正在喵喵叫');
};

var cat = new Cat('小猫');
cat.miao(); // 小猫正在喵喵叫
cat.eat(); // 小猫正在吃东西

在上面的代码中,我们定义了两个构造函数 Animal 和 Cat。Animal 构造函数用于创建动物对象,Cat 构造函数用于创建猫对象。我们通过 Object.create 方法将 Cat 的原型属性指向了 Animal 的原型属性,这样 Cat 就继承了 Animal 的所有属性和方法。

Cat 构造函数有一个 miao 方法,用于输出猫正在喵喵叫的信息。Animal 构造函数有一个 eat 方法,用于输出动物正在吃东西的信息。我们通过 Cat 对象 cat 调用 miao 和 eat 方法,发现它们都能正常输出信息。

示例二

function Car(name, price) {
  this.name = name;
  this.price = price;
}

Car.prototype.run = function() {
  console.log(this.name + '正在行驶');
};

function Audi(name, price) {
  Car.call(this, name, price);
  this.type = 'SUV';
}

Audi.prototype = new Car();

var audi = new Audi('Audi Q5', '50万');
audi.run(); // Audi Q5正在行驶
console.log(audi.price); // 50万

在上面的代码中,我们定义了两个构造函数 Car 和 Audi。Car 构造函数用于创建汽车对象,Audi 构造函数用于创建奥迪对象。

我们通过 Car.call(this, name, price) 将 Audi 构造函数的作用域指向了 Car,这样我们就可以在 Audi 对象中调用 Car 构造函数中的属性和方法。

接下来,我们通过 Audi.prototype = new Car() 将 Audi 对象的原型指向了 Car 对象。这样一来,Audi 就继承了 Car 的所有属性和方法。

最后,我们创建了一个 Audi 对象 audi,并通过 audi.run() 方法输出 Audi Q5 正在行驶的信息。我们还通过 console.log(audi.price) 输出了 Audi Q5 的价格。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript 原型与原型链详情 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • Spring Boot分离配置文件的多种方式总结

    下面是Spring Boot分离配置文件的多种方式总结的详细攻略: 一、什么是Spring Boot分离配置文件? Spring Boot项目开发中,会有很多需要配置的参数和信息,如数据库的连接信息、端口号、日志级别等等。这些配置信息会在项目启动时进行加载,而在传统的开发中,这些配置信息通常放在一个名为application.properties的配置文件中…

    other 2023年6月25日
    00
  • java获取文件扩展名的方法小结【正则与字符串截取】

    Java获取文件扩展名的方法小结【正则与字符串截取】 在Java中,获取文件扩展名的方法有多种。本文将介绍两种常用的方法:正则表达式和字符串截取。 方法一:正则表达式 使用正则表达式可以方便地从文件名中提取出扩展名。下面是一个示例代码: import java.util.regex.Matcher; import java.util.regex.Patter…

    other 2023年8月6日
    00
  • CentOS 6.8 安装vsftpd的方法步骤

    下面是 CentOS 6.8 安装 vsftpd 的方法步骤的详细攻略: 安装 vsftpd 使用 SSH 登录 CentOS 6.8 的服务器。 执行以下命令以更新系统软件包: sudo yum update 执行以下命令以安装 vsftpd: sudo yum install vsftpd 安装完成后,使用以下命令启动 vsftpd 服务: sudo s…

    other 2023年6月27日
    00
  • Jackson 反序列化时实现大小写不敏感设置

    Jackson 反序列化时实现大小写不敏感设置攻略 在使用 Jackson 进行反序列化时,有时候我们希望忽略属性名称的大小写,使其不区分大小写。下面是实现这一目标的完整攻略。 步骤一:添加依赖 首先,确保你的项目中已经添加了 Jackson 的相关依赖。在 Maven 项目中,可以在 pom.xml 文件中添加以下依赖: <dependency&gt…

    other 2023年8月18日
    00
  • SpringBoot使用JPA实现查询部分字段

    下面是SpringBoot使用JPA实现查询部分字段的完整攻略: 1. 准备工作 在项目中添加以下依赖: <!–SpringBoot JPA依赖–> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spri…

    other 2023年6月25日
    00
  • Java利用AQS实现自定义锁

    Java利用AQS实现自定义锁 在Java中,我们可以使用synchronized关键字或者Lock接口来进行锁的控制。但是,如果我们需要更加精细化地控制锁的获取和释放,那么可以自定义一个锁。本文介绍如何通过AQS(AbstractQueuedSynchronizer)来实现自定义锁。 AQS简介 AQS是一个抽象的同步器,它被Lock接口中的具体实现所使用…

    other 2023年6月25日
    00
  • C/C++合并两个升序链表的方式

    当合并两个已按升序排列的链表时,可以使用指针遍历两个链表,并选择合适的节点插入到一个新链表中。以下是一般的步骤: 创建一个新链表的头结点,并用指针指向它。 使用两个指针,一个指向第一个链表的头结点,另一个指向第二个链表的头结点。 遍历两个链表直到其中一个链表已到达结尾。在每次遍历时选择相对较小的节点并插入到新链表。 如果其中一个链表到达结尾而另一个链表仍然有…

    other 2023年6月27日
    00
  • teigha.net开发入门1-teigha介绍

    Teigha.net开发入门1-Teigha介绍 Teigha是一款强大的CAD开发平台,其可用于开发AutoCAD兼容的底层CAD应用程序,包括图形编辑器、CAD/CAM/CAE应用程序和定制业务应用程序。Teigha平台专注于AutoCAD DWG文件格式的读写和编辑,并提供了一些强大而易于使用的API来进行开发,同时也支持其他CAD格式的转换和导入。 …

    其他 2023年3月29日
    00
合作推广
合作推广
分享本页
返回顶部