面试常考总结

css

BFC

Block formatting context 块级格式上下文
形成独立的渲染区域
内部元素的渲染不会影响外界

形成 BFC 的常见条件:

  1. 浮动元素:float不是none
  2. 块级元素:overflow不是visible
  3. flex元素
  4. inline-block元素

JS

变量类型

原始类型:string、number、boolean、Symbol
引用类型:object、array、null、function

原始类型值存在栈中
引用类型值存在栈中,栈对应存的是一个内存地址,这个内存地址存在堆中

类型转换:

// 字符串拼接
const res1 = 10 + 1 // 11
const res2 = 10 + '1' // 101
const res3 = true + '1' // true1

// !!是将取反后的布尔值再取反,转换为布尔值
console.log(!!undefined)  // false
console.log(!!null)   // false
console.log(!!0)    // false
console.log(!!"")   // false
console.log(!!window.hi)    // false

字符串类型值,会将空值("")转换成false,其余转换成true。
数字类型,会将0转换成false,其余为true。
null、undefined会转换成false。

falsy:
说明
false false关键字
0 数值0
-0 数值-0
On 当 BigInt 作为布尔值使用时,遵从其作为数值的规则。0n 是 falsy 值。
"", '', `` 这是一个空字符串 (字符串的长度为零). JavaScript 中的字符串可用双引号 "", 单引号 '', 或 模板字面量 `` 定义。
null 缺少值
undefined 原始值
NaN 非数值
truthy

falsy以外的值都为truthy

原型链:

function foo() {}
const f1 = new foo()
F1.__proto__ === foo.prototype
foo.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null

每一个对象,都有一个原型对象与之关联,这个原型对象它也是一个普通对象,这个普通对象也有自己的原型对象,这样层层递进,就形成了一个链条,这个链条就是原型链。通过原型链可以实现JS的继承,把父类的原型对象赋值给子类的原型,这样子类实例就可以访问父类原型上的方法了。

闭包:

一个函数和他周围状态的引用捆绑在一起的组合。

例1:// 函数作为返回值
function test () {  const a = 1
    return function () {
        console.log(‘a’, a)
    } 
}

const fn = test()
const a = 2
fn()    // a 1

例2:// 函数作为参数
function test (fn) {
    const a = 1
    fn()
}

const a = 2
function fn () {
    console.log(‘a’, a)
}

test(fn)    // a 2

解释:函数会在定义的地方去向上查找变量

This:

This的值是在 函数执行时 决定的,不是在 函数定义时 决定的

setTimeout里的普通函数是指向window的,如果是箭头函数就是指向上层的this

call&apply&bind:

bind函数:

  1. 改变this指向
  2. 第一个参数是this的值,后面的参数是函数接收的参数
  3. 返回值不变
  4. 不会立即调用函数

call函数:

  1. 改变this指向
  2. function.call(thisArg, arg1, arg2, ...)
  3. 会立即调用函数
// 手写call
Function.prototype.myCall = function () {
    const args = [...arguments]
    const self = this
    const _this = args.shift()
    _this.fn = self
    const res = _this.fn(...args)
    delete _this.fn
    return res
}

apply函数:

  1. 改变this指向
  2. function.apply(thisArg, argsArray)
  3. 会立即调用函数

宏任务&微任务:

microtasks(微任务):

唯一,整个事件循环中仅存在一个;执行为同步,同一个事件循环中的microtasks会按队列顺序串行执行完毕

  • process.nextTick
  • promise
  • Object.observe
  • MutationObserver

macrotask(宏任务):

  • setTimeout
  • serInterval
  • setImmediate
  • I/O
  • UI渲染

先执行微任务 > DOM渲染 > 再执行宏任务
** 如果遇到async的代码 await后面的程序就挂起 类似于微任务 等到后面的同步任务执行完了再执行

setTimeout(() => {
    console.log(1)

    setTimeout(() => {
        console.log(2)
    }, 0)

    new Promise((r) => {
        console.log(3)
        r()
    }).then(() => {
        console.log(4)
    })

    console.log(5)
}, 0)

new Promise((r) => {
    console.log(6)
    r()
}).then(() => {
    console.log(7)
})

console.log(8)

// 6 8 7 1 3 5 4 2

异步异常处理:

async function test1 () {
    try {
        await test2()
    } catch (error) {
        console.error('catch in test', error)
    }
}

function test2 () {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('error in test2')
        }, 1000)
    })
}

test1()

防抖&节流:

防抖就是n秒内重复触发同一个操作时,只执行最后一次操作。

function debounce(func, time) {
  let timer;
  return function() {
    clearTimeout(timer);
    let args = arguments;
    timer = setTimeout(() => {
      func.apply(this, args);
    }, time)
  }
}

节流就是在某一个时间段内事件只能被触发一次并执行,如果想再次触发执行需等待下一个时间周期

function throttle(func, delay) {
  let timer = null;
  return function() {
    if (timer) {
        return
    }
    timer = setTimeout(() => {
        func.apply(this, args)
        timer = null
    }, delay)
  }
}

易混淆方法:

splice(index, howmany[0为添加], addOne, addTwo, …) 删除或添加 改变原数组
slice(start[-1从最后一个开始], end[没有的话就到最后]) 截取 返回截取数组 不改变原数组

改变原数组: push、pop、shift、unshift、splice、sort、reverse
返回新数组: filter、concat、slice

框架

VUE

vue原理:

我们new dep类,是为了收集所有的watcher。在读取数据的时候,会触发getter函数,把当前的watcher指向dep.target并触发addDep,收集到dep中去,在写数据时会触发setter函数,通知dep去调用notify来触发每个watcher的渲染函数render,生成一个新的 Virtual DOM Tree,此时 Vue 会对新老 Virtual DOM Tree 进行 Diff,查找出需要操作的真实 DOM 并对其进行更新。

Object.defineProperty处理响应式的问题:
  1. 检测不到对象属性的添加和删除
  2. 数组API方法无法监听到
  3. 需要对每个属性进行遍历监听,如果嵌套对象需要深层监听,造成性能问题。
为什么data是一个函数

因为如果是一个对象的话,每个组件的data都指向同一个内存地址,一个数据变了就都跟着变了。只有用函数,让组件实例都有自己的作用域,相互独立才不会受影响。

v-module

本质是v-bind实现绑定数据,v-on触发oninput事件并传递数据

v-if & v-show

v-show是给元素设置display: none的样式,适合会频繁改变显示隐藏状态的情况
v-if是直接显示或隐藏元素,适合直接就能确定显示隐藏的元素,否则频繁操作dom元素性能开销会比较大。

为什么v-if和v-for不能连用

因为v-for优先级比v-if高,先循环出来列表再每一项去判断v-if就会在性能方面有浪费,所以不建议连用。

为什么for循环的key不建议使用index
  1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
  2. 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
  3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
导航守卫
  1. 全局守卫:beforeEach路由进入之前 afterEach路由进入之后
  2. 组件内守卫:beforeRouteEnter路由进入之前
    beforeRouteUpdate路由更新之前 beforeRouteLeave路由离开之前
  3. 独享守卫 beforeEnter路由进入之前。只在进入路由时触发,不会在 params、query 或 hash 改变时触发
常用事件修饰符

@click.native 监听原生事件
@click.stop 阻止事件冒泡
@click.capture 事件捕获
@click.self 只有在点击事件绑定的元素与当前被点击元素一致时才触发点击事件
@click.prevent 阻止默认行为
@click.once 只执行一次
@scorll.passive 提升滚动性能

vuex

vue Components会去派发(dispatch)一个动作(actions),这个动作会调配(commit)一个转变(mutation)去改变状态(state)

// Store文件
const Store = new Vuex.Store({
    state: {
        isLogin: true
    },
    mutations: {
        setLoginStatus(state, status) {
            state.isLogin(status)
        }
    },
    actions: {
        getLoginStatus({ commit }) {
            // 请求获取登录状态 axios.get('xxx')…
            commit.setLoginStatus(true)
        }
    }
})
// vue文件
methods: {
    onLogin() {
        this.$store.commit('setLoginStatus', true)
    }
},
mounted() {
    this.$store.dispatch('getLoginStatus')
}

vue2和vue3的区别

1. 响应式原理发生了变化
- vue2双向数据绑定是利用了Object.defineProperty()进行数据劫持,综合发布订阅模式来实现。
- vue3使用了ES6的Proxy对数据代理
- 经过控制台调试发现: vue2添加属性或者移除属性的时候,Object.defineProperty会劫持不到。需要this.$set(target, key, value) 、Object.assign、splice等方法去解决该问题。
2. template部分有细微的改动
- vue2中的slot="name名"替换成了v-slot:name名
- vue2中页面必须包一个根标签,vue3中不需要,它会默认添加一个虚拟的Fragment组件到页面中。用调试工具即可看到,这样就减少了标签层级和内存占用【理论上来说确实是】
3. hooks代替mixins
- mixins的变量来源不明确,不利于阅读,使代码变得难以维护
- 多个mixins的生命周期融合到一起运行,但是同名属性、同名方法无法融合,可能会导致冲突。
- mixins和组件可能出现多对多的关系,复杂度较高(即一个组件可以引用多个mixins,一个mixins也可以被多个组件引用)。
- hooks复用代码,让setup中的逻辑更清楚易懂。
- ⾃定义hook相对于普通js复⽤逻辑的抽离,然后可以在页⾯调⽤。
4. vue3对Ts的支持性比较高

React

jsx

  • 允许使用熟悉的语法来定义 HTML 元素树;
  • 提供更加语义化且移动的标签;
  • 程序结构更容易被直观化;
  • 抽象了 React Element 的创建过程;
  • 可以随时掌控 HTML 标签以及生成这些标签的代码;
  • 是原生的 JavaScript。
单向数据流

state状态来驱动页面view,页面里有一些动作actions,动作又会驱动状态改变,这样形成一个单向数据流。

首屏加载慢:

原因:

  1. 网络延时问题
  2. 资源文件体积大
  3. 加载脚本时渲染内容阻塞了
    解决方案:
  • 减小入口文件体积:路由懒加载
  • 静态资源本地缓存:采用http缓存等
  • UI框架按需加载
  • 组件重复打包:在webpack的config文件中,修改CommonsChunkPlugin的配置,minChunks: 3 表示会把使用3次及以上的包抽离出来,放进公共依赖文件,避免了重复加载组件
  • 开启GZip压缩

http:

缓存

强制缓存

浏览器向服务器发送请求,服务器返回资源时头部会设置Cache-Control:max-age=秒数,浏览器就会缓存资源文件。下一次再请求时会检查max-age有没有过期,没过期就直接从缓存里取。
总结:有缓存&没过期

协商缓存

浏览器向服务器发送请求,服务器会返回资源和资源的标识,浏览器会缓存到本地。后续请求时,浏览器发送请求和资源标识(Last-Modified和ETag),服务器判断当前资源版本和服务器版本是否一致,如果一致就返回304状态码,浏览器从缓存里拿资源。如果不一致服务器会返回200状态码和资源文件和最新资源标识。
总结:有缓存,已过期,但是有Last-Modified和ETag

强制缓存和协商缓存的前提都是有缓存,不同点在于是否过期

三次握手

  • 一开始客户端和服务端都处于close状态,客户端主动打开连接,服务端被动打开连接,服务端处于listen状态。
  • 第一次握手:客户端发送SYN包(携带随机初始化序号client_isn)到服务器,状态变为syn-sent状态。
  • 第二次握手:服务器收到SYN包,发送SYN包(携带随机初始化序号server_isn)和ACK包(携带client_isn + 1)发送给客户端,状态变为syn-rcvd
  • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=server_isn + 1),此包发送完毕,客户端和服务器进入established状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

四次挥手

  • 一次挥手:客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入 FIN_WAIT_1 状态。
  • 二次挥手:服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSED_WAIT 状态。
  • 三次挥手:客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
  • 四次挥手:客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态
    服务器收到了 ACK 应答报文后,就进入了 CLOSED 状态,至此服务端已经完成连接的关闭。
    客户端在经过 2MSL 一段时间后,自动进入 CLOSED 状态,至此客户端也完成连接的关闭。

再来回顾下四次挥手双方发 FIN 包的过程,就能理解为什么需要四次了。

关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。

常用状态码

  1. 1XX:消息状态码。
  2. 2XX:成功状态码。
  3. 3XX:重定向状态码。
  4. 4XX:客户端错误状态码。
  5. 5XX:服务端错误状态码。
  • 200:OK 请求成功。一般用于 GET 与 POST 请求。
  • 204:No Content 客户端发送的请求已经在服务器端正常处理了,但是没有返回的内容。
  • 301 Moved Permanently 永久重定向。使用场景:当我们想换个域名,旧的域名不再使用时,用户访问旧域名时用301就重定向到新的域名。
  • 302 Found 临时重定向 使用场景:未登陆的用户访问用户中心重定向到登录页面。
  • 304 Not Modified 浏览器缓存相关。状态码304并不是一种错误,而是告诉客户端有缓存,直接使用缓存中的数据。
  • 400 Bad Request 该状态码表示请求报文中存在语法错误。
  • 401 Unauthorized 没有权限
  • 403 Forbidden 该状态码表明请求资源的访问被服务器拒绝了
  • 404 Not Found 资源找不到
  • 405 Method Not Allowed 客户端请求的方法虽然能被服务器识别,但是服务器禁止使用该方法。
  • 500 Internal Server Error 服务器端在执行请求时发生了错误。
  • 502 Bad Gateway 扮演网关或代理角色的服务器,从上游服务器中接收到的响应是无效的。

http和https的区别

https = http + 加密 + 认证 + 完整性保护
https是身披SSL外壳的http,通常情况下http是直接和tcp进行通信的,当使用ssl时,则演变成http先和ssl通信,ssl再和tcp通信。

  • HTTPS 协议需要申请证书
  • HTTP 和 HTTPS 使用端口不一样,前者是80,后者是443
  • HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,HTTPS 运行在 SSL/TLS 之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的
  • HTTPS 可以有效的防止运营商劫持

http1.0&1.1&2.0

1.0
无状态、无连接,服务器不跟踪不记录请求过的状态,浏览器每次请求都需要建立tcp连接,容易阻塞。
1.1
-长链接可以设置keep-alive保持链接不断开
-缓存处理:cache-control
-断点传输
2.0
-二进制分帧,提高传输效率
-多路复用:在共享TCP链接的基础上同时发送请求和响应

webpack:

工作原理:

一般项目都会散落各种代码以及资源文件,webpack会根据配置找到资源文件的入口(一般是js文件),顺着入口文件的代码寻找import或者require语句,解析推断出来对应的资源模块。然后分别解析每个资源模块所对应的依赖,最终将项目中有用到的文件形成一个依赖树,webpack遍历依赖树,找到每个资源节点所对应的文件,然后通过配置的loader加载器去加载,最后将加载到的结果添加到bundle.js中。

module & chunk & bundle:

modulechunkbundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字:
我们直接写出来的是 module,webpack 处理时是 chunk,最后生成浏览器可以直接运行的 bundle。

常用Plugins:

html-webpack-plugin: 用于生成出来一个html文件,其中引入我们打包好的js文件。
clean-webpack-plugin: 自动清理输出目录
copy-webpack-plugin: 拷贝文件到输出目录

常用Loaders:

file-loader: 帮助webpack处理图片格式文件
url-loader: 处理图片格式文件,生成base64格式。对比file-loader,少了图片文件,减少一次http请求。如果图片很大就会导致js文件很大,加载时间长导致空白页。可设置limit区分图片是用什么方式处理。
style-loadercss-loadersass-loaderpostcss-loader

Optimization(优化):

splitChunks代码分割:

optimization: {
    splitChunks: {
        // all 对同步、异步代码都做代码分割
        // async 对异步代码做代码分割
        // initial 只对同步代码做分割
        // 同步代码例如: import lodash from 'lodash'
        // 异步代码例如:import('lodash')
        chunks: 'all',
        cacheGroups: {
            // 第三方模块
            vendor: {
                // 每个组的名字
                name: 'vendor',
                // 优先级,优先级越高,越优先检测处理
                // 第三方模块 可能也会被作为公共模块来检测处理
                priority: 1,
                // 检测方法,例如:检测模块是否来自node_modules
                test: /node_modules/,
                // 设置代码分割的界限
                minSize: 5 * 1024,
                // 检测模块被引用了几次
                // 对于第三方模块而言,引用1次就应该单独打包
                // 对于公共模块而言,引用2次以上就应该单独打包
                minChunks: 1
            }
        }
    }
}

常见攻击方式

  1. XSS(Cross Site Scripting)跨站脚本攻击,允许攻击者将恶意代码植入到提供给其它用户使用的页面中
    • 通过 HTML 转义,可以防止简单的 XSS 攻击。
    • 对于链接跳转,如
  2. CSRF(Cross site request forgery)跨站请求伪造
    • 阻止不明外域的访问
      • 同源检测
      • Samesite Cookie
    • 提交时要求附加本域才能获取的信息
      • CSRF Token
      • 双重Cookie验证

Docker

我们只需要在dockerfile中指定需要哪些程序、依赖什么样的配置,之后把dockerfile交给“编译器”docker进行“编译”,也就是docker build命令,生成的可执行程序就是image(镜像),之后就可以运行这个image了,这就是docker run命令,image运行起来后就是docker container。

例:

docker run ubuntu:15.10 /bin/echo "Hello world"

以上命令完整的意思可以解释为:Docker 以 ubuntu15.10 镜像创建一个新容器,然后在容器里执行 bin/echo "Hello world",然后输出结果。

模块化

你可能感兴趣的:(面试常考总结)