企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
> 下面的三级标题,代表的是此小节视频对应的标题。四级标题为对应的面试题。 ### 何为变量提升 #### 变量提升 ```javascript // 变量提升 ES5 console.log(a) // undefined var a = 200 // 等价于下面,js 执行中会把所有 var 提升的前面; var a console.log(a) // undefined a = 200 ``` #### typeof 判断类型 * undefined、string、number、boolean、symbol * object(typeof null === 'object') * function ### 手写深度比较 isEqual #### 手写深度比较 lodash.isEqual ```javascript // 实现效果如下 const obj1 = {a:10,b:{x:100,y:200}} const obj2 = {a:10,b:{x:100,y:200}} isEqual(obj1,obj2) === true ``` ```javascript function isEqual(obj1, obj2) { if (!isObject(obj1) || !isObject(obj2)) { 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 } ``` #### split() 和 join() ```javascript '1-2-3'.split('-') // [1,2,3] [1,2,3].join('-') // '1-2-3' ``` #### 数组的 pop push unshift shift * 功能是什么? * 返回值是什么? * 是否会对原数组造成影响? ```javascript const arr = [10,20,30,40] // pop, 删除数组的最后一个元素,返回数组的最后一个元素值,原数组发生改变 const popRes = arr.pop() console.log(popRes,arr) // 40,[10,20,30] // shift,删除数组的第一个元素,返回被删除的元素值,原数组发生改变 const shiftRes = arr.shift() console.log(shiftRes, arr) // 10 [ 20, 30, 40 ] // push,向数组后面追加一个元素,返回新数组的length,原数组发生改变 const pushRes = arr.push() console.log(pushRes, arr) // 4 [ 10, 20, 30, 40 ] // unshift() ,在数组第一个元素前增加一个元素(传入的参数值),返回新数组的length,原数组发生改变 const unshiftRes = arr.unshift(20) console.log(unshiftRes, arr) // 5 [ 20, 10, 20, 30, 40 ] ``` ##### 纯函数 * 不改变源数组(没有副作用) * 返回一个数组 ```javascript // arr1.concat(arr2) 拼接两个数组,不影响源数组,返回新数组 const arr1 = arr.concat([50, 60, 70]) // arr1:[10, 20, 30, 40, 50, 60, 70 ] // 遍历数组 const arr2 = arr.map((num) => num * 10) // [ 100, 200, 300, 400 ] // 过滤数组 const arr3 = arr.filter((num) => num > 25) // slice 从已有的数组中返回选定的元素。可以用来做深拷贝 // https://www.w3school.com.cn/jsref/jsref_slice_array.asp const arr4 = arr.slice() ``` ### 你是否真的会用数组 #### 数组 slice 和 splice 的区别 * 功能区别(slice-切片,splice-剪接) ```javascript const arr = [10,20,30,40] // slice(start?:number,end?:number),截取函数的一部分,返回新函数,不改变旧函数 const arr4 = arr.slice(1,3) // [ 10, 20, 30, 40 ] // splice(start:number,deleteCount?:number),剪接函数,改变源函数 const arr5 = arr.splice(1, 2) // arr5:[ 20, 30 ] arr:[ 10, 40 ] const spliceReg = arr.splice(1, 2, 'a', 'b', 'c') // spliceReg:[ 20, 30 ] arr:[ 10, 'a', 'b', 'c', 40 ] ``` #### [10,20,30].map(parseInt) * map 的参数和返回值 * parseInt 的参数和返回值 * **parseInt(\*string\*, \*radix\*)** 解析一个字符串并返回指定基数的十进制整数, `radix` 是2-36之间的整数,表示被解析字符串的基数 ```javascript [10,20,30].map(parseInt) // [10, NaN, NaN] // 拆解 [10,20,30].map((item,index)=>{ return parseInt(item,index) }) ``` #### ajax 请求 get 和 post 的区别 * get 一般用于查询操作,post 一般用于提交操作 * get 参数拼接在 url 上,post 放在请求体内(数据体积可以更大) * 安全性:post 易于防止 CSRF ### 再学闭包 #### 函数 call 和 apply 的区别 ```javascript // 传参不同,call 参数分开传,apply 传一个数组 fn.call(this,p1,p2,p3) fn.apply(this,arguments) ``` #### 事件代理(委托)是什么 ![image-20210313095115799](https://image.mdashen.com/pic/image-20210313095115799.png) #### 闭包是什么,有什么特性?有什么负面影响? 跳转 [闭包](# 闭包) 闭包影响:变量会常驻内存,得不到释放。闭包不要乱用 ### 回顾 DOM 操作和优化 #### 如何阻止事件冒泡和默认行为? ```javascript event.stopPropagation() event.preventDefault() ``` #### 如何减少 DOM 操作? ```javascript // DOM 查询结果做缓存 // 创建一个文档片段,document.createDocumentFragment() ``` ![image-20210313100717748](https://image.mdashen.com/pic/image-20210313100717748.png) ### jsonp 本职是 ajax 吗? #### 解释 jsonp 的原理,为何它不是真正的 ajax * 浏览器的同源策略(服务端没有同源策略)和跨域 * jsonp 原理 ![image-20210313101530622](https://image.mdashen.com/pic/image-20210313101530622.png) > jsonp 是使用 script 标签来进行通信。没有使用 xhr 请求。 #### document load 和 ready 的区别 ![image-20210313101634495](https://image.mdashen.com/pic/image-20210313101634495.png) #### == 和 === 的不同 跳转 [链接](# == 运算符) * 日常使用中 除了 == null 之外,其他都一律使用 === ### 常见的正则表达式 #### 关于作用域和自由变量的场景题 ```javascript // question 1 let i for(i=1;i<=3;i++){ setTimeout(function(){ console.log(i) },0) } // answer1 4 4 4 ``` ```javascript // question 2 let a = 100 function test(){ alert(a) a = 10 alert(a) } test() alert(a) // answer 2 100 10 10 ``` #### 判断字符串以字母开头,后面字母数字下划线,长度 6-30 ```javascript const reg = /^[a-zA-Z]\w{5,29}$/ ``` [正则表达式30分钟入门教程](https://deerchao.cn/tutorials/regex/regex.htm) ### 如何获取最大值 #### 手写字符串 trim 方法,保证浏览器兼容性 ```javascript String.prototype.trim = function(){ return this.replace(/^\s+/,'').replace(/\s+$/,'') } // 原型、this、正则表达式 ``` #### 如何获取多个数字中的最大值 ```javascript function max() { const nums = Array.prototype.slice.call(arguments) // 变为数组 let max = 0 nums.forEach((n) => { if (n > max) { max = n } }) return max } ``` #### 如何用 JS 实现继承 * class 继承 * protorype 继承 ### 解析 url 参数 #### 如何捕获 JS 程序中的异常? ```javascript try{ // todo } catch (ex) { console.error(ex) // 手动捕获 catch } finally { // todo } ``` ```javascript window.onerror = function(message,source,lineNom,colNom,error){ // 第一,对跨域的 js,如 CDN 的,不会有详细的报错信息 // 第二,对于压缩的 js,还要配合 sourceMap 反查到未压缩代码的行、列 } ``` #### 什么是 JSON * json 是一种数据格式,本职是一段字符串 * json 格式和 js 对象结构一致,对 JS 语言更友好 * window.JSON 是一个全局对象:JSON.stringify JSON.parse #### 获取当前页面 url 参数 ![image-20210318173641426](https://image.mdashen.com/pic/image-20210318173641426.png) ![image-20210318173718181](https://image.mdashen.com/pic/image-20210318173718181.png) ### 数组去重有几种方式 #### 将 url 参数解析为 JS 对象 ```javascript // 传统方法 function queryToObj() { const res = {} const search = location.search.substr(1) // 去掉前面的 '?' search.split('&').forEach((paramStr) => { const arr = paramStr.split('=') const key = arr[0] const val = arr[1] res[key] = val }) return res } ``` ```javascript // 新方法 function queryToObj() { const res = {} const pList = new URLSearchParams(location.search) pList.forEach((val, key) => { res[key] = val }) return res } ``` #### 手写数组 flatern,考虑多层级 > flatern 可以理解为,拍平,扁平化(v.) ```javascript flat([1,2,[1,2,3],4,5]) // [1,2,1,3,4,5] ``` ```javascript 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] } const res = Array.prototype.concat.apply([], arr) // 这东西还是没看懂 return flat(res) // 递归 } console.log(flat([1, 2, [3, 4, [5, 6, 7, [8, 9]]]])) ``` #### 数组去重 ```javascript // 传统方式 function unique(arr) { const res = [] arr.forEach((item) => { if (res.indexOf(item) < 0) { res.push(item) } }) return res } console.log(unique([1, 2, 2, 2, 11, 3, 3])) ``` ```javascript // 使用 Set(无序,不能重复) function unique(arr){ const set = new Set(arr) return [...set] } ``` ### 是否用过 requestAnimationFrame #### 手写深拷贝 [跳转链接](# 深拷贝) * Object.assign 不是深拷贝 ```javascript 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 } ``` #### RAF requestAnimationFrame * 要想动画流畅,更新频率要 60帧/s,即16.67ms 更新一次视图 * setTimeout 要手动控制频率,而 RAF 浏览器会自动控制 * 后台标签或隐藏 iframe 中,RAF 会暂停,而 setTimeout 依然执行 #### 前端性能如何优化?一般从哪几个方面考虑? * 原则:多使用内存、缓存、减少计算、减少网路请求 * 方向:加载页面,页面渲染,页面操作流畅度 ***** **完** # 祝大家早日找到心仪的工作