当前位置: 首页 > news >正文

JavaScript 中的原型和原型链

JavaScript 中的原型和原型链也是一个相对较难理解透彻的知识点,下面结合详细例子来进行说明:

一、原型的概念

在 JavaScript 中,每个函数都有一个 prototype 属性,这个属性指向一个对象,这个对象就是所谓的 “原型对象”。当通过构造函数创建一个新的实例对象时,该实例对象会自动拥有一个指向构造函数原型对象的内部属性(在大多数浏览器中可以通过 proto 来访问这个内部属性,虽然它并非标准属性,但方便理解)。
例如:

function Person(name, age) {this.name = name;this.age = age;
}Person.prototype.sayHello = function () {console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
};let john = new Person('John', 30);
john.sayHello(); 
// 输出:Hello, my name is John and I'm 30 years old.

在这个例子中:
首先定义了一个 Person 函数作为构造函数,用来创建 Person 类型的实例。
然后给 Person 函数的 prototype 属性添加了一个 sayHello 方法。
当通过 new Person(‘John’, 30) 创建了 john 这个实例后,john 本身并没有 sayHello 这个方法的定义,但是它可以通过内部的 proto 属性(指向 Person.prototype)找到并调用 sayHello 方法。

二、原型链的形成

当在一个对象上访问某个属性或方法时,如果该对象本身没有这个属性或方法,JavaScript 就会自动沿着它的原型链去查找。原型链就是由对象的 proto 属性连接起来的一系列对象。
继续上面的例子,假设我们有这样一个情况:

function Employee(name, age, department) {Person.call(this, name, age);this.department = department;
}Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;Employee.prototype.sayDepartment = function () {console.log(`I work in the ${this.department} department.`);
};let jane = new Employee('Jane', 25, 'Engineering');
jane.sayHello(); 
// 输出:Hello, my name is Jane and I'm 25 years old.
jane.sayDepartment(); 
// 输出:I work in the Engineering department.

在这个例子中:
首先定义了 Employee 函数作为构造函数来创建 Employee 类型的实例,并且在构造函数内部通过 Person.call(this, name, age) 调用了 Person 构造函数,以便让 Employee 实例继承 Person 实例的 name 和 age 属性。
然后通过 Employee.prototype = Object.create(Person.prototype) 让 Employee 的原型对象继承自 Person 的原型对象,这样 Employee 实例就可以沿着原型链找到 Person 原型对象上的方法(如 sayHello)。同时,重新设置了 Employee.prototype.constructor 为 Employee,以保证构造函数的正确性。
最后给 Employee 原型对象添加了 sayDepartment 方法。
当我们在 jane(Employee 实例)上调用 sayHello 方法时,jane 本身没有 sayHello 方法,它会通过自己的 proto 属性(此时指向 Employee.prototype)找不到,就继续沿着 Employee.prototype 的 proto(因为 Employee.prototype = Object.create(Person.prototype),所以 Employee.prototype.proto 指向 Person.prototype)找到 Person.prototype 上的 sayHello 方法并调用。

三、原型链的查找顺序

为了更清楚地展示原型链的查找顺序,我们再看一个例子:

function Animal() {}
Animal.prototype.eat = function () {console.log('Eating...');
};function Dog() {}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.bark = function () {console.log('Woof!');
};let myDog = new Dog();
myDog.eat(); 
// 输出:Eating...
myDog.bark(); 
// 输出:Woof!// 查看原型链
console.log(myDog.__proto__ === Dog.prototype); 
// 输出:true
console.log(myDog.__proto__.__proto__ === Animal.prototype); 
// 输出:true
console.log(myDog.__proto__.__proto__.__proto__ === Object.prototype); 
// 输出:true
console.log(myDog.__proto__.__proto__.__proto__.__proto__ === null); 
// 输出:true

在这个例子中:
定义了 Animal 函数和其原型对象上的 eat 方法。
定义了 Dog 函数,并让其原型对象继承自 Animal 原型对象,同时在 Dog 原型对象上添加了 bark 方法。
当创建了 myDog 这个 Dog 实例后,在 myDog 上调用 eat 方法时,首先 myDog 本身没有 eat 方法,它会沿着原型链查找。先通过 myDog.proto(指向 Dog.prototype)找不到,再通过 myDog.proto.proto(指向 Animal.prototype)找到并调用 eat 方法。同样,调用 bark 方法时,在 myDog 本身找不到,通过 myDog.proto 就能找到并调用。
从 myDog 的原型链可以看出,查找顺序是先在实例本身查找,然后沿着 proto 指向的对象依次查找,一直到 Object.prototype,最后 Object.prototype.proto 为 null,表示原型链的尽头。

四、原型和原型链的难点

概念的抽象性:原型和原型链涉及到对象、函数、构造函数、原型对象等多个概念的相互关系,理解起来比较抽象。例如,要清楚地区分函数的 prototype 属性和实例对象的 proto 属性,以及它们之间是如何相互作用来实现属性和方法的继承的,这对于初学者来说并不容易。
复杂的继承关系:当涉及到多层继承时,如上面例子中的 Employee 继承自 Person,再加上可能还有更多层的继承关系,要准确把握每个实例沿着原型链查找属性和方法的路径以及如何正确设置继承关系(比如通过 Object.create 等方式),这需要对原型链的机制有深入的理解。
与其他语言的差异:对于有其他编程语言背景的开发者来说,JavaScript 的原型继承方式与基于类的继承(如 Java、C++ 等)有很大的不同。在基于类的继承中,继承关系通常是通过类的定义和关键字(如 extends)来明确规定的,而 JavaScript 是通过原型和原型链这种相对更灵活但也更抽象的方式来实现继承的,所以习惯了类继承的开发者可能会觉得难以适应。
原型和原型链在 JavaScript 中是非常重要的概念,虽然理解起来有一定难度,但掌握它们对于深入理解 JavaScript 的对象系统以及编写高效、灵活的代码至关重要。

http://www.lryc.cn/news/494231.html

相关文章:

  • 数组变换(两倍)
  • GBN协议、SR协议
  • 三维扫描检测仪3d扫描测量尺寸-自动蓝光测量
  • 大模型翻译能力评测
  • MySQL隐式转换造成索引失效
  • SuperMap Objects组件式GIS开发技术浅析
  • 多组数输入a+b:JAVA
  • R语言结构方程模型(SEM)在生态学领域中的应用
  • 架构-微服务-服务调用Dubbo
  • 【SpringBoot问题】IDEA中用Service窗口展示所有服务及端口的办法
  • OpenCV 图像轮廓查找与绘制全攻略:从函数使用到实战应用详解
  • 电机驱动MCU介绍
  • 人工智能学习框架详解及代码使用案例
  • 修改Textview中第一个字的字体,避免某些机型人民币¥不显示
  • 彻底理解quadtree四叉树、Octree八叉树 —— 点云的空间划分的标准做法
  • Python时间序列优化之道滑动与累积窗口的应用技巧
  • Buffered 和 BuffWrite
  • 【娱乐项目】基于cnchar库与JavaScript的汉字查询工具
  • 泷羽sec-蓝队基础之网络七层杀伤链 (下)学习笔记
  • FPGA 开发工程师
  • 【Leetcode 每日一题】3250. 单调数组对的数目 I
  • 较类中的方法和属性比较
  • nVisual可视化资源管理工具
  • 自动类型推导(auto 和 decltype)
  • 新型大语言模型的预训练与后训练范式,谷歌的Gemma 2语言模型
  • 基于投影寻踪博弈论-云模型的滑坡风险评价
  • WRF-Chem模式安装、环境配置、原理、调试、运行方法;数据准备及相关参数设置方法
  • Spring中每次访问数据库都要创建SqlSession吗?
  • 力扣刷题TOP101:6.BM7 链表中环的入口结点
  • 浅谈telnet和ping