0


前端高频面试题 Day02

面试题

var 和 let const 的区别

  • var 是 ES5 及之前的语法,let const 是 ES6 语法
  • var 和 let 是变量,可修改;const 是常量,不可修改
  • var 有变量提升,let const 没有
  • var 没有块级作用域,let const 有 (ES6 语法有块级作用域)
// var 变量提升
console.log('a', a)var a =100// let 没有变量提升
console.log('b', b)let b =200
// var 没有块级作用域for(var i =0; i <10; i++){var j =1+ i
}
console.log(i, j)// let 有块级作用域for(let x =0; x <10; x++){let y =1+ x
}
console.log(x, y)

typeof 有哪些返回类型?

// 判断所有值类型let a
console.log(a)// 'undefined'const str ='abc'typeof str  // 'string'const n =100typeof n // 'number'const b =truetypeof b // 'boolean'const s =Symbol('s')typeof s // 'symbol'

列举强制类型转换和隐式类型转换

  • 强制 parseInt``````parseFloat
  • 隐式 if==+ 拼接字符串

手写深度比较,如 lodash isEqual

// 实现如下效果const obj1 ={a:10,b:{x:100,y:200}}const obj2 ={a:10,b:{x:100,y:200}}isEqual(obj1, obj2)===true
// 判断是否是 objectfunctionisObject(obj){returntypeof obj ==='object'&& obj !==null}// 全相等functionisEqual(obj1, obj2){if(!isObject(obj1)||!isObject(obj2)){// 值类型,不是对象或数组(注意,equal 时一般不会有函数,这里忽略)return obj1 === obj2
    }if(obj1 === obj2){// 两个引用类型全相等(同一个地址)returntrue}// 两个都是引用类型,不全相等// 1. 先取出 obje2 obj2 的 keys,比较个数const obj1Keys = Object.keys(obj1)const obj2Keys = Object.keys(obj2)if(obj1Keys.length !== obj2Keys.length){// keys 个数不相等,则不是全等returnfalse}// 2. 以 obj1 为基准,和 obj2 依次递归比较for(let key in obj1){// 递归比较const res =isEqual(obj1[key], obj2[key])if(!res){// 遇到一个不相等的,则直接返回 falsereturnfalse}}// 3. 都相等,则返回 truereturntrue}

split()

join()

的区别

'1-2-3'.split('-')[1,2,3].join('-')

数组的

pop
push
unshift
shift

分别做什么

注意以下几点

  • 函数作用是什么?
  • 返回值是什么?
  • 对原数组是否造成影响?
  • 如何对原数组不造成影响?concat``````slice``````map``````filter

【扩展】数组 API 的纯函数和非纯函数

纯函数 —— 1. 不改变来源的数组; 2. 返回一个数组

  • concat
  • map
  • filter
  • slice
const arr =[100,200,300]const arr1 = arr.concat([400,500])const arr2 = arr.map(num=> num *10)const arr3 = arr.filter(num=> num >100)const arr4 = arr.slice(-1)

非纯函数

情况1,改变了原数组

  • push
  • reverse
  • sort
  • splice

情况2,未返回数组

  • push
  • forEach
  • reduce
  • some

数组 slice 和 splice 的区别?

slice - 切片;splice - 剪接;

// slice()const arr1 =[10,20,30,40,50]const arr2 = arr1.slice()// arr2 和 arr1 不是一个地址,纯函数,重要!!!// arr.slice(start, end)const arr1 =[10,20,30,40,50]const arr2 = arr1.slice(1,4)// [20, 30, 40]// arr.slice(start)const arr1 =[10,20,30,40,50]const arr2 = arr1.slice(2)// [30, 40, 50]// 负值const arr1 =[10,20,30,40,50]const arr2 = arr1.slice(-2)// [40, 50]
// arr.splice(index, howmany, item1, ....., itemX)const arr1 =[10,20,30,40,50]const arr2 = arr1.splice(1,2,'a','b','c')// [20, 30]// arr1 会被修改,不是纯函数,即有副作用

[10, 20, 30].map(parseInt)

的结果是什么?

// 拆解开就是[10,20,30].map((num, index)=>{returnparseInt(num, index)// parseInt 第二个参数是进制// 如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。// 如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN})
// 可以对比[10,20,30].map((num, index)=>{returnparseInt(num,10)})

ajax 请求中 get 和 post 的区别

  • get 一般用于查询操作,post 一般用于提交操作
  • get 参数在 url 上,post 在请求体内
  • 安全性:post 请求易于防止 CSRF

(post 代码演示:网页,postname)

call 和 apply 的区别

  • fn.call(this, p1, p2, p3)
  • fn.apply(this, arguments)

事件委托(代理)是什么

const p1 = document.getElementById('p1')const body = document.body
bindEvent(p1,'click',e=>{
    e.stopPropagation()// 注释掉这一行,来体会事件冒泡alert('激活')})bindEvent(body,'click',e=>{alert('取消')})

闭包是什么,有什么特性,对页面有什么影响

  • 回归作用域和自由变量
  • 闭包的应用场景:函数作为参数被传入,函数作为返回值被返回
  • 关键点:自由变量的查找,要在函数定义的地方,而不是执行的地方

对页面的影响

  • 变量内存得不到释放,可能会造成内存积累(不一定是泄露)
// 自由变量示例 —— 内存会被释放let a =0functionfn1(){let a1 =100functionfn2(){let a2 =200functionfn3(){let a3 =300return a + a1 + a2 + a3
        }fn3()}fn2()}fn1()
// 闭包 函数作为返回值 —— 内存不会被释放functioncreate(){let a =100returnfunction(){
        console.log(a)}}let fn =create()let a =200fn()// 100// 闭包 函数作为参数 —— 内存不会被释放functionprint(fn){let a =200fn()}let a =100functionfn(){
    console.log(a)}print(fn)// 100

如何阻止事件冒泡和默认行为

event.stopPropagation()
event.preventDefault()

解释 jsonp 的原理,以及为什么不是真正的 ajax

  • 浏览器的同源策略,什么是跨域?
  • 哪些 html 标签能绕过跨域?
  • jsonp 原理

document load 和 document ready 的区别

window.addEventListener('load',function(){// 页面的全部资源加载完才会执行,包括图片、视频等})
document.addEventListener('DOMContentLoaded',function(){// DOM 渲染完即可执行,此时图片、视频还可能没有加载完})

==

===

的不同

  • == 会尝试进行类型转换
  • === 严格相等

函数声明与函数表达式的区别?

const res =sum(10,20)
console.log(res)// 30// 函数声明functionsum(x, y){return x + y
}
const res =sum(100,200)
console.log(res)// 报错!!!// 函数表达式constsum=function(x, y){return x + y
}

new Object()

Object.create()

的区别

示例一

const obj1 ={a:10,b:20,sum(){returnthis.a +this.b
    }}const obj2 =newObject({a:10,b:20,sum(){returnthis.a +this.b
    }})const obj3 = Object.create({a:10,b:20,sum(){returnthis.a +this.b
    }})// 分别打印看结构

示例二

const obj1 ={a:10,b:20,sum(){returnthis.a +this.b
    }}const obj2 =newObject(obj1)
console.log(obj1 === obj2)// trueconst obj3 = Object.create(obj1)
console.log(obj1 === obj3)// falseconst obj4 = Object.create(obj1)
console.log(obj3 === obj4)// false// 然后修改 obj1 ,看 obj2 obj3 和 obj4
obj1.printA=function(){
    console.log(this.a)}

对作用域上下文和 this 的理解,场景题

const User ={count:1,getCount:function(){returnthis.count
    }}
console.log(User.getCount())// what?const func = User.getCount
console.log(func())// what?

对作用域和自由变量的理解,场景题

let i
for(i =1; i <=3; i++){setTimeout(function(){
      console.log(i)},0)}// what?

判断字符串以字母开头,后面可以是数字,下划线,字母,长度为 6-30

const reg = /^[a-zA-Z]\w{5,29}$/
/\d{6}/// 邮政编码/^[a-z]+$/// 小写英文字母/^[A-Za-z]+$/// 英文字母/^\d{4}-\d{1,2}-\d{1,2}$/// 日期格式/^[a-zA-Z]\w{5,17}$/// 用户名(字母开头,字母数字下划线,5-17位)/\d+\.\d+\.\d+\.\d+/// 简单的 IP 地址格式

以下代码,分别 alert 出什么?

let a =100functiontest(){alert(a)
    a =10alert(a)}test()alert(a)// what?

手写 trim 函数,保证浏览器兼容性

String.prototype.trim=function(){returnthis.replace(/^\s+/,"").replace(/\s+$/,"")}

知识点:原型,this,正则

如何获取多个数值中的最大值?

Math.max(10,30,20,40)// 以及 Math.min
functionmax(){const nums =Array.prototype.slice.call(arguments)// 变为数组let max =0
    nums.forEach(n=>{if(n > max){
            max = n
        }})return max
}

如何用 JS 实现继承?

class 代码

程序中捕获异常的方法

try{// todo}catch(ex){
    console.error(ex)// 手动捕获 catch}finally{// todo}
// 自动捕获 catch(但对跨域的 js 如 CDN 的,不会有详细的报错信息)
window.onerror=function(message, source, lineNom, colNom, error){}

什么是 JSON ?

首先,json 是一种数据格式标准,本质是一段字符串,独立于任何语言和平台。注意,json 中的字符串都必须用双引号。

{"name":"张三","info":{"single":true,"age":30,"city":"北京"},"like":["篮球","音乐"]}

其次,JSON 是 js 中一个内置的全局变量,有

JSON.parse

JSON.stringify

两个 API 。

获取当前页面 url 参数

自己实现

// const url = 'https://www.xxx.com/path/index.html?a=100&b=200&c=300#anchor'functionquery(name){const search = location.search.substr(1)// 去掉前面的 `?`const reg =newRegExp(`(^|&)${name}=([^&]*)(&|$)`,'i')const res = search.match(reg)if(res ===null){returnnull}returndecodeURIComponent(res[2])}
console.log(query('a'))
console.log(query('c'))

新的 API

URLSearchParams
const pList =newURLSearchParams(location.search)
pList.get('a')

将 url 参数解析为 JS 对象?

自己编写

functionqueryToObj(){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
}

新的 API

URLSearchParams
functionqueryToObj(){const res ={}const pList =newURLSearchParams(location.search)
    pList.forEach((val, key)=>{
        res[key]= val
    })return res
}

实现数组 flatern ,考虑多层级

functionflat(arr){// 验证 arr 中,还有没有深层数组,如 [1, [2, 3], 4]const isDeep = arr.some(item=> item instanceofArray)if(!isDeep){return arr // 没有深层的,则返回}// 多深层的,则 concat 拼接const res =Array.prototype.concat.apply([], arr)// 回归上文,apply 和 call 的区别returnflat(res)// 递归调用,考虑多层}flat([[1,2],3,[4,5,[6,7,[8,9,[10,11]]]]])

数组去重

要考虑:

  • 顺序是否一致?
  • 时间复杂度

ES5 语法手写。

// 写法一functionunique(arr){const obj ={}
    arr.forEach(item=>{
        obj[item]=1// 用 Object ,去重计算高效,但顺序不能保证。以及,非字符串会被转换为字符串!!!})return Object.keys(obj)}unique([30,10,20,30,40,10])
// 写法二functionunique(arr){const res =[]
    arr.forEach(item=>{if(res.indexOf(item)<0){// 用数组,每次都得判断是否重复(低效),但能保证顺序
            res.push(item)}})return res
}unique([30,10,20,30,40,10])

用 ES6 Set

// 数组去重functionunique(arr){const set =newSet(arr)return[...set]}unique([30,10,20,30,40,10])

手写深拷贝

粘贴代码

【注意】

Object.assign

不是深拷贝,可以顺带讲一下用法

  • Object.assign(obj1, {...})
  • const obj2 = Object.assign({}, obj1, {...})

介绍一下 RAF requestAnimationFrame

想用 JS 去实现动画,老旧的方式是用 setTimeout 实时刷新,但这样效率非常低,而且可能会出现卡顿。

  • 想要动画流畅,更新频率是 60帧/s ,即每 16.6ms 更新一次试图。太慢了,肉眼会感觉卡顿,太快了肉眼也感觉不到,资源浪费。
  • 用 setTimeout 需要自己控制这个频率,而 requestAnimationFrame 不用自己控制,浏览器会自动控制
  • 在后台标签或者隐藏的<iframe>中,setTimeout 依然会执行,而 requestAnimationFrame 会自动暂停,节省计算资源

(代码演示)

对前端性能优化有什么了解?一般都通过那几个方面去优化的?

原则

  • 多使用内存、缓存或者其他方法
  • 减少 CPU 计算、较少网络

方向

  • 加载页面和静态资源
  • 页面渲染

本文转载自: https://blog.csdn.net/qq_63299825/article/details/132329008
版权归原作者 ~black- 所有, 如有侵权,请联系我们删除。

“前端高频面试题 Day02”的评论:

还没有评论