记录一次 BAT 一线互联网公司前端 JavaScript 面试
Github来源:一个合格的初级前端工程师需要掌握的模块笔记 | 求星星 ✨ | 给个❤️关注,❤️点赞,❤️鼓励一下作者
大家好,我是魔王哪吒,很高兴认识你~~
哪吒人生信条:如果你所学的东西 处于喜欢 才会有强大的动力支撑。
每天学习编程,让你离梦想更新一步,感谢不负每一份热爱编程的程序员,不论知识点多么奇葩,和我一起,让那一颗四处流荡的心定下来,一直走下去,加油,2021
加油!欢迎关注加我vx:xiaoda0423
,欢迎点赞、收藏和评论
不要害怕做梦,但是呢,也不要光做梦,要做一个实干家,而不是空谈家,求真力行。
大家好,我是魔王哪吒,话不多说,今天带来的是一篇《记录一次 BAT 一线互联网公司前端 JavaScript 面试》文章,欢迎大家喜欢。前沿
置身世外只为暗中观察!!!Hello 大家好,我是魔王哪吒!
面试内容
你需要一些 HTML 和 css 的基础知识,掌握 JavaScript 和 ES6 的基本语法,对事物的好奇心。
初级 JS 面试题
JS Web API,开发环境,运行环境
原型,作用域,异步,Ajax,事件,webpack 等
观察所有面试题的考点
学习梳理知识体系图
找准知识体系,刻意掌握
善于总结观点,原理
typeof 能判断哪些类型
typeof 运算符:
可以识别所有值的类型
可以识别函数
可以用力判断是否为引用类型
考点:JS 变量类型
代码:
// 判断所有值的类型
let a; typeof a // 'undefined'
const str = 'abc'; typeof str // 'string'
const n = 10; typeof n // 'number'
const b = true; typeof b // 'boolean'
const s = Symbol('s'); typeof s // 'symbol'
// 能够判断函数
typeof console.log // 'function'
typeof function(){} // 'function'
// 能够识别引用类型
typeof null // 'object'
typeof ['a','b'] // 'object'
typeof {x:10} // 'object'
什么时候使用===,什么时候使用==
考点:强制类型转换
字符串拼接的类型转换
const a = 10 + 1 // 11
const b = 10 + '10' // '1010'
const c = true + '1' // true1
解决方法:
10 + parseInt('1') // 11
==
运算符的类型转换
100 == '100' // true
0 == '' // true
0 == false // true
false == '' // true
null == undefined // true
类型转换
除了
0
之外的所有数字,转换为布尔型都为true
。除了
“ ”
之外的所有字符,转换为布尔型都为true
。null
和undefined
转换为布尔型为false
。
什么时候使用===
,什么时候使用==
除了
==
null 外,其他地方用===
const obj = {a:1}
if(obj.b==null){}
// 相当于
//if(obj.b === null || obj.b === undefined){}
window.onload 和 DOMContentLoaded 的区别
考点:页面加载过程
创建 10 个标签,点击后弹出对应的序号
考点:JS 作用域
手写节流 throttle,防抖 debounce
考点:性能,体验优化
Promise 解决了什么问题
考点:JS 异步
值类型和引用类型的区别
值类型的表示:
// 值类型
let a = 10
let b = a
a= 20
console.log(b); // 10
引用类型的表示:
// 引用类型
let a = { age:12 }
let b = a
b.age = 13
console.log(a.age) // 13
手写深拷贝
判断是值类型还是引用类型
判断是数组还是对象
递归
代码:
// 浅拷贝
const obj1 = {
age: 12,
name: 'web',
address: {
city: 'beijing'
},
}
const obj2 = obj1
obj2.address.city = 'shanghai'
console.log(obj1.address.city)
结果:
shanghai
深拷贝:定义要拷贝的对象
const obj2 = deepClone(obj1)
obj2.address.city = 'shanghai'
console.log(obj1.address.city)
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
}
深拷贝结果
beijing
原型,原型链
如何判断一个变量是不是数组?
a instanceof Array
手写一个简易的
jquery
,考虑插件和扩展性?
代码:
class jQuery {
constructor(selector){
const result = document.querySelectorAll(selector)
const length = result.length
for(let i=0; i<length; i++){
this[i] = result[i]
}
this.length = length
}
get(index){
return this[index]
}
each(fn){
for(let i=0;i<this.length;i++){
const elem = this[i]
fn(elem)
}
}
on(type,fn){
return this.each(elem=>{
elem.addEventListener(type,fn,false)
})
}
}
插件的扩展性
jQuery.prototype.dialog = function(info) {
alert(info)
}
复写机制:
class myJQuery extends jQuery {
constructor(selector) {
super(selector)
}
// 扩展自己的方法
study(){
}
}
class
的原型本质,如何理解?
原型和原型链的图示
class 类似模板
constructor
属性
方法
代码:
// 类
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
study() {
console.log('study')
}
}
// 通过类 new 对象,实例
const web = new Person('哪吒', 10)
class 实际上是函数,可见的语法糖
typeof Student
结果:
"function"
typeof People
结果:
"function"
继承
extends
super
扩展,重写方法
原型
原型关系:
每个
class
都有显示原型prototype
每个实例都有隐式原型
__proto__
实例的
__proto__
指向对应class
的prototype
原型链
console.log(Student.prototype.proto)
console.log(People.prototype)
console.log(People.prototype === Student.prototype.proto)
类型判断 instanceof
[] instanceof Array // true
[] instanceof Object // true
{} instanceof Object // true
作用域,闭包
this
的不同应用场景下,如何取值?手写
bind
函数实际开发中闭包的应用场景,举例说明
创建 10 个 a 标签,点击弹出对应序号
let i, a
for(i=0; i<10; i++) {
a=document.createElement('a')
a.innerHTML = i + '<br>'
a.addEventListener('click', function(e){
e.preventDefault()
alert(i)
})
document.body.appendChild(a)
}
作用域
作用域分:
全局作用域
函数作用域
块级作用域(es6 新增)
代码:
let a = 0
function fn1() {
let a1 = 100
function fn2() {
let a2 = 200
function fn3() {
let a3 = 300
return a+a1+a2+a3
}
fn3()
}
fn2()
}
fn1()
块级作用域
if(true) {
let x=100
}
console.log(x) // 会报错
自由变量
如果一个变量在当前作用域没有定义,但被使用了,向上级作用域去找,一层一层一次寻找,直到找到为止,如果到了全局作用域都没有找到,就会报错xx is not defined
闭包
闭包的表现:
函数作为参数被传递
函数作为返回值被返回
做一个简单的
cache
工具
闭包隐藏数据
function createCache() {
const data={}
// 闭包中的数据,被隐藏,不被外界访问
return {
set: function(key,val) {
data[key] = val
},
get: function(key){
return data[key]
}
}
}
const c = createCache()
c.set('a', 100)
console.log(c.get('a'))
函数作为返回值
function create(){
let a = 100
return function(){
console.log(a)
}
}
let fn = create()
let a = 200
fn()
结果:
100
函数作为参数
function print(fn) {
let a = 200
fn()
}
let a = 100
function fn(){
console.log(a)
}
print(fn)
结果:
100
所有自由变量的查找是在函数定义的地方,向上级作用域查找,不是执行的地方。
this
作为普通函数被调用
使用
call,apply,bind
被调用作为对象方法被调用
在
class
方法中被调用箭头函数
this
取什么值,是在函数执行的时候确定的,不是函数定义的时候确定的。
call
指向,bind
会返回新的函数
代码:
function fn1(){
console.log(this)
}
fn1() // window
fn1.call({x:10}) // {x:10}
const fn2 = fn1.bind({x:20})
fn2() // {x:20}
代码:
const Jeskson = {
name: 'web',
sayHi() {
// this 是指当前对象
console.log(this)
},
wait(){
setTimeout(function(){
// this === window
console.log(this)
}
}
}
代码:
const Jeskson = {
name: 'web',
sayHi() {
// this 是指当前对象
console.log(this)
},
wait() {
setTimeout(()=>{
// this 指当前对象
console.log(this)
})
}
}
代码:
class People {
constructor(name){
this.name = name
this.age = 20
}
sayHi(){
console.log(this)
}
}
const Jeskson = new People('web')
Jeskson.sayHi() // Jeskson 对象
手写bind
函数
Function.prototype.bind1 = function(){
// 将参数解析为数组
const args = Array.prototype.slice.call(arguments)
// 获取 this
const t = args.shift()
const self = this // 当前函数
// 返回一个函数
return function() {
// 执行原函数,并返回结果
return self.apply(t, args)
}
}
代码:
function fn1(a,b,c) {
console.log('this',this)
console.log(a,b,c)
return 'this is fn1'
}
const fn2 = fn1.bind ({x:100},10,20,30)
const res = fn2()
console.log(res)
Chrome
:
fn1.hasOwnProperty('bind')
//false
fn1.proto == Function.prototype
// true
Function.prototype.apply/call/bind
异步,单线程
同步和异步的区别是什么?
手写用
Promise
加载一张图片前端使用异步的场景有哪些?
代码:
// setTimeout 笔试题
console.log(1)
setTimeout(function(){
console.log(2)
},1000)
console.log(3)
setTimeout(function(){
console.log(4)
},0)
console.log(5)
单线程和异步
应用场景
callback hell
和Promise
JS 是单线程语言,同时只能做一件事
浏览器和
nodejs
支持js
启动进程,如web worker
JS 和 dom 渲染共用同一线程。
异步:
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
console.log(300)
同步
console.log(100)
alert(200)
console.log(300)
异步和同步的区别
基于
js
是单线程语言异步不会阻塞代码的执行
同步会阻塞代码的执行
手写用Promise
加载一张图片
function loadImg(src) {
return new Promise(
(resolve, reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
const err = new Error(图片加载失败 ${src}
)
reject(err)
}
img.src = src
}
)
}
const url = ''
loadImg(url).then(img => {
console.log(img.width)
return img
}).then(img => {
console.log(img.height)
}).catch(ex=>console.error(ex))
异步-单线程
单线程和异步,异步和同步区别
前端异步的应用场景
Promise
解决callback hell
异步-callback
异步,以回调callback
函数形式
异步-应用场景
网络请求
定时任务
第一网络请求,如ajax
图片加载,第二定时任务,如setTimeout
。
//ajax
console.log('start')
$.get('./data.json', function(data1){
console.log(data1)
})
console.log('end')
图片的加载
console.log('start')
let img = document.createElement('img')
img.onload = function() {
console.log('loaded')
}
img.src="/xxx.png"
console.log('end')
代码应用场景:
// setTimeout
console.log(100)
setTimeout(function(){
console.log(200)
},1000)
console.log(300)
// setInterval
console.log(100)
setInterval(function(){
console.log(200)
},1000)
console.log(300)
异步-Promise
Promise
代码:
function getData(url){
return new Promise((resolve, reject) => {
$.ajax({
url,
success(data) {
resolve(data)
},
error(err) {
reject(err)
}
})
}
}
Promise
const url1 = '/data1.json'
const url2 = '/data2.json'
const url3 = '/data3.json'
getData(url1).then(data1=>{
console.log(data1)
return getData(url2)
}).then(data2 => {
console.log(data2)
return getData(url3)
}).then(data3 => {
console.log(data3)
}).catch(err => console.error(err))
calback hell
,回调地狱
代码:
// 获取第一个数据
$.get(url1, (data1) => {
console.log(data1)
// 获取第二个数据
$.get(url2, (data2) => {
console.log(data2)
// 获取第三个数据
$.get(url3, (data3) => {
console.log(data3)
// 还可能获取更多的数据
})
})
})
DOM,BOM
Dom 操作,操作网页上的 Dom 元素,浏览器上的文本,图片
Bom 操作,操作浏览器上的一些事情,浏览器的导航,地址等
事件绑定,ajax,存储
DOM,Document Object Model
DOM 是哪种数据结构
DOM 操作的常用 API
attr 和 property 的区别
DOM
的本质,节点操作,结构操作,DOM
性能
xml
是一种可扩展的标记语言,可以描述任何结构的数据,html
是一种特定的xml
。
DOM
的本质,它就是一颗树。
获取
Dom
节点attribute
property
获取DOM
节点
const div1 = document.getElementById('div1') // 元素
const divList = document.getElementsByTagName('div') // 集合
console.log(divlist.length)
console.log(divList[0])
const containerList = document.getElementsByClassName('.container') // 集合
const pList = document.querySelectorAll('p') // 集合
DOM
节点的property
const pList = document.querySelectorAll('p');
const p = pList[0]
console.log(p.style.width) // 获取样式
p.style.width='100px' // 修改样式
console.log(p.className) // 获取 class
p.className='p1' // 修改 class
// 获取 nodeName 和 nodeType
console.log(p.nodeName)
console.log(p.nodeType)
property
和attribute
property
修改对象属性,不会体现到html
结构中
attribute
修改html
属性,会改变html
结构
两种都有可能引起DOM
重新渲染
DOM
结构操作
新增/插入节点
获取子元素列表,获取父元素
删除子元素
新增,插入节点
const div1 = document.getElementById('div1')
// 添加新节点
const p1 = document.createElement('p')
p1.innerHTML = 'this is p1'
div1.appendChild(p1)
const p2 = document.getElementById('p2')
div1.appendChild(p2)
获取子元素列表和获取父元素
//获取子元素列表
const div1 = document.getElementById('div1')
const child = div1.childNodes
//获取父元素
const div1 = document.getElementById('div1')
const parent = div1.parentNode
删除节点
const div1 = document.getElementById('div1')
const child = div1.childNodes
div1.removeChild(child[0])
DOM 性能
DOM
操作会耗费 cpu,避免频繁,对DOM
查询做缓存
DOM
查询做缓存
// 不缓存 DOM 查询结果
for(let=0; i<document.getElementsByTagName('p').length;i++) {
// 每次循环,都会计算 length,频繁进行 dom 查询
}
// 缓存 dom 查询结果
const pList = document.getElementsByTagName('p')
const length = pList.length
for(let i = 0; i<length; i++){
// 缓存 length,只进行一次 dom 查询
}
讲频繁的操作修改为一次性操作
const listNode = document.getElementById('list')
// 创建一个文档片段,此时没有插入到 dom 树中
const frag = document.createDocumentFragment();
// 执行插入
for(let x=0; x<10; x++) {
const li = document.createElement("li")
li.innerHTML="List item"+x
frag.appendChild(li)
}
// 都完成后,再插入到 dom 树中
listNode.appendChild(frag)
问题:
dom
是哪种数据结构dom
操作的常用api
attr
和property
的区别一次性插入多个
dom
节点,考虑性能问题
property
和attribute
的区别
property
修改对象属性,不会体现到html
结构中attribute
修改html
属性,会改变html
结构
两者都有可能引起dom
重新渲染
dom
本质dom
节点操作dom
结构操作dom
性能
BOM
操作browser object model
问题:如何识别浏览器的类型
问题:分析拆解url
各个部分
BOM navigator
代码:
//navigator
const ua = navigator.userAgent
const isChrome = ua.indexOf('Chrome`)
console.log(isChrome)
事件绑定和事件冒泡
事件监听函数
描述事件冒泡的流程
BOM screen
代码:
//screen
console.log(screen.width)
console.log(screen.height)
BOM location
BOM history
ajax
手写一个简易的
ajax
跨域的常用实现方式
ajax XMLHttpRequest
代码:
//get 请求
const xhr = new XMLHttpRequest()
xhr.open('GET','/api', true)
xhr.onreadystatechange = function() {
// 这里的函数异步执行
if (xhr.readyState === 4){
if(xhr.state === 200) {
alert(xhr.responseText);
}
}
}
xhr.send(null)
ajax 状态码
xhr.readyState
0 为还没有调用
send()
方法1 为已调用
send()
方法,正在发送请求2 为
send()
方法执行完成,已经接收到全部响应内容3 为正在解析响应内容
4 为响应内容解析完成,可以在客户端调用
xhr.status
2xx 表示成功处理请求
3xx 表示需要重定向,浏览器直接跳转
4xx 表示客户端请求错误
5xx 表示服务器端错误
ajax 跨域
什么是跨域,同源策略
JSONP
CORS,服务器端支持
同源策略-跨域
ajax
请求时,浏览器要求当前网页和server
必须同源
同源就是:协议,域名,端口,一致
代码
function ajax(url) {
const p = new Promise((resolve, reject)=>{
const xhr = new XMLHttpRequest()
xhr.open('GET',url,true)
xhr.onreadystatechange=function(){
if(xhr.readyState === 4){
if(xhr.status===2000) {
resolve(
JSON.parse(xhr.responseText)
)
}else if(xhr.status === 404) {
reject(new Error('404 not found'))
}
}
}
xhr.send(null)
}
}
事件
事件绑定
事件冒泡
事件代理
事件-绑定
代码:
const btn = document.getElementById('btn1')
btn.addEventListener('click',event=>{
console.log('clicked')
})
代码:
//通用的绑定函数
function bindEvent(elem, type, fn)
elem.addEventListener(type,fn)
}
const a = document.getElementById('web')
bindEvent(a, 'click', e=>{
e.preventDefault()
alert('clicked')
})
事件-冒泡
代码:
<body>
<div id="div1">
<p id="p1">web</p>
<p id="p2">it</p>
<p id="p3">it</p>
<p id="p4">it</p>
</div>
<div id="div2">
<P id="p5">it</p>
<p id="p6">it</p>
</div>
</body>
const p1 = document.getElementById('p1')
const body = document.body
bindEvent(p1, 'click', e=>{
e.stopPropagation() // 阻止冒泡
alert('web')
})
bindEvent(body, 'click', e=>{
alert('it')
})
事件-代理
代码:
<div id="div1">
<a href="#">a1</a>
<a href="#">a2</a>
</div>
<button>点击
</button>
const div1 = document.getElementById('div1')
div1.addEventListener('click', e=>{
const target = e.target
if(e.nodeName === 'A') {
alert(target.innerHTML)
}
})
事件代理,可以减少浏览器内存占用
通用的事件绑定函数
const btn1 = document.geteElementById('btn1')
bindEvent(btn1,'click',event=>{
// console.log(event.target) // 获取触发的元素
event.preventDefault() // 阻止默认行为
alert('clicked')
})
const div2 = document.getElementById('div2')
bindEvent(div2,'click',event=>{
event.preventDefault()
const target = event.target
if(target.nodeName === 'A') {
alert(target.innerHTML)
}
})
代码:
function bindEvent(elem, type, selector, fn) {
if(fn==null) {
fn = selector
selector = null
}
elem.addEventListener(type, e=>{
let target
if(selector) {
// 需要代理
target = e.target
if(target.matches(selector)){
fn.call(target, e)
}
} else {
// 不需要代理
fn(e)
}
})
}
存储
cookie
,localStorage
,sessionStorage
区别
localStorage
数据会永远存储,除非代码回手动删除sessionStorage
数据只存在于当前会话,浏览器关闭则清空
一般用localStorage
会更多一些
存储-cookie
存储大小,最大 4kb
http
请求时需要发送到服务器端,增加请求数据量
localStorage 和 sessionStorage
html5
专门为存储而设计的,最大可为 5Mapi
简单易用setItem
,getItem
不会随着http
请求被发送出去
存储-localStorage
代码:
localStorage.setItem('a',100)
localStorage.getItem('a')
存储-sessionStorage
代码:
sessionStorage.setItem('a',100)
sessionStorage.getItem('a')
开发环境
git
调试工具
抓包
webpack babel
linux 常用命令
git
大型项目需要多人协作开发,必用 git
代码:常用 git 命令
git add .
git checkout xxx
git commit -m 'xxx'
git push origin master
git pull origin master
git branch
git checkout -b xxx / git checkout xx
git merge xxx
调式
chrome 调试工具
Elements
Network
Console
Application
debugger
调式,抓包
移动端 h5 页,查看网络请求,需要用工具抓包
windows
一般用fiddler
webpck 和 babel
代码:
const path = require('path')
module.exports = {
mode: 'development',
entry: path.join(__dirname, 'src', 'index.js'),
output: {
filename: 'bundle.js',
path: path.join(__dirname,'dist')
}
}
linux 命令
线上机器一般都是 linux
代码:
ssh work
ls
rm -rf abc
cd dist
mv index.html index1.html
rm a1.js
touch d.js
rm -rf d.js
运行环境
运行环境既浏览器
server
端有nodejs
网页加载过程
性能优化
安全
页面加载和渲染过程
从输入
url
到渲染出页面的整个过程window.onLoad 和 DOMContentLoaded 的区别
加载资源的形式
加载资源的过程
渲染页面的过程
资源的形式:html
代码,媒体文件,如图片,视频等,javascript
,css
。
加载过程:dns
解析,域名,ip
地址。浏览器根据ip
地址向服务器发起http
请求。
服务器处理http
请求,并返回给浏览器。
渲染过程,根据html
代码生成dom tree
,根据css
代码生成cssom
。讲dom tree
和cssom
整合行程render tree
。
根据render tree
渲染页面,遇到script
暂停渲染,优先加载并执行js
代码,完成再继续。
页面渲染
window.onLoad
和DOMContentLoaded
代码:
window.addEventListener('load',function(){
// 页面的全部资源加载完才会执行,包括图片,视频等
})
document.addEventListener('DOMContentLoad',function(){
// dom 渲染完既可执行,此时图片,视频还可能没有加载完
})
window.onload
资源全部加载完才能执行,包括图片DOMContentLoaded DOM
渲染完成即可,图片可能还没下载
性能优化
手写防抖,节流
原则:
多使用内存,缓存或其他方法
减少 cpu 计算量,减少网络加载耗时
让加载更快,渲染更快,减少资源体积。减少访问次数,合并代码,ssr
服务器端渲染,缓存。
对dom
查询进行缓存,频繁dom
操作,合并到一起插入dom
结构,节流throttle
防抖debounce
。
ssr
服务器端渲染,讲网页和数据一起加载,一起渲染
非 ssr,先加载网页,再加载数据,再渲染数据
懒加载
代码:
<img id="img1" src="preview.png" data-realsrc="abc.png"/>
<script type="text/javascript">
var img1 = document.getElementById('img1')
img1.src = img1.getAttribute('data-realsrc')
</script>
防抖 debounce
监听一个输入框,文字变化后触发 change 事件
直接用 keyup 事件,则会频繁触发 change 事件
防抖,用户输入结束或暂停时,才会触发 change 事件。
代码:
const input1 = document.getElementById('input1')
input1.addEventListener('keyup', function(){
console.log(input1.value)
}}
代码:
const input1 = document.getElementById('input1')
let timer = null
input1.addEventListener('keyup', function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
// 模拟触发 change 事件
console.log(input1.value)
// 清空定时器
timer = null
},500)
})
debounce 防抖代码:
function debounce(fn, delay = 500) {
// timer 是闭包中的
let timer = null
return function() {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(this, arguments)
timer = null
},delay)
}
}
input1.addEventListener('keyup', debounce(()=>{
console.log(input1.value)
}),600)
节流throttle
拖拽一个元素时,要随时拿到该元素被拖拽的位置
代码:
const div1 = document.getElementById('div1')
let timer = null
div1.addEventListener('drag',function(e){
if(timer){
return
}
timer = setTimeout(()=>{
console.log(e.offsetX, e.offsetY)
timer = null
},100)
})
节流代码:
function throttle(fn, delay = 100) {
let timer = null
return function() {
if(timer) {
return
}
timer = setTimeout(()=>{
fn.applay(this,arguments)
timer = null
},delay)
}
}
div1.addEventListener('drag',throttle(function(e){
console.log(e.offsetX,e.offsetY)
},200))
web 安全
前端 web 常见的攻击方式有哪些?
xss 跨站请求攻击
xsrf 跨站请求伪造
运行环境
页面加载:加载,渲染
性能优化:加载资源优化,渲染优化
安全:xss,csrf
总结
什么是变量提升
var 和 let const 的区别
typeof 返回哪些类型
举例强制类型转换和隐式类型转换
var 和 let const 的区别
var 是 es5 的语法,let const 是 es6 语法,var 有变量提升
var 和 Let 是变量,const 是常量,不可修改
let const 有块级作用域,var 没有
object 和 function
强制类型转换和隐式类型转换
强制:parseInt,parseFloat,toString 等
isEqual
手写深度比较
split()和 join()的区别
数组,pop,push,unshift,shift
lodash.isEqual
//实现如下效果
const obj1 = {a:10, b:{x:100,y:200}}
const obj2 = {a:10, b:{x:100,y:200}}
isEqual(objq,obj2) === true
判断是否是对象或数组
代码:
function isObject(obj){
return typeof ojb === 'object' && obj !== null
}
// 全相等
function isEqual(obj1,obj2){
if(!isObject(obj1) || !isObject(obj2)) {
// 值类型
return obj1===obj2
}
if(obj1===obj2){
return true
}
// 两个都是对象或数组,而且不相等
const obj1key = Object.keys(obj1)
const obj2key = Object.keys(obj2)
if(obj1key.length !== obj2key.length){
return false
}
for(let key in obj1){
const res = isEqual(obj1[key],obj2[key])
if(!res){
return false
}
}
return true
}
split()和 join()的区别
'1-2-3'.split('-') // [1,2,3]
[1,2,3].join('-') // '1-2-3'
数组的 pop,push,unshift,shift
功能分别是什么,返回值是什么,有什么影响。
pop 返回删除的最后一个值
push 返回追加后元素的长度
unshift 插入到最前面,返回长度 length
shift 删除最前面的,返回删除的值
pop,shift-》返回值
unshift, push-》length
数组 slice 和 splice 的区别
[10,20,30].map(parseInt)返回结果
ajax 请求 get 和 post 的区别
get 用于查询,post 用于提交
get 参数拼接在 url 上,post 放在请求体内
call 和 apply 的区别
代码:
fn.call(this, p1,p2,p3)
fn.apply(this, argument)
事件代理是什么
代码:
const p1 = document.getElementById('p1')
const body = document.body
bindEvent(p1, 'click', e=>{
e.stopProgation();
alert()
})
bindEvent(body, 'click', e=>{
alert()
})
闭包是什么,有什么特性,有什么负面影响
作用域和自由变量
自由变量的查找,要在函数定义的地方,不是执行的地方
闭包不要乱用,变量会常驻内容,不会释放
闭包:
function create() {
let a=100
return function(){
console.log(a)
}
}
let fn = create()
let a=200
fn() // 100
如何阻止事件冒泡和默认行为?
查找,添加,删除,移动 dom 节点的方法
如何减少 dom 操作
event.stopPropagation()
event.preventDefault()
代码:
// 新建节点
const newP = document.createElement('p')
newP.innerHTML = 'this is newP'
// 插入节点
div1.appendChild(newP)
// 移动节点
const p1 = document.getElementById('p1')
div2.appendChild(p1)
//获取父元素
console.log(p1.parentNode)
如何减少 dom 操作
缓存 dom 查询结果
多次 dom 操作,合并到一次插入
代码:
const list = document.getElementById('list')
const frag = document.createDocumentFragment()
for(let i=0; i<20; i++){
const li = document.createElement('li')
li.innerHTML = 'list'
frag.appendChild(li)
}
list.appendChild(frag)
jsonp 的原理,为什么它不是真正的 ajax
浏览器的同源策略和跨域
document load 和 ready 的区别
load:
页面的区别资源加载完才会执行
ready:
dom 渲染完既可执行,图片,视频等还没有加载完
函数声明和函数表达式的区别
函数声明
function fn(){...}
函数表达式:
const fn = function(){...}
函数声明会预加载,而函数表达式不会
new Object()和 Object.create()的区别
{}
等同new Object()
,原型 Object.prototype
场景题 1
代码:
let i
for (i = 1; i <= 3; i++) {
setTimeout(function(){
console.log(i)
},0)
}
场景 2
代码:
let a = 100
function test() {
alert(a)
a = 10
alert(a)
}
test()
alert(a)
场景 3
手写字符串 trim 方法,保证浏览器兼容性
用 Js 实现继承
场景 4
如何捕获 Js 程序中的异常
什么是 json
获取当前页面 url 参数
代码:
try {
// todo
}catch(error) {
console.error(error)
}finally{
}
json 是一种数据格式,本质是一段字符串,json 格式和 js 对象结构一致。
window.JSON
是一个全局对象,JSON。stringify
,JSON.parse
获取当前页面 url 参数
传统方式,查找location.search
新api,URLSearchParams
场景 5
讲 url 参数解析为 js 对象
手写数组 flatern
数组去重
代码:
// 1
function queryToObj(){
const res={}
const search = location.search.substr(1)
search.split('&').forEach(res => {
const arr = res.split('=')
const key = arr[0]
const key = arr[1]
res[key] = val
})
}
使用 URLSearchParams
function queryToObj() {
const res = {}
const pList = new URLSearchParams(location.search)
pList.forEach((val,key) => {
res[key] = val
})
return res
}
手写数组 flatern
代码:
function flat(arr) {
const isDeep = arr.some(item=> item instanceof Array)
if(!isDeep) {
return arr
}
const res = Array.prototype.concat.apply([],arr)
return flat(res)
}
数组去重
代码:
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]
}
场景 6
手写深拷贝
使用
RAF requestAnimateFrame
前端性能的优化
代码:
function deepClone(obj = {}) {
if(type of obj !== 'object' || obj == null){
return obj
}
let result
if(obj instanceof Array) {
result = []
}else{
result = {}
}
for(let key in obj){
if(obj.hasOwnProperty(key)){
result[key] = deepClone(obj[key])
}
}
return result
}
Object.assign
不是深拷贝!
性能优化
原则:多使用内存,缓存,减少计算,网络请求
加载页面,页面渲染,页面操作等多多思考问题。
❤️关注+点赞+收藏+评论+转发❤️,原创不易,鼓励笔者创作更好的文章
点赞、收藏和评论
我是Jeskson
(达达前端),感谢各位人才的:点赞、收藏和评论,我们下期见!(如本文内容有地方讲解有误,欢迎指出☞谢谢,一起学习了)
我们下期见!
文章持续更新,可以微信搜一搜「 程序员哆啦 A 梦 」第一时间阅读,回复【资料】有我准备的一线大厂资料,本文 http://www.dadaqianduan.cn/#/ 已经收录
github
收录,欢迎Star
:https://github.com/webVueBlog/WebFamily
版权声明: 本文为 InfoQ 作者【魔王哪吒】的原创文章。
原文链接:【http://xie.infoq.cn/article/bbb97a0ea25b74cd363df82d9】。未经作者许可,禁止转载。
评论