1. var和let const 的区别
- var是ES5语法,let const 是ES6语法;var有变量提升
- var和let 是变量,可修改;const是常量,不可修改;
- let const 有块级作用域,var没有
2. typeof返回哪些类型
- undefined string number boolean symbol
- object (注意,typeof null === 'object')
- function
3. 列举强制类型转换和隐式类型转换
- 强制类型转换:parseInt parseFloat toString等
- if(truely)、逻辑运算(boolean)、==(字符转换为数字)+ 拼接字符串(转换为字符串类型)
4. 手写深度比较,模拟lodash isEqual
// 判断是否是对象或数组
function isObject(obj) {
return typeof obj === 'object' && obj !== null
}
function isEqual(obj1,obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 值类型 (注意,参与 equal 的一般不会是函数)
return obj1 === obj2
}
//判断引用类型是否是一种类型
const obj1type = Object.prototype.toString.call(obj1)
const obj2type = Object.prototype.toString.call(obj2)
if(obj1type !== obj2type){
return false
}
// obj1与obj2是否是一个内存地址
if (obj1 === obj2) {
return true
}
// key长度是否相等
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false
}
// 以obj1为基准,和obj2 依次递归比较
for (let key in obj1) {
// 比较当前可以 的val ---递归
const res = isEqual(obj1[key],obj2[key])
if (!res) {
return false
}
}
return true
}
console.log(isEqual(obj1,obj2)) //true
****
5. split()和join()的区别
'1-2-3'.split('-') //[1,2,3]
[1,2,3].join('-') //'1-2-3'
6. 数组的pop push unshift shift分别做什么
功能是什么?
- pop() 删除数组的最后一个元素并返回删除的元素。
- shift() 删除并返回数组的第一个元素。
- unshift() 向数组的开头添加一个或更多元素,并返回新的长度。
- push() 向数组的末尾添加一个或更多元素,并返回新的长度。
返回值是什么?
- pop shift 返回被删除的数
- unshift() push() 返回增加后数组的长度
是否会对原数组造成影响?
- 会
扩展: 数组的API,哪些是纯函数?
- concat 连接两个或更多的数组,并返回结果。
- map 通过指定函数处理数组的每个元素,并返回处理后的数组。
- filter() 检测数值元素,并返回符合条件所有元素的数组。
- slice() 选取数组的一部分,并返回一个新数组。
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()
非纯函数
- push pop shift unshift
- forEach
- some every
- reduce
7. 数组slice和splice的区别
功能
- slice:选取数组的一部分,并返回一个新数组
- splice:从数组中添加或删除元素
参数和返回值
const arr = [10, 20, 30, 40, 50]
// slice 纯函数
const arr1 = arr.slice() // arr1=[10, 20, 30, 40, 50]
const arr2 = arr.slice(1, 4) //arr2= [20, 30, 40]
const arr3 = arr.slice(2) // arr3 = [30, 40, 50]
const arr4 = arr.slice(-2)//arr4 = [40, 50]
// splice 非纯函数 返回截取的值
const spliceRes = arr.splice(1, 2, 'a', 'b', 'c') //spliceRes =[20, 30] arr=[10,'a', 'b', 'c', 40, 50]
const spliceRes1 = arr.splice(1, 2) //spliceRes1 = [20,30] arr=[10, 40, 50]
const spliceRes2 = arr.splice(1, 0, 'a', 'b', 'c') //spliceRes2 = [], arr = [10, "a", "b", "c", 20, 30, 40, 50]
console.log(spliceRes, arr)
8. [10,20,30].map(parseInt)返回结果是什么?
const res = [10, 20, 30].map(parseInt)
console.log(res) //[10,NaN,NaN]
// 拆解
[10, 20, 30].map((num, index) => {
return parseInt(string, index)
})
string
- 要被解析的值。如果参数不是一个字符串,则将其转换为字符串(使用ToString抽象操作)。字符串开头的空白符将会被忽略。
radix
- 从 2 到 36,表示字符串的基数。例如指定 16 表示被解析值是十六进制数。请注意,10不是默认值!
所以返回结果为
[10,NaN,NaN]
9. ajax请求get和post的区别
- get一般用于查询,post一般用于用户提交操作
- get参数拼接在url上,post放在请求体内(数据体积更大)
- 安全性:post易于防止CSRF
10. 函数call 和apply的区别
一个分别传值,一个数组或类数组传值
fn.call(this,p1,p2,p3)
fn.apply(this,arguments)
11. 事件代理(委托)是什么?
const p1 = document.getElementById('p1')
const body = document.body
bindEvent(p1,'click',e=>{
e.stopPropagation() //阻止事件冒泡
alert('激活')
})
bindEvent(body,'click',e=>{
alert('取消')
})
12. 闭包是什么,有什么特性?有什么负面影响
- 作用域和自由变量
- 回顾闭包应用场景:函数作为参数被传入,作为返回值被返回
- 回顾:自由变量的查找,要在函数定义的地方(而非执行的地方)
- 影响:变量会常驻内存,得不到释放。闭包不要乱用
13. 如何阻止事件冒泡和默认行为
- event.stopPropagetion()
- event.preventDefault()
14.查找、添加、删除、移动DOM节点的方法
- 见DOM笔记
15.如何减少DOM操作
- 缓存DOM查询结果
- 多次DOM操作,合并到一次插入
16. 解析jsonp的原理,为何它不是真正的ajax
-
浏览器的同源策略(服务端没有同源策略),和跨域
-
哪些html标签能绕过跨域?
<img src=xxx>
<link href=xxxx>
<script src=xxxx>
- jsonp的原理
- jsonp通过js标签,ajax是通过XMLHttpRequset
17. document load 和 ready 的区别
18. == 和 === 的区别
- == 会尝试类型转换
- === 严格相等
19. 函数声明和函数表达式的区别
- 函数声明
function fn() {...} - 函数表达式
const fn = function() {...} - 函数声明会在代码执行前预加载,而函数表达式不会
20. new Object()和Object.create()的区别
- {}等同于
new Object(),原型Object.prototype-Object.create(null)没有原型 Object.create({...})可指定原型,即:object.__proto__是{...}
21. 关于this的场景题
- 第一个this是User 执行结果是1
- 第二个是this是window 执行结果是undefined
22. 关于作用域和自由变量的场景题 -1
结果3个3
23. 判断字符串以字母开头,后面字母数字下划线,长度6-30
- 答案:
const reg = /^[a-zA-Z]\w{5,29}$/
常用正则:
// 邮政编码
/\d{6}/
// 小写英文字母
/^[a-z]+$/
// 英文字母
/^[a-zA-Z]+$/
// 日期格式 2019.12.1
/^\d{4}-\d{1,2}-\d{1,2}$/
// 用户名
/^[a-zA-Z]\w{5, 17}$/
// 简单的 IP 地址匹配
/\d+\.\d+\.\d+\.\d+/
24. 关于作用域和自由变量的场景题 -2
- 答案:100 10 10
25. 手写字符串trim方法,保证浏览器兼容性
判断是否兼容
if(String.prototype.trim == null){
String.prototype.trim = function() {
return this.replace(/^\s+/,'').replace(/\s+$/,'')
}
}
26. 如何获取多个数字中的最大值
ES5
function max() {
const nums = Array.prototype.slice.call(arguments)//变为数组
let max = 0
nums.forEach(n=>{
if( n>max ){
max = n
}
})
return max
}
ES6
function max(...args){
const nums = args
//变为数组
console.log(nums)
let max = 0
nums.forEach(n=>{
if( n>max ){
max = n
}
})
return max
}
//API
max(10,20,30,40,50,32,65,45) //65
Math.max(10,20,30,40,50,32,65,45) //65
27. 如何用JS实现继承
- class继承
- prototype继承
28. 如何捕获js程序中的异常
//手动捕获
try {
//todo尝试执行代码块
}
catch (error) {
console.error(error) //错误代码
} finally {
//todo 无论 try / catch 结果如何都会执行的代码块
}
// 自动捕获
window.onerror = function (message,source,lineNum,colNum,error) {
// 第一,对跨域的js,如CDN的,不会有详细的报错信息
// 第二,对于压缩的js,还要配合 sourceMap 反查到未压缩代码的行、列
}
29. 什么是JSON
- json是一种数据格式,本质是一段字符串。
- json格式和JS对象结构一致,对JS语言更友好
- window.JSON是一个全局对象:
JSON.stringifyJavaScript 对象转换为 JSON 字符串JSON.parseJSON 字符串转换为 JavaScript 对象
{
"name":"张三",
"info":{
"single":true,
"age": 30,
"city":"北京"
},
"like":["篮球","音乐"]
}
30. 获取当前页面url参数
- 传统方式:查找
location.search - 新API,
URLSearchParams
// 传统方式
function query(name) {
const search = location.search.substr(1) // 类似 array.slice(1)
// search: 'a=10&b=20&c=30'
const res = search.split('&')
// res :["a=10", "b=20", "c=30"]
if (res === null) {
return null
}
for(let i = 0;i < res.length;i++){
let pair = res[i].split('=')
if(pair[0]==name){
return pair[1]
}
}
}
query('a')
// URLSearchParams
function query(name) {
const search = location.search
const p = new URLSearchParams(search)
return p.get(name)
}
console.log( query('b') )
31. 将URL参数解析为js对象
// 传统方式
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
}
// URLSearchParams
function queryToObj(name) {
const res = {}
const pList = new URLSearchParams(search)
pList.forEach((val,key)=>{
res[key] = val
})
return res
}
32. 手写数组flatern,考虑多层级
var arr = [1,2,[3,4],5]
function flat(arr){
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) //递归
}
const res = flat(arr)
console.log(res)
33. 数组去重
- 传统方式,遍历元素挨个比较、去重
- 使用set
//传统方式
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)
34. 手写深拷贝
/**
* 深拷贝
* @param {Object} obj 要拷贝的对象
*/
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
}
//object.assign不是深拷贝,只拷贝第一层级,如
const obj = {a:10,b:{c:20,d:30},e:40}
const obj1 = object.assign({},obj,{f:40})
console.log(obj1) //{a:10,b:{c:20,d:30},e:40,f:40}
//如果修改obj.b.c
obj.b.c = 50
//则
obj1.b.c = 50
35. 介绍一下RAF requestAnimationFrame
- 想要动画流畅,更新频率要60帧/s,即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
// setTimeout
function animate() {
curWidth = curWidth + 3
$div1.css('width', curWidth)
if (curWidth < maxWidth) {
setTimeout(animate, 16.7) // 自己控制时间
}
}
animate()
// RAF
function animate() {
curWidth = curWidth + 3
$div1.css('width', curWidth)
if (curWidth < maxWidth) {
window.requestAnimationFrame(animate) // 时间不用自己控制
}
}
animate()
36. 前端性能如何优化?一般从几个方面考虑
- 笔记 运行环境
- 原则:多使用内存、缓存、减少计算、减少网络请求
- 方向:加载页面更快,页面渲染,页面操作流畅度