文章目录

  • 原生JS
    • (1)什么是`JavaScript`?
    • (2) 简单介绍一下`JS`基本类型
    • (3) 字符串如何转换成布尔类型?
    • (4) 数值类型是如何转换布尔类型
    • (5) 字符串是如何转换成数值的?
    • (6) 都了解过哪些运算符?
    • (7) `||`和`??`运算符区别是什么?
    • (8) 给变量赋值的方式有哪些?
    • (9) 变量的命名有什么规范?
    • (10) 你了解过预解析吗?
    • (11) let、var、const区别?
    • (12) 谈谈你对命名空间和作用域的理解?
    • (13) 简单谈一下作用域链?
    • (14) 都有哪些作用域?
    • (15) 你是怎么理解if-else的?
    • (16) continue和break的区别?
    • (17) 如何跳出forEach()循环?
    • (18) 数组中常见的函数
    • (19)观察代码,筛选符合条件的数据
    • (20) 观察代码,按照需求转换合理的数据
    • (21) 数组中数据剔重
    • (22) Array.of()和new Array()
    • (23) 数组如何深拷贝?
    • (24) NaN == NaN的结果?
    • (25)null和undefined的区别?
    • (26) 请说明常见的字符串的操作函数
    • (27) 请说明Math中常用的操作函数
    • (28) 请说明Date中常用的操作函数
    • (29)请简述bind、call、apply三个函数的区别
    • (30)如何获取数组中的最大值
    • (31)如何判断一个变量的类型?
    • (32)简述函数形式参数和实际参数区别
    • (33) 常见函数声明方式
    • (34) 简述一下函数的预解析
    • (35) 观察下面的代码说明输出结果以及为什么
    • (36) 观察下面的代码,说明输出结果以及为什么
    • (37) 简述什么是匿名函数
    • (38) 说说你对函数递归的理解
    • (39) 什么是自执行函数
    • (40) 什么是闭包?项目中如何使用?
    • (41) 什么是回调函数?
    • (42) 说说对全局污染的理解
    • (43)简单说说你是怎么理解BOM
    • (44) 你是怎么解决浏览器兼容性问题
    • (45) 获取页面标签节点有几种方式
    • (46)你是怎么理解DOM
    • (47) 如何操作标签节点
    • (48) 如何操作标签属性
    • (49) 如何操作标签内容
    • (50) 如何操作标签样式
    • (51) 什么是面向对象
    • (52)你是怎么理解原生JS面向对象
    • (53)原生JS中怎么实现继承关系
    • (54)原生JS中怎么实现继承关系
    • (55) 什么是Ajax
    • (56) 异步请求底层实现有哪些
    • (57) 原生JS Ajax实现步骤
    • (58) 什么是跨域?如何解决跨域?
  • JS高级
    • (1) 什么是Promise
    • (2) Promise有几种状态
    • (3) Promise有哪些执行方法
    • (4) 如何同时执行多个异步任务
    • (5) aysnc和await
    • (6) 回流reflow、重绘repaint
    • (7) new一个对象的过程
    • (8) 常驻内存、内存泄漏、内存溢出
    • (9) 防抖、节流
    • (10) 函数柯里化
    • (11) 常见排序算法
    • (12) JS实现冒泡排序
    • (13) 什么是JS事件循环
    • (14) 宏任务、微任务,简述执行顺序
    • (15) 简述JS垃圾回收机制
    • (16) eval的作用
    • (17) 本地存储有哪些方式,说说它们的区别
    • (18) 栈内存和堆内存
    • (19) 栈和队列
    • (20) 数组和链表
    • (22) 写一个单例模式
  • ES6
    • (1) 什么是ES6
    • (2) Symbol类型
    • (3) ...延展运算符
    • (4) 剩余参数运算符
    • (5) Set
    • (6) Map
    • (7) this指向
    • (8) 数据解构
    • (9) 箭头函数和普通函数区别
    • (10) call()可以修改箭头函数this指向吗
    • (11) Array.of()和Array.from()
    • (12) 模板字符串
    • (13) for.in和for.of的区别
    • (14) 如何获取对象的属性、属性值
    • (15) 如何合并两个对象
    • (16) 面向对象编程
    • (17) module、export、import的作用
    • (18) 了解过那些前端开发规范
  • Git 基础
    • (1)什么是git
    • (2) git和svn
    • (3) 初始化git配置
    • (4) 添加文件到暂存区
    • (5) 提交文件到本地仓库
    • (6) 查看仓库状态
    • (7) 查看本地提交历史
    • (8) 常见代码托管平台
    • (9) 如何绑定远程仓库
    • (10) 如何查看远程仓库
    • (11) 如何拉取远程更新
    • (12) 如何推送代码到远程
    • (13) 如何忽略指定的文件或者文件夹
    • (14) git log 和 git reflog的区别
    • (15) 怎么查看不同版本指定的差异
    • (16) git head的作用
    • (17) git reset的作用
    • (18) 简述git常见的操作命令及其含义>git init 初始化本地仓库
  • Git高级
    • (1) 什么是分支
    • (2) 了解过什么分支模型
    • (3) 如何创建、切换、合并和删除分支
    • (4) 合并分支会删除对应的分支吗
    • (5) git fetch和git pull的区别
    • (6) git push之前需要做什么操作
    • (7) 什么是git stash
    • (8) 什么是版本冲突,如何解决
    • (9) git删除文件,不删除本地文件
    • (10) 如何将svn迁移到git
  • Node.js
    • (1) 了解过NodeJS吗
    • (2) NodeJS都能做什么
    • (3) NodeJS提供了哪些全局对象
    • (4)同步和异步的区别
    • (5) 使用NodeJS的优缺点
    • (6) 什么是npm
    • (7) 常见的npm命令及其含义
    • (8) 什么是 EventEmitter?
    • (9) express项目的基本结构
    • (10) express如何管理接口
  • vue2.x
    • (1) 什么是Vue
    • (2) Vue的两个核心
    • (3) 简述常见的指令及其含义
    • (4) v-if和v-show的区别
    • (5) v-for中key的作用
    • (6) Vue实例 el 选项的作用
    • (7) Vue实例 data选项的作用
    • (8) Vue实例 methods选项的作用
    • (9) Vue实例 watch选项的作用
    • (10) Vue实例 computed选项的作用
    • (11) Vue实例 filters选项的作用
    • (12) Vue实例 components选项的作用
    • (13) Vue实例 name选项的作用
    • (14) Vue实例 常见生命周期
    • (15) created()、mounted()区别
    • (16) 组件页面加载的时候触发哪些生命周期钩子
    • (17) 监听器和普通函数的区别
    • (18) 计算属性和普通函数的区别
    • (19) 如何监听对象的属性
    • (20) 如何让监听器开始时立即调用
    • (21) 计算属性名称可以和data中的数据重名吗
    • (22) 简述父子组件传值的方式
    • (23) 什么是插槽,插槽的作用
    • (24) 什么是边界传值/操作
    • (25) 什么是代理注入传值
    • (26) 如何理解Vue单向数据流
    • (27) Vue组件的data为什么必须是函数
    • (28) 怎么实现组件的缓存
    • (29) 组件缓存对应的生命周期有哪些
    • (30) Vue如何给标签/组件绑定事件
    • (31) 常见的事件修饰符及其作用
    • (32) 常见的按键修饰符及其作用
    • (33) 常见的系统修饰符及其作用
    • (34) 什么是混入mixin,有什么作用
    • (35) 如何设置通用样式,如何设置组件样式
    • (36) 什么是路由
    • (37) 项目中vue-router版本对应关系
    • (38) Vue项目中怎么配置路由
    • (39) 编程式导航常用的方法
    • (40) vue-router如何实现页面跳转
    • (41) 如何实现导航高亮
    • (42)路由重定向和别名的区别
    • (43) 路由路径是如何匹配的,出现冲突如何解决
    • (44) 什么是命名路由,有什么作用
    • (45) 什么是命名视图,有什么作用
    • (46) 路由如何传递查询字符串参数
    • (47) 路由如何传递动态路径参数
    • (48)` $route`、`$router`的区别
    • (49) 如何实现路由嵌套
    • (50) 什么是路由元数据
    • (51) 如何监听页面滚动行为
    • (52) 什么是导航守卫
    • (53) 常见的导航守卫
    • (54) 导航守卫、拦截器的区别
    • (55) Vuex中的5个核心属性
    • (56) Vuex解决了哪些问题
    • (57) 简述Vuex中数据传递流程
    • (58) mutations和actions区别
    • (59) 辅助函数mapState/mapMutations/mapActions/mapGetters
    • (60) Vuex模块化开发的构建
    • (61) Vuex模块中namespaced的作用
    • (62) 什么是axios
    • (63) jQuery、axios的区别
    • (64) axios的请求配置
    • (65) axios的响应数据
    • (66) axios拦截器
    • (67) 如何取消请求
    • (68) 用过的视图组件库有哪些
    • (69) 项目中如何添加element-ui
  • vue高级
    • (1) 什么是MVVM
    • (2) MVVM和MVC的区别
    • (3) 什么是SPA,优缺点是什么
    • (4) 什么是虚拟DOM
    • (5) 虚拟DOM如何提高优化效率
    • (6) v-model的底层实现原理
    • (7) 数据双向绑定底层实现原理
      • 数据劫持加发布者订阅模式
    • (8) nextTick()底层实现原理
    • (9) Object.defineProperty()和Proxy()区别
    • (10) 简述自定义组件的封装过程
    • (11) 为什么需要避免v-if和v-for一起使用
    • (12) vue-loader的作用
    • (13) 如何触发Vue的强制更新
    • (14) 数组哪些操作可以触发页面更新,哪些不可以,有什么解决方案
    • (15) 父子组件嵌套时各自生命周期执行顺序
    • (16) 父组件如何操作子组件DOM数据
    • (17) 什么是组件递归调用
    • (18) Vue页面渲染如何保留模板中的注释
    • (19) Vue2.0兼容IE那个版本
    • (20) Vue为什么首页加载缓慢
    • (21) 如何做首屏加载优化
    • (22) Vuex中strict属性的作用
    • (23) 为什么mutations中不能做异步操作
    • (24) 什么是SSR,优缺点是什么
    • (25) Vue2、Vue3区别
    • (26) 项目中如何管理导入依赖,说说自动导入的优缺点
    • (27) 数组列表渲染,如果只更新单个数据,数组是否会重新渲染
    • (28) 简单阐述一些Vue页面加载过程
    • (29) 说明一下你理解的diff算法
    • (30) 你都用过哪些代码检查工具
    • <font color = Orange>## ***···以上来自博主老师的总结,我只是知识的搬运工***

原生JS

(1)什么是JavaScript

JavaScript是一个弱类型的、支持面向过程和面向对象编程的,主要工作在浏览器一侧给网页提供动态功能的、解释型的编程语言;
在发展过程中JavaScript也可以基于Node环境开发服务端应用,是很多前后端框架的底层实现技术!

(2) 简单介绍一下JS基本类型

原生JS中基本类型包括
字符串String
数值类型Number、
布尔类型Boolean
空值类型Null、
未声明类型Undefined
ES6中提供了新的唯一值类型Symbol

拓展

面试官可能会追问:Object算不算基本类型? | 还有其他的吗?

原生JS中面向对象编程的理念是一切皆对象,有些资料里面也会把Object当成基本类型对待

(3) 字符串如何转换成布尔类型?

字符串可以和布尔类型进行转换,空字符串转换结果是false非空字符串转换结果是true
在项目中一般对字符串的有效值判断直接放在if条件中进行隐式判断

const response = getListJson()// 获取接口字符串数据
if(response.data) { // data存储的字符串格式的json数据
…有效数据
} else {
… 无效数据,提示错误
}

(4) 数值类型是如何转换布尔类型

数值可以转换成布尔类型,0的转换结果是false,非0转换结果true
项目中大部分场景中主要做非0判断,可以直接将数值放在if条件中进行隐式判断,或者可以和逻辑运算符结合起来进行默认值赋值处理
const price = goodsPrice || 1
一部分场景中0也属于有效数据,可以借助??运算符进行赋值和判断处理
const price = goodsPrice ?? 1

(5) 字符串是如何转换成数值的?

其他问法:parseInt('111', 2)转换结果是什么?

数值可以是字符串形式的,所以字符串类型的数字可以转换成数值
parseInt()可以将字符串数字转换成整数
parseFloat()可以将字符串数字转换成浮点数
如果字符串包含字母和数字,数字开头的话转换结果截取数字部分;如果字母开头转换结果NaN
parseInt()转换的时候可以指定进制单位,如parseInt('111', 2)当成2进制转换结果是7

(6) 都了解过哪些运算符?

原生JS常用的运算符赋值运算符算术运算符比较运算符逻辑运算符三元/三目运算符
一些算法结构中为了提高性能会用到位运算符

(7) ||??运算符区别是什么?

常规声明变量的时候如果没有赋值就会默认存储undefined
变量默认值赋值一般出现在函数或者数据处理流程中,使用||和??进行默认值赋值
||赋值时会将空字符串和0这样的数据当成无效数据,赋值指定默认数据
??赋值时只会判断null和undefined当成无效数据,赋值指定默认数据

(8) 给变量赋值的方式有哪些?

其他问法:你项目中是怎么给变量做初始化处理的?10%概率

变量的声明和赋值方式有不同的形式

  • 标准方式可以直接声明变量并且赋值数据,let a = 12; let a = 10, b = 12;
  • 也可以先声明变量(let a;),需要的时候赋值数据(a=11),赋值数据之前变量中存储的是undefined
  • 如果需要多个相同数据的变量,可以进行连续赋值,let a = b = c = 1

(9) 变量的命名有什么规范?

其他问法:你们项目组变量是什么命名的? 5%概率

变量语法规则是字母数字下划线$符号组成,数字不能开头
我们项目中要求变量尽量做到使用英文单词做到见名知意,使用小驼峰命名法进行命名处理

(10) 你了解过预解析吗?

其他问法:你知道什么是变量提升吗? 70%概率

原生JS中使用var声明的变量,存在预解析处理操作
声明的变量在当前作用域中会将变量的声明部分提前到最前面,赋值部分保留在原位置,也被称为变量声明提升
主要作用就是为了保障当前作用域中使用这个变量不会报错,提高代码的容错性

(11) let、var、const区别?

其他问法:原生JS中var声明变量和ES6中let、const声明变量有什么区别?80%概率

var声明变量是原生JS中的声明方式,主要区分声明全局变量局部变量,存在变量预解析
letconst都是ES6中提供的用于声明变量和常量的方式,是对var的补充
let声明的变量存在块级作用域、不能重复声明,没有变量预解析
const主要用于声明常量

(12) 谈谈你对命名空间和作用域的理解?

其他问法:命名空间和作用域的区别?80%概率

命名空间是包含了当前作用域范围中的所有变量函数类型等数据的空间!
(所有数据)
作用域是某个变量、某个函数或者某个数据可以被访问的范围!
(单个数据)

(13) 简单谈一下作用域链?

其他问法:一个变量是怎么被访问到的?50%概率

每个变量可以被访问的范围是它的作用域,原生JS中不同的作用域之间形成作用域链
变量的访问顺序是按照作用域链的过程被访问,首先读取当前作用域中的变量,如果没有该变量的声明就会继续访问上一级作用域,如果所有作用域中都没有访问到该变量就会报错提示变量没有定义!

(14) 都有哪些作用域?

其他问法:什么是作用域链 ?50%概率

每个变量可以被访问的范围是它的作用域,原生JS中不同的作用域之间形成作用域链
从大到小依次是环境作用域、系统作用域、全局作用域、局部作用域、嵌套/[闭包]作用域

(15) 你是怎么理解if-else的?

其他问法:你项目中如果出现了大量条件分支,你是怎么使用if-else的? 10%概率

项目中如果出现的分支并不是很多,可以直接使用if-else的多分支结构进行判断处理即可,我自己的项目中一般要求6个分支以内使用多分支结构
如果分支数量比较庞大,为了提高代码的可读性,同时降低代码的重复率可以借助数组或者对象和if双分支结构进行语法优化


const role = “会员” // 游客、管理员
if(role === “会员”) {…}
else if(role === “管理员”){…}
else if(role === “游客”) {…}
else {…}

const level = {“青铜1”: […], “青铜2”: […], …, “王者22星”: […]}
const info = level[“王者11星”]
if(info){

} else {

}

(16) continue和break的区别?

continuebreak都可以出现在循环结构中
continue终止本次循环直接开始下一次循环
break直接跳出循环
break还可以出现在switch-case中用于结束一个分支

(17) 如何跳出forEach()循环?

其他问法:forEach()循环结构可以使用break吗? 10%概率

break只能跳出循环结构 forEach()是一个函数无法使用break完成跳出循环的操作 forEach()`函数参数是一个闭包函数,也不能使用return跳出循环


forEach()满足跳出循环的条件时抛出异常,外部代码中使用try-catch捕获异常的方式跳出循环
let jobs = [“需求工程师”, “开发工程师”,…]

// 1、无法跳出
jobs.forEach(item => {

break; 无法跳出循环,报错~break无法使用在switch和循环结构之外的其他地方
return; 无法跳出循环,只会结束本次循环闭包函数直接开始下一次循环,不会报错
})

// 2、跳出
try{
jobs.forEach(item => {

if(满足条件) {
throw new Error(“自定义错误”)
}
})
} catch(e) {
捕获错误,代码继续向下执行
}

(18) 数组中常见的函数

其他问法:请说出至少5个数组的操作函数,65%概率

push() / pop()
unshift() / shift()
forEach() / map() / filter()
indexOf()/lastIndexOf()
find()/findLast()
findIndex()/findLastIndex()
include()
startsWith()/endsWith()
match()/search()
reverse() / sort()

数组中常见的函数

(19)观察代码,筛选符合条件的数据

const accounts = [“admin”, “manager”, “tom123”, “jerry”, “shuke_123”]
// 需求:保留账号长度在6~18位的账号
// 考察:filter()
accounts = accounts.filter(item => { return item.length >= 6 && item.length <= 18 })

(20) 观察代码,按照需求转换合理的数据

const accounts = [“admin”, “manager”, “tom123”, “jerry”, “shuke_123”]
// 需求:将上述账号统一加上部门前缀-dept
// 考察:map()
accounts = accounts.map(item => { return dept${item} })

(21) 数组中数据剔重

其他问法:请将数组中重复的数据剔重 ;65%概率(笔试题)

const accounts = [“admin”, “manager”, “admin”, “jerry”, “jerry”]
// 需求:将上述代码中的重复数据剔除
// 考察:代码逻辑思考能力、新技术点掌握能力
let newAccounts = new Set(accounts) // ES6 Set类型
accounts = Array.from(newAccounts) // ES6 Array.from()类型转换

xxxxxxxxxx // 使用普通for循环,可以|(除非要求使用底层代码实现)否则不推荐
function uniqueArr(arr) {
let newArr = []
for(var i = 0; i < arr.length ; i++) {
if(!newArr.includes(item)) {
newArr.push(item)
    }
}
return newArr
}
accounts = uniqueArr(accounts)

(22) Array.of()和new Array()

其他问法:原生JS创建数组的对象语法有什么问题?如何处理? 20%概率

原生JS中可以使用字面量创建数组,可以使用new对象语法创建数组
new Array()创建数组时如果数组中只包含一个整数数据,会创建一个包含多个空数据的数组
ES6中提供了Array.of()封装了对象语法创建数组的方式,优化了原生JS数组对象创建的问题

let arr1 = [2] // 结果:[2]
let arr2 = new Array(2) // 结果:[undefined, undefined]
let arr3 = Array.of(2) // 结果:[2]

(23) 数组如何深拷贝?

其他问法:深浅拷贝的区别?70%概率

数据的基本复用方式主要有三种,引用赋值、浅拷贝和深拷贝
浅拷贝用于复制目标数据,但是如果目标数据是数组或者对象,存储的数据或者属性中包含内存地址就会直接复制内存地址,所以复制前后如果操作内部包含地址的数据,被复制和复制的数据都会受到影响
深拷贝可以完整的复制目标数据,包括内部数据如果包含内存地址就会找到内存地址存储的数据进行复制;所以复制前后操作任何数据,被复制和复制的数据不会互相影响

(24) NaN == NaN的结果?

其他问法:请描述NaN是什么类型?70%概率

false
NaN语义是 Not A Number,表示不是一个数字,属于一种特殊的数值类型!每个NaN都是唯一的!

(25)null和undefined的区别?

其他问法:null == undefined的结果是什么?为什么?70%概率

null和undefined在语法上都可以用于描述无效数据,底层表示数据的二进制开头数值一致,所以null==undefined返回结果true,项目中经常用于变量中存储有效数据的判断

undefined一般声明普通变量并且不赋值时的默认数据,使用Number()转换结果是NaN,转换布尔类型是false
null一般是用于获取对象数据时没有数据会得到null,使用Number()转换结果是0,转换布尔类型是false

(26) 请说明常见的字符串的操作函数

其他问法:请写出至少5个字符串的操作函数及其含义 60%概率

大小写转换函数 toLowerCase()、toUpperCase()
索引查询函数 indexOf()、lastIndexOf()
字符串截取 substr(start, length)、substring(start, end)、slice(start, end)
字符串拆分 split(ch)
字符串搜索替换 match()、search()、replace()、replaceAll()
字符串判断 startsWith()、endsWith()、includes()
剔除空格 trim() 、trimLeft()/trimStart()、trimRight()/trimEnd()
字符补全 padStart()、padEnd()
其他函数 charAt()、charCodeAt()、codePointAt()…

(27) 请说明Math中常用的操作函数

其他问法:Math.ceil()、Math.floor()、Math.round()、Math.trunc()之间的区别

Math.ceil() 数据向上取整
Math.floor() 数据向下取整
Math.round() 四舍五入
Math.trunc() 整数截断
Math.random() 随机数
Math.max()/min() 获取最大/最小值
Math.pow() 指数函数
Math.log() 对数函数
Math.sin()/cos()… 三角函数

(28) 请说明Date中常用的操作函数

其他问法:如何获取两个时间点之间的差值?如何计算距离目标某一天的时间?

new Date() 获取当前系统时间
new Date(time) 指定时间创建一个日期对象
getFullYear() 获取年份
getMonth() 获取0~11月份
getDate() 获取一个月中的第几天(
getDay() 获取一周中的第几天(

getHours() 获取小时
getMinutes() 获取分钟
getSeconds() 获取秒钟
getMilliseconds() 获取毫秒
getTime() 获取1970年0点到现在毫秒数
setFullYear() 设置年份

(29)请简述bind、call、apply三个函数的区别

其他问法:call、apply有什么差异?项目中有没有使用过? 概率80%

bind、call、apply都是用于辅助执行目标函数的辅助函数,用于在执行目标函数过程中修改函数内this指向
bind()给目标函数绑定一个新的this指向并且返回函数声明,不会立即执行函数;如React中绑定无参事件函数
call()函数给目标函数绑定一个新的this指向,接受数据序列作为函数参数,并且立即执行函数
apply()函数给目标函数绑定一个新的this指向,接受数组作为函数参数,并且立即执行函数

function show(…args){
console.log(this)
console.log(args)
}

let obj = {}

// 使用bind,不会立即执行函数
show.bind(obj, “vue”)

// 使用bind并立即执行函数
show.bind(obj, “vue”)()

// 使用call,立即执行函数
show.call(obj, “vue”, “vue3”)

// 使用apply,立即执行函数
show.apply(obj, [‘html’, “css”])

(30)如何获取数组中的最大值

给定一个数组
const arr = [1,2,4,4,4,6,65,56,3,234,23,2,45,45,5,656,76,78]
需求:获取数组中的最大值、最小值

1. 使用普通for循环
function getMax(arr){
// 声明一个变量,存储最大数据
let max = 0
for(var i = 0; i < arr.length; i++) {
if(max < arr[i]) {
max = arr[i]
}
}
return max
}

2. 借助辅助函数
let max = Math.max.apply(null, arr)
let max = Math.max.call(null, …arr)

3 直接查询最大值
let max = Math.max(…arr)

(31)如何判断一个变量的类型?

其他问法:请描述typeof(dat)typeof dat的区别?

项目查询变量的数据类型,一般使用typeof类型判断函数进行获取和判断
typeof的语法格式,包含函数形式和表达式形式两种格式

  • typeof() 函数形式在获取类型时使用较多
  • typeof dat表达式格式在条件判断时使用较多

(32)简述函数形式参数和实际参数区别

其他问法:形参、实参

形式参数简称形参,一般在声明函数的括号中包含,用来表示当前函数执行时需要的数据!

  • 声明位置:函数声明的括号中
  • 数据格式:描述了变量,没有具体数据
  • 作用:描述函数执行需要哪些数据

实际参数简称实参,一般调用执行函数式传递给函数的具体数据

  • 声明位置:调用执行函数时,函数括号中的具体数据
  • 数据格式:包含数据类型和值的具体数据
  • 作用:传递给函数代码运算的数据

(33) 常见函数声明方式

我自己项目中常用的函数声明方式主要有两种
使用function关键字函标准语法声明

  • function show() {…}
    使用表达式变量赋值语法声明函数
  • let show = function() {…}

注意:关于Function关键字

有些情况下面试官可能会追问,你是否了解过Function对象,用它操作过函数没有?

Function对象是原生JS中所有类型基础类型,原生JS中所有的函数或者类型都是直接或者间接从Function对象衍生出来的
所以可以使用Function对象声明函数,操作方式过于底层,开发中没有必要使用!

// 使用Function声明函数
let show = new Function( 'console.log("hello function!")' ) // 调用执行函数 show()

(34) 简述一下函数的预解析

函数的主要的声明方式有两种,有不同的解析效果
使用function标准语法声明的函数,包含函数预解析功能,在函数所在的作用域中可以任意调用执行
使用function表达式的方式声明的函数,包含变量预解析功能,函数声明前不能调用执行函数

hello() // 可以调用执行成功,包含函数预解析功能 hello(存储函数)
function hello(){

}

world() // 调用失败,报错; 包含变量预解析功能 world(undefined)
var world = function() {

}

(35) 观察下面的代码说明输出结果以及为什么

let name = “tom”

function changeName(n) {
n = “jerry”
console.log(n) // 1
console.log(name) // 2
}

changeName(name)
console.log(n) // 3
console.log(name) // 4

1位置输出 jerry;
2位置输出 tom
3位置语法上无法访问变量n,n是局部变量所以会报错
4位置无法执行;4位置变量name中存储的数据是tom

(36) 观察下面的代码,说明输出结果以及为什么

let techs = [“html”, “css”]

function changeTechs(t){
t.push(“vue”)
console.log(t) // 1
console.log(techs) // 2
}

changeTechs(techs)
console.log(t) // 3
console.log(techs) // 4

(37) 简述什么是匿名函数

匿名函数就是没有名称的函数

匿名函数在项目中主要有两种操作方式
第一种形式就是将一个函数当成另一个函数的参数进行使用;
jQuery: $(“button”).click(function() {
// 用户单击的时候执行的操作
})
第二种形式就是将一个匿名函数赋值给变量进行调用,函数的一种声明方式
let show = function() {…}

(38) 说说你对函数递归的理解

函数递归,是函数一种调用方式,某个函数的内部满足条件的情况下调用自己

函数递归导致正在执行的函数常驻内存,递归执行的函数一定要有结束条件、递归的次数需要进行限制一般要求不超过20次(需要根据函数内实际操作的数据确定)

面试官:你项目中用过递归吗?或者你用递归的依据是什么?
项目中使用递归的地方比较多的,如之前开发的文件内容管理模块,对文件的遍历方式使用的就是递归方式;
项目中使用递归主要是看业务的执行过程是否重复,某个业务流程执行过程中出现类似循环执行的重复方式,如果业务数据量又不是非常庞大的情况下使用递归能提高代码的可读性

(39) 什么是自执行函数

自执行函数,就是函数在声明完成的同时就会立即执行的函数!

自执行函数的特点是声明之后立即执行、只能在创建的同时执行一次无法再次调用执行

自执行函数通常都是给网页初始化网页特效或者准备初始数据!

;(function(){…}()) 使用较多
;(function(){…})() 使用较多
;~function() {…} ()
;!function() {…} ()

(40) 什么是闭包?项目中如何使用?

函数的闭包,就是在函数的内部代码中,声明了内部函数!内部函数使用了外部函数的变量数据!
function outer() {
// 局部作用域
let name = “tom”

function inner() {// 闭包: 嵌套作用域let msg = "闭包函数"console.log(name, "使用外部函数的数据")
}}

闭包的特点:

  • 闭包函数执行过程中,延长局部作用域中变量的作用域链,扩大局部变量作用域
  • 闭包函数执行时,内部函数如果被外部变量引用,导致外部函数常驻内存,注意内存使用问题

闭包使用场景

  • 结合自执行函数,完成页面效果的初始化,并通过闭包解决变量/函数的全局污染问题
  • 可以用于封装自定义JS功能插件,闭包可以解决插件和其他代码之间的变量污染问题

(41) 什么是回调函数?

回调函数,描述的是一种函数的异步执行情况,执行过程中回头调用声明的函数

面试官:什么是回头调用声明的函数?为什么说是异步执行情况?
通常函数的同步执行,按照调用步骤进行执行,先执行第一个函数,然后执行第二个函数,再次执行第三个函数,每个函数执行完成前后续的函数都处于等待状态
异步执行先执行第一个函数,可以让第二个函数作为回调函数等待执行,第三个函数执行完成后回调第二个函数完成整体步骤,一般回调函数都出现在异步执行流程中,作为函数的参数进行传递执行

(42) 说说对全局污染的理解

全局污染,描述了在一个模块中,出现了重名的变量或者函数,导致变量或者函数的数据被覆盖或者修改的情况,数据被影响的形式称为全局污染;

面试官:为什么会出现重名变量?开发人员难道不注意吗?
前端开发过程中,JS代码根据功能形式会分布在大量文件中,原生JS使用var声明变量是允许重复声明的,所以在某个文件中声明一个变量无法完整的检查其他的所有相关文件是否声明过这个变量,一旦变量名称重复后续的某个功能可能受到影响,这样的问题排查起来比较麻烦

面试官:你是怎么解决全局污染问题的?
对于一些通用功能,尽量使用let关键字声明变量,保障当前模块中不会重复声明该变量
在同时关联多个js文件时,开发的功能尽量使用闭包的方式进行封装,将变量通过局部作用域进行隔离解决全局污染问题

(43)简单说说你是怎么理解BOM

其他问法:都用过哪些BOM对象

BOM是浏览器对象模型,主要用于描述代码中通过BOM对象和浏览器进行交互
BOM中主要包含window窗口对象、history访问历史、location访问信息、navigator浏览器版本信息、screen计算机屏幕信息、document网页文档信息
项目中我用的比较多的window、history、location用的比较多一些


面试官:简单说说history你都用过那些功能?
原生JS封装功能的时候,使用history实现过历史记录访问


面试官:简单说说location你都用过那些功能?
原生JS封装功能的时候,使用location做过href页面跳转、reload()页面刷新这些操作


面试官:简单说说window你都用过那些功能?
window是一个环境对象,表示了浏览器窗口,如默认弹窗alert()、声明的全局变量或者普通函数都是默认挂载到window对象上的,浏览器相关的各种事件都可以直接操作,也使用window实现过滚动条相关动态效果

(44) 你是怎么解决浏览器兼容性问题

原生JS里面有很多对象的操作都存在兼容性问题
一般处理的时候直接使用了公共的Hack进行CSS兼容性问题处理,使用公共封装的JS Hack解决通用JS兼容性问题
我们公司自己处理的自研项目中自己封装了JS navigator模块进行浏览器版本判断,低版本直接提示浏览器版本过低请升级的信息,对于低版本浏览器基本不做兼容性处理!

(45) 获取页面标签节点有几种方式

原生JS中获取页面标签,可以使用内建函数直接操作
document.getElementById() 通过id获取单个标签节点
document.getElementsByName() 通过name属性获取多个节点
document.getElementsByClassName() 通过class属性获取多个节点
document.getElementsByTagName() 通过标签名称获取多个节点
document.querySelector()根据css选择器获取单个节点
document.querySelectorAll() 根据css选择器获取多个节点
我自己在项目中封装使用的时候,用querySelectorAll()比较多一些


面试官:还有其他获取节点的方式吗?
前面说的这些都是通过document直接获取节点,也可以结合DOM提供的函数获取对应的节点
ele.children 获取子节点
ele.parent 获取父节点
ele.previousElement / ele.perviousSiblingElement 获取上一个兄弟节点
ele.nextElement / ele.nextSiblinElement 获取下一个兄弟节点
document.forms获取页面中的表单节点 等等

(46)你是怎么理解DOM

DOM描述的是文档模型对象
原生JS可以将网页数据读取加载到内存中形成DOM树结构,通过树结构完成节点的遍历查询
DOM模型中封装很多函数,可以用于标签节点、标签属性、标签样式、标签内容增删改查

(47) 如何操作标签节点

创建节点:document.createElement("div")
删除节点:div.remove()
替换节点:parent.replace(old, new)
追加节点:div.appendChild(child)、div.insertBefore(child, old)

原生JS操作标签节点,可以对节点进行增删改查处理,createElement创建节点、appendChild或者insertBefore可以添加节点、remove可以删除节点、replace可以替换节点

(48) 如何操作标签属性

原生JS提供了标签函数可以直接操作属性
div.setAttribute(key, value)设置属性或者修改属性
div.getAttribute(key) 获取属性数据
div.removeAttribute(key) 删除属性
还可以直接通过标签节点对象对属性进行访问和设置,如div.id = "title"

(49) 如何操作标签内容

原生JS提供了内容的处理属性
div.innerText可以获取或者设置文本内容
div.innerHTML可以获取或者设置富文本内容
div.textContent也可以获取或者设置文本内容,等价于innerText

(50) 如何操作标签样式

原生JS中操作标签的样式,可以区分设置样式和获取样式
设置样式 div.style.样式名称 = 样式值,可以单独设置一条样式
div.style.cssText= "width:100px; height:200px;" 可以设置多条样式
获取样式 getComputedStyle(div).样式名称 获取指定样式
兼容性语法 div.currentStyle.样式名称 获取指定样式

(51) 什么是面向对象

面向对象是一种编程思想,开发一个功能的时候分析拆分解决时参与的对象和对象的属性和方法,通过多个对象之间的方法调用完成功能处理,封装好的对象就可以在其他流程中实现复用,提高代码的复用性!

(52)你是怎么理解原生JS面向对象

原生JS主要编程方式还是函数式编程,面向对象中通过构造函数的方式模拟面向对象编程
我自己在项目中使用面向对象的方式封装功能时,用的比较多的是ES6 class语法

// 声明构造函数
function Person(name, age) {this.name = namethis.age = age
}
// 创建对象
let tom = new Person("汤姆", 18)

(53)原生JS中怎么实现继承关系

原生JS面向对象是通过函数的方式模拟出来的,继承关系主要通过原型对象进行实现,继承方式有这么几种
第一种是经典继承,通过构造函数的原型对象继承,也称为原型继承
第二种是冒充继承,通过call()辅助函数完成继承
第三种是组合继承,结合了原型继承和冒充继承完成
第四种是寄生组合继承,优化了组合继承方式完成的继承(最优)

(54)原生JS中怎么实现继承关系

原生JS中所有的对象都是直接或者间接继承自Object对象,底层就是通过原型链进行关联的;
原型和原型链就是原生JS中通过继承关系统一管理数据的底层实现方式

面试官:什么是原型?
原生JS中所有的构造函数或者类型都会有自己的原型对象,使用prototype表示,描述当前类型属于什么对象类型!

面试官:什么是原型链?
原生JS中所有的对象都是被构造出来的,可以通过 对象的原型链__proto__ 查询当前对象是由那个原型对象构造的!
通过原型链可以让当前对象使用原型链上的所有对象的公共属性和方法,实现的数据的统一管理和复用

(55) 什么是Ajax

Ajax是一种异步请求基础,实现页面的局部数据刷新功能
默认底层实现主要是通过JavaScript XMLHttpRequest对象,结合XML数据或者JSON数据完成异步数据交互的技术

(56) 异步请求底层实现有哪些

异步请求的底层实现方式主要有两种

  • 第一种是通过JS XHR对象实现的,也是一种主流的实现方式
  • 第二种是使用浏览器API fetch模块实现的,目前各大主流浏览器较新的版本中都开始支持,也是以后的趋势

(57) 原生JS Ajax实现步骤

xxxxxxxxxx
1. 创建异步对象
2. let _http = new XMLHttpRequest()3. 2. 连接服务器
4. _http.open("GET", "http://www.example.com/api")5. 3. 发送请求
6. _http.send()7. 4. 处理响应数据
8. _http.onreadystatechange = function() {
9.    if(_http.status === 200 && _http.readyState === 4){        // 处理服务器返回的数据
10. }
11. }

(58) 什么是跨域?如何解决跨域?

跨域,描述了请求不同主机、不同端口的数据的方式!

同源策略,描述了网络数据请求过程中,检查当前主机接收到的请求是否来自同一个域名,如果是同一个域名的请求可以放行处理业务并返回数据;如果请求来自不同的域名/端口,提示跨域错误Cross-Origin-Access-Control

浏览器控制台报错:Cross-Origin-Access-Control,表示当前接口不接收跨域访问!

跨域问题:解决不同域名/不同端口的情况下,实现数据访问

  • JSONP跨域【熟悉-客户端】
  • CORS跨域【熟悉-服务端】
  • websocket跨域
  • postmessage跨域
  • window + iframe跨域
  • document + iframe跨域

JS高级

(1) 什么是Promise

Promise是ES6提供的主要用于解决 异步回调问题 / 回调地狱问题 的对象

什么是异步回调问题?

传统的编码方式中,当出现大量的异步函数的操作情况,使用回调函数完成异步函数的结果处理,大量的回调函数导致代码的可读性严重下降,称为回调地狱问题!

(2) Promise有几种状态

Promise执行过程中,包含三种状态:执行中、处理成功、处理失败

  • 执行中Pending:正在执行异步函数操作过程
  • 处理成功fulfilld:Promise中异步函数处理成功,调用resolve()返回结果
  • 处理失败rejected:Promise中异步函数执行失败,调用reject()返回结果

(3) Promise有哪些执行方法

Promise对象提供了一些执行函数,可以方便的处理异步操作结果

  • .then()函数:处理Promise执行成功的函数、或者处理上一个.then()返回的结果
  • .catch()错误函数:处理Promise执行失败的函数
  • .finally()最终函数:无论成功失败都会最后执行的函数
  • .all()并发函数:同时执行多个异步任务的函数,保障多个任务同时执行成功
  • .ract()竞争函数:同时执行多个异步任务并且返回优先执行成功结果的函数

(4) 如何同时执行多个异步任务

Promise中提供了一个函数all()可以同时执行多个异步任务,.then()按照执行异步任务的顺序依次返回结果

(5) aysnc和await

ES6提供了一种异步函数的编程语法,可以结合Promise更加友好的完成异步任务的处理
异步函数使用async声明,内部代码中通过await调用异步任务,将异步编程方式转换成同步编程代码,提高代码可读性;await只能出现在async声明的函数内部!

(6) 回流reflow、重绘repaint

回流和重绘,都是浏览器渲染网页视图的操作方式,和网页优化方案相关性较大
回流描述了网页文档第一次渲染加载、或者整个网页视图中的元素需要重新布局定位时就会发生
重绘描述了网页视图中的某个元素发生了变化,浏览器对当前元素进行重新渲染的过程

  • 回流一定会触发重绘
  • 重绘不一定会触发回流

注意:浏览器加载网页的过程

  • 1.解析HTML网页文档构建DOM树
  • 2.解析css样式转换成样式数据结构体
  • 3.解析DOM树将页面中需要展示的节点进行构建,形成渲染树Render
  • 4.页面布局(回流),根据渲染数Render树从根节点递归调用,计算每一个元素的大小、位置等,给每个节点所应该出现在屏幕上的精确坐标。
  • 5.绘制渲染树Render树(重绘):遍历渲染树,每个节点将使用UI后端层来绘制。

(7) new一个对象的过程

new 创建一个对象,主要经历三个过程:

  • 内存中创建空白对象,赋值给this关键字,继承函数原型
  • 通过this对象初始化对象的属性、方法等数据
  • 返回this指向新的实例,赋值给引用变量

(8) 常驻内存、内存泄漏、内存溢出

常驻内存:表示一个程序中在使用的数据,分配到内存空间中不会被回收删除,这样的数据称为常驻内存

内存泄漏:一个常驻内存的数据,在程序代码中没有被任务地方使用,但是不能被回收的数据

内存溢出:一个正在运行的程序占用的内存已经超过申请的内存大小,就会导致内存溢出程序崩溃

(9) 防抖、节流

防抖和节流都是在业务流程处理过程中的优化方案,避免用户高频率的操作导致程序处理压力过大

防抖(debounce)限制用户操作频率,超时时间范围内只生效一次操作,如果超时间内用户再次操作重新计算超时事件;如点击秒杀按钮

节流(throttle)显示用户操作频率,限制时间降低用户操作频率,如果用户触发了高频率事件如连续点击,就可以通过节流函数限制用户1秒内最多操作20次

防抖函数

// 防抖函数
function debounce(fn, delay){// 声明一个计时器var timer = null;return function() {// 如果已经开始计时执行,重新计算时间timer && clearTimeout(timer)// 执行目标函数timer = setTimeout(() => {return fn()}, delay)}
}// 用户点击秒杀按钮
function secondBuy() {// 秒杀购买商品函数
}btn.onclick = debounce(secondBuy, 500) // 防抖函数设置0.5S

节流函数

// 节流函数
function throttle(fn, delay) {var flag = truereturn function() {if(!flag) returnflag = falsesetTimeout(() => {flag = truereturn fn()}, delay)}
}// 抢红包点击频率 节流处理
function getRegbeg() {// 抢红包...
}btn.onclick = throttle(getRegbeg, 50) // 抢红包函数 节流处理~每秒最多点击20次

(10) 函数柯里化

函数柯里化,通过闭包函数的方式,将一个函数内部的处理逻辑进行拆分优化的方案
同时优化了函数的调用过程

代码操作如下

// 函数:第一次执行-增加一个数据,第二次执行-乘法运算一个数据
function getResult(m, n) {var init = 10return (10 + m)  * n
}
getResult(10, 2) // 两个数据对应的业务逻辑混合到一起,无法确认每个流程// 函数柯里化: 通过闭包,将不同的处理过程拆分,提高可读性、方便维护
function getResult(m) {var init = 10m += 10return function(n) {return n *= m }
}
getResult(10)(2)

(11) 常见排序算法

项目中进行大量数据处理时,经常用到各种数据算法,可以借助第三方模块实现
实现方式有外部(外空间)排序和内部(内空间)排序两种,常规的排序方式直接可以使用内部排序

内部排序常见的有冒泡排序选择排序插入排序快速排序桶排序堆排序希尔排序计数排序基数排序归并排序 10大排序算法

关于内空间排序:

一般面试或者笔试的时候,常见的需要手写的排序方式:冒泡排序、选择排序、插入排序、快速排序

(12) JS实现冒泡排序

冒泡排序相关博客

(13) 什么是JS事件循环

JS是一种事件驱动的编程语言,底层将网页中发生的各种事件保存在一个任务队列中,然后通过主线程循环遍历这个任务队列,当某个任务触发的时候就会循环到这个任务并触发任务函数执行功能代码

(14) 宏任务、微任务,简述执行顺序

JS底层代码执行过程,会将执行的任务分配成宏任务(MarcoTask)、微任务(MicroTask)
类似setTimeout/setInterval触发的都是宏任务
Promise.resolve()触发的都是微任务
JS在执行过程中优先执行宏任务,当一个宏任务执行完成后继续查询是否存在为微任务(存在的话轮询执行),完成一次事件循环之后下一次循环继续优先执行宏任务,依次类推…

(15) 简述JS垃圾回收机制

JS作为一个成熟的编程语言,最重要的特点之一就是包含垃圾回收功能,将代码执行过程中的无效数据或者对象从内存中进行回收销毁的功能

JS垃圾回收主要通过引用计数结合标记清除的方式进行处理,判断代码中某个数据的引用变量的数量,数量为0表示这个数据已经没有在使用就可以直接清除;如果出现循环引用的时候就会启用标记清除的方式进行数据销毁
由于垃圾回收机制执行过程比较消耗性能,所以进行优化实行分代回收机制,将数据按照执行过程中回收情况进行分类处理

(16) eval的作用

eval()是JS提供的一个转换函数,可以将字符串转换成可解释运行的表达式进行执行

console.log("12 + 13")  // 12 + 13
console.log(eval("12 + 13")) // 25

(17) 本地存储有哪些方式,说说它们的区别

本地存储webStorage就是一种浏览器的缓存数据的方式,主要包含会话缓存和本地缓存
会话缓存sessionStorage,是本地存储的一种,缓存的数据的有效时间是一个会话周期,当前会话结束时缓存在会话缓存中的数据就会销毁
本地缓存localStorage,是本地存储的一种,缓存的数据长期有效,除非软件进行清除或者用户主动清除数据

(18) 栈内存和堆内存

JS代码运行时是在计算机的内存空间中运行的,为了优化程序运行使用的内存空间体积,将内存拆分为了栈内存和堆内存
栈内存中编译器可以很快速的进行数据的分配和回收,只能从栈的开始位置添加和删除数据,称为入栈和出栈,通常都会分配一些引用变量
堆内存中的数据一旦分配就会比较稳定,通常都会分配一些创建的对象数据

let tom = new Person()
// tom:引用变量,分配在栈内存;保存的是对象在堆内存中的内存地址
// new Person():对象,分配在堆内存

(19) 栈和队列

栈、队列都属于数据结构的一部分

栈Stock中存储数据只能从栈的开头位置添加或者删除,其他位置的数据不能操作
添加数据称为入栈、删除数据称为出栈

队列Queuen中存储的数据,可以从队尾添加数据、队首删除数据,其他位置不能操作数据
添加数据称为入队,删除数据称为出队

(20) 数组和链表

常规的设计模式有23种,区分为创建型模型、行为型模式、结构型模式

  • 创建型模式了解过单例模式、建造模式、原型模式、简单工厂模式、工厂方法模式
  • 行为型模式了解过装饰器模式、适配器模式等
  • 结构性模式了解过策略模式、观察者模式等

什么是设计模式?
设计模式就是按照固定的操作流程处理一些特定问题的解决方案!

(22) 写一个单例模式

单例模式的几种写法

ES6

(1) 什么是ES6

ES是一种前端编程语言语法标准,这种语法标准主要参考了JavaScript语言语法
ES6是Ecma 2015年推出的语法标准,包含了大量新语法格式和语法优化,一般描述的过程中指代的就是JavaScript

(2) Symbol类型

ES6推出的一种基本类型,用于表示唯一值数据的。
一般项目中可以用Symbol封装私有属性或者进行一些唯一值的操作!

(3) …延展运算符

…延展运算符(spread)
将数组数据或者对象数据,进行展开运算的运算符号

(4) 剩余参数运算符

…剩余参数运算符(rest)
函数的形式参数,可以结合位置参数,通过…运算符接受0~多个形式参数
主要用于替代原生JS中的arguments特殊参数的

(5) Set

Set中可以存放没有顺序的、不能重复的、可以是多种类型的数据
Set是ES6推出的一种集合类型,主要用于在功能上对原生JS中的数组进行补充

(6) Map

Map中可以存储key:value键值对的数据,其中的key值可以是任意类型的数据
Map是ES6中退出的一种映射类型,也称为字典类型;主要用于在功能上对原生JS中的JSON对象进行功能补充

(7) this指向

普通函数中,this指向window
JSON对象中,this指向当前对象
事件函数中,this指向事件发生的DOM节点
闭包函数中,this指向当前函数
箭头函数中,this指向外部函数的this,或者指向window

(8) 数据解构

数据解构方式,ES6提供的一种可以便捷的从数组或者对象中根据数据的表现结构获取数据的语法
包含了完整结构、部分结构、结构失败、解构重命名等各种解构获取数据的语法

(9) 箭头函数和普通函数区别

箭头函数语法上更加简洁
箭头函数没有自己的this,依赖上下文环境
箭头函数不能作为构造函数
箭头函数没有原型对象
箭头函数没有arguments
箭头函数不能作为Generator生成器参数

(10) call()可以修改箭头函数this指向吗

不能,箭头函数内的this不能被直接修改!

(11) Array.of()和Array.from()

ES6针对数组进行了优化
Array.of()优化了创建数组的方式
Array.from()优化了转换数组的方式

(12) 模板字符串

ES6提供的模板字符串使用反引号包含一段字符串,可以在字符串中使用${}完成表达式的插入,语法上对原生字符串进行了优化

(13) for.in和for.of的区别

for.in循环,遍历数据的时候获取数据的索引,循环内部根据索引获取对应的数据
for.of循环,遍历包含Symbol(iterator)迭代属性的数据,循环遍历时可以直接获取遍历数据进行处理

(14) 如何获取对象的属性、属性值

ES6中对Object进行了优化扩展
提供了Object.keys(obj)可以直接获取对象的所有属性组成的数组
提供了Object.values(obj)可以直接获取对象的所有属性值组成的数组

(15) 如何合并两个对象

ES6中提供了Object.assign(tgt, src1, src2...)可以用于复制或者合并多个对象数据

(16) 面向对象编程

ES6对原生JS的面向对象进行了扩展,提供了class语法用于在语法和语义上进行升级优化
class Person {
}类型的内部可以包含构造函数constructor()用于初始化属性数据
class Person{constructor(name, age) {this.name = namethis.age = age}
}类型的内部可以包含普通函数作为类型的方法
class Person {eat() {console.log("有点饿了...")}
}可以通过extends关键字直接实现继承关系,优化了原生js中复杂的继承语法
class Mouse extends Pet {....}

(17) module、export、import的作用

module主要用于表示一个模块,语法上通常一个.js文件就是一个模块
exportimport主要用于模块化开发过程中进行特定数据的导出和导入操作!

(18) 了解过那些前端开发规范

前端开发中包含了很多规则规范,通用规范了解过AMD规范、CMD规范、CommonJS规范、ES6规范等等
前端项目中使用较多的都是ES6规范,之前做过一段时间Node开发主要使用的CommonJS规范

面试官:CommonJS规范和ES6规范使用上的差异?

最明显的差异就是模块的导出导入上,从语法上表现不同
导出模块CommonJS使用module.exports = {}、ES6使用default export {}
导入模块CommonJS使用let md5 = require("md5"),ES6使用import md5 from "md5"

CommonJS模块时运行时加载、ES6模块编译时加载

面试官:AMD规范和CMD规范的区别?
1、CMD和AMD都是为了JavaScript模块化开发的规范
2、CMD是sea.js推广过程中对模块定义的规范化产出;AMD是require.js推广过程中对模块定义的规范化产出
3、CMD 推崇依赖就近,AMD 推崇依赖前置


存在的一些问题:
1、AMD是异步模块定义的意思,他是一个在浏览器端模块开发规范,由于不是JS原生支持,使用AMD规范进行页面开发时,需要对应的函数库
2、require.js解决的问题,多个JS文件可以有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器,JS加载的时候浏览器停止页面渲染,加载文件越多,页面失去响应时间越长
3、CMD通用模块定义,是国内发展的,有浏览器实现Sea.js,Sea.js要解决的问题和require.js一样,只不过模块定义的方式和模块加载时机有所不同

Git 基础

(1)什么是git

git是一个代码或者软件开源的、分布式的、版本管理工具!

结合代码托管平台可以有效的管理各种类型的代码编写或者软件开发!

(2) git和svn

git是一个开源的分布式的版本管理工具、svn是一个基于服务端的代码软件集中存储版本管理工具

git是目前各种项目的主流版本管理工具,可以离线操作、不依赖服务器、被作为各种类型的项目版本管理的主要工具

svn是一个集中式存储的版本管理工具,必须联网结合服务器的情况完成版本管理,传统的本地开发的项目中,经常被当成局域网版本管理工具进行使用

(3) 初始化git配置

其他问法:能简单说一下部署git的时候需要提前做什么吗

从git官方网站下载软件并在本地电脑上安装软件工具
安装完成后需要使用git config执行初始化配置:
git config --global user.name ''
git config --global user.email ''
进入需要管理的本地项目,执行git init初始化本地项目

(4) 添加文件到暂存区

其他问法:什么是暂存区?如何添加文件到暂存区?

暂存区,是git用于临时管理项目文件的索引文件,最终将暂存区的文件进行提交之后才能存储到本地仓库并形成提交记录

将一个新文件添加到暂存区的操作命令:
git add 添加一个文件到暂存区
git add * 添加当前目录所有文件到暂存区

(5) 提交文件到本地仓库

git commit <file> -m "注释" ,提交一个文件到本地仓库
git commit -a -m "注释", 提交所有暂存区文件到本地仓库
git commit -am "注释"

(6) 查看仓库状态

git可以通过命令检查当前本地仓库中被管理的文件状态,如新增的文件、修改的文件等等
git status

(7) 查看本地提交历史

git可以通过命令查看本地仓库中的提交记录
git log 或者git reflog

(8) 常见代码托管平台

其他问法:什么是代码托管平台?你都用过哪些托管平台?

代码托管平台是第三方团队研发的在线存储项目的平台网站,主要用于提供7*24小时的代码共享和同步服务,我知道的托管平台如githubgitlabgitee等等,在项目中我使用比较多的是gitee,平时经常上github去看别人的一些源代码!

(9) 如何绑定远程仓库

其他问法:本地仓库怎么样和代码托管平台简历关联?

git中执行remote相关的命令绑定远程仓库
git remote add origin 仓库地址

(10) 如何查看远程仓库

git中执行remote相关的命令查看远程仓库
git remote --verbose

(11) 如何拉取远程更新

git中执行命令git pull可以用于拉取远程仓库的代码到本地仓库
常用的git pull origin 分支名称,如git pull origin master

(12) 如何推送代码到远程

git中推送本地仓库的代码到远程仓库
可以使用git push origin 分支名称,如git push origin master

(13) 如何忽略指定的文件或者文件夹

git中管理当前项目时,有些文件或者文件夹不需要接受管理,如node_modules/
管理过程中可以创建一个.gitignore配置文件,用于配置需要忽略的文件或者文件夹

(14) git log 和 git reflog的区别

git log用于查看当前分支的所有提交历史记录
git reflog用于查看所有分支的提交历史记录

(15) 怎么查看不同版本指定的差异

git中可以通过git diff命令查看指定的差异信息,如暂存区和本地仓库的文件内容差异、不同提交版本之间的文件内容差异等等

(16) git head的作用

git HEAD中的HEAD是一个特殊引用,指向最近的一次提交的记录,经常用于操作最近提交记录的操作

(17) git reset的作用

git reset主要用于版本回退,项目中如果没有致命的错误或者误操作时,一般慎重使用回滚命令

(18) 简述git常见的操作命令及其含义>git init 初始化本地仓库

git config 进行本地配置
git add 添加文件到暂存区
git commit 提交文件到本地仓库
git log 查看提交历史记录
git reflog 查看提交历史记录
git pull 拉取更新
git push 推送代码
git reset 回退代码


git branch 分支操作命令
git fetch 抓取分支命令

Git高级

(1) 什么是分支

分支branch,是git进行差异化开发的一种管理方式,并行开发多个互相独立的功能时为了互相之间不受影响,可以借助分支模型进行开发和管理

(2) 了解过什么分支模型

开发过程中为了合理的处理发布的版本、开发的功能模块、需要优化的问题内容,以及新功能的拓展研发,可以建立多分支模型进行开发管理,我接触过的分支模型都有

  • 主分支开发模型,小型项目中直接使用master主分支进行开发管理
  • 双分支模型,master主分支用于发布特定版本,dev开发分支用于管理开发的代码
  • 三分支模型,master主分支用于发布版本,dev开发分支用于管理开发代码,hotfix修复分支用于修复bug或者优化功能
  • 多分支模型,可以包含发布版本主分支、管理开发代码的开发分支、完善问题的修复分支,新功能的开发拓展分支以及多版本管理分支等

(3) 如何创建、切换、合并和删除分支

  • git branch 分支名称 用于从当前分支创建一个特定名称的分支,新分支中包含当前分支目前所有的代码内容
  • git checkout 分支名称用于切换分支,可以切换到不同的分支进行代码开发,当前分支开发的代码在其他分支中不可见
  • git merge 分支名称 将指定的分支上的代码合并到当前分支
  • git branch -d 分支名称用于删除指定名称的分支

(4) 合并分支会删除对应的分支吗

git merge命令合并指定的分支,只是将指定分支的代码合并到当前分支;被合并的分支不会被删除依然存在

(5) git fetch和git pull的区别

  • git pull拉取远程仓库指定分支的代码到本地,合并到本地仓库当前分支,相当于抓取远程分支代码git fetch和合并命令git merge结合体
  • git fetch拉取远程仓库指定多分支代码到本地,不会合并到本地仓库当前分支

(6) git push之前需要做什么操作

git push将本地仓库的代码推送到远程仓库

推送之前首先确保本地仓库的所有文件已经完成添加、暂存区没有文件,其次需要拉取远程仓库的代码到本地、保障本地仓库的代码是最新的代码;然后就可以执行推送代码的操作!

(7) 什么是git stash

git stash主要用于缓存暂存区的内容,当开发人员无法确认本次修改是否完成的情况下,需要和远程仓库进行代码同步,可以使用git stash命令将当前暂存区的内容进行缓存保障其他命令可以正常执行!

(8) 什么是版本冲突,如何解决

版本冲突一般指代多个开发人员修改了相同的文件,在其他人已经提交的情况下,自己没有更新最新代码直接进行提交;导致自己本地代码版本和服务器代码版本冲突无法提交的情况,称为版本冲突

出现版本冲突之后,先拉取远程更新,将最新的代码同步到本地,手工进行冲突文件的内容比较,保留正确的代码之后再次进行提交!

(9) git删除文件,不删除本地文件

git rm命令可以删除指定文件,但是同样会删除本地文件夹中的文件导致文件丢失!

git reset命令可以删除指定文件(从本地仓库/暂存区移除文件),但是不会删除本地文件夹中的文件

(10) 如何将svn迁移到git

拉取svn项目,手工复制和添加配置,完成项目的迁移

Node.js

(1) 了解过NodeJS吗

其他问法:什么是NodeJS

NodeJS是一个基于谷歌V8引擎、提供了事件驱动、非阻塞I/O模型的JavaScript运行环境!

(2) NodeJS都能做什么

NodeJS提供了服务端开发环境,可以借助它完成

  • 网络通信项目
  • 网站开发
  • 数据接口开发
  • 分布式项目
  • 常规项目开发…

(3) NodeJS提供了哪些全局对象

NodeJS不同于浏览器环境
浏览器环境提供了windowdocument这样的基于环境的全局对象可以直接使用
NodeJS中提供了globalprocesconsole等全局对象可以直接使用

(4)同步和异步的区别

同步和异步都描述了多任务的执行顺序和执行过程
同步执行描述了多个任务按照顺序依次执行的过程,上一个任务没有执行完成的情况下 下一个任务只能处于等待状态(阻塞状态)
异步执行描述了多个任务同时执行,没有前后执行顺序;多个任务不论那个任务执行完成可以通过回调函数完成后续功能的操作

(5) 使用NodeJS的优缺点

NodeJS发布于2009年,所以优缺点比较明显
比较新的编程语言,借鉴了很多编程语言的优势,优点:

  • 事件驱动、异步编程方式
  • 事件循环机制,处理高并发任务
  • 非阻塞IO处理方式,更加适合I/O密集型应用
  • 使用JavaScript作为首选语言,前后端技术成本低
  • 社区发展活跃

缺点也比较明显:

  • 单线程,单进程,只支持单核CPU,不能充分的利用多核CPU服务器
  • 非阻塞IO处理方式,不利于计算/CPU密集型应用
  • 版本更新迭代快,导致很多模块不是非常稳定
  • 很多功能企业项目应用较少

(6) 什么是npm

npmnode package management,基于node的包管理工具
可以借助npm完成项目中第三方依赖的管理

(7) 常见的npm命令及其含义

npm init 初始化node项目
npm install 安装依赖,简化npm i
npm uninstall 卸载依赖,简化npm un
npm view 查看依赖版本

(8) 什么是 EventEmitter?

EventEmitterNodeJS中事件循环的核心对象,可以通过该对象完成事件注册、事件队列、事件轮询、事件移除等各种操作,是NodeJS高并发的底层实现!

(9) express项目的基本结构

项目文件夹/app.js   项目入口模块public/  项目静态文件存放目录routes/  项目路由模块存放目录utils/   项目工具模块存放目录-- views/   项目视图模块存放目录(ejs),接口项目中不需要视图模块package.json  项目依赖配置文件

面试题:你都用过哪些nodejs框架?
或者:你用的nodejs都是基于什么框架的?

我做前端开发用的Vue脚手架像vue/cli、vite这些都是基于nodejs的框架实现,react脚手架项目以及uni-app的项目都是基于nodejs框架实现
也有了解过后端开发的一些框架,如express、egg等等

(10) express如何管理接口

express中构建应用对象 const app = express()
express添加路由模块 const router = express.Router()
express中添加中间件 app.use(...)

express通过路由模块进行模块化接口开发

vue2.x

(1) 什么是Vue

Vue是一个 开发前端应用的 轻量级 开发框架
Vue本身采用的是自底向上的逐层构建方式,可以很方便的进行项目整合,提供的单文件组件系统可以很大的提升前端应用的开发效率

(2) Vue的两个核心

Vue框架的两大核心包含:数据驱动、组件化

注意:Vue两大核心的由来

Vue开发过程中,将结构和数据进行了分离,通过组件的方式完成复用提升开发效率

(3) 简述常见的指令及其含义

v-text:文本渲染指令
v-html:富文本渲染指令
v-once:一次性加载指令
v-show:条件渲染指令
v-if:条件渲染指令
v-for:列表渲染指令
v-bind:动态绑定属性指令
v-on:事件绑定指令
v-model:表单数据绑定指令
v-slot:插槽处理指令
也可以通过Vue.directive()自定义指令实现基础DOM操作的复用

注意:关于Vue中的指令

Vue框架本身不推荐资源消耗比较庞大的DOM操作,但是项目中不可避免的会出现少量DOM操作,如获取文件域中的文件拖动鼠标效果滚动条处理效果等等

Vue中建议将少量的DOM操作封装在指令中进行复用!

(4) v-if和v-show的区别

v-if和v-show都是条件渲染指令
v-if是通过DOM挂载的方式完成指定元素的显示或者隐藏
v-show是通过display:none|block样式完成指定元素的显示或者隐藏

注意:部分面试场景中,面试官会问到v-if和v-show谁的优先级高?

  • v-if 优先级高于 v-show

(5) v-for中key的作用

v-for是列表渲染指令,可以将一个数组数据在页面视图中进行循环遍历
key属性就是用于遍历的数据状态保持,可以通过key属性提高列表渲染效率

(6) Vue实例 el 选项的作用

el选项是element的缩写,主要用于将Vue实例挂载到页面的DOM节点上!

注意:Vue中el选项、$mount()谁的优先级高?

  • el选项$mount()都是用于将实例挂载到标签上的
  • el选项 优先级高于 $mount()
// 普通项目
const app = new Vue({el: "#app"
})
// 脚手架项目
new Vue({routes,store
}).$mount("#app")

(7) Vue实例 data选项的作用

Vue实例或者组件中的data选项
给当前实例或者组件声明需要使用的数据的!

// 实例中声明数据
new Vue({data: {title: "实例需要的数据"}
})
// 组件中声明数据
export default{data() {return {msg: "组件中需要使用的数据"}}
}

(8) Vue实例 methods选项的作用

Vue实例或者组件中,可以通过methods选项声明普通的函数
我在项目中通过**函数封装各种操作功能实现代码复用**,这样的函数都包含在methods选项中

(9) Vue实例 watch选项的作用

Vue中的watch主要用于声明监听器/侦听器
可以监听一个变量中数据的变化,当变量中数据发生更新时自动调用对应的侦听器函数完成操作

(10) Vue实例 computed选项的作用

Vue中的computed选项,主要用于声明计算属性
将一段运算表达式包含在一个函数中提高数据运算效率,在视图中可以当成属性进行访问
访问计算属性时如果参与运算的变量数据没有发生更新,直接使用上一次运算的结果

(11) Vue实例 filters选项的作用

Vue实例或者组件中的filters选项用于声明局部过滤器
过滤器本质上是一个函数,在不修改源数据的情况下转换数据表现形式提供给视图进行渲染

(12) Vue实例 components选项的作用

Vue实例或者组件中的components选项主要用于注册局部组件,当成当前组件的子组件进行使用

(13) Vue实例 name选项的作用

Vue实例或者组件的名称,默认名称就是当前组件文件名称
一般用于组件注册或者组件递归调用时使用

(14) Vue实例 常见生命周期

生命周期 描述了一个实例或者组件从创建、加载、运行到销毁的整个过程
生命周期钩子/函数,描述了监听各个阶段的生命周期的执行函数,在某个生命周期阶段对应的生命周期钩子会自动执行

beforeCreate()/created() 实例/组件创建过程
beforeMount() / mounted() 实例/组件DOM挂载过程
beforeUpdate() / updated() 实例/组件数据更新过程
beforeDestroy()/destroyed()实例/组件销毁过程

(15) created()、mounted()区别

created()mounted()都是生命周期函数
created()描述了当前组件实例创建完成,可以访问data数据,但是不能访问DOM节点
mounted()描述了当前组件DOM挂载完成,可以访问data数据,也可以访问DOM节点

(16) 组件页面加载的时候触发哪些生命周期钩子

项目中一个组件页面挂载过程中,会触发实例创建和DOM挂载生命周期钩子,分别是
beforeCreate()
created()
beforeMount()
mounted()

(17) 监听器和普通函数的区别

监听器本质上也是一个可执行函数
监听器可以根据监听的变量或者对象中的数据是否发生更新,自动执行对应的监听函数
普通函数只能被调用执行,或者绑定到事件上通过触发事件执行函数

注意:关于监听器名称

面试的时候面试官可能会提到watch函数监听器侦听器

(18) 计算属性和普通函数的区别

计算属性本质上也是一个可执行函数
计算属性被设计用来按照需求计算结果数据,可以被当成属性进行访问,访问时获取计算属性的结果,如果计算属性中参与运算的数据没有发生数据更新,就会使用上一次缓存的运算结果提高计算效率;如果参与运算的任意数据发生了更新,才会执行计算属性对应的函数代码完成运算并缓存运算结果

普通函数只能被调用执行,不会缓存运算结果,每次调用都会重复执行函数内代码

注意:计算属性的含义

计算属性,从字面意义上就可以看出,这是一个专门给业务运算设计的运算函数,可以通过缓存结果的方式提高运算效率;页面视图中访问计算属性函数时可以当成属性进行访问!

(19) 如何监听对象的属性

watch选项中声明的监听器,如果要监听对象内部所有的属性的变化,可以通过deep:true启用深度监听

(20) 如何让监听器开始时立即调用

watch选项中可以在监听器中启用immediate:true,让当前监听器函数在加载的时候就会立即执行一次!后续就会跟随监听变量中数据的更新自动执行!

(21) 计算属性名称可以和data中的数据重名吗

计算属性函数,在当前页面中可以被当成属性进行访问
所以不能和data选项中的数据重名

(22) 简述父子组件传值的方式

组件嵌套关系中,父子组件之间有多种数据传递方式

  • 父组件可以通过自定义属性传递数据给子组件
  • 子组件可以通过自定义事件传递数据给父组件
  • 可以通过消息中心完成组件之间的数据传递
    还有其他的组件之间数据传递方式,适用于不同的应用场景,比如边界传值、插槽传值、代理注入传递等等

① 如何给一个组件添加自定义属性

组件中可以通过props选项,给组件添加自定义属性接收数据

② 如何触发自定义事件

组件中可以通过Vue实例函数$emit()触发自定义事件,可以使用自定义事件传递数据

③ 如何声明一个消息中心

可以通过创建一个空Vue实例,使用事件触发$emit和监听函数$on完成消息中心的声明,我在项目中使用实例完成消息中心的操作比较多
也可以使用第三方模块event构建通过事件传递数据的消息中心

(23) 什么是插槽,插槽的作用

插槽在自定义组件中经常使用,通常是在子组件中预留的一个视图位置,可以在父组件中通过插槽给子组件注入特定的视图完成子组件定制视图的输出!

面试官:你在项目中都用过什么插槽?

我开发的项目里面,通用视图类组件使用具名插槽比较多;带有数据操作性质的功能组件中使用作用域插槽比较多

(24) 什么是边界传值/操作

边界传值是Vue中提供的一种父子嵌套关系下,直接访问对方数据的方式
父组件可以通过子组件的ref属性,直接访问子组件实例以及实例上的数据和方法
子组件可以通过$parent属性,直接访问父组件实例以及实例上的数据和方法

面试官:Vue中不是不推荐子组件中直接操作父组件数据吗?
其他问法:Vue中什么情况下子组件可以直接操作父组件数据?

Vue中一般不推荐在子组件中直接操作父组件数据,很容易导致数据源发生混乱,但是如果一个父组件只有唯一一个子组件的情况下,数据源修改方式基本是确定的,可以使用子组件直接访问父组件的数据!

(25) 什么是代理注入传值

我的项目中有时候有一些组件共享数据需要传递给大量组件进行使用,这些组件之间都是直接或者间接嵌套自根组件的
可以在根组件或者上级父组件中通过provider声明需要共享的数据
需要使用数据的子组件中通过injrect注入和使用需要的数据

(26) 如何理解Vue单向数据流

Vue中组件嵌套关系中,父组件传递数据给子组件建议使用单向数据流
父组件可以传递数据给子组件,或者操作子组件实例
规范上不允许子组件操作父组件实例数据,父组件中包含多个子组件时很容器造成数据源混乱
这样的流程称为单向数据流

(27) Vue组件的data为什么必须是函数

Vue实例中的data是一个对象
Vue组件中的data必须是一个函数,组件是项目中可以复用的视图,如果data数据是一个对象造成多个复用组件的地方它们的数据互相影响;如果data是一个函数多个复用组件的地方每个地方都是独立的数据,互相之间没有影响!

(28) 怎么实现组件的缓存

组件的缓存,通过<keep-alive><keep-alive>包含需要缓存的组件实现缓存
组件被缓存之后,显示和隐藏的过程中就不会被多次销毁和创建,提高了组件的复用效率

(29) 组件缓存对应的生命周期有哪些

其他问法:组件被缓存之后,如何实现组件中数据的更新?

Vue给缓存的组件提供了两个额外的生命周期函数

  • activated:缓存组件显示时调用的生命周期,可以完成数据更新
  • deactivated:缓存组件隐藏时调用的生命周期

(30) Vue如何给标签/组件绑定事件

Vue中可以通过v-on指令绑定事件
绑定事件时可以优化为简洁语法,使用 @事件名称 直接绑定对应的事件

(31) 常见的事件修饰符及其作用

Vue将事件操作中的各种功能,封装成了修饰符进行使用
阻止事件冒泡,可以使用.stop修饰符
阻止默认行为,可以使用.prevent修饰符
一次性绑定,可以使用.once修饰符
转换捕获触发方式,可以使用.capture修饰符
事件只能由当前标签触发,可以使用.self修饰符

① 原生JS:阻止事件冒泡

if(e.stopPropagation) {
e.stopPropagation()
} else {
e.cancelBubble = true
}
----
e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true

② 原生JS:阻止默认行为

if(e.preventDefault) {
e.preventDefault()
} else {
e.returnValue = false
}
----
e.preventDefault ? e.preventDefault() : e.returnValue = false

(32) 常见的按键修饰符及其作用

Vue提供了封装的按键修饰符,拓展事件的触发方式,常见的按键修饰符
@keydown.enter="" 按下回车键触发事件
@keydown.esc="" 按下esc键触发事件
.tab 按下tab按键触发事件
.delete 按下删除键触发事件
.space 按下空格键触发事件
.left/.up/.right/.down 按下对应方向键触发事件
也可以通过keyCode自定义按键修饰符

(33) 常见的系统修饰符及其作用

Vue提供的系统功能键结合事件进行使用,常见的功能键
@click.ctrl="" 按下ctrl键同时单击鼠标触发
@click.alt="" 按下alt键同时单击鼠标触发
@click.shift="" 按下shift键同时单击鼠标触发

(34) 什么是混入mixin,有什么作用

Vue提供的混入模块mixin,可以将多个组件中公共的代码部分拆分出来进行复用!
混入模块可以声明为全局混入,或者在某些组件中注册局部混入使用

(35) 如何设置通用样式,如何设置组件样式

通用样式,一般声明为外部样式,在App.css/App.vue中引入使用,全局生效
组件样式通常在某个组件中的<style>标签上添加scoped属性,该属性会让当前样式只能在当前组件中生效

注意:scoped的工作原理?

组件中的style标签上,添加scoped属性,让样式只能被当前组件使用
底层解析的时候会给当前组件的标签上添加一个唯一属性,当前组件中声明的样式会使用属性选择器进行修饰,所以只能在当前组件上生效

(36) 什么是路由

路由Router,描述了根据用户发起的请求,匹配并渲染展示对应视图的过程
Vue项目中使用vue-router给项目添加路由功能

(37) 项目中vue-router版本对应关系

vue2.x对应的路由vue-router@3
vue3.x对应的路由vue-router@4

(38) Vue项目中怎么配置路由

关键词:安装、创建、初始化配置、挂载

首先需要安装路由模块,执行npm i vue-router@3 -S
项目中创建router/文件夹,创建index.js,配置路由对象
index.js中构建路由对象,并添加初始配置
项目入口模块main.js中将路由挂载到Vue实例上
`

(39) 编程式导航常用的方法

衍生:router.push()和router.replace()区别

router.push() 保留当前记录跳转到下一个路径
router.replace() 不保留当前记录跳转到下一个路径
router.go() 历史页面跳转(任意历史记录)[了解]
router.back() 历史页面跳转(上一个记录)[了解]
router.forward() 历史页面跳转(下一个记录)[了解]

(40) vue-router如何实现页面跳转

vue-router中提供了声明式导航、编程式导航,都可以实现页面跳转
<router-link to="">实现声明式导航页面跳转
router.push()/replace()实现编程式导航页面跳转

(41) 如何实现导航高亮

项目中有两种常见方式都可以配置导航高亮
第一种可以直接给导航添加router-link-active样式设置高亮样式
第二种也可以编辑路由对象,添加linkActiveClass配置指定高亮样式

 const router = new Router({  routes,  mode: "history",  linkActiveClass: "active"})

(42)路由重定向和别名的区别

路由重定向,描述了用户发起了一个url请求,运行时将用户的请求地址修改成为另一个url地址访问对应的视图,可以使用redirect配置
路由别名,描述了用户发起了一个url请求,运行时将 用户的请求指向了另一个地址对应的视图,访问过程中没有修改用户url请求地址,可以使用alias配置

const routes = [{path: "/",redirect: "/login"   //重定向},{path: "/main",alias: "/index",   //别名component: () => import("../views/Index")}
]

(43) 路由路径是如何匹配的,出现冲突如何解决

路由路径的匹配方式,默认可以使用正则匹配
当同一级路由路径中出现包含关系,可能会导致路由访问冲突
出现路由路径冲突时可以添加exact严格匹配方式解决冲突问题
exact ---- 严格的

(44) 什么是命名路由,有什么作用

命名路由,就是给路由匹配规则添加name属性
可以在声明式导航或者编程式导航时,通过命名路由进行跳转

{name: "main",   // 命名路由path: "/main",component: Main
}
<router-link :to="{name: 'main'}"></router-link>
router.push({name: "main"})

(45) 什么是命名视图,有什么作用

命名视图,就是给<router-view>添加name属性
实现一个路由页面中可以匹配多个视图组件

<template><div><!-- 命名视图--><router-view name="header"></router-view><!-- 匿名/默认视图--><router-view></router-view><router-view name="aside"></router-view></div>
</template>
{path: "/main",components: {header: () => import("../components/Header.vue"),aside: () => import("../components/Aside.vue"),default: () => import("../views/Main.vue")}
}

(46) 路由如何传递查询字符串参数

声明式导航或者编程式导航中,通过链接直接传递查询字符串数据
跳转到的目标组件中使用$route.query进行接收

<template><router-link to="/goods?id=12">跳转</router-link>
</template>
<script>...this.$router.push({name: "goods", query:{id: 12}})...
</script>
<template>
...{{ $route.query.id }}
...
</template>
<script>...const id = this.$route.query.id...
</script>

(47) 路由如何传递动态路径参数

声明式导航或者编程式导航中,通过链接直接传递动态路径数据
跳转到的目标组件中使用$route.params进行接收

{path: "/goods/:id",component: Goods
}
<template><router-link to="/goods/12">跳转</router-link>
</template>
<script>...this.$router.push({name: "goods", params:{id: 12}})...
</script>
<template>{{ $router.params.id }}
</template>
<script>...const id = this.$route.params.id...
</script>

(48) $route$router的区别

$route 表示当前路由对象
$router 表示全局路由对象

(49) 如何实现路由嵌套

路由匹配规则中,通过添加children属性完成路由嵌套规则的配置
.vue页面组件中在对应的父级路由组件中添加路由视图显示嵌套路由组件

(50) 什么是路由元数据

路由规则配置时,可以通过meta选项添加一些基础数据传递给路由组件
路由组件中通过$route.meta接受元数据
路由元数据一般用于进行权限处理、认证操作、访问规则等

(51) 如何监听页面滚动行为

通过路由对象的scrollBehavior配置选项进行添加
可以通过返回{x: 0, y: 0}禁用滚动行为,也可以根据需求保留指定页面滚动位置提高用户使用体验

(52) 什么是导航守卫

导航守卫是vue-router提供的用于拦截用户页面请求的中间组件
通过导航守卫可以拦截用户视图页面的请求并进行认证等处理

(53) 常见的导航守卫

导航守卫根据作用范围主要包含三大类守卫
全局守卫:全局前置守卫、全局解析守卫、全局后置钩子
beforeEach() beforeResovle() afterEach()
路由守卫:路由独享守卫
beforeEnter()
组件守卫:组件进入守卫、组件更新守卫、组件卸载守卫
beforeRouteEnter/ beforeRouteUpdate / beforeRouteLeave

(54) 导航守卫、拦截器的区别

导航守卫、拦截器都可以起到拦截请求的目的
导航守卫拦截的是用户发起的页面请求
拦截器拦截的是前端发送给后端的接口请求

(55) Vuex中的5个核心属性

state 数据存储单元
mutations 同步函数单元
actions 异步操作单元
getters数据计算单元
modules 模块化构建单元

(56) Vuex解决了哪些问题

Vuex解决了vue项目中组件数据的管理问题,提高了组件中数据的复用性

(57) 简述Vuex中数据传递流程

Vue组件发起数据更新请求
调用Vuex actions函数,发送接口请求完成服务器请求处理
调用Vuex mutations函数,调用同步函数修改/更新state中的数据
state数据更新后,vue组件中可以同步渲染更新后的数据

(58) mutations和actions区别

mutations函数,主要用于编写同步函数,用于直接更新state数据
actions函数,主要用于编写异步调用函数,如调用后端接口,同时可以调用mutations函数完成数据同步

(59) 辅助函数mapState/mapMutations/mapActions/mapGetters

辅助函数包含了maptStatemapMutationsmapActionsmapGetters,都是用来在vue组件中进行数据转换,将vuex中的数据或者方法转化成vue组件中可以直接访问的数据和方法,优化了数据的使用方式和函数的调用语法!

(60) Vuex模块化开发的构建

开发数据比较复杂的项目时,可以通过Vuex将不同的数据拆分到不同的模块中进行管理,实现数据分离方便后期的开发和维护!
子模块中配置namespaced添加独立的命名空间,其他数据操作方式一致
主模块中通过引入子模块并注册到modules选项中,完成数据的统一管理

(61) Vuex模块中namespaced的作用

Vuex模块化开发时,子模块中通过namespaced选项启用子模块独立的命名空间,方便项目中多个子模块数据的统一管理和维护

(62) 什么是axios

axios是一个底层使用promise封装的异步网络访问模块

(63) jQuery、axios的区别

jQuery是一个通过封装JavaScript实现了大量功能函数的函数库,可以用于进行选择器操作DOM操作动画效果异步请求操作等各种功能
axios是一个通过Promise封装的异步网络访问模块,主要用于发起异步请求

(64) axios的请求配置

axios进行封装的时候,可以对请求进行配置处理,主要包含
url请求地址
method 请求方式
headers 请求头
params get参数处理
data post参数处理
timeout 超时时间等等

(65) axios的响应数据

axios对接口返回的数据进行了二次封装
接口返回的数据主要包含在axios响应对象的data属性中

(66) axios拦截器

axios提供了用于拦截请求响应拦截器,扩展异步请求模块功能
请求拦截器可以拦截前端应用中发送给接口的所有请求,可以在拦截器中进行一些公共配置,如添加特定的请求头
响应拦截器可以拦截接口返回的所有数据,可以在响应拦截器中进行网络访问成功的判断、根据返回的数据进行权限认证、可以解析返回真实数据

(67) 如何取消请求

axios 0.22版本之前,可以通过自身提供的CancelToken进行请求取消
axios 新版本中可以借助浏览器的AbortControll()添加的信号对象进行取消

(68) 用过的视图组件库有哪些

前端应用中使用过的视图组件库
Bootstrap
element-ui
vantui
iview
miniview
ant design
TDesign

(69) 项目中如何添加element-ui

首先安装ui库:npm i element-ui -S
进行完整添加或者按需配置,完整引入编辑element.js进行封装引入
main.js中引入代码让element.js生效即可
vue组件中直接使用element-ui提供的各种视图组件

vue高级

(1) 什么是MVVM

关键词:视图、模型、数据同步、基础理论

MVVM是一种数据结构化操作方式,描述了Model-View-ModelView三个组成部分
通过ModelView完成数据在Model模型中和View视图中的双向更新
是数据双向绑定的理论基础!

(2) MVVM和MVC的区别

关键词:数据结构化操作方式、软件架构基础理论

MVVM是一种数据结构化操作方式,是数据双向绑定的理论基础,通过中间件ModelView实现数据在Model模型和View视图上的双向更新和同步

MVC是一种软件架构模式的基础理论,描述Model模型View视图以及Controller控制器,通过核心的控制器完成请求分发处理不同的数据,最终在合适的视图中进行展示的过程

(3) 什么是SPA,优缺点是什么

关键词:单页面应用、体验、效率、首页、SEO

SPA是单页面应用(Singleton Page Application)的简称,在一个项目中只有一个html网页视图
通过组件化开发的方式,在同一个页面中实现组件页面的切换提高用户的使用体验

优点比较明显:

  • 只有一个页面,切换过程非常迅速,提升了用户的使用体验
  • 组件化开发方式,可以更好的处理前后端分离开发
  • 组件内可以添加业务逻辑,减轻接口服务端的压力

缺点:

  • 首屏加载缓慢(路由懒加载)
  • 不利于SEO(搜索引擎优化技术)(SSR、或者花钱-人民币玩家)

(4) 什么是虚拟DOM

关键词:加载、解析、内存、抽象节点树

虚拟DOM,Vue加载组件视图模板,将视图在解析时在内存中提取成VNode节点抽象树进行存储,在内存中的抽象树节点上进行数据操作,虚拟节点操作相比较真实DOM操作消耗的资源非常少,提高页面处理效率
存储在内存中的抽象数据节点称为虚拟DOM

(5) 虚拟DOM如何提高优化效率

虚拟DOM就是将视图结构,在加载到内存的过程中,解析成抽象节点树
通过抽象树完成数据解构的遍历和数据操作,最终将处理好的内存数据直接渲染到页面中,避免了页面视图上直接进行大量DOM操作,从而优化页面渲染的效率

(6) v-model的底层实现原理

v-modelVue中用于双向绑定表单数据的
底层通过v-on:input读取表单数据、v-bind:value同步表单数据,实现数据双向同步更新!

(7) 数据双向绑定底层实现原理

Vue最大的特点就是视图数据的双向绑定(监听、通知、更新)
双向绑定的底层执行过程,通过数据劫持Object.defineProerty()的方式完成数据的声明和监听
同时通过查询订阅模式收集变量数据,完成数据通知机制;
如果数据一旦发生更新就会触发劫持函数遍历数据确定更新的节点,通过订阅模式完成页面渲染函数的调用更新


数据劫持加发布者订阅模式


(8) nextTick()底层实现原理

父子组件嵌套时,父组件需要操作子组件DOM节点,父组件在子组件更新过程中无法确认子组件是否DOM挂载完成的
可以使用setTimeout()延时函数等待子组件挂载完成操作子组件DOM节点,这种操作方式优点操作简单,缺点无法确定子组件加载时间,很容易造成BUG
可以使用nextTick()异步回调,等待子组件数据更新完成,DOM挂载成功之后,再去调用子组件DOM节点,推荐的使用方式

extTick底层是基于JavaScript宏任务、微任务执行方式进行了封装实现的异步调用


首先判断promise是否存在,存在的情况下将当前更新任务封装成微任务添加到任务队列中等待执行;如果promise无法使用的情况下可以借助mutationObserver封装微任务的实现
其次微任务无法调用执行的情况下会判断选择setTimeout或者setImmediate封装宏任务进行异步的任务排队,等待DOM更新后再访问数据

(9) Object.defineProperty()和Proxy()区别

其他问法:数据劫持和代理方式实现的双向绑定有什么区别

数据劫持方式也就是Object.defineProerty()实现的数据双向绑定,是Vue2.x中数据双向绑定的底层实现原理,一般可以用于监听指定变量中数据的更新,通过set()函数get()函数可以完成数据的更新和读取,也可以监听指定对象中指定方法的执行

代理方式也就是Proxy()实现的数据双向绑定,是Vue3.x中数据双向绑定的底层实现原理,一般将需要监听的变量包装成了一个代理对象,变量中不论存储的是基本数据还是对象数据,都需要通过代理对象进行更新,通过代理对象就可以监听到任意数据或者对象的更新,变量的数据监听上优于数据劫持方式

数据劫持方式是Vue2.x数据双向绑定的底层原理,可以直接监听变量结合查询订阅完成数据双向绑定

代理方式是Vue3.x数据双向绑定的底层原理,将目标变量包装了代理对象,通过代理对象的操作结合查询订阅完成数据双向绑定

(10) 简述自定义组件的封装过程

其他问法:你在项目中怎么封装组件的?

需求分析,分析组件中的通用功能(视图结构、数据模型)
属性和事件,分析组件中需要通过哪些自定义属性完成视图定制、通过哪些事件需要发送数据
– 代码实现,通过封装.vue文件完成自定义功能组件的封装,其他页面中引入成子组件使用
– 扩展:借助Vue.extend()函数,将组件进行底层封装,方便在当前实例的项目中引用

(11) 为什么需要避免v-if和v-for一起使用

v-ifv-for一般情况下不建议一起使用
v-for的优先级高于v-if,导致每次循环都会触发v-if操作,导致循环数据不确定或者造成数据遍历的性能问题!

<div v-for="item in goodsList" v-if="item.price > 200">不推荐
</div>
<div v-for="item in goodsList"><div v-if="item.price > 200">推荐</div>
</div>

(12) vue-loader的作用

vue-loader是一个.vue文件的加载器模块,结合webpack实现SPA项目的构建!
vue/cli脚手架底层实现模块

(13) 如何触发Vue的强制更新

默认情况下Vue2.x中的变量实现了数据双向绑定的,变量中的数据的更新一定会同步视图
某些特殊场景中如果变量更新没有触发视图更新,可以调用vm.forceUpdate()函数强制更新
需要注意的是一旦使用强更新,99.9%以上的概率你的代码设计出现了问题!

(14) 数组哪些操作可以触发页面更新,哪些不可以,有什么解决方案

Vue2.x中数据的数据操作,如push()/pop()/unshift()/shift()等这样的函数操作可以触发页面的数据更新,但是直接通过数组的索引增删改内部数据,页面视图不会发生更新

Vue2.x数据劫持的方式监听目标变量,可以监听到变量中对象上指定的方法,直接操作对象内部数据不会触发set()函数,不会触发查询订阅通知更新,不会调用渲染函数

Vue提供了Vue.set()函数或者实例vm.$set()函数,可以直接操作数组索引或者对象的属性数据,同时达到更新视图的目的!

(15) 父子组件嵌套时各自生命周期执行顺序

父子组件嵌套时,有两种执行情况
第一种执行方式父子组件在页面加载时都加载渲染展示,商品列表页面(父组件)按照列表(子组件)的方式展示所有商品
父beforeCreate() //开始创建实例
父created() //创建完成
父beforeMount() //开始创建模板
子beforeCreate() //开始创建实例
子created() //创建完成
子beforeMount() //开始创建模板
子mounted() //创建完成
父mounted() 创建完成

第二种执行方式父子组件在页面加载时只加载父组件,子组件在后期通过事件进行展示;商品列表页面(父组件),点击新增按钮打开新增商品的弹窗(子组件)
父beforeCreate()
父created()
父beforeMount()
父mounted()

(16) 父组件如何操作子组件DOM数据

方案一:通过ref直接调用子组件的方法

<template><div><Button @click="handleClick">点击调用子组件方法</Button><Child ref="child"/></div>
</template>    <script>
import Child from './child';export default {methods: {handleClick() {this.$refs.child.sing();},},
}
</script>//子组件中<template><div>我是子组件</div>
</template>
<script>
export default {methods: {sing() {console.log('我是子组件的方法');},},
};
</script>

方案二:通过组件的 $emit、$on方法

//父组件中<template><div><Button @click="handleClick">点击调用子组件方法</Button><Child ref="child"/></div>
</template>    <script>
import Child from './child';export default {methods: {handleClick() {this.$refs.child.$emit("childmethod")    //子组件$on中的名字},},
}
</script>//子组件中<template><div>我是子组件</div>
</template>
<script>
export default {mounted() {this.$nextTick(function() {this.$on('childmethods', function() {console.log('我是子组件方法');});});},
};
</script>

父组件中操作子组件DOM节点,分两种情况

第一种情况子组件跟随父组件直接渲染完成,可以直接通过子组件的ref属性,访问子组件实例并操作子组件DOM数据

第二种情况父组件中触发事件加载子组件,需要结合nextTick()函数等待子组件DOM挂载完成后通过子组件的ref属性访问子组件实例并操作子组件DOM数据

(17) 什么是组件递归调用

组件的递归调用,就是在当前组件中,通过组件的name选项的值调用自身的过程
一般n级树形菜单加载、树形列表数据加载等业务中经常使用

(18) Vue页面渲染如何保留模板中的注释

一般设计到项目最终打包发布时,如果需要保留模板中的展示方便发布之后进行一些简单问题的排查,可以在Vue组件中添加渲染comments:true,保留模板注释

(19) Vue2.0兼容IE那个版本

vue2默认兼容ie10以上的版本,部分兼容ie10
我们公司的项目对ie浏览器进行了判断,封装了插件用于解决ie10及以上版本兼容性问题

(20) Vue为什么首页加载缓慢

Vue开发的SPA应用中,默认情况下用户访问第一个页面时,会造成导入的所有模块及路由页面的加载过程,性能上会受到一些影响,出现首屏加载缓慢的问题

(21) 如何做首屏加载优化

路由页面中首页直接导入使用,其他页面进行懒加载导入
页面中的导入的第三方模块使用CDN方式进行处理
项目进行打包压缩

(22) Vuex中strict属性的作用

Vuex中的strict属性主要用于启用代码严格模式
严格模式下要求开发人员使用vuex处理数据时,处理流程严格按照规范的方式进行操作,如果出现了规范以外的数据操作方式,会在控制台提示警告错误信息提供给开发人员进行优化!

(23) 为什么mutations中不能做异步操作

mutations中规范要求实现数据的同步操作函数,主要用于同步更新state中的数据
mutations如果调用异步接口实现异步操作,功能上可以实现,但是会导致数据调试工具如vue-devtools数据跟踪出现问题,导致后期维护问题!

(24) 什么是SSR,优缺点是什么

SSR称为服务端渲染技术,Vue可以借助NuxtJS框架搭建服务端渲染应用,可以直接将Vue编写在服务端应用中实现前后端耦合开发,用户发起请求访问页面时,服务端会将编译好的html页面直接响应给用户进行展示,提高了用户访问页面的效率同时有利于seo(搜索引擎优化)

(25) Vue2、Vue3区别

面试官:其他问法:Vue3都升级了哪些东西?

Vue2中主要使用的是选项式开发语法
Vue3中可以选择使用选项式语法、也可以选择使用组合式语法
Vue3中提供了Telport传送组件,可以完成多层嵌套组件中的功能组件结构提升
Vue3中提供了Fragment,优化组件中视图结构编写方式
Vue3中优化了编译方式提高了视图编译效率
Vue3中选项式生命周期改造了销毁的钩子函数,组合式生命周期中封装了生命周期钩子
Vue3中升级了数据双向绑定模式,从数据劫持bject.defineProperty()改为了Proxy()代理方式
还有其他的升级,目前项目中使用不是特别多

(26) 项目中如何管理导入依赖,说说自动导入的优缺点

项目中经常会用到第三方依赖,常规导入方式有两种:

  • 完整导入
  • 按需导入

模块的管理中有一个插件unplugin-auto-import,添加到项目中之后,可以在任意需要模块的页面中不需要导入直接使用模块,该插件在当前文件加载解析时自动导入需要的模块,开发人员在任何文件中不再需要编写import代码了!
优点是优化了代码的编写方式,提高了开发效率
缺点是文件中编写的模块代码,需要非常熟悉各种依赖模块的情况下才能读懂代码

(27) 数组列表渲染,如果只更新单个数据,数组是否会重新渲染

v-for进行数组列表渲染,如果只更新单个数据,数组不会重新全部渲染
vue底层将页面视图解析成虚拟DOM节点,再执行页面更新时会遍历所有虚拟DOM节点完成数据更新,最终调用视图渲染函数将视图进行回流或者重绘!
所以列表渲染过程中,如果更新单个数据不会导致数组数据全部重新绘制,而是只绘制被更新的数据!提高了页面渲染效率

(28) 简单阐述一些Vue页面加载过程

Vue中的.vue文件加载时会经历这样几个步骤
第一步解析器读取.vue文件,将文件模板解析抽象节点树
第二步读取抽象节点树,完成数据同步替换
第三步调用渲染函数完成页面视图加载展示

(29) 说明一下你理解的diff算法

面试官问:什么是diff算法

diff 算法是一种通过同层的树节点进行比较的高效算法
diff算法在Vue中主要用于抽象节点树中替换DOM节点和数据
是一种非常高效的数据差异化算法
底层通过抽取虚拟DOM树,进行逐层判断,获取差异化数据节点层
通过前序遍历后序遍历以及交叉遍历的方式组合最新的虚拟DOM,提供给页面进行渲染

(30) 你都用过哪些代码检查工具

其他问法:了解过代码质量审查吗?或者 怎么提高代码编写质量?

前端开发过程中经常使用一些插件或者模块检查代码格式以及代码中流程的复杂度,使用比较多的是ESLint工具
可以通过配置文件强制约束代码格式,如缩进必须是2个空格
也可以配置流程复杂度,如单个函数中的圈复杂度不能超过10等等

## ···以上来自博主老师的总结,我只是知识的搬运工

WEB 前端面试题 (实战)(大全)相关推荐

  1. 2021年最新Web前端面试题精选大全及答案

    目录 HTML.CSS相关 Javascript相关 三者的异同 Vue相关 55.Vue路由懒加载(按需加载路由) React相关 react 生命周期函数 ******为什么虚拟 dom 会提高性 ...

  2. 2023最新Web前端面试题精选大全及答案(一)

    1.Opacity和grba的区别 是 opacity 会继承父元素的 opacity 属性,而 rgba 设置元素的后代元素不会继承不透明属性 2.Display:none和visibilty区别 ...

  3. web前端面试题(面试题大全)

    web前端面试题[持续更新中...] React系列 UmiJS系列 Webpack系列 ES6系列 Vue系列 JavaScript系列 CSS系列 HTTP系列 模块化系列 版本控制系列 Type ...

  4. JavaScript中的load事件的作用_史上最全的web前端面试题汇总及答案JavaScript之二(二)...

    作者:樱桃小丸子儿 链接:https://www.jianshu.com/p/abadcc84e2a4 JavaScript JS的基本数据类型 number,string,boolean,objec ...

  5. 应届生web前端面试题_2020最新Web前端经典面试题试题及答案(持续更新)

    Web前端面试题 Web前端面试题:说说你对webpack的看法 解析:webpack是一个模块打包工具,可以使用webpack管理你的模块依赖,并编译输出模块们所需要的静态文件.能很好的管理.打包w ...

  6. java 前端页面传过来的值怎么防止篡改_答对这40道经典web前端面试题,想不拿到offer都难!...

    想成功就业web前端工程师,想要能高薪就业,那么除了好的web前端技能以外,还得有好的面试技巧,如果提前就了解更多企业的面试要求及面试题目,那么可以让我们的面试成功的几率大大的提高. 今天小编就整理了 ...

  7. Web前端面试题集锦

    Web前端面试题集锦 前端开发面试知识点大纲: 注意 转载须保留原文链接(http://www.cnblogs.com/wzhiq896/p/5927180.html )作者:wangwen896 H ...

  8. javascript array添加图片_史上最全的web前端面试题汇总及答案JavaScript之二(二)...

    作者:樱桃小丸子儿 链接:https://www.jianshu.com/p/abadcc84e2a4 JavaScript JS的基本数据类型 number,string,boolean,objec ...

  9. 金三银四,磨砺锋芒;剑指大厂,扬帆起航(2020年最全大厂WEB前端面试题精选)上

    引言 元旦匆匆而过,2020年的春节又接踵而来,大家除了忙的提着裤子加班.年底冲冲冲外,还有着对于明年的迷茫和期待!2019年有多少苦涩心酸,2020年就有更多幸福美好,加油,奥利给!怀着一颗积极向上 ...

  10. WEB前端面试题整理

    WEB前端面试题 文章目录 WEB前端面试题 一.html部分 1.Doctype有什么作用?标准模式与兼容模式有什么区别 2.标准模式与兼容模式(怪异模式)各有什么区别? div1和div2之间的距 ...

最新文章

  1. ARP欺骗防御工具arpon
  2. Blend Tree Type
  3. OS_CORE.C(10)
  4. python中复制、浅层拷贝、深层拷贝的区别
  5. 【CF1189D】Add on a Tree【结论】【构造】
  6. 【转】ubuntu 开机sudo启动应用程序
  7. boot空间不足 linux,linux——boot空间不足
  8. python实现屏幕录制_JavaScript 屏幕录制 API 学习
  9. TX2Ubuntu16.04远程登录
  10. 如何在苹果Mac中使用“启动安全性实用工具”?
  11. 老路MBA商学课|第001课:机会成本|放弃掉的鱼,是选择熊掌的代价?
  12. 【精选】申请免费的服务器
  13. Redis 安装说明
  14. IE9环境下。LODOP打印,首次打印时,图片加载不出来、加载不完全问题
  15. 统信uos 没有通过系统安全验证,无法运行
  16. Android 页面Scheme配置
  17. LRM-00101: unknown parameter name 'location' LRM-00101: unknown parameter name 'valid_for' 粗心引起问题一例
  18. 基于Android的照片分组共享APP设计与实现
  19. iis php mysql wiki_如何创建自己的wiki-Dokuwiki
  20. 我帮粉丝赚了10w+

热门文章

  1. 上海··高房价的城市
  2. 学习ps能做些什么呢
  3. 信息安全等级保护措施之网络安全技术
  4. 互联网晚报 | 8月16日 | iPhone14或仍有刘海;​百度回应前腾讯新闻负责人王诗沐是否加入;微信聊天对话框支持放大编辑...
  5. 计算机与农业机械化,计算机与农业机械化的相关性研究.pdf
  6. TX1储存空间装满不能正常进入系统问题的解决
  7. python实现视频分割
  8. 中软JavaEE软件工程师培训有感
  9. 最新PHP一些面试题
  10. python数据挖掘领域工具包 - wentingtu - 博客园