前端基础知识
- Object.is() 和 “”==”” 的区别
- 双等号(==):如果两边类型相同,则会进行严格比较;如果类型不同,进行类型转换,再进行比较。如null和undefined相等、布尔转数字、字符串转数字、对象转原始。
- Object.is():类型不同,返回false;类型相同,进行比较。其中,-0和+0不等,NaN和NaN相等。
- 0.1+0.2为什么不等于0.3,原理是什么?
- 在JavaScript中,小数的计算会先将小数转换为二进制数,再进行计算。由于0.1和0.2不能准确转换为有限的二进制数,所以会有精度损失,导致这两个数的相加不等于准确值。
- 小数转换为二进制数
- 整数部分的转换方法已掌握。
- 小数部分:将小数乘以2,记录整数部分,将小数部分继续乘以2,直到小数部分为0,将整数正序排列。
- typeof能区分数组和对象吗?判断数组的方法有哪些?
- typeof不能区分出数组和对象,因为得到的值都为’object’。
- 判断数组的方法:使用Object.prototype.toString(结果为[object array])、使用arr.prpto === Array.prototype、arr instanceof Array、ES6的方法Array.isArray()。
- 常见的隐式转换有哪些?
- 比较操作==、四则运算(+-*/)、条件语句后面的表达式。
- js中隐式类型的转换规则
- ==比较:若操作数类型相同,严格比较;若不同:字符串和数字,字符串转换为数字;布尔值和数字,布尔值转换为数字;对象和数字或字符串,对象转原始。
- 四则运算(+):都为数字,相加;都为字符串,拼接;一个数字,一个字符串,数字转字符,然后拼接;有对象,转原始,根据转换规则进行加法;有布尔值,布尔转数字,根据转换规则进行加法;若另一个为字符串,直接拼接;有null或者undefined,null变为0,undefined变为NaN,根据转换规则进行加法;若另一个为字符串,直接拼接。
- 四则运算(-*/):字符串与数字,字符转数字;布尔与数字,布尔转数字;null或者undefined与数字,null变为0,undefined变为NaN。
- if语句后的表达式:以下为false,其他为true:false、0、NaN、null、undefined、””。
- 对象转原始
- 先调用valueOf()方法,当valueOf()返回的不是基本类型,才调用toString()。
- js数据类型有哪些?
- Number、String、Boolean、Null、Undefined、Symbol、BigInt。
- 数据类型的检测方式有哪些?
- typeof判断基本类型(除了null,会返回’object’);instanceof判断一个对象是否为某个构造函数的实例;Object.prototype.toString.call()(表示对象的内部属性 [object Number])。
- null和undefined的区别
- null表示空对象引用,undefined表示声明未赋值。
- 类型检测的区别:使用typeof操作符,null为’object’,undefined为’undefined’。
- 比较操作:==相等,代表没有值;===不相等,不同类型。
- 变量赋值:undefined表示未赋值(开发者忘了),null表示这个变量没有值(开发者主动赋值)。
- type能够判断null吗?
- 不能,结果为’object’。这是js的历史遗留问题。js在设计之初,使用二进制表示js的值,低位表示数据类型。对象的类型标识符为000,而null表示空指针,它的二进制全为0。所以这两个的类型标识符一样。
- || 和 && 的计算规则
- ||:都为假,返回最后一个表达式;第一个为真,返回第一个表达式的值;第一个为假,返回第二个。
- &&:都为真,返回最后一个表达式;第一个为假,返回第一个;第一个为真,返回第二个。
- 数组浅拷贝的方式
- slice():const arr2 = arr.slice()。
- concat():const arr2 = [].concat(arr)。
- 扩展运算符:const arr2 = […arr]。
- Array.from():const arr2 = Array.from(arr)。
- 对象浅拷贝方式
- Object.assign()。
- 扩展运算符:const newObj = {…obj}。
- for in。
- keys().foreach()。
- 深拷贝的方式
- JSON.parse(JSON.stringify(obj))的缺点:无法拷贝函数,无法拷贝特殊对象(Date、正则表达式),不会拷贝原型链上的属性,忽略symbol和undefined属性。
- 如何安全的获取undefined的值
- 使用void:const safeUndefined = void 0。
- js中的包装类型是什么?
- 为原始值临时提供方法和属性。js中有三个包装类型:String、Number、Boolean。
- 包装类型的行为:当试图访问原始值的属性和方法时,js会自动创建对应的包装对象,然后在对象上调用方法和属性。操作完成后,临时创建的对象就会被销毁。
- ES6中的rest参数
- 表示不确定数量的参数。在函数中定义,将传入的多个参数包装成一个数组。rest参数必须是最后一个参数,并且只有一个。
- var、let、const的区别
- 作用域:var在函数作用域中有效,函数外部声明,提升到全局作用域;函数内部声明,提升到函数作用域。let、const在块级作用域有效。
- 重新赋值:var可以重新赋值且重新声明;let可以重新赋值不可重新声明;const不能重新赋值不可重新声明,必须初始化。
- 提升:var会被提升到其作用域的顶部;let和const也会提升到作用域的顶部,但是在初始化前不可以访问(暂时性死区)。
- 暂时性死区
- 定义:在变量声明之前访问变量会导致引用错误。
- 作用:可以防止因变量提升带来的潜在错误。
- 为什么const定义的对象的属性可以被修改
- const常量指的是变量标识符的引用,而不是它所指向的值。例如:const myObject = {name: “Alice”}; myObject.name = “Bob”;这是允许的。而myObject = {name: “Charlie”};会抛出错误。
- 冻结对象
- 可以用Object.freeze方法,使对象的属性变得不可变。但只能冻结第一层属性。若属性也是对象,这些内部对象的属性依然可变,深度冻结需要递归的使用Object.freeze。
- 箭头函数的特性
- 定义方式、参数处理、函数体。
- 没有自己的this对象,箭头函数的this指向不能通过call、apply、bind改变,不能成为构造函数,不能new,没有arguments对象、没有原型prototype,不能用作生成器函数、不能使用yield、没有super。
- 箭头函数的this指向
- 指向定义它时所处上下文的对象的this。
- 扩展运算符的作用以及使用场景
- 作用:允许可迭代的数组或者字符串被展开为一系列的参数。
- 使用场景:数组和对象的复制、克隆、及合并;将数组或者对象中元素作为函数参数。
- Proxy的作用
- 拦截和定义对象的基本操作。
- 数据验证、属性保护。
- 实现观察者模式。
- 创建虚拟属性和方法。
- 代理外部接口,可以将API调用封装为本地对象的属性访问操作。
- 数组解构和对象解构
- 数组解构:const [a, b, c] = [1, 2, 3]; console.log(a); // 输出1;console.log(b); // 输出2;console.log(c); // 输出3。
- 对象解构:const user = {name: ‘Alice’, age: 25}; const {name, age} = user;console.log(name); // 输出’Alice’;console.log(age); // 输出25。
- 解构的高级用法
- 嵌套解构。
- 默认值:const [x = 10, y = 5] = [undefined]; console.log(x); // 输出10;console.log(y); // 输出5;const {a = 3, b = 4} = {a: undefined}; console.log(a); // 输出3;console.log(b); // 输出4。
- 交换变量:[m, n] = [n, m]。
- 函数参数解构。
- 什么是原型和原型链
- 原型:是js中每个对象都具有的一个内部属性[[Prototype]],通常可以通过__proto__属性访问,每个对象的原型也是一个对象,它存储了一些可以被共享的属性和方法。
- 原型链:是一个从对象的原型开始,逐级向上查找的链式结构。每个对象的原型也是一个对象,这个原型对象也有自己的原型,如此递归,直到找到null(Object.prototype的原型是null)。
- 原型修改与原型重写
- 原型修改:更改原型对象的方法和属性。
- 原型重写:将对象的原型更改为一个全新的对象。
- 获取对象非原型链上的属性
- hasOwnProperty、Object.getOwnPropertyNames、Object.keys、Object.getOwnPropertySymbols。
- Promise的创建,以及拥有哪些方法
- 创建Promise:接收一个执行器函数,包含resolve和reject参数,分别处理成功和失败的情况。
- then:处理Promise执行的结果。接收两个参数,一个处理成功,一个处理失败。
- catch:处理Promise失败的结果,then方法的语法糖。
- finally:无论执行成功,都会执行的回调。
- Promise.all和Promise.race:Promise.all:并行执行多个Promise,所有Promise成功时返回成功结果,任一失败则返回失败。Promise.race:并行执行多个Promise,第一个完成的Promise决定结果。
- Promise.allSettled。
- Promise.all和Promise.allSettled的区别
- 都是并行执行多个Promise。
- 区别:Promise.all只有当所有Promise都成功,返回一个成功的Promise,结果是包含所有成功的Promise结果的数组。如有一个失败,立即返回一个失败的Promise,错误信息为失败的错误的Promise的错误。Promise.allSettled无论Promise执行失败与否,都会返回一个Promise,结果是包含每个Promise结果的数组。每个结果对象包含status和value或者reason属性。
- async/await是什么
- 是处理异步操作的语法糖,基于Promise实现。让异步代码看起来像同步代码,提高代码的可读性和可维护性。
- async:声明异步函数。返回Promise。若显式返回一个值,则包装成一个Promise。可以单独使用。
- await:暂停等待Promise完成,返回结果。只能在async中使用。
- async/await 对比 Promise 的优势是什么?
- 代码可读性:消除链式调用的复杂性。
- 错误处理:使用try/catch捕获,更加直观。若Promise有内部错误,会使用嵌套的catch。
- 调试方便:若有多个异步操作,若其中有一个发生错误,使用Promise的话,不清楚是谁发生错误。而async/await则可以。
- 回调函数以及缺点
- 是将一个函数作为参数传给另外一个函数,在主函数执行完被调用。
- 缺点:回调地狱、错误处理复杂、代码执行流程不直观。
- 异步编程有哪些实现方式
- 回调函数、Promise、async/await、生成器与迭代器。
- setTimeout、setInterval、requestAnimationFrame 各有什么特点?
- setTimeout:只延迟执行一次代码、延迟时间可能不准确(会受到其他线程的影响)。
- setInterval:按照固定的时间重复执行代码、延迟时间可能不准确(会受到其他线程的影响)。
- requestAnimationFrame:专门处理动画效果、在浏览器下一次重绘之前调用回调函数、执行频率与设备屏幕刷新率同步。
- 什么是闭包
- 闭包本质上是一个函数,它可以访问到其创建时所在的作用链中的变量。即使外部函数已经执行完,闭包仍然可以访问这些变量。
- 闭包的应用
- 创建私有变量和方法、事件处理器和回调函数、函数工厂,生成不同行为的函数。
- 闭包的实现原理
- 作用域链保存:函数创建的时候,会有一个内部属性[[Scope]],用于保存其创建时所在的作用域链。函数执行时,会创建一个新的上下文和作用域链。新的作用域链包含当前函数的变量以及[[Scope]]中的作用域链。
- 垃圾回收:闭包会阻止外部函数的变量被垃圾回收机制回收。当闭包函数被销毁时,外部函数的变量才被回收。这也是闭包会导致内存泄露的原因。
- 作用域是什么,类型有哪些?作用域链是什么
- 作用域:一个变量或者函数在代码中能够访问的范围。
- 类型:全局、局部(函数、块级(let/const))。
- 作用域链:当前变量在当前作用域未能找到时,js会一层一层往外寻找,直到找到或者到全局作用域。
- 垃圾回收机制
- 引用-计数法:每个对象有一个引用计数器,记录指向这个对象的引用数量。当这个对象的引用计数为零时,这个对象就会被回收。缺点:无法处理循环引用。
- 标记-清除法:从根对象出发,递归标记所有可达对象。未被标记的对象就会被回收。缺点:可能会导致内存碎片化。
- WeakMap 和 Map 的区别
- 键的类型:WeakMap的键是对象,不能是原始值;Map的键可以是任意类型。
- 垃圾回收:WeakMap的键是弱引用,可以被垃圾回收;Map的键是强引用,不会被垃圾回收。
- 应用类型:WeakMap适用于临时存储的场景,避免内存泄露;Map适用于频繁增删改查的场景。
- 什么是 JavaScript 的执行上下文?
- 是代码执行时的环境,包括代码执行所需的信息。
- 执行上下文类型:全局执行上下文、函数执行上下文、eval函数执行时上下文。
- 执行上下文组成:变量对象(变量声明和函数声明)、作用域链、this绑定。
- this的指向规则
- this的指向取决于函数的调用方式,不是函数的定义位置。
- call、apply 及 bind 函数有什么区别?
- call:立即调用、参数逐个传递。
- apply:立即调用、参数为数组。
- bind:返回一个新函数,参数逐个传递。
- call、apply 及 bind 函数使用场景
- call和apply用于借用方法,显示绑定this。
- apply用于参数不太确定的情况。
- bind用于创建一个函数并绑定this,适合事件处理。
- 对象创建的方式有哪些?
- 直接{}、构造函数、Object.create()、class,然后new、由函数返回一个对象。
- Object.create()的工作原理
- 创建一个空的构造函数fun。
- fun.prototype指向传入的参数。
- 返回new fun()。
- js继承方式
- 原型链继承、构造函数继承、组合继承、寄生组合继承、类继承。
- 原型链继承以及缺点
- 实现方式:将子类的原型prototype指向父类的实例(Child.prototype = new Parent();)。
- 缺点:所有实例共享父类属性,修改子类属性会影响到所有实例。
- 构造函数继承
- 实现:在子类的构造函数中调用父类的构造函数(Parent.call(this);)。
- 优点:每个实例都有自己的属性,不会共享父类的属性。
- 缺点:不能继承父类的原型方法。
- 组合继承
- 实现:调用父类构造函数,子类的原型指向父类的实例(Parent.call(this); Children.prototype = new Parent();)。
- 优点:结合了原型链继承和构造函数继承的优点。
- 缺点:调用了两次父类构造函数,开销较大。
- 寄生组合继承
- 实现:子类调用父类的构造函数,子类的原型指向空实例对象,该空实例对象原型指向父类原型(Parent.call(this); Children.prototype = Object.create(Parent.prototype);)。
- 优点:避免调用两次构造函数。
- ES Module 与 CommonJS 模块方案有什么异同?
- 相同:模块化规范,导入导出模块。
- 不同点:ES Module是ES6引入的,动态导入,编译时加载,异步;CommonJS出现在ES6之前,不支持动态导入,运行时加载,同步。
- 什么是变量提升?导致了什么问题
- 在代码执行之前,将变量和函数提升到当前作用域的顶部。
- 问题:变量在声明前可调用,但是值为undefined,逻辑错误;变量声明和使用距离远,代码可读性差;变量名冲突;var声明的变量会提升到全局,导致全局作用域污染。
- forEach 和 map 方法有什么区别?
- forEach方法没有返回值,map方法会返回一个新数组。
- forEach主要用于修改原数组、打印等,map主要用于将原数组的每个元素转换新的元素。
- 数组的遍历方法有哪些?
- for、forEach、map、for…of、filter、reduce等。
- 脚本延迟加载的方式
- 使用async属性、使用defer属性、动态创建脚本元素、使用模块化加载工具。
- hook的使用以及注意方式
- 只能在最外层使用,不能写在循环、条件、其他普通js函数中;只能在react相关的函数中使用,如use开头的自定义hook函数中。
- 为什么要有hooks
- 组件之间复用状态逻辑困难、类组件会有复杂的生命周期、难以理解的class。
- 什么是跨域?
- 浏览器由于同源策略限制一个源到另一个源的资源请求,如果协议、域名、端口任一不同,都会跨域。
- 如何解决跨域问题
- 后端采用CORS、前端采用代理服务器(让请求先发送到本地服务器,再由服务器转发请求,避免浏览器限制)。
- ES6新特性有哪些?
- let、const、箭头函数、模版字符串、解构赋值、默认参数、扩展运算符、类与模块、Promise、Symbol、Map、Set。
- 什么是纯函数?
- 确定性:对于相同的输入,纯函数总是产生相同的输出。
- 无副作用:纯函数不会修改其外部环境,包括全局变量、输入参数对象等。
- js有哪些纯函数?
- toSorted()、toReversed()、toSpliced()、with()。
- 设计一个组件需要考虑哪些因素
- 功能性:组件的用途及逻辑交互(如点击、拖拽等)。
- API设计:确定暴露的属性(props)及默认值,考虑是否需要事件回调。
- 可复用性:确保组件能在多种场景下使用。
- 可维护性:代码需清晰、可读、易维护。
- 状态管理:决定使用受控组件还是非受控组件,是否需要useState及外部状态管理。
- axios二次封装
- 统一设置baseURL、timeout等基础配置。
- 利用请求和响应拦截器,实现token自动注入与异常处理。
- 封装常用GET、POST等方法,简化接口调用。
- HTML语义化是什么,有哪些优点?
- HTML语义化指根据内容结构和含义选择合适标签,更好表达内容意义和层次。
- 优点:利于SEO优化,提高代码维护性和可读性。
- CSS盒模型
- 由margin、border、padding、content组成。
- 标准盒模型(box-sizing: content-box):设置的width和height为content区域,整体宽高需加border和padding。
- 怪异盒模型(box-sizing: border-box):设置的width和height为整个盒子大小。
- 浮动及问题解决
- 浮动使元素左右排列,可能造成父元素高度塌陷及兄弟元素显示问题。
- 清除浮动方式:
- 浮动元素后加空元素,设clear: both。
- 父元素后加伪元素::after,设clear: both。
- 父元素设overflow: hidden。
- 父元素设display: flow-root。
- CSS选择器优先级
- 优先级从高到低:!important > 内联样式 > id选择器 > 属性选择器 > 类选择器 > 伪类选择器(:hover) > 标签选择器 > 伪元素选择器(::after) > 后代选择器 > 子类选择器 > 相邻选择器 > 通配符选择器。
- CSS尺寸单位
- px:像素,绝对单位。
- em:以自身或继承父元素font-size为基准。
- rem:以根元素font-size为基准。
- vw:以视窗宽度为基准。
- vh:以视窗高度为基准。
- BFC及其特点、设置方式
- BFC(块级格式化上下文)是独立渲染区域。
- 特点:不受边界外元素影响内部渲染。
- 设置方式:
- float不为none。
- display为inline-block、table-cell、table-caption、flex、inline-flex。
- position为absolute、fixed。
- overflow不为visible。
- 未知元素宽高实现水平垂直居中
- 方法一:绝对定位 + transform: translate(-50%, -50%)。
- 方法二:flex布局,设display: flex,justify-content: center,align-items: center。
- 方法三:grid布局,设display: grid,place-items: center。
- 三栏布局
- 方法一:flex布局,设display: flex,左右栏宽固定,中间栏设flex: 1。
- 方法二:grid布局,设display: grid,grid-template-columns: 200px auto 200px。
- 伪数组
- 具备类似数组特性但非数组的对象,通常有length属性和按索引存储的元素。
- map和forEach
- map返回新数组,不改变原数组,处理速度快,方便链式调用。
- forEach可能改变原数组,无返回结果。
- new操作符发生了什么
- 创建空JS对象。
- 新对象添加__proto__属性,链接至构造函数原型对象。
- 新对象作为this上下文。
- script标签中的defer和async属性的区别
- html文件默认按顺序执行,script标签无defer或async属性时,浏览器遇script标签会加载js文件,暂停html解析,加载完后立即执行js,再继续解析html。
- 加defer或async属性时,浏览器遇script标签异步加载js文件,html解析与js加载并行,不阻塞html解析。
- async:js加载完后立即执行,阻塞html解析。
- defer:等待html解析完成后执行js文件。
- 静态方法和实例方法
- 静态方法:用static关键字定义,属类本身,只能由类名调用。
- 实例方法:由类实例调用,不能由类名调用。
- cookie、sessionStorage、localStorage的区别
- 共同点:均属浏览器存储,存储于本地,遵循同源原则。
- 不同点:
- cookie由服务端写入,sessionStorage和localStorage在前端写入。
- cookie生命周期由服务端确定,localStorage长期存在(除非手动清除),sessionStorage页面关闭时清除。
- cookie存储空间约4kb,sessionStorage和localStorage空间较大(5M)。
- 前端发请求给后端时,自动携带cookie,sessionStorage和localStorage不携带。
- token能否放在cookie中
- 理论上可以,但是不建议将token放在cookie中。若token放在cookie中,发请求时会自动携带,无法有效防止CSRF攻击。
- 受控组件和非受控组件
- 受控组件:组件状态由React state管理,用户输入值反映到state中,表单组件值不由DOM直接管理。
- 非受控组件:DOM元素值由浏览器管理,不受React state影响,通常用ref获取DOM节点value。
- TypeScript相较于JavaScript的优点
- 类型安全性:强类型检查,在编译阶段发现错误。
- 更好的工具支持:提供强大代码提示和补全功能。
- 内置ES6模块。
- 元素隐藏的方式及性能比较
- JavaScript事件循环及其必要性
- 必要性:JS单线程,无法同时执行多任务,需事件循环管理任务执行顺序。
- 组成部分:
- 调用栈:执行同步代码。
- 微任务队列:执行微任务(Promise.then、MutationObserver、queueMicrotask),优先级高于宏任务。
- 宏任务队列:执行宏任务(setTimeout、setInterval、requestAnimationFrame等)。
- 执行顺序:
- 执行同步代码(所有同步任务进入调用栈)。
- 执行所有微任务。
- 执行一个宏任务。
- 重复执行微任务和宏任务,直至所有任务完成。
- 如何用CSS画三角形
- 通过设置div的width和height为0,利用border属性创建三角形。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS三角形</title>
<style>
div {
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 100px solid red;
}
</style>
</head>
<body>
<div></div>
</body>
</html> - 事件委托和事件代理
- 事件委托是事件代理的具体应用场景。
- 事件代理:借助事件冒泡机制,将子元素事件委托给父元素。父元素监听事件,判断来源,处理目标元素。
- 事件流的三个阶段
- 事件捕获阶段:从window向目标元素传递事件,父元素优先捕获。
- 目标阶段:事件到达目标元素。
- 事件冒泡阶段:从目标元素向上冒泡至window。
- React响应式原理
- 通过State和Props驱动UI更新,采用虚拟DOM技术,结合高效DIFF算法实现高效页面渲染。
- 虚拟DOM及其优势
- 是JS对象,模拟真实DOM结构,避免直接操作DOM,提升性能。
- 示例:
- 真实DOM:
<div><h1>Hello</h1></div>
- 虚拟DOM(JS对象):
{type: 'div', props: {children: [{type: 'h1', props: {children: 'Hello'}}]}}
- 真实DOM:
- Diff算法解析
- 用于比较新旧虚拟DOM,找出最小差异,高效更新真实DOM。
- 树的Diff:节点类型相同则比较子节点,不同则替换节点。
- 组件的Diff:同类型组件对比Props并重新渲染,不同类型组件卸载旧组件并挂载新组件。
- 元素的Diff:更新属性、样式、事件变化。
- 浏览器回流和重绘及其优化方法
- 回流:元素尺寸、位置或布局改变时,浏览器重新计算页面结构和布局并绘制,消耗较大。
- 重绘:元素视觉属性变化时(如颜色、背景、边框),浏览器重新绘制像素,不涉及布局计算,消耗较低。
- 优化方法:
- 合并样式变更:避免多次操作DOM,使用类名切换一次性修改样式。
- 使用transform、opacity、visibility:不触发回流。
- 减少布局操作。
- HTML、CSS和JavaScript在前端开发中的作用及关系
- HTML:定义网页结构和内容,如房子框架。
- CSS:美化网页外观和布局,如房子装修。
- JavaScript:负责交互和动态效果,如房子智能系统。
- 常用Hook及使用场景
- useState:管理组件状态,适用于表单输入、切换状态等场景。
- useEffect:处理副作用,如数据获取、手动DOM操作,常用于组件挂载、更新和卸载时。
- useContext:在组件树中共享状态,避免过多props传递。
- useEffect的依赖项设置及空数组与不传依赖项的区别
- 不传依赖项:每次渲染都执行。
- 依赖项为空数组:仅组件挂载时执行一次,适合一次性副作用(如发API、初始化第三方库)。
- 带依赖项:依赖项变化时执行副作用。
- 自定义Hook的方法
- 自定义Hook是use开头的函数,可使用其他Hook封装状态逻辑、数据请求等。
- JS加载对CSS解析的影响
- JS加载不影响CSS解析。若JS依赖CSS,可能会等待CSS加载完成。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 MIRAI 的博客!