【前端】面试八股文——原型链

【前端】面试八股文——原型链

1. 什么是原型链?

在JavaScript中,每个对象都有一个原型(prototype),而原型本身又可能是另一个对象的原型。通过这种链式关系,可以实现属性和方法的继承,这就是原型链(prototype chain)。

简单来说,原型链是当我们试图访问对象的某个属性时,JavaScript引擎会首先查找对象本身,如果没有找到,则会沿着原型链逐级向上查找,直到找到该属性或到达原型链的顶端。

2. 原型与原型链的基本概念

  • 对象:JavaScript中的一切都是对象,顶层对象是Object
  • 构造函数:用于创建对象的函数,被称为构造函数。示例:function Person(){}
  • 原型:每个构造函数都有一个prototype属性,指向一个对象。
  • 实例:通过构造函数创建的对象实例。使用new关键字。示例:const person = new Person();
  • __proto__:每个对象都有一个__proto__属性,指向创建这个对象的构造函数的原型对象。

3. 原型链的示例

function Person(name) {this.name = name;
}Person.prototype.greet = function() {console.log(`Hello, my name is ${this.name}`);
}const alice = new Person('Alice');
alice.greet();  // 输出: Hello, my name is Aliceconsole.log(alice.__proto__ === Person.prototype);  // true
console.log(Person.prototype.__proto__ === Object.prototype);  // true
console.log(Object.prototype.__proto__ === null);  // true

在上面的例子中,我们可以看到原型链是如何形成的:

  • alice__proto__指向Person.prototype
  • Person.prototype__proto__指向Object.prototype
  • Object.prototype__proto__null,表示原型链的顶端。

4. 常见面试题及解答方法

问题1:解释JS中的原型链?

解答
JavaScript中的原型链是一种对象继承机制,它使得一个对象可以访问另一个对象的属性和方法。每个对象通过__proto__指针指向其原型,形成一个链式结构,最终指向null。这种机制允许我们通过原型链实现属性和方法的继承。

问题2:构造函数与原型链的关系是什么?

解答
构造函数用于创建对象,每个构造函数都有一个prototype属性,指向一个原型对象。通过构造函数创建的对象,其__proto__属性指向构造函数的原型对象,从而形成原型链。

问题3:原型链的终点是什么?

解答
原型链的终点是nullObject.prototype是所有对象的终极原型,其__proto__属性指向null

问题4:如何实现继承?

解答
JavaScript中可以通过原型链实现继承。例如,通过一个构造函数创建子类,并将其prototype属性指向另一个构造函数的实例。

function Parent() {this.parentProperty = true;
}
Parent.prototype.greet = function() {console.log('Hello from Parent');
}function Child() {Parent.call(this);  // 继承构造函数的属性
}
Child.prototype = Object.create(Parent.prototype);  // 继承原型的属性和方法
Child.prototype.constructor = Child;const child = new Child();
child.greet();  // 输出:Hello from Parent
console.log(child.parentProperty);  // true

5. 进阶:原型链性能和优化

理解原型链性能对于高级开发者至关重要。在访问属性时,查找过程会沿着原型链逐级向上,但过长的原型链可能影响性能。因此,保持适当长度的原型链和合理的对象层次结构是必要的。

6. 实际应用

1. 创建对象的共享方法
场景:避免重复定义方法

当多个实例对象需要共享相同的方法时,我们可以将这些方法定义在原型中,而不是在构造函数中重复定义。这样可以节省内存,并提高代码的可维护性和效率。

function Car(model, color) {this.model = model;this.color = color;
}Car.prototype.startEngine = function() {console.log(`${this.model}'s engine is starting...`);
};const car1 = new Car('Toyota', 'Red');
const car2 = new Car('Honda', 'Blue');car1.startEngine();  // 输出: Toyota's engine is starting...
car2.startEngine();  // 输出: Honda's engine is starting...console.log(car1.startEngine === car2.startEngine);  // true

通过将startEngine方法定义在Car的原型上,所有通过Car构造函数创建的实例都会共享这一方法,从而避免重复定义,节省内存。

2. 扩展和定制内置对象
场景:定制内置对象的方法

有时我们需要为内置对象(如数组或字符串)添加或修改方法,这可以通过修改其原型来实现。

Array.prototype.last = function() {return this[this.length - 1];
};const numbers = [1, 2, 3, 4, 5];
console.log(numbers.last());  // 输出:5

通过为Array.prototype添加last方法,所有数组实例都可以直接调用这个方法,从而在全局范围内扩展了数组对象的功能。

3. 面向对象编程
场景:实现类和继承

原型链是实现面向对象编程和继承的重要机制。在实际开发中,我们经常使用原型链来创建类和实现继承。

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('Rex');
dog.speak();  // 输出:Rex barks.

通过这种方式,我们可以使用原型链实现类的继承,并在子类中覆盖父类的方法,从而实现更复杂的面向对象编程模式。

4. 实现混入(Mixin)
场景:复用代码片段

混入是一种代码复用模式,通过将一个对象的方法“混入”另一个对象或类中,来分享功能,而不是通过直接继承。

const canFly = {fly() {console.log(`${this.name} is flying.`);}
};const canSwim = {swim() {console.log(`${this.name} is swimming.`);}
};function Bird(name) {this.name = name;
}Object.assign(Bird.prototype, canFly);const bird = new Bird('Eagle');
bird.fly();  // 输出:Eagle is flying.

通过使用Object.assign方法,我们可以将多个对象的方法混入一个对象的原型中,从而实现代码复用,而不需要通过继承链的方式。

5. 实现模块化和命名空间
场景:避免命名冲突

为了避免全局命名空间污染,我们可以使用对象和原型链将功能模块化,确保每个模块有自己的命名空间。

const MyApp = {Models: {},Views: {},Controllers: {}
};MyApp.Models.User = function(name) {this.name = name;
};MyApp.Models.User.prototype.getName = function() {return this.name;
};const user = new MyApp.Models.User('Alice');
console.log(user.getName());  // 输出:Alice

通过这种方式,我们可以清晰地组织代码,避免全局命名冲突,提高代码的可读性和可维护性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/15603.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为机考入门python3--(29)牛客29-字符串加解密

分类:字符变换 知识点: 字符是字母 char.isalpha() 字符是小写字母 char.islower() 字符是数字 char.isdigit() b变C chr((ord(b) - ord(a) 1) % 26 ord(A)) 题目来自【牛客】 # 加密 def encrypt_string(s):result ""for ch…

【算法】决策单调性优化DP

文章目录 决策单调性四边形不等式决策单调性 形式1法1 分治法2 二分队列例题 P3515Solution 形式2例题 P3195Solution 形式3例题 CF833BSolution 形式4例题Solution 后话 决策单调性 四边形不等式 定义: 对于二元函数 w ( x , y ) w(x,y) w(x,y),若 ∀ a , b , …

强烈推荐 20.7k Star!企业级商城开源项目强烈推荐!基于DDD领域驱动设计模型,助您快速掌握技术奥秘,实现业务快速增长

更多资源请关注纽扣编程微信公众号 1 项目简介 商城是个从零到一的C端商城项目,包含商城核心业务和基础架构两大模块,推出用户、消息、商品、订单、优惠券、支付、网关、购物车等业务模块,通过商城系统中复杂场景,给出对应解决方案。使用 …

Linux网络编程:HTTP协议

前言: 我们知道OSI模型上层分为应用层、会话层和表示层,我们接下来要讲的是主流的应用层协议HTTP,为什么需要这个协议呢,因为在应用层由于操作系统的不同、开发人员使用的语言类型不同,当我们在传输结构化数据时&…

golang windows环境下集成swag的步骤

1、设置环境变量GOBIN%GOPATH%\bin 2、下载依赖包 go get github.com/swaggo/swag/cmd/swag 3、编译swag,进入目录 cd $GOPATH\pkg\mod\github.com\swaggo\swagv1.7.4\cmd\swag 执行:go install 执行结束后就会发现在GOBIN目录下生成了一个 swag.ex…

webpack编译过程

webpack编译过程 初始化 此阶段,webpack会将**CLI参数**、**配置文件**、**默认配置**进行融合,形成一个最终的配置对象。​ 对配置的处理过程是依托一个第三方库yargs完成的 ​ 此阶段相对比较简单,主要是为接下来的编译阶段做必要的准备 ​…

Docker学习(3):镜像使用

当运行容器时,使用的镜像如果在本地中不存在,docker 就会自动从 docker 镜像仓库中下载,默认是从 Docker Hub 公共镜像源下载。 一、列出镜像列表 可以使用 docker images 来列出本地主机上的镜像。 各个选项说明: REPOSITORY&am…

【Linux学习】进程

下面是有关进程的相关介绍,希望对你有所帮助! 小海编程心语录-CSDN博客 目录 1. 进程的概念 1.1 进程与程序 1.2 进程号 2. 进程的状态 2.1 fork创建子进程 2.2 父子进程间的文件共享 3. 进程的诞生与终止 3.1 进程的诞生 3.2 进程的终止 1. 进…

使用docker commit创建新镜像

前言 我们知道,从docker-hub上拉取的镜像所创建的容器是最小版本的,比如ubuntu内部是没有vim编辑器的,我们需要自己手动安装,但是当我们安装后假如有人把我们的容器误删了,那么我们再次根据原始镜像创建的容器就没有了…

CCF20230501——重复局面

CCF20230501——重复局面 代码如下&#xff1a; #include<bits/stdc.h> using namespace std; int main() {int n;cin>>n;char a[101][64];int i,j;for(i0;i<n;i){for(j0;j<64;j){cin>>a[i][j];}}int temp0,flag1;for(i0;i<n;i){flag1;for(j0;j<…

MySQL---通用语法及分类

目录 一、SQL通用语法 二、 SQL分类 1.DDL 1.1 DDL数据库操作 1.2 DDL表操作---查询 1.3 DDL表操作---创建​编辑 1.4 DDL表操作---数据类型 1.5 DDL表操作---修改 1.6 DDL表操作---删除 1.7 DDL总结 2. 图形化界面工具DataGrip 2.1 创建 2.2 使用 3. DML 3.1 DML介绍 3.2 DM…

521源码-免费源码下载-最新个人主页、个人引导页、网站导航页、引流页源码

这款源码采用HTML、CSS和JavaScript的组合&#xff0c;为您打造了一个既美观又实用的个人主页。通过简单的记事本工具打开源码文件&#xff0c;您可以轻松进行内容、文字以及样式上的修改&#xff0c;以满足您的个性化需求。 源码文件包含了完整的HTML结构、CSS样式表和JavaSc…

【深度学习实战—7】:基于Pytorch的多标签图像分类-Fashion-Product-Images

✨博客主页&#xff1a;王乐予&#x1f388; ✨年轻人要&#xff1a;Living for the moment&#xff08;活在当下&#xff09;&#xff01;&#x1f4aa; &#x1f3c6;推荐专栏&#xff1a;【图像处理】【千锤百炼Python】【深度学习】【排序算法】 目录 &#x1f63a;一、数据…

[Linux]Crond任务调度以及at任务调度

一.crond任务定时调度 crond是反复检测执行的&#xff0c;一个任务结束后&#xff0c;在所规定的时间之后会再次执行 crontab 指令可以给系统分配定时任务 crontab -e 进入编辑页面&#xff0c;设定任务 crontab -l 查看已有定时任务 crontab -r 删除所有任务 编辑时&#xff…

vue3全局封装table分页

1.封装element-plus table分页 <template><el-pagination:page-sizes"pageSizes"v-model:current-page"currentPage"v-model:page-size"pageSize":total"totals"size-change"handleSizeChange"current-change&quo…

MySQL - 信贷业务报表

表结构与字段解释 注册表 u_user id&#xff1a;自增主键&#xff0c;唯一标识用户。username&#xff1a;用户名。mobile&#xff1a;手机号。password&#xff1a;用户密码。nickname&#xff1a;用户昵称。role_type&#xff1a;角色类型&#xff08;-1&#xff1a;普通用…

FreeRTOS 源码概述

FreeRTOS 目录结构 使用 STM32CubeMX 创建的 FreeRTOS 工程中&#xff0c;FreeRTOS 相关的源码如下: 主要涉及2个目录&#xff1a; Core Inc 目录下的 FreeRTOSConfig.h 是配置文件 Src 目录下的 freertos.c 是 STM32CubeMX 创建的默认任务 Middlewares\Third_Party…

mac M3芯片 goland 2022.1 断点调试失败(frames are not available)问题,亲测有效

遇到如上问题&#xff0c;解法 步骤1&#xff1a;下载dlv文件 执行 go install github.com/go-delve/delve/cmd/dlvlatest 然后在 $GOPATH/bin里发现多了一个dlv文件 (找不到gopath? 执行 go env 可以看到) 步骤2&#xff1a;配置dlv 将这个dlv文件移到 /Applications/G…

go recover

recover 可以中止 panic 造成的程序崩溃。它是一个只能在 defer 中发挥作用的函数&#xff0c;在其他作用域中调用不会发挥作用。 // A _panic holds information about an active panic. // // A _panic value must only ever live on the stack. // // The argp and link fi…