JavaScript 原型与原型链详情

yizhihongxing

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日

相关文章

  • IE6下CSS多类选择符优先级不起作用的bug分析及解决方法

    IE6下CSS多类选择符优先级不起作用的bug分析及解决方法 问题分析 在IE6浏览器中,CSS多类选择符(如.class1.class2)的优先级不起作用,导致某些样式无法正确应用。这是由于IE6在解析多类选择符时存在bug,无法正确处理样式的优先级关系。 解决方法 为了解决这个问题,我们可以使用以下两种方法之一: 1. 使用JavaScript 通过使用…

    other 2023年6月28日
    00
  • 微信小程序页面生命周期详解

    微信小程序页面生命周期详解 微信小程序是一种轻量级的应用程序,由于其小巧灵活,短时间内便可开始运作等特点,越来越受开发人员喜爱。在开发小程序页面时,了解各个生命周期函数的调用顺序和作用,对于开发高质量的小程序至关重要。 页面生命周期函数介绍 以下是微信小程序页面的生命周期函数: 生命周期函数 触发时间 作用 onLoad 页面加载时 在页面被展示前,执行页面…

    other 2023年6月27日
    00
  • 适用于linux的7种最佳notepad++替代品

    以下是关于“适用于Linux的7种最佳Notepad++替代品”的完整攻略,过程中包含两个示例。 背景 Notepad++是一款行的文本编辑器,它提供了许多有用的功能,如语法高亮、动、宏录等。但是,Not++只能在Windows操作系统上运行。对于Linux用户,我们需要寻找其他的文本编辑器来代Notepad++。本攻略将介绍适用于Linux的7种最佳Not…

    other 2023年5月9日
    00
  • 解析:继承ViewGroup后的子类如何重写onMeasure方法

    当我们继承自 ViewGroup 后,需要重写 onMeasure() 方法来计算并设置该布局的子 View 布局参数,在该方法中,会通过 MeasureSpec 来获取父布局传递的测量模式和大小值,我们需要根据这些值来确定子 View 的大小和位置。 下面是重写 onMeasure() 方法的步骤: 1.实现该方法 我们需要在子类中重写该方法并在其中编写测…

    other 2023年6月26日
    00
  • 巧用U盘进入设密码系统免于输入用户名和登录密码

    下面是关于“巧用U盘进入设密码系统免于输入用户名和登录密码”的完整攻略。 背景 一些使用 Windows 操作系统的用户可能会觉得每次输入用户名和登录密码比较麻烦。因此,这里讲解一种巧妙利用 U 盘的方式来实现免于输入用户名和登录密码的功能。 准备工作 一个 U 盘,建议容量至少 4GB Windows 操作系统安装光盘或 ISO 镜像文件 Windows …

    other 2023年6月27日
    00
  • C++运算符重载三种形式(成员函数,友元函数,普通函数)详解

    C++运算符重载三种形式详解 在C++中,运算符重载是一种非常强大的机制,可以根据不同的需求重载各种运算符。在运算符重载中,常见的有成员函数、友元函数和普通函数三种形式,本文将详细介绍这三种形式的使用方法和注意事项。 成员函数形式 成员函数形式是在类内部定义的一个成员函数,其函数名以运算符关键字开头,在函数定义中并没有指定访问级别。当运算符作用于类的对象时,…

    其他 2023年3月28日
    00
  • element.style覆盖样式因优先级顺序导致的解决方法

    解决方法:使用!important修饰符 问题背景 在网页开发中,样式的优先级是由优先级顺序来决定的。当多个样式定义冲突时,浏览器会根据特定的规则来确定最终生效的样式。然而,有时候我们希望通过JavaScript等动态方式修改元素的样式,但修改后的样式可能会被其他样式覆盖,导致修改无效。 解决方案 为了解决这个问题,可以使用CSS的!important修饰符…

    other 2023年6月28日
    00
  • 【unity入门】碰撞检测与触发检测

    【Unity入门】碰撞检测与触发检测 在Unity开发游戏过程中,经常需要对游戏对象之间的碰撞进行检测。碰撞检测可以实现游戏的物理效果,例如实现物体相互碰撞弹开、人物受伤等效果。而触发检测则对碰撞检测实现了进一步的扩展,允许程序员自定义事件的响应。 碰撞检测 Unity提供了各种方式的碰撞检测,可以选择根据需求灵活使用。 碰撞检测器 在Unity中为物体添加…

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