concat函数(向数组后面追加一个数组)、map函数、filter函数(过滤)、slice函数(类似于深拷贝)
// 纯函数:1. 不改变源数组(没有副作用);2. 返回一个数组
const arr = [10, 20, 30, 40]
// concat
const arr1 = arr.concat([50, 60, 70])
// map
const arr2 = arr.map(num => num * 10)
// filter
const arr3 = arr.filter(num => num > 25)
// slice
const arr4 = arr.slice()
(2)非纯函数:push pop shift unshift、forEach、some 、every、 reduce
7. 数组slice和splice的区别
(1)功能区别:slice切片、splice剪接
(2)参数和返回值:返回值都为数组
(3)是否为纯函数:slice纯函数、splice非纯函数
const arr = [10, 20, 30, 40, 50]
// slice 纯函数
const arr1 = arr.slice() // 10,20,30,40,50
const arr2 = arr.slice(1, 4) // 20,30,40
const arr3 = arr.slice(2) // 30,40,50
const arr4 = arr.slice(-2) //40,50
// splice 非纯函数
const spliceRes = arr.splice(1, 2, 'a', 'b', 'c') // 把1-2的位置移除再放入a,b,c
// const spliceRes1 = arr.splice(1, 2) // 把1-2位置移除
// const spliceRes2 = arr.splice(1, 0, 'a', 'b', 'c')
console.log(spliceRes, arr)
8. Ajax请求get和post区别
get一般用于查询操作,post一般用于用户提交操作
get参数拼接在url上,post放在请求体内(数据体积可以更大),因此当进行上传提交操作时如果较大就是用post
安全性:post易于防止CSRF
9. [10, 20, 30].map(parseInt)
map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
parseInt() 函数可解析一个字符串,并返回一个整数 parseInt(string, radix),将string以radix进制转化为10进制
const res = [10, 20, 30].map(parseInt)
console.log(res) //[10, NAN, NAN]
// 拆解
[10, 20, 30].map((num, index) => {
return parseInt(num, index)
})
10. 闭包是什么?有何特性?有何影响?
闭包就是:有权访问另一个函数作用域变量的函数都是闭包
应用场景:函数作为参数被传递(函数在一个地方定义好之后,到另一个地方去执行)
函数作为返回值被返回(函数定义好之后会被返回到另一个地方执行)
机制:当我们调用一个闭包函数,在函数执行时,其上下文有个Scope属性,该属性作为一个作用域链包含有该函数被定义时所有外层的变量对象的引用,所以定义了闭包的函数虽然销毁了,但是其变量对象依然被绑定在函数inner上,保留在内存中。
注意:所有的自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方
影响:变量会常驻内存,得不到释放。因此闭包不要乱用,可能会影响性能(一般情况一个函数(函数作用域)执行完毕,里面声明的变量会全部释放,被垃圾回收器回收。但闭包让作用域里面的变量,在函数执行完之后依旧保存没有被垃圾回收处掉。)
11. 如何阻止事件冒泡和默认行为
event.stopPropagation()
event.preventDefault()
12. 如何减少DOM操作
DOM操作非常‘昂贵’(占用CPU,可能会造成浏览器重排,耗时),因此尽量避免频繁的DOM操作;
(1)缓存DOM查询结果
(2)多次DOM操作合并到一起
13. 解释jsonp的原理,为什么不是真正的Ajax
14. 函数声明和函数表达式的区别
函数声明 function fn() { … }
函数表达式 const fn = function() { … }
函数声明会在代码执行前预加载(类似变量提升),而函数表达式不会
// 函数声明
const res = sum(10, 20)
console.log(res)
function sum(x, y) {
return x + y
}
// 函数表达式
var sum = function (x, y) {
return x + y
}
var res = sum(10, 20)
console.log(res)
15. new Object()和Object.create()区别
{} 等同于new Object(), 原型 Object.prototype
ob1 = Object.create(null) 没有原型 obj2 = new Object()有原型 Object.create({ … })可以指定原型
const obj1 = {
a: 10,
b: 20,
sum() {
return this.a + this.b
}
}
const obj2 = new Object({
a: 10,
b: 20,
sum() {
return this.a + this.b
}
})
const obj21 = new Object(obj1) // obj1 === obj21
const obj3 = Object.create(null) // {}没有属性没有原型
const obj4 = new Object() // {}有原型
// 意思创建一个空对象,把原型挂载到create内容上
const obj5 = Object.create({
a: 10,
b: 20,
sum() {
return this.a + this.b
}
})
// 通过ob1创建obj6,那么obj6的原型指向obj1 obj6._proto_ === obj1
const obj6 = Object.create(obj1) // obj6的原型指向obj1
16. 正则表达式
(1)用户名 字符串 字母开头 后面字母数字下划线,长度为6-30
const reg = / ^[a-zA-Z] \w {5,29} $ /
^开始 [ ] 选择 \w 字母数字下划线 {} 长度范围 $结尾 \d数字 +一次或多次 .匹配任意字符
(2)邮政编码 / \d{6} /
(3)小写英文字母 /^[a-z]+$ /
(4)英文字母 /^[a-zA-Z]+$/
(5)日期 /^\d{4}-\d{1,2}-\d{1,2}$/
(6)简单IP地址 /\d+\.\d+.\d+.\d+/ \. 表示.
常用正则表达式教程 https://www.runoob.com/regexp/regexp-syntax.html
17. 如何捕获JS程序中的异常
(1)手动捕获异常try-catch
(2)自动捕获异常 window.onerror
// 自动捕获
window.onerror = function (message, source, lineNum, colNum, error) {
// 第一,对于跨域的js,如CDN的,不会有详细的报错信息
// 第二,对于压缩的js,还要配合sourceMap反查到未压缩代码行列
}
18. 什么是JSON
json是一种数据格式,本质是一段字符串
json格式和JS对象结构一致,对JS语言更友好
window.JSON是全局对象(key都需要双引号),JSON.stringify JSON.parse
19. 获取当前页面url参数
(1)传统方法,查找Location.search
(2)URLSearchParams
// 传统方式
function query(name) {
const search = location.search.substr(1) // 类似 array.slice(1)除去第一个
// search: 'a=10&b=20&c=30'
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i') // i表示大小写不区分
const res = search.match(reg)
if (res === null) {
return null
}
return res[2] // 固定写法 输出b对应的值20
}
query('b')
// URLSearchParams
function query(name) {
const search = location.search
const p = new URLSearchParams(search)
return p.get(name)
}
console.log( query('b') )
20. 介绍一下RAF requestAnimationFrame
在Web应用中,实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout或者setInterval 来实现,css3 可以使用 transition 和 animation 来实现,html5 中的 canvas 也可以实现。除此之外,html5 还提供一个专门用于请求动画的API,那就是 requestAnimationFrame,顾名思义就是请求动画帧。
要想动画流畅,更新频率要60帧/秒,即16.67ms更新一次视图
setTimeout需要手动控制频率,而RAF浏览器会自动控制
后台标签或者隐藏iframe中(最小化),RAF会暂停,而setTimeout依然执行
// 3s 把宽度从 100px 变为 640px ,即增加 540px
// 60帧/s ,3s 180 帧 ,每次变化 3px
const $div1 = $('#div1')
let curWidth = 100
const maxWidth = 640
// RAF
function animate() {
curWidth = curWidth + 3
$div1.css('width', curWidth)
if (curWidth < maxWidth) {
window.requestAnimationFrame(animate) // 时间不用自己控制
}
}
animate()
手写代码题
=========
1. 手写深度比较,模拟lodash.isEqual
// 判断是否是对象或数组(不考虑函数)
function isObject(obj) {
return typeof obj === 'object' && obj !== null
}
// 全相等(深度)
function isEqual(obj1, obj2) {
// 首先判断是否是对象
if (!isObject(obj1) || !isObject(obj2)) {
// 值类型(注意,参与 equal 的一般不会是函数)
return obj1 === obj2
}
if (obj1 === obj2) {
return true
}
// 两个都是对象或数组,而且不相等
// 1. 先取出 obj1 和 obj2 的 keys ,比较个数
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false
}
// 2. 以 obj1 为基准,和 obj2 依次递归比较
for (let key in obj1) {
// 比较当前 key 的 val —— 递归!!!
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false
}
}
// 3. 全相等
return true
}
// 测试
const obj1 = {
a: 100,
b: {
x: 100,
y: 200
}
}
const obj2 = {
a: 100,
b: {
x: 100,
y: 200
}
}
// console.log( obj1 === obj2 )
console.log( isEqual(obj1, obj2) )
const arr1 = [1, 2, 3]
const arr2 = [1, 2, 3, 4]
2. 手写字符串trim方法,保证兼容(正则表达式)
trim 掐头去尾去空格
String.prototype.trim = unction () {
return this.replace(/^\s+/,'').replace(/\s+$/,'') // \s字符
}
3.将url参数解析为JS对象
// 传统方法,分析search
function queryToObj() {
const res = {}
const search = location.search.substr(1) // 去掉?
search.split('&').forEach(paramStr => {
const arr = paramStr.split('=')
const key = arr[0]
const key = arr[1]
res[key] = val
})
return res
}
// 使用URLSearchParams
function queryToObj() {
const res = {}
const pList = new URLSearchParams(location.search)
pList.forEach((val, key) => {
res[key] = val
})
return res
}
4. 手写flatern考虑多层级
将[1, 2, [3, 4, [10, 20, [100, 200]]], 5]变为[1, 2, 3, 4, 10, 20, 100, 200, 5]
function flat(arr) {
// 验证 arr 中,还有没有深层数组 [1, 2, [3, 4]]
const isDeep = arr.some(item => item instanceof Array)
if (!isDeep) {
return arr // 已经是 flatern [1, 2, 3, 4]
}
// oncat只能解决单层[]
const res = Array.prototype.concat.apply([], arr)
return flat(res) // 递归
}
const res = flat( [1, 2, [3, 4, [10, 20, [100, 200]]], 5] )
console.log(res)
5. 数组去重
// 传统方式
function unique(arr) {
const res = []
arr.forEach(item => {
if (res.indexOf(item) < 0) { // 没有当前元素
res.push(item)
}
})
return res
}
// 使用 Set (无序,不能重复)
function unique(arr) {
const set = new Set(arr)
return [...set] // 解构
}
const res = unique([30, 10, 20, 30, 40, 10])
console.log(res)
6. 手写深拷贝
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
// obj 是 null ,或者不是对象和数组,直接返回
return obj
}
// 初始化返回结果
let result
if (obj instanceof Array) { // 判断是否为数组
result = []
} else {
result = {}
}
for (let key in obj) {
// 保证 key 不是原型的属性
if (obj.hasOwnProperty(key)) {
// 递归调用!!!
result[key] = deepClone(obj[key])
}
}
// 返回结果
return result
}
7. 手写一个简易的jQuery,考虑插件和扩展性
class jQuery {
### 最后


**资料过多,篇幅有限**
>自古成功在尝试。不尝试永远都不会成功。勇敢的尝试是成功的一半。
30, 40, 10])
console.log(res)
6. 手写深拷贝
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
// obj 是 null ,或者不是对象和数组,直接返回
return obj
}
// 初始化返回结果
let result
if (obj instanceof Array) { // 判断是否为数组
result = []
} else {
result = {}
}
for (let key in obj) {
// 保证 key 不是原型的属性
if (obj.hasOwnProperty(key)) {
// 递归调用!!!
result[key] = deepClone(obj[key])
}
}
// 返回结果
return result
}
7. 手写一个简易的jQuery,考虑插件和扩展性
class jQuery {
### 最后
[外链图片转存中...(img-ysn867G8-1718098799963)]
[外链图片转存中...(img-5UVTke4G-1718098799965)]
**资料过多,篇幅有限**
>自古成功在尝试。不尝试永远都不会成功。勇敢的尝试是成功的一半。