前端面试八股文:JavaScript 原型链

前端面试八股文:JavaScript 原型链

一、前言:为什么原型链如此重要?

在 JavaScript 的世界里,原型链是一个"既熟悉又陌生"的概念。很多开发者知道它的存在,却很少直接操作它。然而,原型链是 JavaScript 面向对象编程的基石,理解它不仅能帮助你通过面试,更能让你深入理解 JavaScript 的设计哲学。

二、核心概念:什么是原型链?

1. 基本定义

原型链是 JavaScript 中实现继承和共享属性的机制。每个对象都有一个内部链接指向它的原型(通过 __proto__[[Prototype]]),当访问对象的属性时,如果对象自身没有该属性,就会沿着原型链向上查找,直到找到属性或到达链的末端(null)。

2. 关键术语澄清

// 混淆点澄清functionPerson(){}const p =newPerson();// 易混淆的三个概念: console.log(p.__proto__);// Person.prototype console.log(Person.prototype);// 构造函数的原型对象 console.log(Person.__proto__);// Function.prototype// 正确的关系:// p.__proto__ === Person.prototype// Person.prototype.__proto__ === Object.prototype// Object.prototype.__proto__ === null

3. 原型链的"终点站"

任意对象 → Object.prototype → null ↑ └── 原型链查找的终点 

三、原型链的工作原理

1. 属性查找过程

const obj ={name:'小明'};// 查找 obj.toString() 的过程:// 1. 检查 obj 自身是否有 toString 属性 ❌// 2. 检查 obj.__proto__ (Object.prototype) ✅ 找到!// 3. 调用 Object.prototype.toString()

2. 完整的继承体系

functionAnimal(name){this.name = name;}Animal.prototype.eat=function(){ console.log(`${this.name}正在吃`);};functionDog(name, breed){Animal.call(this, name);// 继承属性this.breed = breed;}// 设置原型链Dog.prototype = Object.create(Animal.prototype);Dog.prototype.constructor = Dog;Dog.prototype.bark=function(){ console.log(`${this.name}在叫:汪汪!`);};const myDog =newDog('旺财','柯基');// 原型链结构:// myDog → Dog.prototype → Animal.prototype → Object.prototype → null

四、现代 JavaScript 中的原型链

1. ES6 Class 是语法糖

// ES6 Class 写法classPerson{constructor(name){this.name = name;}sayHello(){ console.log(`Hello, ${this.name}`);}}// 等价于 ES5 写法functionPerson(name){this.name = name;}Person.prototype.sayHello=function(){ console.log(`Hello, ${this.name}`);};// 验证const p1 =newPerson('Alice'); console.log(p1.sayHello ===Person.prototype.sayHello);// true

2. 继承的实现

// ES6classAnimal{constructor(name){this.name = name;}}classDogextendsAnimal{constructor(name, breed){super(name);this.breed = breed;}}// Babel 编译后的 ES5 代码function_inherits(subClass, superClass){ subClass.prototype = Object.create( superClass && superClass.prototype,{constructor:{value: subClass }});if(superClass){ Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass): subClass.__proto__ = superClass;}}

五、原型链在实际开发中的应用

应用1:框架中的原型扩展

// Vue 2 的实例方法Vue.prototype.$nextTick=function(fn){returnnextTick(fn,this);};Vue.prototype.$set = set;Vue.prototype.$delete= del;// 使用exportdefault{mounted(){this.$nextTick(()=>{// DOM 更新后执行});}};

应用2:工具库的实现

// Lodash 风格的工具函数function_(){if(!(thisinstanceof_))returnnew_(...arguments);this.__wrapped__ = arguments[0];}// 链式调用支持_.prototype.chain=function(){this.__chain__ =true;returnthis;};// 取值_.prototype.value=function(){returnthis.__wrapped__;};

应用3:Polyfill 实现

// Array.prototype.includes 的 Polyfillif(!Array.prototype.includes){ Object.defineProperty(Array.prototype,'includes',{value:function(searchElement, fromIndex){// 实现逻辑...},writable:true,configurable:true});}

六、性能优化:为什么原型链重要?

1. 内存优化对比

// ❌ 方法定义在构造函数内(浪费内存)functionPerson(name){this.name = name;this.sayHello=function(){ console.log(`Hello, ${this.name}`);};}// 创建1000个实例const people1 =[];for(let i =0; i <1000; i++){ people1.push(newPerson(`Person${i}`));}// 内存中有1000个sayHello函数// ✅ 方法定义在原型上(节省内存)functionBetterPerson(name){this.name = name;}BetterPerson.prototype.sayHello=function(){ console.log(`Hello, ${this.name}`);};// 创建1000个实例const people2 =[];for(let i =0; i <1000; i++){ people2.push(newBetterPerson(`Person${i}`));}// 内存中只有1个sayHello函数

2. 属性查找性能

// 原型链查找 vs 闭包classWithPrototype{calculate(){returnthis.x +this.y;}}classWithClosure{constructor(x, y){this.calculate=()=> x + y;}}// 性能测试:原型方法通常更快,因为共享代码

七、面试常见问题与回答

Q1:new 操作符做了什么?

A:

functionmyNew(Constructor,...args){// 1. 创建空对象,链接原型const obj = Object.create(Constructor.prototype);// 2. 绑定 this 并执行构造函数const result =Constructor.apply(obj, args);// 3. 返回对象(如果构造函数返回对象则使用它)return result instanceofObject? result : obj;}

Q2:如何实现继承?

A:

// 最佳实践:组合继承 + 寄生组合继承functionParent(name){this.name = name;}Parent.prototype.sayName=function(){ console.log(this.name);};functionChild(name, age){Parent.call(this, name);// 继承实例属性this.age = age;}// 继承原型方法(寄生组合式)Child.prototype = Object.create(Parent.prototype);Child.prototype.constructor = Child;// 添加子类方法Child.prototype.sayAge=function(){ console.log(this.age);};

Q3:Object.create 和 new 的区别?

A:

// Object.create:创建新对象,指定原型const proto ={x:10};const obj1 = Object.create(proto);// obj1.__proto__ === proto// new:创建新对象,执行构造函数functionF(){}const obj2 =newF();// obj2.__proto__ === F.prototype// Object.create(null) 创建无原型的"纯净"对象const pureObj = Object.create(null); console.log(pureObj.toString);// undefined

Q4:ES6 Class 和 ES5 原型的区别?

A:

  1. 语法糖:Class 是更清晰的语法,底层仍是原型
  2. 继承实现extends 自动处理原型链
  3. 静态方法:Class 有明确的 static 语法
  4. 私有字段:ES2022 支持真正的私有字段 #field
  5. super 关键字:更优雅地调用父类方法

八、最佳实践与注意事项

✅ 推荐做法

// 1. 使用 Class 语法classUser{constructor(name){this.name = name;}// 方法自动进入原型sayHello(){return`Hello, ${this.name}`;}// 静态方法staticcreateAdmin(){returnnewUser('admin');}}// 2. 避免污染内置原型// ❌ 不推荐// Array.prototype.myMethod = function() { ... };// ✅ 推荐classMyArrayextendsArray{myMethod(){// 自定义方法}}// 3. 使用组合优于复杂继承const canEat ={eat(){ console.log(`${this.name} is eating`);}};const canSleep ={sleep(){ console.log(`${this.name} is sleeping`);}};classAnimal{constructor(name){this.name = name;}}// 混入 Object.assign(Animal.prototype, canEat, canSleep);

⚠️ 注意事项

  1. 原型链不宜过深(通常不超过3层)
  2. 避免修改 __proto__(使用 Object.setPrototypeOf
  3. 注意性能:深层原型链查找会影响性能
  4. 使用 Object.hasOwnProperty 区分自身属性和继承属性

九、总结:原型链的现代意义

原型链不是过时的概念,而是 JavaScript 核心特性。虽然现代开发中直接操作原型链的场景减少,但理解原型链能帮你:

  1. 深入理解框架:Vue/React 等框架底层都依赖原型
  2. 编写高效代码:合理使用原型节省内存
  3. 调试更顺畅:知道属性/方法的来源
  4. 通过面试:这是必考的基础知识
  5. 学习新特性:理解 ES6+ 特性的底层实现

记住:你每天写的 array.map()class extendsthis.$router 都在使用原型链。它不是你需要"使用"的工具,而是你需要"理解"的基石。

掌握原型链,你就掌握了 JavaScript 面向对象编程的核心。

Read more

FPGA商用级ISP:动态坏点校正(DPCC)的滑窗架构与并行判决实现

FPGA商用级ISP:动态坏点校正(DPCC)的滑窗架构与并行判决实现

【写在前面:为什么要写这个专栏?】 在数字图像处理领域,ISP(图像信号处理器)的算法原理并不罕见,但真正能够支持 4K@60fps 实时处理、并经过商用验证的 Verilog 硬核实现思路 却往往秘和封装在黑盒之中。 我手里有一套商用级的 ISP 源码,通过对其进行深度拆解,我希望能够分析并抽象出其背后的设计逻辑。这不仅是对高性能图像处理架构的复盘,更是希望能为广大 FPGA 开发者和 ISP 算法工程师提供一个硬核的设计基线(Baseline)。通过分享这些商用 IP 的实现细节,我希望能帮助更多人了解如何将复杂的图像算法转化为高效的硬件流水线,为行业提供一份有价值的参考。 1. 深度解析:为什么“商用级”坏点校正极其困难? 在传感器(Sensor)制造中,由于半导体工艺缺陷或后期老化,不可避免会出现常亮像素(Hot Pixel)或死像素(Dead Pixel)。 * 痛点一:误杀边缘。 如果只是简单的中值滤波,图像中真实的星星、

NWPU VHR-10数据集 无人机遥感目标检测数据集 飞机 储罐 棒球场 网球场篮球场 港口车辆桥梁检测 遥感图像中的地理空间目标检测

NWPU VHR-10数据集 无人机遥感目标检测数据集 飞机 储罐 棒球场 网球场篮球场 港口车辆桥梁检测 遥感图像中的地理空间目标检测

NWPU VHR-10数据集 遥感数据集 NWPU VHR-10数据集是 10个类别地理空间目标检测的挑战性数据集,共650张图片。 YOLO和COCO格式 数据集按默认划分比例:390张训练集、130张验证集、130张测试集。 手动标注了757架飞机、302艘船只、655个储罐、390个棒球场、524个网球场、159个篮球场、163个田径场、224个港口、124座桥梁和598辆车辆。 📊 一、数据集总体信息 项目描述数据集名称NWPU VHR-10(Northwestern Polytechnical University Very High Resolution 10-class Dataset)任务类型遥感图像中的地理空间目标检测(Object Detection in Remote Sensing Images)图像总数650 张(均为高分辨率遥感图像,源自 Google Earth 等平台)图像分辨率约 600×600

FPGA内部资源详解:LUT、FF、BRAM、DSP、PLL是什么?综合报告怎么看

FPGA内部资源详解:LUT、FF、BRAM、DSP、PLL是什么?综合报告怎么看

本文是《FPGA入门到实战》专栏第8篇。上一篇完成了第一个下板项目,本篇从芯片内部视角出发,深入讲解 FPGA 的五大核心硬件资源:LUT、FF、BRAM、DSP 和 PLL。理解这些资源的工作原理和使用限制,是写出高质量 FPGA 代码、读懂综合报告的基础。 FPGA内部资源详解:LUT、FF、BRAM、DSP、PLL是什么?综合报告怎么看 * 1. 为什么要了解内部资源 * 1.1 Artix-7 资源概览 * 2. LUT 查找表 * 2.1 LUT 是什么 * 2.2 LUT 实现任意 6 输入函数 * 2.3 LUT 的双输出模式(O5/

【Microi 吾码】基于 Microi 吾码低代码框架构建 Vue 高效应用之道

【Microi 吾码】基于 Microi 吾码低代码框架构建 Vue 高效应用之道

我的个人主页 文章专栏:Microi吾码 引言 在当今快速发展的软件开发领域,低代码开发平台正逐渐崭露头角,为开发者们提供了更高效的应用构建途径。Microi 吾码低代码框架结合 Vue的强大前端能力,更是为打造高效应用提供了绝佳的组合。在这里,我将深入探讨如何基于 Microi 吾码低代码框架构建 Vue 高效应用。 Microi吾码官网: https://microi.net GitEE开源地址: microi.net: 一:Microi吾码安装指南 1、系统要求 * 操作系统:支持Windows、Linux等主流操作系统。 * 数据库:需要安装并配置支持的数据库,如MySql5.5+、SqlServer2016+、Oracle11g+等。 * 其他软件:安装.NET 8 SDK、Redis,并且最好安装Git用于代码获取。对于一些高级功能,可能还需要安装Docker、MinIO、MongoDB、RabbitMQ、