[toc]
#### 1.js数据类型,区别
1.基本数据类型
+ string
+ number
+ null
+ undefined
+ boolean
+ Symbol
```javascript
唯一数
let sym = Symbol('描述')
```
+ BigInt 大整数
```js
js最大支持53位数,为了表示最大精度以外的数,新增了BigInt类型
let num = 123n
typeof num // bigint
可以进行加减乘除
如果除法有小数,会被舍弃小数部分
```
2.引用数据类型
+ Object
+ Function
+ Array
#### 2.Object.assign(target, source)
将后面的对象key以及内容覆盖到target对象上
#### 3.constructor
#### 4.手写map
```js
Array.prototype._map = function(callback) {
let res = []
for (let i=0; i<this.length; i++) {
res.push(callback(this[i], i, this))
}
return res
}
```
#### 5. for of 能遍历对象吗?
不能,只能遍历可迭代的对象,如 Array, Map, Set 等
#### 6.instanceOf 作用,手写一个
instanceOf 主要用于判断一个实例是否属于某种类型
`left instanceOf rightClass`,右边的prototype在左边的原型链上即可
```js
function instance_of(l, r) {
let o = r.prototype // 获取 r 的原型
let pL = l.__proto__ // 获取 l 的原型
// 寻找 l 的原型链,看是否存在 o,直到原型指向 null
while(true) {
if (pL === null) {
return false
}
if (o === pL) { // 找到 l 的原型链上存在 等于 l
return true
}
pL = pL.__proto__
}
}
```
#### 7.数组去重
1. new Set(arr) 方法
2. 遍历法,includes判断
3. Map方法,把值存在 Map对象的 key上,遍历arr,判断Map是否存在该item,如果不存在则push到新数组
```js
// set
function uni(arr) {
return [...new Set(arr)]
}
// Map 遍历法
function uni2(arr) {
let map = new Map()
let res = []
arr.forEach(item => {
if (!map.has(item)) {
map.set(item, true)
res.push(item)
}
})
return res
}
// 双指针修改原数组
function uni3(arr) {
let i = 0, // 非重复位置
j = 0; // 指针
// 指针j开始移动
for (0; j<arr.length; j++) {
// 如果j和i项不一样,则i+1,将j的值赋给i, j继续+1
// 如果一样,i不动,j++
// 所以 i的位置就是非重复数组的最后一个位置
if (arr[i] !== arr[j]) {
i++
arr[i] = arr[j]
}
}
return arr.slice(0, i+1)
}
let arr = [1, 1, 2, 2, 3, 3]
console.log(uni(arr));
console.log(uni2(arr));
console.log(uni3(arr));
```
#### 8.类数组与数组区别,转换
1.类数组
+ 通过class类名,或者标签名获取dom,得到的就是类数组
+ 函数内arguments代替参数
+ 手动实现
```js
let al1 = {
length: 5,
0: 0,
1: 1,
3: 3,
4: 4,
5: 5,
};
```
2.类数组转化为数组
+ Array.from
+ Array.prototype.slice.call(objArr)
```js
console.log(Array.from(objArr));
console.log(Array.prototype.slice.call(objArr));
```
#### 9.手写发布订阅
原理:创建一个发布订阅 class,用event对象{}存储事件名,以及回调函数
on,订阅,指的是创建事件名对应的回调函数,或者往callbacks数组中push方法
off,解除订阅,指的是callbacks数组中删除某个方法,或者清空
emit, 触发订阅事件,指的是遍历执行callbacks数组中的方法,但是注意args传参,以及this指向
```js
class EventPubSub {
constructor() {
this.event = {} // 事件集合
}
// 订阅
// type: 事件名, callback: 回调函数
on(type, callback) {
// 如果订阅的事件不存在
if(!this.event[type]) {
this.event[type] = [callback] // 发布订阅需要执行的callback是一个callback数组
} else {
this.event[type].push(callback)
}
}
// 解除订阅,解除的是callback一样的某一个
off(type, callback) {
if(!this.event[type]) {
return
}
if (callback) {
this.event[type] = this.event[type].filter(item => {
return item !== callback
})
} else {
this.event[type] = []
}
}
// 发布,执行
emit(type, ...args) {
if (!this.event[type]) {
return
}
this.event[type].forEach(callback => {
callback.apply(this, args)
})
}
}
let event = new EventPubSub
event.on('click', (args) => {
console.log('触发click方法', args);
})
event.emit('click', 1)
```
#### 10.手写数组转成树
```js
// 例如将 input 转成output的形式
let input = [
{
id: 1, val: '学校', parentId: null
}, {
id: 2, val: '班级1', parentId: 1
}, {
id: 3, val: '班级2', parentId: 1
}, {
id: 4, val: '学生1', parentId: 2
}, {
id: 5, val: '学生2', parentId: 2
}, {
id: 6, val: '学生3', parentId: 3
},
]
let output = {
id: 1,
val: '学校',
children: [{
id: 2,
val: '班级1',
children: [
{
id: 4,
val: '学生1',
children: []
},
{
id: 5,
val: '学生2',
children: []
}
]
}, {
id: 3,
val: '班级2',
children: [{
id: 6,
val: '学生3',
children: []
}]
}]
}
```
```js
function arrToTree(arr) {
// 1.先定义 id 的 hashMap, id -> item
// 2.再遍历 arr,通过 parentId判断将当前 item 挂到 hashMap下哪一个item的children中
let idMap = {}
arr.forEach(item => {
idMap[item.id] = item
})
arr.forEach(item => {
if (idMap[item.parentId]) {
let parentItem = idMap[item.parentId]
if (parentItem.children && parentItem.children.length >= 0) {
parentItem.children.push(item)
} else {
parentItem.children = [item]
}
}
})
// 3.获取树的最外层,即没有parentId的那层
return arr.filter(item => !item.parentId)
}
```
#### 11.Set, Map, WeakSet和WeakMap区别
+ Set
```js
1.可以遍历, forEach
2.类似不能重复的数组 new Set([1, 2, 3])
3.add, delete, has
```
+ WeakSet
```js
1.不可以遍历
2.成员都是对象,引用类型
3.不能遍历,add, delete, has,没有size
```
+ Map
```js
1.可以遍历,forEach((value, key))
2.类似对象,但是key可以是多种类型
3.set, delete, has, clear, size
```
+ WeakMap
```js
弱引用类型
1.不可以遍历,没有size, values, keys等,key必须为对象,目的是为了不会被垃圾回收考虑,当WeakMap没有被使用时会自动销毁
2.set, delete
```
#### 12.js存在哪几种内存泄露的情况
1. 意外的全局变量
2. 闭包
3. 未被清空的定时器
4. 未被销毁的事件监听
5. DOM引用
#### 13.js判断数组
```js
1.Array.isArray(arr) // true
2.arr instanceof Array // true
3.arr.constructor === Array // true
4.Object.prototype.toString.call(arr) === '[Object Array]'
```
- JavaScript
- 1. DOM事件流
- 2. 模拟 new, Object create(), bind
- 5. 封装函数进行字符串驼峰命名的转换
- 6. 什么是promise
- 7. 判断一个数是否为数组
- 10. __proto__和prototype以及原型,原型链,构造函数
- 11. 继承
- 12. 闭包
- 13. 回调函数
- 14. var 和 let 区别
- 15. this、bind、call、apply
- 16.undefined和null的区别
- 17.内存泄漏
- 18.垃圾回收机制
- html css
- 1. 元素垂直水平居中
- 2. 清除浮动
- 3. bootstrap栅格系统
- 4. px rpx em rem vw 的区别
- 5. 两种盒子模型
- 6. 合集
- web类
- 1. html5的新特性以及理解(web标签语义化)
- 2. 什么是路由,关于前端路由和后端路由
- 3. 对优质代码的理解
- 4. cookie 和 sessionStorage和localStorage
- 5. 浏览器内核
- 6. http 状态码
- 7. href 和 src 的区别
- 8. link 和 @import 的区别
- 9. http 状态码
- 10. websocket
- 11. 浏览器解析url
- 12.http缓存
- vue
- 1.vue2和vue3有哪些区别
- 1. 对 mvvvm 的理解
- 2. mvvm的优缺点
- 3. 数据双向绑定的原理
- 4. 生命周期
- 5. 组件如何通信
- 6. computed和watch的区别
- 7. proxy 和 Object.defineProperty
- 8. 虚拟dom和 diff算法
- 9. 路由的嵌套与传参
- 10. 路由导航钩子
- 11. axios 的理解
- 12. vue自定义指令 diretive
- 13. diff 的实现
- 14. 实现一个简单的双向绑定
- 15. 为什么 data 是一个函数
- 题谱
- js
- 手写篇
- css
- vue
- react
- 算法
- 自我介绍
- 八股文
- 源项目地址
- 1.计算机网络
- 2.浏览器
- 3.html和css
- 4.javascript
- 6.typescript
- 7.vue
- 8.react
- 大厂面试
- 面试题大全
- 常见性能优化
- 面试实战
- 面试分析
- 押题
- 1.微前端在项目中的实际应用
- 2.性能优化
- vue相关
- 1.说一说HashRouter和HistoryRouter的区别和原理
- 无敌之路,牛客网面试题自测记录
- 前端基础
- 1.html
- 2.js基础
- 珠峰性能优化
- WebWorker
- url到渲染
- 浏览器加载机制
- 自我介绍1
- 手写题
- 1.compose
- 2.setTimeout模拟setInterval
- 3.手写数组拍平
- 4.手写promise.all
- 5.手写深拷贝
- webpack
- 实战