概述
前端面试知识点
菜鸟的面试日记
HTML/CSS
1.flex布局
学习flex之后使用flex布局绘制色子
(1)容器属性
属性 | 属性值 |
---|---|
flex-direction | row / row-reverse / column / column-reverse 决定主轴的方向 |
flex-wrap | nowrap / wrap / wrap-reverse 如何换行 |
flex-flow | flex-direction + flex-wrap |
justify-content | flex-start / flex-end / center / space-between / space-around 水平对齐方式 |
align-items | flex-start / flex-end / center / baseline / stretch 垂直对齐方式 |
align-content | flex-start / flex-end / center / space-between / space-around / stretch 多根轴线对齐方式,若只有一根轴线,该属性不起作用 |
(2)项目属性
属性 | 属性值 |
---|---|
order | 项目的排列顺序。数值越小,排列越靠前,默认为0 |
flex-grow | 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大 。1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。 |
flex-shrink | 项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。 |
flex-basis | 在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小 |
flex | flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。两个快捷值auto (1 1 auto) 和 none (0 0 auto) |
align-self | 单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch |
详情见:https://ruanyifeng.com/blog/2015/07/flex-grammar.html
2.三栏布局(两边固定,中间自适应)
<body>
<div class="left">左边</div>
<div class="right">右边</div>
<div class="middle">中间</div>
</body>
(1)float + margin
.middle {
height: 100%;
margin: 0px 右的width 0px 左的width;
background-color: tomato;
}
(2) float + calc
.left {
width: 300px;
height: 100%;
background-color: pink;
float: left;
}
.right {
width: 200px;
height: 100%;
float: left;
background-color: skyblue;
}
.middle {
width: calc(100% - 300px - 200px);
height: 100%;
float: left;
background-color: tomato;
}
(3) flex
body {
display: flex;
}
.middle {
height: 100%;
background-color: tomato;
flex-grow: 1;
}
(4) grid
body {
display: grid;
grid-template-columns: 300px auto 200px; // 左 中 右
}
3.Grid布局
阮一峰老师网格布局教程link
4.双飞翼/圣杯布局
教程link
5.px em rem vw vh的区别
px | 像素,相对长度单位,相对于屏幕分辨率 |
---|---|
em | 参考父元素的font-size,具有继承的特点。父:14px,则1em=14px |
rem | 参考物是HTML根元素的font-size大小 |
vm / vh | 相对于视窗宽度和高度的百分比。1vm = 视窗的1% |
rem实现页面等比例放大/缩小
<script>
initPage();
function initPage() {
var clientWidth =
document.documentElement.clientWidth || document.body / clientWidth; //获取屏幕可视区宽
var html = document.getElementsByTagName("html")[0]; //获取html根元素
html.style.fontSize = clientWidth / 10 + "px"; //动态设置font-size大小
}
window.onresize = initPage;
</script>
响应式布局
百分比布局, 媒体查询布局, rem布局, vm/vh响应式布局, flex
6.position
属性值 | 描述 |
---|---|
static | 默认,出现在正常文档流中 |
relative | 相对于自身应该出现的位置进行调整 |
absolute | 相对于最近已定位父元素调整,如果没有,则相对于body调整 |
fixed | 相对于浏览器窗口进行位置调整 |
stick | 基于用户的滚动位置定位 |
7.水平/垂直居中
水平居中
行内元素 | text-align: center |
---|---|
子绝父相 | margin-left: (父width - 子width) |
table+margin | display: table, margin: auto |
inline-block | diaplay:inline-block; text-align: center |
绝对定位+transform | transform: traslateX(-50%,-50%) |
flex布局 | justify-content: center |
垂直居中
纯文字类 | line-height |
---|---|
子绝父相 | margin: auto |
flex布局 | display: flex; align-item: center |
table布局 | verical-align: middle |
更多居中方式戳link
8.css图形绘制
绘制三角形
口诀: 宽高皆为零,三边皆透明
width设置数值可以绘制梯形
.triangle {
width: 0;
height: 0;
border-right: 100px solid transparent; /*右三角*/
border-bottom: 100px solid transparent; /*下三角*/
border-left: 100px solid transparent; /*左三角*/
border-top: 100px solid #000; /*上三角*/
}
效果:
绘制五角星
9.css动画
css实现动画的三种方式:transition, transform, animation
关于以上三种方式详解戳link
前端实现动画的方式
css3的transition 属性
css3的animation 属性
原生JS动画
使用canvas绘制动画
SVG动画
Jquery的animate函数
使用gif图片
CSS动画和JS实现的动画分别有哪些优缺点?
CSS动画
- 优点
浏览器可以对动画进行优化
代码相对简单,性能调优方向固定
对于帧速表现不好的低版本浏览器,CSS3可以做到自然降级,而JS则需要撰写额外代码 - 缺点
运行过程控制较弱,无法附加事件绑定回调函数
代码冗长,想用CSS实现稍微复杂一点动画,最后CSS代码都会变得非常笨重
JS动画
- 优点
控制能力很强, 可以在动画播放过程中对动画进行控制:开始、暂停、回放、终止、取消都是可以做到的。
动画效果比css3动画丰富,有些动画效果,比如曲线运动,冲击闪烁,视差滚动效果,只有js动画才能完成
CSS3有兼容性问题,而JS大多时候没有兼容性问题 - 缺点
代码的复杂度高于CSS动画
JavaScript在浏览器的主线程中运行,而主线程中还有其它需要运行的JavaScript脚本、样式计算、布局、绘制任务等,对其干扰导致线程可能出现阻塞,从而造成丢帧的情况
手写动画的最小时间间隔
多数显示器默认频率是60Hz,一秒刷新60次,所以理论上最小间隔为1/60*1000ms = 16.7ms
10.css性能优化
GUP加速
在 css 中,只有 3D 转换、opacity 透明、filter 滤镜和添加了 will-change 属性的元素,浏览器才会开启硬件(GPU)加速。
11.隐藏元素的方法
(1)opacity:0
隐藏元素但不会改变页面布局,绑定的事件仍然能够触发
(2)visibility: hidden
隐藏元素,元素占据的位置依然存在,页面布局不变,但绑定的事件不能触发。(重绘)父元素隐藏,子孙元素全隐藏,给子孙设置visibility: visible时可见
(3)display:none
改变页面布局(回流+重绘),父元素隐藏,子孙元素没有任何办法可见。
回流/重排(reflow): 当Dom的变化影响元素的几何信息,浏览器需要重新计算元素大小,将其安放在合适的位置。
重绘(repaint):外观发生变化但布局没变。
重绘不一定出现重排,重排一定出现重绘
(4)overflow: hidden
该样式加在父元素身上,当父元素内的子元素超出父元素的宽高时,隐藏超出部分。(父元素必须是块级或行内块)。前三种都是加在自己身上的。(这个不算隐藏元素的方法,面试会问visibility,display,overflow区别)
(5)transform: scal(0)
(6)z-index
把元素放在最下面
属性:
Auto:堆栈顺序将设置为等于父级。
Number:数字可以是正数或负数。它定义了堆栈顺序。
Initial:默认值 0 设置为属性。
Inherit:属性是从父级继承的
12.background属性
属性 | 值/描述 |
---|---|
background-color | 背景颜色 |
background-position | 背景图像位置 |
background-size | 背景图片尺寸 |
background-repeat | 如何重复背景图像 |
background-origin | 规定背景图片定位区域 |
background-clip | 背景绘制区域 |
background-attachment | 背景图像是否固定或者随着页面的其余部分滚动 |
background-image | url('图像位置‘) |
具体属性值戳link
12. css样式隔离
https://juejin.cn/post/6844904034281734151#heading-9
13.BFC
BFC(block formatting context): 块级格式上下文。是一个独立的渲染区域,内部子元素的排列不会影响外部其他。
布局原理
- 内部的元素垂直方向排列,垂直方向距离由margin决定。相邻元素margin会重叠。
- BFC不与float box重叠
- 计算BFC高度时,浮动元素也算在内
如何开启BFC
- HTML元素
- float不为none
- position为absolute或fixed
- display为inline-block,table-cell,table-caption
- overflow不为visible
BFC使用场景
- 清除浮动
- 去除边距重叠
- 避免某元素被浮动元素覆盖
JS
1. 检测数据类型
(1)typeof
- 语法:
typeof 变量
- typeof 的返回值类型为字符串类型
- typeof 判断基本数据类型时,除了 null 的输出结果为’object’ 其它类型都能正确判断
- typeof 判断引用数据类型时,除了判断函数会输出’function’ 其它都输出’objec
(2)instanceof
- 语法
obj1 instanceof obj2
- 缺点:只能判断对象是否在目标对象的原型链上,不能判断具体的类型
(3)constructor
- 语法“
"".constructor === string
- 当一个函数 F 被定义时,JS 引擎会为 F 添加 prototype 原型,然后在 prototype 上添加了一个 constructor 属性,并让其指向 F 的引用
- constructor 是不稳定的,因为开发者可以重写 prototype,重写后,原有的 constructor 引用会丢失,需要我们重新指定 constructor 的引用
- 在没有重新指定时,constructor 会默认为 Object
(4) Object.prototype.toString.call()
- 语法:
Object.prototype.toString.call(a)
- Object对象,直接调用toString() 就能返回 [object Object]
- 其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息
2.null 和 undefined区别
相同点
- 都是基本数据类型,存储在栈中
- undefined,null转换成布尔值都为false
不同点
转换成数字不同:
Number(undefined) //NaN
Number(null) //0
**注意:**undefined == null //true
3. == 和===的区别
==进行比较时会将类型不同的进行隐式类型转换,转换规则如下
- 对象 --> 字符串 --> 数值
- 布尔值 --> 数值
特殊情况
NaN === NaN; // false NaN和任何数据都不相等,包括自身
[] == []; // false 比较的是地址
{} == {}; // false 比较的是地址
undefined == null; // true; 特殊情况,记下
console.log([] == false); // true
//[] 转换成字符串是'' ,然后'' 转换成数值是 0
//false 转换成数值是 0 所以最后比较的值是 0==0 ,结果为 true
if ([]) {
alert("能弹出吗?"); // 可以弹出弹窗
}
//if 语句中的表达式或值都会被转成 boolean
//[] 转成布尔值是 true,所以可以弹出。
4.typeof(NaN)
- NaN不是数字的数字类型,因此返回结果为number
- NaN === NaN 为false,应该用isNaN判断是不是NaN
5.手写call、apply、bind
Function.myCall = function(context){
if(typeof this !== 'function'){
throw new TypeError('not a function')
}
context = context || window
context.fn = this
let args = Array.from(arguments).slice(1)
let result = context.fn(...args)
delete context.fn
return result
}
Function.prototype.myApply = function(context){
if(typeof this !== 'function'){
throw new TypeError('not a function')
}
context = context || window
context.fn = this
let result
if(arguments[1]){
result = context.fn(...arguments)
}else{
result = context.fn()
}
delete context.fn
return result
}
Function.prototype.myBind = function(context){
if(typeof this !== 'function'){
throw new TypeError('not a function')
}
const _this = this
let args = Array.prototype.slice.call(arguments, 1)
return function F(){
if( this instanceof F){
return new _this(...args, ...arguments)
}else{
return _this.apply(context, args.concat(...arguments))
}
}
}
6. 手写new
字面量创建对象
- 更简单,方便阅读,不需要解析作用域,速度快
new创建对象
- 创建一个新对象
- 使新对象的__ proto __指向函数的prototype
- 将函数的this指向新对象,执行结果保存起来
- 判断执行结果为
function myNew(fn, args){
let obj = {}
obj.__proto__ = fn.prototype
let result = fn.apply(obj, args)
return result instanceof Object ? result : obj
}
7. const let var的区别
- 变量提升和暂时性死区: var 存在变量提升,let 和 const 不存在变量提升,所以 let 和 const 会存在暂时性死区
- 块级作用域: var 不存在块级作用域,let 和 const 存在块级作用域
- 重复声明: var 允许重复声明变量,let 和 const 在同一作用域下不允许重复声明变量
- 修改变量: var 和 let 声明的变量可以修改,const 是不可以的。
- 使用:const 用来声明常量,引用类型值。其它情况推荐用 let ,避免用 var
注意
const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个栈内存地址所保存的数据不得改动。
对于简单类型的数据(数值、字符串、布尔值)值就保存在变量指向的那个栈内存地址,因此等同于常量。
引用类型的数据(主要是对象和数组)变量指向的栈内存地址,保存的只是一个指向实际数据的指针
const 只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了
8.闭包里的变量为什么不会被回收
什么是垃圾回收
在 js 中所谓的垃圾就是指不会再被使用的值,就会被当成垃圾回收掉
- 标记清除:
- 当函数被调用,变量进入上下文时,会被加上存在上下文标记,是不会被清理的。
- 当函数执行完成后,就会去掉存在上下文中的标记,随后垃圾回收程序会做一次内存清理,销毁这些变量。
- 引用计数(循环引用,可能造成内存永远无法释放)
- 引用计数就是追踪值被引用的次数。声明变量并给它赋一个引用类型值时,这个值的引用数 为 1。
- 如果同一个值又被赋给另一个变量,那引用数+1 。如果保存该值引用的变量被其它值覆 盖了,则引用数减 1。
- 当引用计数为 0 时,表示这个值不再用到,垃圾收集器就会回收他所占 用的内存。
什么是闭包
函数执行,形成私有的执行上下文,使内部私有变量不受外界干扰,起到保护和保存的作用
闭包为什么不回收
这里我们要明确一个点,如果闭包函数的引用计数为 0 时,函数就会释放,它引用的变量也会被释放。
- 只有当闭包函数的引用计数不为 0 时,说明闭包函数随时有可能被调用,他被调用后,就会引用他在定义时所处的环境的变量。
- 闭包中的变量就得一直需要在内存中,则就不会被垃圾回收掉
9.JavaScript内存泄露如何检测?场景有哪些?
闭包是内存泄露吗
闭包不可以垃圾回收,但闭包不属于内存泄露。内存泄露指不在预期内的不能回收,闭包属于预期内的不可回收。
但有时由于程序员不严谨,写了非必要的闭包,就有可能引起内存泄露。
检测内存变化方法
develop tools中的performance,取消screenshots,选中memory(先点垃圾桶,再点第一个小圆圈,再开始)
内存泄露场景(Vue为例)
- 被全局变量、函数引用,组件销毁时未清除
- 被全局事件、定时器引用,组件销毁时未清除
- 被自定义事件引用,组件销毁时未清除
重点
前几年前端不太注重内存泄露,因为不像后端7*24持续运行
近几年前端功能不断复杂,内存问题也要重点考虑
set map weakMap WeakSet 弱引用
-
set:类似于数组,但成员是唯一且无序的,没有重复的值。
- 方法:set.has/add/delete/clean
- 遍历;keys()/values()/entries()/forEach(callback)
-
WeakSet
- WeakSet 只能储存对象引用,不能存放值,而 Set 对象都可以
- WeakSet 对象中储存的对象值都是被弱引用的,则这个对象将会被垃圾回收掉(不考虑该对象还存放于 WeakSet 中),所以 WeakSet 对象里有多少个成员元素,取决于垃圾回收机制有没有运行,运行前后成员个数可能不一致,遍历结束之后,有的成员可能被垃圾回收了,WeakSet 对象是无法被遍历的(ES6 规定 WeakSet 不可遍历),也没有办法拿到它包含的所有元素
-
map就是字典
10.执行上下文和执行栈
可执行代码
有三种:全局代码、函数代码、eval代码
当执行到一个函数的时候,就会进行准备工作,这里的“准备工作”,让我们用个更专业一点的说法,就叫做"执行上下文(execution context)"。
执行上下文
全局执行上下文、函数执行上下文、eval执行上下文
对与每个执行上下文都有三个重要属性:变量对象(VO)、作用域链、this
执行上下文栈
为管理执行上下文,js创建执行上下文栈(Execution context stack,ECS)。栈底永远是全局执行上下文,栈顶永远是正在执行的函数执行上下文。
11.作用域链
作用域
变量可作用范围称作作用域
作用域链
当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
12 .变量对象
变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。
全局上下文
全局上下文中的变量对象就是全局对象
函数上下文
在函数上下文中,我们用活动对象(activation object, AO)来表示变量对象。
活动对象和变量对象其实是一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在 JavaScript 环境中访问,只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,所以才叫 activation object 呐,而只有被激活的变量对象,也就是活动对象上的各种属性才能被访问。
活动对象是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化。arguments 属性值是 Arguments 对象。
13.js的静态作用域和动态作用域
- 静态作用域:又称词法作用域,是指作用域在词法阶段就被确定了(函数定义的位置就决定了函数的作用域)不会改变,javascript 采用的是词法作用域。
- 动态作用域:函数的作用域在函数调用时才决定的。
14.原型和原型链
原型分为隐式原型和显式原型,每个对象都有一个隐式原型,它指向自己的构造函数的显式原型。每个构造方法都有一个显式原型。
- __ proto 是隐式原型;prototype是显式原型
- 所有实例的__ proto__都指向他们构造函数的prototype__
- 所有的prototype都是对象,自然它的__ proto__指向的是Object()的prototype
- 所有的构造函数的隐式原型指向的都是Function()的显示原型
- Object的隐式原型是null
原型链
:多个__ proto__组成的集合称作原型链
原型链查找
- 当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果这个对象本身没有这个属性时,它就会去他的
__proto__隐式原型
上去找(即它的构造函数的 prototype)。 - 如果还找不到,就去原型的原型(
即构造函数的prototype的__proto__
)上去找,…一直找到最顶层(Object.prototype
)为止。 - 如果还没有找到,则返回 undefined。
重写原型的问题
- 在已经创建了实例的情况下重写原型,会切断现有实例与新原型之间的联系
- 如果要重写原型,一定要在重写原型后,再创建实例。
Object.create
//Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
Object.create = function(o){
function Fn(){}
Fn.protorype = o
return new Fn()
}
in操作符
用来判断该属性是否在实例或原型上,不管该属性是在实例上还是原型上,只要能找到就返回 true
<script>
function Person() {}
Person.prototype.name = "张三";
let p1 = new Person();
p1.age = 23;
console.log("name" in p1); //true 在原型Person.prototype
console.log("age" in p1); // true 来自实例
console.log("toString" in p1); //在原型Object的.prototype上
</script>
hasOwnProperty
- 用来判断某个属性是否是实例自身的属性,如果是返回 true,不是则返回 false
- hasOwnProperty 是 JavaScript 中唯一一个处理属性但是不查找原型链的函数
//对象实例.hasOwnProperty(属性名);
function Person() {}
Person.prototype.name = "张三";
let p1 = new Person();
p1.age = 23;
console.log(p1.hasOwnProperty("name")); //false 在原型上
console.log(p1.hasOwnProperty("age")); // true 来自实例
console.log(p1.hasOwnProperty("toString")); //false 在原型上
案例:如何判断某个属性是否是原型上的属性
function hasPrototypeProperty(obj, name) {
return name in obj && !obj.hasOwnProPerty(name);
}
//in为true,has为false
for … in
以任意顺序遍历一个对象的可枚举属性(除 Symbol 类型的属性)
for in 循环只会遍历我们自定义的属性,原型上默认的属性不会遍历出来
for (let key in object) {
// key 是一个变量,每次循环时会将object的一个属性的键值给变量key,直到对象中所有属性都遍历完
// 要执行的代码
}
Object.keys() 、Object.values()、Object.entries( )
以上三个方法,都只会枚举对象自身可枚举的属性,不会枚举原型上的。同时也不会枚举 Sysmbol 类型属性。
//Object.keys()方法可以将一个对象作为参数,然后把这个对象[key,value]对中的 key 值以数组的形式遍历出来。
//Object.values()方法可以将一个对象作为参数,然后把这个对象[key,value]对中的 value 值以数组的形式遍历出来。
//Object.entries()方法可以将对象作为参数,返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)
Object.prototype.score = 100;
let s = Symbol("不可枚举");
const obj = {
name: "张三",
age: "23",
[s]: "不可枚举",
};
var arrkeys = Object.keys(obj);
var arrvalues = Object.values(obj);
var arrentries = Object.entries(obj);
console.log(arrkeys); // ['name', 'age']
console.log(arrvalues); // ['张三', '23']
console.log(arrentries); // [['name','张三'],['age',23]]
说说 [] 的原型链
[].__proto__
指向 Array.prototypeArray.prototype.__proto__
指向Object.prototype
Object.protytype.__proto__
的最终指向为 null
15.继承
原型链继承
function Parent () {
this.name = 'kevin';
}
Parent.prototype.getName = function () {
console.log(this.name);
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
console.log(child1.getName()) // kevin
缺点:子类的实例共享了父类构造函数的引用属性 不能传参
借用构造函数(经典继承)
function Parent () {
this.names = ['kevin', 'daisy'];
}
function Child () {
Parent.call(this);
}
var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]
var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy"]
优点:1.避免了引用类型的属性被所有实例共享 2.可以在 Child 中向 Parent 传参
缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法。
组合继承
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child1 = new Child('kevin', '18');
child1.colors.push('black');
console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
var child2 = new Child('daisy', '20');
console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]
优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。
原型式继承
function createObj(o) {
function F(){}
F.prototype = o;
return new F();
}
就是 ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型。
缺点:包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。
寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
function createObj (o) {
var clone = Object.create(o);
clone.sayName = function () {
console.log('hi');
}
return clone;
}
缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。
寄生组合式继承
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function prototype(child, parent) {
var prototype = object(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
// 当我们使用的时候:
prototype(Child, Parent);
16.手写深拷贝和浅拷贝
浅拷贝
var shallowObj2 = { ...obj1 }
//方式2
function shallowClone1(o) {
let obj = {}
for (let i in o) {
obj[i] = o[i]
}
return obj
}
深拷贝
function cloneJson(o) {f
return JSON.parse(JSON.stringify(o))
}
//方式2
function deepClone(o, hash = new Map()){
if(!isObject(o)) return o
if(hash.has(o)) return hash.get(o)
let obj = Array.isArray(o) ? [] : {}
hash.set(o, obj)
for( let i in o){
if(isObject(o[i])){
obj[i] = deepClone(o[i])
}else{
obj[i] = o[i]
}
}
return obj
}
17.浏览器和nodejs的事件循环有什么区别
浏览器的事件循环
-
js是单线程的(因为JS里有可视DOM,如果是多线程,一个正在操作DOM,一个正在删除,导致JS不知道听谁的)
-
宏任务和微任务
- 宏任务,setTimeout、setInterval、setImmediate
- 微任务,promise.then()、async/await
- 微任务在下一轮DOM渲染(把元素绘制到页面上)前执行,宏任务在之后执行
- Promise是宏任务(同步执行),promise.then/catch/finally是异步
-
浏览器事件循环
- 执行宏任务script
- 进入script后,所有的同步任务主线程执行
- 所有宏任务放入宏任务执行队列
- 所有微任务放入微任务执行队列
- 先清空微任务队列,
- 再取一个宏任务,执行,再清空微任务队列
- 依次循环
nodejs异步
nodejs也是单线程,也需要异步。异步任务分宏任务和微任务。但是,他的宏任务和微任务,分不同类型,有不同优先级
-
宏任务类型和优先级(从上到下,上最高)
- Timers - setTimeout setInterval
- I/O callbacks - 处理网络、流、tcp的错误回调
- Idle, prepare - 闲置状态(nodejs内部使用)
- poll轮询 - 执行poll中的I/O队列
- Check检查 - 存储setImmediate回调
- Close callbacks - 关闭回调,如socket.on(‘close’)
-
微任务类型和优先级
- promise.then(), async/await, process.nextTick
- process.nextTick优先级最高
-
nodejs的event loop
- 执行同步代码
- 执行微任务
- 按顺序执行6个类型的宏任务(每个结束都执行当前的宏任务)
区别
- 浏览器和nodejs的event loop流程基本相同
- nodejs宏任务和微任务分类型,有优先级
- 推荐使用setImmediate代替process.nextTick
- Node 端,微任务 在事件循环的各个阶段之间执行
浏览器端,微任务 在事件循环的宏任务 执行完之后执行
18.变量提升
- 对所有函数声明进行提升(除了函数表达式和箭头函数),引用类型的赋值
- 开辟堆空间
- 存储内容
- 将地址赋给变量
- 对变量进行提升,只声明,不赋值,值为
undefined
19. 模块化
为什么使用模块化
- 防止命名冲突
- 更好的分离,按需加载
- 更好的复用性
- 更高的维护性
模块包装格式
-
Commonjs
- 同步执行的,不适合前端
- 使用方式
module.exports = xxx require('xxx')
-
AMD/CMD区别
- AMD 是依赖前置(把依赖放在前面)、提前执行(即使没有用到某个模块,也会提前执行)
- CMD依赖就近、延时执行(用到的时候在声明依赖)
-
ESM
- 使用 export 、 export default 来导出模块,使用 import 来引入模块
20. 箭头函数和普通函数区别
- 箭头函数是普通函数的简写,但是它不具备很多普通函数的特性
- 第一点,this指向问题,箭头函数的this指向它定义时所在的对象,而不是调用它的对象
不会进行函数提升 - 没有arguments对象,不能使用arguments,如果要获取参数的话可以使用rest运算符
- 没有yield属性,不能作为生成器Generator使用
- 不能new
- 没有自己的this,不能调用call和apply
- 没有prototype,new关键字内部需要把新对象的_proto_指向函数的prototype
21. 手写ajax
var ajax = {
get: function(url, callback){
let xhr = XMLHttpRequest()
xhr.open('get',url, false)
xhr.onreadystatechange = function () {
if(xhr.readyState === 4){
if(xhr.status === 200){
callback(xhr.responseText)
}
}
}
xhr.send()
},
post: function(url, callback){
let xhr = XMLHttpRequest()
xhr.open('post', url, true)
xhr.setRequestHeader("Content-type","x-www-form-urlencoded")
xhr.onreadystatechange = function() {
if(xhr.readyState === 4){
if(xhr.status === 200){
callback(xhr.responseText)
}
}
}
xhr.setRequestHeader("content-type","application/x-www-urlencoded")
xhr.send()
}
}
22.手写防抖节流
- 防抖:n秒后在执行该事件,若在n秒内被重复触发,则重新计时
- 节流:n秒内只运行一次,若在n秒内重复触发,只有一次生效
//防抖函数
function debounce(fn, delay){
let timeout
return function(){
if(timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
fn(arguments)
}, delay)
}
}
//节流函数
function throttle(fn, time){
let pre = 0
return function() {
let now = Date.now()
const _this = this
if(now - pre > time){
fn.apply(_this, arguments)
pre = now
}
}
}
23.函数柯里化原理
柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
https://github.com/mqyqingfeng/Blog/issues/42
24.是否了解过requestIdleCallback? 和requestAnimationFrame有什么区别
- 由react fiber引起的关注
- 组件树转换成链表,可分段渲染
- 渲染时可以暂停,去执行其他高优任务,空闲时再继续渲染
- 如何判断空闲? – requestIdleCallback
- requestAnimationFrame每次渲染完都会执行,高优
- requestAnimationFrame请求数据帧可以用做动画执行
- 可以自己决定什么时机调用该回调函数
- 能保证每次频幕刷新的时候只被执行一次
- 页面被隐藏或者最小化的时候暂停执行,返回窗口继续执行,有效节省CPU
- requestIdleCallback空闲时才执行,低优
25.设计模式
26. fetch优缺点
- fetch脱离了XHR,基于promise实现
- 对某些错误不会reject,比如状态码400、500
- fetch不支持超时timeout处理
- fetch默认不携带cookie,需要手动配置
- fetch没有办法监测请求进度,而xhr可以
27.Dom性能优化
- 合并多次对css样式的修改,改为一次处理
- 对DOM查询做缓存
- 将频繁DOM操作改为一次性操作
- 操作DOM前,先把DOM节点删除或隐藏,采用事件代理处理事件
浏览器的渲染机制
- 解析HTML,构建DOM树
- 解析CSS,构建CSS规则树
- 合并DOM树和CSS规则树,生成render(渲染)树
- 从DOM树的根节点开始遍历每个可见节点
- 对每个可见节点,找到css规则树中对应的规则,并应用他们
- 根据每个可见节点以及其对应的样式,组合生成渲染树
- 布局render树(回流/重排),负责个元素尺寸、位置的变化
- 绘制render树(重绘),绘制页面像素信息
- 浏览器将各层信息发送给GPU,GPU会将各层合成,显示在屏幕上
注意
- 样式为display: none的节点会在DOM树中而不在渲染树中
- visibility和opacity隐藏的节点在DOM和渲染树中同时存在
什么是重排和重绘
- 重排:当DOM的变化影响了元素的几何信息,浏览器需要重新计算元素的几何属性,将其安放在界面中正确的位置,这个过程叫做重排。表现为重新布局,重新排列元素
- 重绘:当一个元素的外观发生变化,但没有改变布局,重新把元素绘制出来的过程
- 重排一定引起重绘,但重绘不一定引起重排
什么情况会发生重排
- 添加或删除可见DOM元素
- 元素位置发生变化
- 元素的尺寸发生变化(外/内边距,边框大小、高度宽度)
- 内容发生变化(例如:图片尺寸变化)
- 页面渲染初始化(必须要首次重排)
- 浏览器窗口resize尺寸变化
28.点击三百秒延迟
如何解决移动端Click事件300ms延迟的问题? - 知乎 (zhihu.com)
FastClick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。FastClick的实现原理是在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,并把浏览器在300ms之后的click事件阻止掉。
- 使用:npm install fastclick -S
//main.js引入
import fastClick from 'fastclick'
//初始化FastClick实例。在页面的DOM文档加载完成后
fastClick.attach(document.body)
现在的浏览器基本上不需要fastclick了,只要定义了 viewport 就不会有,定义一个meta标签。
29. 如何上传视频
input type = 'file’去接收,用 window.URL.createObjectURL(file)把 file 文件转换为 URL(现场的/前端转为 URL)
扩展:input的type可以取哪些值
- text
- image
- reset /submit 定义重置/提交按钮。重置按钮会清除表单中的所有数据。
- password
- radio单选按钮
- checkbox复选按钮
- button定义按钮,不能和js合作并且绑定事件
- hidden隐藏
- file上传文件
- h5新增
- number 输入1-5之间数字
- date输入日期,会弹出日期选择器
- color,弹出颜色选择器
- range,包含一定范围的值的输入字段,可显示为滑动控件
- month输入年月
- week,选择周和年
- time,选择时间(无时区)
- datetime,日期和时间(有时区)
- datetime-local日期和时间(无时区)
- search用于搜索字段(搜索字段的表现类似常规文本字段)
- tel
- url用于应该包含URL地址的输入字段。
VUE
https://namephil.github.io/2022/07/10/Vue/#Vue
GIT
Webpack
计算机网络
算法
代码
最后
以上就是魁梧可乐为你收集整理的前端面试知识点前端面试知识点HTML/CSSJSVUEGITWebpack计算机网络算法代码的全部内容,希望文章能够帮你解决前端面试知识点前端面试知识点HTML/CSSJSVUEGITWebpack计算机网络算法代码所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复