JS基础知识学习--真题总结
JS真题
题目一
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、逻辑运算、==、+拼接字符串
题目二
1. 手写深度比较,模拟lodash的isEqual
function isObject(obj){
return typeof obj === 'object' && obj !== null
}
function isEqual(obj1, obj2) {
if(!isObject(obj1) || !isObject(obj2)){
// 值类型
return obj1 === obj2
}
if(obj1 === obj2){
return true
}
// 两个都是对象或者数组,而且不相等
// 1. 先判断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) {
// 递归比较
const res = isEqual(obj1[key], obj2[key])
if (!res) {
return false
}
}
return true
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
2. split()和join()的区别
答:split是字符串的方法,用来分割字符串为数组,'1-2-3'.split('-') // [1, 2, 3]
join是数组方法,用来拼接数组为字符串,[1, 2, 3].join('-') // '1-2-3'
3. 数组的pop、push、unshift、shift分别做什么
-
pop : 从 数组末尾弹出一个元素,无参,返回弹出的元素,原位操作
-
push : 从数组末尾加入元素,参数是加入的元素,按顺序加入,返回数组长度,原位操作
-
shift : 从数组的开头弹出一个元素,无参,返回弹出的元素,原位操作
-
unshift : 从数组的开头加入元素,参数是加入的元素,按顺序一起加入,返回数组的长度,原位操作
const arr = [10, 20, 30, 40] // pop // const popRes = arr.pop() // console.log(arr, popRes) // [10, 20, 30] 40 // push // const pushRes = arr.push(50, 60) // console.log(arr, pushRes) // [ 10, 20, 30, 40, 50, 60 ] 6 // unshift // const unshiftRes = arr.unshift(-10, -20) // console.log(arr, unshiftRes) // [ -10, -20, 10, 20, 30, 40 ] 6 // shift // const shiftRes = arr.shift() // console.log(arr, shiftRes) // [ 20, 30, 40 ] 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
扩展:数组的API,有哪些纯函数?
纯函数:1. 不改变源数组(没有副作用); 2. 返回一个数组
-
concat
-
map
-
filter
-
slice
// concat // const arr1 = arr.concat([50, 60, 70]) // console.log(arr, arr1) // [ 10, 20, 30, 40 ] [10, 20, 30, 40, 50, 60, 70] // map // const arr2 = arr.map(val => val * 10) // console.log(arr, arr2) // [ 10, 20, 30, 40 ] [ 100, 200, 300, 400 ] // filter // const arr3 = arr.filter(val => val > 25) // console.log(arr, arr3) // [ 10, 20, 30, 40 ] [ 30, 40 ] // slice // const arr4 = arr.slice() // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 10, 20, 30, 40 ]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
非纯函数: push pop shift unshift forEach some every
题目三
1. 数组slice和splice的区别
-
功能区别(slice-切片, splice-剪切)
-
参数和返回值
-
是否纯函数
// slice 纯函数 // const arr4 = arr.slice() // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 10, 20, 30, 40 ] // const arr4 = arr.slice(2, 4) // start, end // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 30, 40 ] // const arr4 = arr.slice(1) // start // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 20, 30, 40 ] // const arr4 = arr.slice(-2) // lastest start // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 30, 40 ] // const arr4 = arr.slice(-3, -2) // lastest start, lastest end // console.log(arr, arr4) // [ 10, 20, 30, 40 ] [ 20 ] // splice 非纯函数 // const spliceRes = arr.splice(1, 2, 'a', 'b', 'c') // start, length, // console.log(arr, spliceRes) // [ 10, 'a', 'b', 'c', 40 ] [ 20, 30 ] // const spliceRes = arr.splice(1, 2) // console.log(arr, spliceRes) // [ 10, 40 ] [ 20, 30 ] // const spliceRes = arr.splice(1, 0, 'a', 'b', 'c') // 只增加不剪切 // console.log(arr, spliceRes) // [ 10, 'a', 'b', 'c', 20, 30, 40 ] []
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
2. [10, 10, 10, 10].map(parseInt) 返回结果是什么
-
map的参数和返回值:参数:数组的每个元素,对应元素下标。返回值:新数组
-
parseInt参数和返回值:参数:待转换的该进制数字,进制。返回值:转换过的十进制数
const res = [10, 10, 10, 10].map(parseInt) // 拆解,相当于: // const res = [10, 10, 10, 10].map((item, index) => { // return parseInt(item, index) // }) // parseInt(num, base = 10) 其中base = 0时,相当于base = 10,也就是相当于10进制转换 console.log(res) // [ 10, NaN, 2, 3 ]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
3. ajax请求的get和post的区别
- get一般用于查询操作,post一般用于用户提交操作
- get参数拼接在url上,post放在请求体内(数据体积可更大)
- 安全性:post易于防止CSRF
题目四
1. 函数call和apply的区别
答:call的参数是直接传入,而apply的参数是放到数组中
fn.call(this, p1, p2, p3)
fn.apply(this, arguments)
2. 事件代理(委托)是什么
答:在父元素上定义一个事件,监听子元素触发时的冒泡。 如果子元素想要阻止冒泡,则使用:e.stopPropagation()
3. 闭包是什么,有什么特性,有什么负面影响
- 回顾作用域和自由变量
- 回顾闭包应用场景:作为参数被传入,作为返回值被返回
- 回顾:自由变量的查找,要在函数定义的地方(而非执行的地方)
- 影响:变量会常驻内存,得不到释放(但不一定会造成内存泄漏,内存泄漏一般属于Bug,而闭包不是Bug,而是故意为之)。闭包不要乱放。
题目五
1. 如何阻止事件冒泡和默认行为
- event.stoppropagation()
- event.preventDefault()
2. 查找、添加、删除、移动DOM节点的方法
- 查找节点:document.getElementById(“id”)、document.getElementsByTagName(“h1”)、document.getElementsByClassName(“container”)、document.querySelector("#div")、document.querySelectorAll("#div")
- 添加节点:document.createElement(“span”)
- 插入、移动节点:ele.appendChild§
- 获取父元素:ele.parentNode
- 获取子元素:ele.childNodes(含有文本节点)、ele.children(不含文本节点)
- 删除子元素:ele.removeChild
3. 如何减少DOM操作
-
缓存DOM查询结果
-
多次DOM操作,合并到一次插入
const list = document.getElementById('list') // 创建一个文档片段 const fragment = document.createDocumentFragment() for(let i = 0; i < 10; i++){ const li = document.createElement('li') li.innerHTML = i // 插入到文档片段,文档片段是在内存中 fragment.appendChild(li) } // 将文档片段一次性插入 list.appendChild(fragment)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
题目六
1. 解释jsonp的原理,为何它不是真正的ajax?
-
浏览器的同源策略(服务端没有同源策略)和跨域
-
哪些HTML标签能绕过跨域? script、link、img
-
ajax是通过XMLHTTPRequest实现的,jsonp是通过script标签实现的
-
实现跨域必须得到服务器端的允许和配合
// jsonp <script> window.callback = function (data) { console.log(data) } </script> <script src="http://localhost:8002/JSONP.js?username=xxx&callback=abc"></script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
2. document load和ready的区别
-
load是页面的资源全部加载完才执行
-
ready是DOM渲染完就执行,此时图片、视频还可能没有加载完
window.addEventListener('load', function () { // 页面的全部资源加载完才执行,包括图片、视频等 }) document.addEventListener('DOMContentLoaded', function () { // DOM 渲染完即可执行,此时图片、视频还可能没有加载完 })
- 1
- 2
- 3
- 4
- 5
- 6
3. == 和 === 的不同
- == 会尝试类型转换
- === 严格相等
- 哪些场景采用== :判断对象属性是否为null:a.xxx == null。 其他场合一律用===
题目七
1. 函数声明和函数表达式的区别
- 函数声明:function fn () {…}
- 函数表达式: const fn = function () {…}
- 函数声明会在代码执行前预加载,而函数表达式不会
2. new Object()和Object.create()的区别
-
{}等同于new Object(),原型Object.prototype
-
Object .create(null)没有原型
-
Object.create({…})可指定原型
const obj1 = {a: 1} const obj2 = new Object(obj1) // obj1 === obj2 const obj1 = {a: 1} const obj2 = new Object({a: 1}) // obj1 !== obj2, isEqual(obj1, obj2) === true const obj1 = {a: 1} const obj2 = Object.create(obj1) // obj2.__proto__ === obj1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
3. this的场景题
- this的取值在执行的时候再知道
const User = {
count: 1,
getCount: function () {
return this.count
}
}
console.log(User.getCount()) // 1
const func = User.getCount
console.log(func()) // undefined
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
题目八
1. 关于作用域和自由变量的场景题 -1
let i
for(i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(i)
}, 0)
}
// 4 4 4
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2. 判断字符串以字母开头,后面字母数组下划线,长度6-30
-
const reg = /1\w{5, 29}$/
// 小写英文字母 /^[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+/
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
3. 关于作用域和自由变量的场景题 -2
let a = 100
function test() {
console.log(a)
a = 10
console.log(a)
}
test()
console.log(a)
// 100 10 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
题目九
1. 手写字符串trim方法,保证浏览器兼容性
String.prototype.trim = function () {
return this.replace(/^\s+/, '').replace(/\s+$/, '')
}
// 原型、this、正则表达式
- 1
- 2
- 3
- 4
2. 如何获取多个数字中的最大值
function max() {
const arr = Array.prototype.slice.call(arguments)
let res = arr[0]
arr.forEach(item => {
if(item > res)res = item
})
return res
}
console.log(max(3, -1, 20, 4, 34))
console.log(Math.max(3, -1, 20, 4, 34))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
3. 如何用JS实现继承
- class继承
- prototype继承
题目十
1. 如何捕获JS程序中的异常
try {
// TODO
} catch(e) {
console.log(e) // 手动捕获异常
} finally {
// TODO
}
// 自动捕获
window.onerror = function (message, source, lineNum, colNum, error) {
// 第一,对跨域的js, 如CDN的,不会有详细的报错信息
// 第二,对于压缩的js,还要配合sourceMap,反查到未压缩代码的行、列
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
2. 什么是JSON
- json是一种数据格式,本质是一段字符串
- json格式和JS对象结构一致,对JS语言更友好
- Window.JSON是一个全局对象:JSON.stringify将json转化成字符串,JSON.parse将字符串转化成json
3. 获取当前页面url参数
-
传统方式,查找location.search
// 传统方式 function query(name) { const search = location.search.substr(1) // search: 'a=10&b=20&c=30' const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i') const res = search.match(reg) console.log(res) // 数组第一个是完全匹配,第二个是匹配的第一个括号里面的内容,第3个是匹配的第2个括号里面的内容,第4个是匹配的第3个括号里面的内容, // ["&b=20&", "&", "20", "&"] if(res === null) { return null } return res[2] } console.log(query('b'))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
-
新的API,URLSearchParams
// URLSearchParams function query(name) { const search = location.search const p = new URLSearchParams(search) return p.get(name) } console.log(query('b'))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
题目11
1. 将url参数解析为JS对象
// 传统方式,分析search
function queryToObj() {
const res = {}
const search = location.search.substr(1) // 去掉前面的?
search.split('&').map(paramStr => {
const arr = paramStr.split('=')
res[arr[0]] = arr[1]
})
return res
}
console.log(queryToObj()) // {a: "10", b: "20", c: "30"}
// 使用URLSearchParams
function queryToObj2(){
const res = {}
const pList = new URLSearchParams(location.search)
pList.forEach((val, key) => {
res[key] = val
})
return res
}
console.log(queryToObj2()) // {a: "10", b: "20", c: "30"}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
2. 手写数组flatern,考虑多层级
function flat(arr){
// 验证arr中有没有深层数组
const isDeep = arr.some(item => item instanceof Array)
if(!isDeep)return arr
const res = Array.prototype.concat.apply([], arr)
return flat(res) // 递归
}
const res = flat([1, 2, [3, [4]], 5])
console.log(res)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
3. 数组去重
// 传统方式 比较慢 可兼容
function unique(arr) {
const res = []
arr.forEach(item => {
if(res.indexOf(item) < 0) {
res.push(item)
}
})
return res
}
console.log(unique([30, 20, 40, 50, 20, 30]))
// set方式 (无序结构,不能重复) 快 新的API,可能有兼容性问题
function unique2(arr){
const set = new Set(arr)
return [...set]
}
console.log(unique2([30, 20, 40, 50, 20, 30]))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
题目12
1. 手写深拷贝
/**
* 深拷贝
* @param {Object} obj 要拷贝的对象
*/
function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) return obj
let newObj
if (obj instanceof Array) {
newObj = []
} else {
newObj = {}
}
for (let key in obj) {
if (obj.hasOwnProperty(key))
newObj[key] = deepClone(obj[key])
}
return newObj
}
// Object.assign是浅拷贝,只拷贝第一层级,不要踩坑
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
2. 介绍一下RAF: window.requestAnimationFrame
-
想要动画流畅,更新频率要60帧/s,即16.67ms更新一次视图
-
setTimeout要手动控制频率,而RAF浏览器会自动控制
-
后台标签或隐藏iframe中,RAF会暂停,而setTimeout依然执行
// 3s 把宽度从100px变为640px,即增加540px // 60帧/s, 3s 180帧,每次变化3px let curWidth = 100 const maxWidth = 1000 const $div1 = $("#div1") // setTimeout // function animate(){ // curWidth += 3 // $div1.css('width', curWidth) // if(curWidth < maxWidth) { // setTimeout(animate, 16.7); // 得自己控制时间 // } // } // animate() function animate(){ curWidth += 3 $div1.css('width', curWidth) if(curWidth < maxWidth) { window.requestAnimationFrame(animate) } } animate()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
3. 前端性能如何优化?一般从哪几个方面考虑?
- 原则:多使用内存、缓存、减少计算、减少网络请求
- 方向:加载页面,页面渲染,页面操作流畅度
a-zA-Z ↩︎
文章来源: blog.csdn.net,作者:爱玲姐姐,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/jal517486222/article/details/105837236
- 点赞
- 收藏
- 关注作者
评论(0)