前端基础整理
1-1. var let const 区别
- var 是es5语法,有变量提升
- let const是es6语法, 有块级作用域
- var let 是变量,可修改;cosnt是常量,不可修改
2-1. typeof返回哪些类型
- 值类型:undefined string number boolean symbol
- 引用类型: object,注意:typeof null === 'object'
- function
- 列举强制类型转换和隐式类型转换
- 强制:parseInt parseFloat toString等
- 隐式:if、逻辑运算、==、 +拼接字符串
2-2. 手写深度比较,模拟lodash isEqual
const obj1 = {
a:100,
b: {x:100,y:200}
}
const obj2 = {
a:100,
b: {x:100,y:200}
}
// 判断是否是对象、数组
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的value -- 递归
const res = isEqual(obj1[key],obj2[key])
if(!res){
return false
}
}
// 3. 全相等
return true
}
console.log(isEqual(obj1,obj2)) // true
- split()和join() 区别
- '1-2-3'.split('-') // [1,2,3]
- [1,2,3].join('-') // 1-2-3
2-3. 数组的pop push unshift shift 区别
pop | push | unshift | shift | |
---|---|---|---|---|
功能 | 删除最后一个 | 在最后加一个 | 在开头加一个 | 删除开头一个 |
返回值 | 最后一个数 | 数组长度 | 数组长度 | 开头的一个数 |
- 是否会对原数组造成影响
// 纯函数: 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()
// 非纯函数
// push pop shift unshift
// forEach
// some every
// reduce
3-1. 数组slice(切片)和splice(剪接)区别:
- 参数和返回值
- 是否是纯函数,slice是纯函数
slice | splice | |
---|---|---|
}返回值 | ||
是否是纯函数 | 是 | 否 |
3-2. [10,20,30].map(parseInt)
返回[10,NaN,NaN]
等同写法:
[10,20,30].map((num,index)=>parseInt(num,index))
// parseInt 按照index进制格式化num
3-3. ajax请求get和post区别
- get用于查询,post用于提交
- get参数拼接在url上,post参数在请求体内,且更大
- 安全性:post更易于防止CSRF
4-1. 函数call和apply的区别
- 第二个参数不同
- fn.call(this,p1,p2,p3...)
- fn.apply(this,[p1,p2,p3...])
4-2. 事件代理(委托)是什么
const p1 = document.getElementById('p1')
const body = document.body
bindEvent(p1,'click',e=>{
e.stopPropagation() // 注释掉这一行,可以体会到事件冒泡
alert('激活')
})
bindEvent(body,'click',e=>{
alert('取消')
})
4-3. 闭包是什么?有何特性?有何影响?
- 影响:常量常驻内存,得不到释放。闭包不可乱用
5-1 如何阻止事件冒泡和默认行为?
- event.stopPropagation()
- event.preventDefault()
5-2 查找、添加、删除、移动dom节点的方法?
- 查找:document。getElementById/TagName/ClassName/ querySelectorAll(xx)
- 新建: document.createElement('xx ')
- 插入: div.appendChild(xx)
- 移动:先获取,再插入
- 删除子元素: parentDiv.removeChild(xx)
5-3 如何减少dom操作
- 缓存DOM查询结果
- 多次Dom操作,合并到一次插入
const list = document.getElementById('list')
// 创建一个文档片段,此时还没有插入到DOM结构中
const frag = document.createDocumentFragment()
for(let i=0; i<20; i++){
const li = document.createElement('li')
li.innerHTML = "item ${i}"
// 先插入文档片段中
frag.appendChild(li)
}
// 都完成之后,统一插入到 DOM结构中
list.appendChild(frag)
6-1: 解释jsonp原理,为何不是真正的ajax
- 浏览器的同源策略(服务端没有同源策略)和跨域
- img、script、标签可以绕过跨域
- 不走xmlhttp 接口,需要服务端支持
<script>
window.abc = function(data){
console.log(data)
}
</script>
// 把function abc作为回调函数传给后端
<script src="http://localhost:8002/jsonp.js?username=xxx&callback=abc"></script>
// 后端 jsonp.js 处理:
abc{
{name:'jsonp666'}
}
6-2. document load 和 ready 的区别
window.addEventListener('load',function(){
// 页面的全部资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded',function(){
// DOM渲染完即可执行,此时图片、视频可能还没加载完
})
6-3. == 和 === 的区别
- ==会尝试进行类型转换
- === 严格相等
- 只有判断null或undefined 时才用
7-1 函数声明和函数表达式的区别
- 函数声明 function fn()
- 函数表达式 const fn = function()
- 函数声明会在代码执行前预加载(类似变量提升),函数表达式不会
7-2 new Object() 和 Object.create() 区别
- {}等同于new Object(), 原型Object.prototype
- Object.create(null),没有原型
- Object.create({...}), 将原型对象指向
- obj1 === obj2时,两个对象的地址是一样的
7-3 this传统场景题
- this的值在执行时决定
8.1 作用域和自由变量1
let a = 100;
function test(){
alert(a)
a = 10
alert(a)
}
8.2 判断字符串以字母开头,后面字母数字下划线,长度6-30
- const reg = /[1]\w{5,29}$/
- ^ xx开头
- \w: 字母、数字、下划线
- $: 结尾
- \d:数字
-
- : 一次到多次
- 用户名:/[2]/
- 简单ip地址: /\d+.\d+.\d+/
- 日期(2019.12.1):/^\d{4}-\d{1,2}- \d{1,2}$/
9-1 手写trim并保证浏览器兼容性
String.prototype.trim = function(){
// 直接放到原型上有点暴力,其实应该先判断一下,原型上是否有,没有再整
// 通过正则解决兼容性问题
return this.replace(/^\s+/,'').replace(/\s+$/,'')
}
9-2 获取最大值
function max(){
const nums = Array.prototype.slice.call(arguments); // 变为数组
let max = 0;
nums.forEach(n=>{
if(n>max){
max = n
}
})
return max;
}
Math.max([1,2,3,4,5])
9-3 如何用js实现继承
- class
- prototype
10-1 如何捕获js中的异常?
// 高风险的地方
try{
// todo
}catch(ex){
console.error(ex) // 手动捕获 catch
}finally{
// todo
}
// 自动捕获
window.onerror = function(message,source,lineNum,colNum,error){
//1. 对跨域的js,如 cdn,不会有详细的报错信息
// 2. 对于压缩的js,还要配合sourceMap反查到未压缩代码的行、列
10-2 什么是JSON
- JSON是一种数据格式,本质是一段字符串
- json格式和js对象结构一致(键值对),对js语言更友好,但是json中只能用双引号
- window.JSON是一个全局对象:JSON.stringify JSON.parse
10-3 获取当前页面的URL参数
- 传统方式:location.search
function query(name){
const search = location.search.substr(1) // substr(1)是为了取消?,类似array.slice()
// search:‘a=10&b=20&c=30’
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`,'i')
const res = search.match(reg)
if(res === null){
return null
}
retrun res[2]
}
- 新api:URLSearchParams,兼容性需要做处理
function query(name){
cosnt search = location.search;
const p = new URLSearchParams(search);
return p.get(name)
}
11-1 将url参数解析为js对象
// 方法1
function queryToObj(){
const res = {}
const search = location.search.substr(1)
search.split('&').forEach(paramStr=>{
const arr = paramStr.split('=')
cosnt key = arr[0]
const val = arr[1]
res[key] = val
})
return res;
}
// 方法2
function queryToObj(){
const res = { }
const pList = new URLSearchParams(location.search)
plist.forEach((val,key)=>{
res[key] = val
})
return res;
}
11-2 手写数组flatten,考虑多层级
// arr = [1,[2,3,[4,5,[6.7],8],9]
function flat(arr){
// 验证arr中,是否有深层数组
const isDeep = arr.some(item=>item instanceof Array)
is(!isDeep){
return arr // 平的,直接返回
}
const res = Array.prototype.concat.apply([],arr)
return flat(res) // 递归
}
const res = flat(arr)
// 不考虑层级,可以用concat(只能拍平一层)
arr2 = [1,2,[3,4],5]
Array.prototype.concat.apply([],arr2)
Array.prototype.concat.call([],...arr2)
[].concat(...arr2)
11-3 数组去重
- 传统方法
function unique(arr){
const res = []
arr.forEach(item=>{
if(res.indexOf(item)<0){
res.push(item)
}
})
return res
}
- 使用 es6 Set(无序,不允许有重复),不需要遍历,效率高于传统方式
function unique(arr){
const res = new Set(arr)
return [...res]
}
12-1 手写深拷贝
注意:Object.assign 不是深拷贝,且有副作用
12-2 RAF requestAnimationFrame
- 想要动画流畅,更新频率要60fps/s,即16.66ms更新一次视图
- setTimeout 要手动控制频率,而RAF浏览器会自动控制(根据屏幕刷新率)
- 后天标签或隐藏iframe中,RAF会暂停,而setTimeout依然会执行
let curWidth = 100;
const maxWidth = 640;
function animate(){
curWidth = curWidth + 3;
$div1.css('width',curWidth)
if(curWidth < maxWidth){
window.requestAnimationFrame(animate)
}
}
animate()
12-3 性能优化
- 原则:多使用内存、缓存,减少计算、减少网络请求
- 方向:加载页面,页面渲染,页面操作流畅度