面试常见问题

任务执行队列

宏任务同步任务 => 微任务同步任务 => 微任务异步任务 => 宏任务异步任务

宏任务:setTimeoutsetIntervalsetImmediatenew Promise() 是宏任务同步任务)

先后顺序:setImmediate => setTimeout => setInterval

注:setImmediate 没有时间参数,它与延迟 0 毫秒的 setTimeout() 回调⾮常相似。所以当 setTimeout 延迟时间也是 0 毫秒时,谁在前面就先执行谁

微任务: process.nextTickPromise(then、catch、finally)

先后顺序:先执行 process.nextTick 后执行 promise

new Promise(function (resolve) {
console.log('1')
resolve()
}).then(function () {
console.log('2')
})
process.nextTick(function () {
console.log('3')
})
setImmediate(() => {
console.info('4')
})
new Promise(function (resolve) {
console.log('5')
resolve()
}).then(function () {
console.log('6')
})
setTimeout(function () {
console.log('7')
setImmediate(() => {
console.info('8')
})
process.nextTick(function () {
console.log('9')
})
new Promise(function (resolve) {
console.log('10')
resolve()
}).then(function () {
console.log('11')
})
})
// 1 5 3 2 6 4 7 10 9 11 8

JS 数据类型及深浅拷贝

NumberStringBooleanNullundefinedObjectsymbolbigIntfunctionDate

基本类型(单类型): StringNumberbooleannullundefined

引用类型:ObjectfunctionArrayDate

简单数据类型存放到栈里面,存储时变量中存储的是值本身

复杂数据类型存放到堆里面,存储的仅仅是地址,真正的对象实例存放在堆空间中,若程序员不释放,由垃圾回收机制回收

浅拷贝共用一个引用地址,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象

深拷贝会创建新的内存地址,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

浅拷贝实现的方式

1、Object.assign()

2、函数库 lodash_.clone()方法

3、展开运算符

4、Array.prototype.concat()

5、Array.prototype.slice()

深拷贝的实现方式:

1、JSON.parse(JSON.stringify())

2、函数库 lodash_.cloneDeep() 方法

3、jQuery.extend()方法 // $.extend(true,{},obj)

4、手写递归方法,遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。

function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj
}
if (obj instanceof Date) {
return new Date(obj.getTime())
}
if (obj instanceof Array) {
return obj.reduce((arr, item, i) => {
arr[i] = deepClone(item)
return arr
}, [])
}
if (obj instanceof Object) {
return Object.keys(obj).reduce((newObj, key) => {
newObj[key] = deepClone(obj[key])
return newObj
}, {})
}
}

数组去重的方法(简单数组)

1、Set()

2、indexOf() + for

3、includes + for

4、双重 for + slice

5、Filter + indexOf

6、reduce

7、map

事件冒泡、事件捕获、事件委托

事件冒泡

在一个对象上触发某类事件(比如单击 onclick 事件)。这个事件会向这个对象的父级对象传播,从里到外,直至它被处理

text -> div -> body -> document -> window

事件捕获

click 事件首先由 document 元素捕获,然后沿 DOM 树依次向下传播,直至到达实际的目标元素上

window-> document -> body -> div -> text

事件委托

把事件处理器添加到一个上级元素上,这样就避免了把事件处理器添加到多个子级元素上,主要得益于浏览器的事件冒泡机制,可以减少事件注册,节省内存

防抖节流及应用场景

防抖

防抖通常用来处理一些高频触发的事件,比如用户连续点击按钮、输入框频繁输入,避免频繁更新 UI 或者发送请求造成性能浪费

export const debounce = (fn: any, wait: num) => {
let timer: any = null
return () => {
if (timer) {
clearTimerout(timer)
}
timer = setTimeout(fn, wait)
}
}

节流

节流是指对于连续触发的事件,每隔一段时间内执行一次,节流里面涉及的时间主要指时间执行的间隔时间。比如 鼠标滚动、窗口 resize、滚动条滑动 等操作。

export const throttle = (fn, wait) => {
let timer = null;
return function(...args) {
if(!timer) {
timer = setTimeout(function() => {
fn(...args);
timer = null
}, wait)
}
}
}

节流中 timer 为什么要等于 null

定时器是有返回值的,返回值是当前定时器的 ID 号,clearTimeout(timer) ,会清除该定时器函数,但不会改变 ID 值。如果我们不将 timer 设置为 null,而是直接设置新的定时器 ID,那么就会无法清除之前的定时器,会导致多个定时器同时执行,达不到节流的效果。所以在每次执行定时器函数之前,将 timer 设置为 null,确保下次事件触发时能正确清除之前的定时器。

es6 新特性

块级作用域变量

let 和 const 使得变量只在当前作用域内有效

{
let a = 10
const b = 1
console.log(a) // 10
console.log(b) // 1
}
console.log(a) // ReferenceError: x is not defined
console.log(b) // ReferenceError: y is not defined

箭头函数

箭头函数是一种新的函数声明方式,可以更简洁地定义函数,且无需考虑 this 指向问题

const add = (x, y) => x + y
console.log(add(1, 2)) // 3
let a = () => {
console.log(1);
}
a() // 1

模板字符串

模板字符串可以用来更方便地拼接字符串

const name = 'ES6'
console.log(`Hello, ${name}!`) // Hello, ES6!

解构赋值

解构赋值可以用来快速地从数组或对象中提取值并赋给变量

const arr = [1, 2, 3];
const [a, b, c] = arr;
console.log(a, b, c); // 1 2 3
const obj = {x: 1, y: 2, z: 3};
const {x, y, z} = obj;
console.log(x, y, z); // 1 2 3 5.默认参数

默认参数

函数可以设置默认参数,当调用函数时没有传入该参数时,会使用默认值。

const greet = (name = 'World') => `Hello, ${name}!`
console.log(greet()) // Hello, World!
console.log(greet('Alice')) // Hello, Alice!

扩展运算符

扩展运算符可以用来将数组或对象展开成一系列参数

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [...arr1, ...arr2];
console.log(arr3); // [1, 2, 3, 4, 5, 6]
const obj1 = {x: 1, y: 2};
const obj2 = {z: 3};
const obj3 = {...obj1, ...obj2};
console.log(obj3); // {x: 1, y: 2, z: 3}

类和继承

ES6 引入了 class 关键字来定义类和继承关系

class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
const d = new Dog("Rex");
d.speak(); // Rex barks

Promise

Promise 是一种特殊的异步构造函数,可以更方便地处理异步操作,解决回调地狱非常好用

有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise 也有一些缺点

无法取消 Promise,一旦新建它就会立即执行,无法中途取消

如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部

当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

回调地狱

函数作为参数层层嵌套,一个函数作为参数需要依赖另一个函数执行调用

var sayhello = function (name, callback) {
setTimeout(function () {
console.log('hello')
console.log(name)
return callback(null)
}, 1000)
}
sayhello('xiaomi', function (err) {
sayhello('apple', function (err) {
sayhello('huawei', function (err) {
console.log('end')
})
})
})
console.log('mobile phone')

用 promise 解决

var sayhello = function (name) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log('hello')
console.log(name)
resolve()
}, 1000)
})
}
sayhello('xiaomi')
.then(function () {
console.log('frist')
})
.then(function () {
return sayhello('huawei')
console.log('second')
})
.then(function () {
console.log('second')
})
.then(function () {
return sayhello('apple')
})
.then(function () {
console.log('end')
})
.catch(function (err) {
console.log(err)
})
console.log('mobile phone')

Promise 的状态只有这 2 种 fulfilled(成功),rejected(失败), 且一个 promise 对象只能改变一次

pending 变为 fulfilled,从 pending 变为 rejectedresolved 成功后调用 then()rejected 失败后调用 catch()

HTML

常见的块元素和行内元素

块元素:<h1>~<h6><p><div><ul><ol><li>

行内元素:<a><strong><b><em><i><del><span>

块级格式化上下文(BFC)

BFC 是指浏览器中创建了一个独立的渲染区域,并且拥有一套渲染规则,他决定了其子元素如何定位,以及与其他元素的相互关系和作用。

特点:容器里面的元素不会在布局上影响到外面的元素。

产生 BFC 方式:

1、给元素添加浮动 float 属性不为 none,浮动元素就具有 BFC 特性 float: left/right;

2、给元素添加定位 position: absolute/fixed

3、给元素添加 display 属性 display: inline-block / flex / inline-flex / table-cell / table-caption

4、给元素添加 overflow 属性 overflow: hidden / auto / scroll (除了 visible);

BFC 解决的问题

1、外边距塌陷问题

2、浮动元素与其他元素的交互限定区域

3、外边距折叠问题

4、浮动元素周围文字环绕问题

flex:1

flex: 1 的值是 flex-grow: 1; flex-shrink: 1; flex-basis: 0%

意思就是: 元素占据剩余宽度的 1 份,收缩比例为 1,因为 flex-basis 具有最高优先级,元素首次分配宽度(flex-direction: colunm; 则为高度)如果父元素有设置宽度,则为 0%;父元素没有设置宽度,则和 auto 效果一致。

样式优先级

!important > 内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器 > 通配选择器

css 引起的重绘和回流

回流必将引起重绘,而重绘不一定会引起回流

Render Tree 中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流

当页面中元素样式的改变并不影响它在文档流中的位置时(例如:colorbackground-colorvisibility 等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。

class 中嵌套 class,从而减少代码的重复,Less 可以向上/向下解析;Sass 只能向上解析

//Less 定义变量@:
@color: #4D926F;
header {
color: @color;
}
//Sass 定义变量$:
$blue : #1875e7; 
div {
color : $blue;
}

如何让元素水平垂直居中

1、Flex

2、Transformtranslate

3、Position fixedabsolute

4、子元素设置 display: inline-block,父元素设置 text-align: centerline-height 等于 height

跨域

协议 http、域名 192.168.43.122 、端口 8080 其中有一个不同即为跨域,正常不允许通信

跨域方法

1、通过 jsonp 跨域

2、document.domain + iframe 跨域

3、location.hash + iframe

4、window.name + iframe 跨域

5、postMessage 跨域

6、跨域资源共享(CORS)

7、nginx 代理跨域

8、nodejs 中间件代理跨域

9、WebSocket 协议跨域

nginx 配置跨域通常在 nginx.conf

// 指定 Nginx 进程运行的用户,通常是 nginx 用户。这是一个安全措施,以确保 Nginx 进程以较低的特权级别运行。
user nginx;
// 表示 Nginx 会自动根据可用的 CPU 核心数量启动多个工作进程来处理请求
worker_processes auto;
// 指定 Nginx 的错误日志文件的路径和日志级别。在这个示例中,错误日志的级别设置为 notice
error_log /var/log/nginx/error.log notice;
// 指定 Nginx 主进程的 PID 文件路径
pid /var/run/nginx.pid;
// 设置每个工作进程能够同时处理的最大连接数
events {
worker_connections 1024;
}
// 引入了 MIME 类型配置文件,用于指定文件扩展名与 MIME 类型的映射关系
http {
// 如果请求的文件类型无法匹配到具体的 MIME 类型,将使用 application/octet-stream 作为默认的 MIME 类型。
include /etc/nginx/mime.types;
default_type application/octet-stream;
// 定义了访问日志的格式,其中包含了各种请求信息,如客户端 IP、请求时间、请求内容等
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
// 指定访问日志文件的路径和使用的日志格式
access_log /var/log/nginx/access.log main;
// 启用了 sendfile 功能,它可以加速文件传输
sendfile on; # tcp_nopush on;
// 设置了 Keep-Alive 超时时间,即 HTTP 长连接的超时时间
keepalive_timeout 65;
// gzip on;
// 引入了 /etc/nginx/conf.d/ 目录下的所有 .conf 文件,通常这些文件包含了虚拟主机配置和其他站点配置
include /etc/nginx/conf.d/.conf;
// listen 80; 和 listen [::]:80; 这两行配置指示 Nginx 监听 HTTP 请求的 80 端口,分别支持 IPv4 和 IPv6。这是 Web 服务器默认的 HTTP 端口。
server {
listen 80;
listen [::]:80; # 这里指定了虚拟主机的域名,这里是 localhost。这意味着这个虚拟主机会响应访问 localhost 的 HTTP 请求。
server_name localhost;
​ # access_log /var/log/nginx/host.access.log main;
​ # 这是一个配置块,定义了对于根路径 / 的请求应该如何处理。
location / { # 这指定了 Nginx 应该在响应这个虚拟主机的请求时从哪里获取文件。在这里,文件将从/usr/share/nginx/html 目录中获取。
root /usr/share/nginx/html; # 这里列出了默认的索引文件。如果请求的 URL 以 / 结尾,Nginx 将尝试查找并提供 index.html 或 index.htm 文件。
index index.html index.htm; # 这指定了允许跨的域 *可表示所有域,也可以是特定的域
add_header 'Access-Control-Allow-Origin' 'http://source01.odocker.com';
add_header 'Access-Control-Allow-Credentials' 'true'; # 缓存时间
add_header 'Access-Control-Max-Age' 86400; # 允许的请求头
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With'; # 允许的方法
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
}
// error_page 404 /404.html; # redirect server error pages to the static page /50x.html
// 这些配置定义了当发生 500 系列错误时,Nginx 应该显示一个自定义的错误页面 50x.html。这个错误页面位于/usr/share/nginx/html 目录下。
error_page 500 502 503 504/50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
server {
listen 443 ssl; // 监听 443 端口,处理 HTTPS 连接,并启用 SSL
server_name example.com; // 域名配置
ssl_certificate /path/to/your/certificate.crt; // SSL 证书路径
ssl_certificate_key /path/to/your/private_key.key; // SSL 私钥路径
location / { // 配置用于处理 HTTPS 请求的规则
}
}
}

hash 及 history 路由

Hash 路由

Hash 可以改变 url ,但是不会刷新页面, 这并不算是一次 http 请求,所以这种模式不利于 SEO 优化

hash 通过 window.onhashchange 的方式,来监听 hash 的改变,借此实现无刷新跳转的功能

hash 永远不会提交到 server 端(可以理解为只在前端自生自灭)

history 路由

更新页面而不发送 http 请求

使用 history 模式时,需要通过服务端来允许地址可访问

新的 url 可以是与当前 url 同源的任意 url ,也可以是与当前 url 一样的地址

通过 history.state,添加任意类型的数据到记录中。

通过 pushStatereplaceState 来实现无刷新跳转的功能。

使用场景

to B 的系统推荐用 hash ,相对简单且容易使用,且因为 hashurl 规范不敏感;

to C 的系统,可以考虑选择H5 history,但是需要服务端支持

cookie、SessionStorage 和 localStorage

存储的时间有效期不同

cookie 的有效期是可以设置的,默认的情况下是关闭浏览器后失效 document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00GMT"

sessionStorage 的有效期是仅保持在当前页面,关闭当前会话页或者浏览器后就会失效

localStorage 的有效期是在不进行手动删除的情况下是一直有效的

存储的大小不同

cookie 较旧的浏览器可能会限制在4KB一个左右,而现代浏览器通常没有这种限制,对于总 cookie 数量,大多数浏览器允许最多 50 个 cookie,如果超过了会覆盖

localStoragesessionStorage 的存储容量是 5MB(官方介绍,可能和浏览器有部分差异性)

与服务端的通信

cookie 会参与到与服务端的通信中,一般会携带在 http 请求的头部中,例如一些关键密匙验证等。

localStoragesessionStorage 是单纯的前端存储,不参与与服务端的通信

weex

运行原理

weex 中,主要包括三大部分:JS BridgeRenderDom,分别对应 WXBridgeManagerWXRenderManagerWXDomManager,三部分通过 WXSDKManager 统一管理。其中 JS BridgeDom 都运行在独立的 HandlerThread 中,而 Render 运行在 UI 线程。

JS Bridge 主要用来和 JS 端实现进行双向通信 比如把 js 端的 dom 结构传递给 Dom 线程。Dom 主要是用于负责 dom 的解析、映射、添加等等的操作,最后通知 UI 线程更新。而 Render 负责在 UI 线程中对 dom 实现渲染。

编译产物

Weex 编译产物 js bundle 可以部署在服务端,APP 通过网络或者是内置的方式加载,染引擎针对不同平台转换为对应的原生组件;和 ReactNative 类似,Weex 在实际运行过程中,js 侧会形成一个 dom,并通过 Bridge 交由原生侧解析,映射到原生控件再由原生能力进行渲染;Weex 基于 JS V8 引擎,基于 Vue 设计,支持 Android、iOS、Web 三端。

js-bundle这通常是在前端开发中使用的,例如在 React Native 应用中。在这种情况下,js-bundle 是通过打包工具(如 WebpackMetro1)将所有的 JavaScript 文件和依赖项打包成一个单独的 JavaScript 文件。这个打包过程可能包括转译(例如,将 ES6 转译为 ES5),压缩和优化等步骤。生成的 js-bundle 可以被浏览器或 JavaScript 运行时环境直接执行。

衍生

uniapp 的底层技术是使用 Vue.js 框架,uni-app App 端内置了一个基于 weex 改进的原生渲染引擎,提供了原生渲染能力。

VUE2 和 VUE3 响应式原理对比

vue2 的响应式原理是利⽤ es5 的⼀个 API ,Object.defineProperty()对数据进⾏劫持结合发布订阅模式的⽅式来实现的。

vue3 中使⽤了 es6 的 proxy API 对数据代理,通过 reactive() 函数给每⼀个对象都包⼀层 Proxy,通过 Proxy 监听属性的变化,从⽽ 实现对数据的监控。

使⽤ proxy 的优势如下

1、defineProperty 只能监听某个属性,不能对全对象监听

2、可以省去 for in、闭包等内容来提升效率(直接绑定整个对象即可)

3、可以监听数组,不⽤再去单独的对数组做特异性操作,通过 Proxy 可以直接拦截所有对象类型数据的操作

Vuex

包含 state(数据源)、mutations(数据更新的唯一方法,必须为同步函数)、actions(异步提交 mutation)、getters(数据源的计算属性)、modules(模块化 store

Vuex 为什么要有异步更新 action

mutation 内部必须是同步函数,因为数据的复用,异步会导致内部状态难以追踪,但有些操作又是异步更新的,所以需要通过 action 提交 dispatchmutation 修改 state

store.commit()是否可以带第三个参数

不可以,多个参数需要用数组或对象的方式传递

vue 常识

Vue 实例中的 data 是对象,Vue 组件中的 data 必须是函数返回值

vue 实例一般情况不会复用,所以 vue 实例 data 可以是一个对象。但是设计组件的目的就是为了复用,所以组件内的 data 不能是对象。

如果 vue 实例对象/vue 组件复用时,data 是一个对象,data 属性的数据在不同的页面中会始终同步,那么造成的后果就是不同组件页面的数据会相互影响。比如说 你在首页使用组件时更改了 data 内数据,其他复用该组件的页面也会同步更新,这在正常情况下是我们不希望看到的,当然 vue 官方也不希望我们这么做(语法报错)。

V-if 和 v-show

v-show 隐藏则是为该元素添加样式 display:none,dom 元素依旧还在

v-if 显示隐藏是将 dom 元素整个添加或删除

V-ifv-for 两者的优先级

vue2 内部 v-for 的优先级是高于 v-if,即每一次 v-if 都需要遍历整个数组,浪费性能

vue3 内部 v-if 的优先级是高于 v-for,同时使用报错

$nextTick()的使用场景

Vue.nextTick() 是 vue 的全局 api,它主要用来在下次 dom 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 dom。 由于 vue 的更新机制是异步的,所以当数据修改之后,dom 还停留在更新之前,此时想要获取更新后的 dom,可以使用 nextTick,表示的是下次 dom 更新循环结束后执行的回调。

应用场景:created 中获取 dom 可以使用 nextTick、异步更新 DOM、异步获取数据、异步更新数据

插槽

插槽有三种:默认插槽、具名插槽、作用域插槽

v-slot —> # 默认插槽的name默认为default 具名插槽的name为插槽名

作用域插槽使用 slot-scope 访问插槽的私有变量

//在子组 Left 中
<slot name='default' msg='hello' :user = 'userInfo'></slot>
//在父组件中
<Left>
<template #default = "{msg , user}" > // scope = "{msg , user}"
<p>123</p>
<p>{{user}}</p>
<p>{{msg}}</p>
</template>
</Left>

自定义指令

私有自定义指令在组件中通过 directives 节点声明

全局自定义指令在 main.js 使用 Vue.directive()声明

posted @   有些东西学不会  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css
点击右上角即可分享
微信分享提示