写点什么

记录一次 BAT 一线互联网公司前端 JavaScript 面试

用户头像
魔王哪吒
关注
发布于: 2021 年 02 月 08 日
记录一次BAT一线互联网公司前端JavaScript面试

Github来源:一个合格的初级前端工程师需要掌握的模块笔记 | 求星星 ✨ | 给个❤️关注,❤️点赞,❤️鼓励一下作者



大家好,我是魔王哪吒,很高兴认识你~~



哪吒人生信条:如果你所学的东西 处于喜欢 才会有强大的动力支撑



每天学习编程,让你离梦想更新一步,感谢不负每一份热爱编程的程序员,不论知识点多么奇葩,和我一起,让那一颗四处流荡的心定下来,一直走下去,加油,2021加油!欢迎关注加我vx:xiaoda0423,欢迎点赞、收藏和评论



不要害怕做梦,但是呢,也不要光做梦,要做一个实干家,而不是空谈家,求真力行。

大家好,我是魔王哪吒,话不多说,今天带来的是一篇《记录一次 BAT 一线互联网公司前端 JavaScript 面试》文章,欢迎大家喜欢。前沿


置身世外只为暗中观察!!!Hello 大家好,我是魔王哪吒!


面试内容


你需要一些 HTML 和 css 的基础知识,掌握 JavaScript 和 ES6 的基本语法,对事物的好奇心。


  • 初级 JS 面试题

  • JS Web API,开发环境,运行环境

  • 原型,作用域,异步,Ajax,事件,webpack 等


观察所有面试题的考点


  1. 学习梳理知识体系图

  2. 找准知识体系,刻意掌握

  3. 善于总结观点,原理


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


类型转换


  1. 除了0之外的所有数字,转换为布尔型都为true

  2. 除了“ ”之外的所有字符,转换为布尔型都为true

  3. nullundefined转换为布尔型为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


手写深拷贝


  1. 判断是值类型还是引用类型

  2. 判断是数组还是对象

  3. 递归


代码:


// 浅拷贝

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


原型,原型链


  1. 如何判断一个变量是不是数组?

  • a instanceof Array


  1. 手写一个简易的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(){

}

}


  1. 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

  • 扩展,重写方法


原型


原型关系:


  1. 每个class都有显示原型prototype

  2. 每个实例都有隐式原型__proto__

  3. 实例的__proto__指向对应classprototype


原型链


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)

}


作用域


作用域分:


  1. 全局作用域

  2. 函数作用域

  3. 块级作用域(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


闭包


闭包的表现:


  1. 函数作为参数被传递

  2. 函数作为返回值被返回


做一个简单的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


  1. 作为普通函数被调用

  2. 使用call,apply,bind被调用

  3. 作为对象方法被调用

  4. class方法中被调用

  5. 箭头函数


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


异步,单线程


  1. 同步和异步的区别是什么?

  2. 手写用Promise加载一张图片

  3. 前端使用异步的场景有哪些?


代码:


// setTimeout 笔试题

console.log(1)

setTimeout(function(){

console.log(2)

},1000)

console.log(3)

setTimeout(function(){

console.log(4)

},0)

console.log(5)


  1. 单线程和异步

  2. 应用场景

  3. callback hellPromise


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)


异步和同步的区别


  1. 基于js是单线程语言

  2. 异步不会阻塞代码的执行

  3. 同步会阻塞代码的执行


手写用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))


异步-单线程


  1. 单线程和异步,异步和同步区别

  2. 前端异步的应用场景

  3. Promise解决callback hell


异步-callback


异步,以回调callback函数形式


异步-应用场景


  1. 网络请求

  2. 定时任务


第一网络请求,如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


  1. DOM 是哪种数据结构

  2. DOM 操作的常用 API

  3. 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)


propertyattribute


property修改对象属性,不会体现到html结构中


attribute修改html属性,会改变html结构


两种都有可能引起DOM重新渲染


DOM结构操作


  1. 新增/插入节点

  2. 获取子元素列表,获取父元素

  3. 删除子元素


新增,插入节点


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)


问题:


  1. dom是哪种数据结构

  2. dom操作的常用api

  3. attrproperty的区别

  4. 一次性插入多个dom节点,考虑性能问题


propertyattribute的区别


  1. property修改对象属性,不会体现到html结构中

  2. 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)


事件绑定和事件冒泡


  1. 事件监听函数

  2. 描述事件冒泡的流程


BOM screen


代码:


//screen

console.log(screen.width)

console.log(screen.height)


BOM location


BOM history


ajax


  1. 手写一个简易的ajax

  2. 跨域的常用实现方式


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 跨域


  1. 什么是跨域,同源策略

  2. JSONP

  3. 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)

}

})

}


存储


cookielocalStoragesessionStorage区别


  1. localStorage数据会永远存储,除非代码回手动删除

  2. sessionStorage数据只存在于当前会话,浏览器关闭则清空


一般用localStorage会更多一些


存储-cookie


  • 存储大小,最大 4kb

  • http请求时需要发送到服务器端,增加请求数据量


  1. localStorage 和 sessionStorage

  2. html5专门为存储而设计的,最大可为 5M

  3. api简单易用setItemgetItem


不会随着http请求被发送出去


存储-localStorage


代码:


localStorage.setItem('a',100)

localStorage.getItem('a')


存储-sessionStorage


代码:


sessionStorage.setItem('a',100)

sessionStorage.getItem('a')


开发环境


  1. git

  2. 调试工具

  3. 抓包

  4. webpack babel

  5. 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 调试工具


  1. Elements

  2. Network

  3. Console

  4. Application

  5. 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


运行环境


  1. 运行环境既浏览器server端有nodejs

  2. 网页加载过程

  3. 性能优化

  4. 安全


页面加载和渲染过程


  1. 从输入url到渲染出页面的整个过程

  2. window.onLoad 和 DOMContentLoaded 的区别


  • 加载资源的形式

  • 加载资源的过程

  • 渲染页面的过程


资源的形式:html代码,媒体文件,如图片,视频等,javascriptcss


加载过程:dns解析,域名,ip地址。浏览器根据ip地址向服务器发起http请求。


服务器处理http请求,并返回给浏览器。


渲染过程,根据html代码生成dom tree,根据css代码生成cssom。讲dom treecssom整合行程render tree


根据render tree渲染页面,遇到script暂停渲染,优先加载并执行js代码,完成再继续。


页面渲染


window.onLoadDOMContentLoaded


代码:


window.addEventListener('load',function(){

// 页面的全部资源加载完才会执行,包括图片,视频等

})

document.addEventListener('DOMContentLoad',function(){

// dom 渲染完既可执行,此时图片,视频还可能没有加载完

})


  1. window.onload资源全部加载完才能执行,包括图片

  2. DOMContentLoaded DOM渲染完成即可,图片可能还没下载


性能优化


手写防抖,节流


原则:


  1. 多使用内存,缓存或其他方法

  2. 减少 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


  1. 监听一个输入框,文字变化后触发 change 事件

  2. 直接用 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 安全


  1. 前端 web 常见的攻击方式有哪些?


  1. xss 跨站请求攻击

  2. xsrf 跨站请求伪造


运行环境


  1. 页面加载:加载,渲染

  2. 性能优化:加载资源优化,渲染优化

  3. 安全:xss,csrf


总结


什么是变量提升


  1. var 和 let const 的区别

  2. typeof 返回哪些类型

  3. 举例强制类型转换和隐式类型转换


var 和 let const 的区别


var 是 es5 的语法,let const 是 es6 语法,var 有变量提升


var 和 Let 是变量,const 是常量,不可修改


let const 有块级作用域,var 没有





object 和 function


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


强制:parseInt,parseFloat,toString 等


isEqual


  1. 手写深度比较

  2. split()和 join()的区别

  3. 数组,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



  1. 数组 slice 和 splice 的区别

  2. [10,20,30].map(parseInt)返回结果

  3. 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()

})


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


  1. 作用域和自由变量


自由变量的查找,要在函数定义的地方,不是执行的地方


闭包不要乱用,变量会常驻内容,不会释放


闭包:


function create() {

let a=100

return function(){

console.log(a)

}

}

let fn = create()

let a=200

fn() // 100


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

  2. 查找,添加,删除,移动 dom 节点的方法

  3. 如何减少 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 操作


  1. 缓存 dom 查询结果

  2. 多次 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


  1. 如何捕获 Js 程序中的异常

  2. 什么是 json

  3. 获取当前页面 url 参数


代码:


try {

// todo

}catch(error) {

console.error(error)

}finally{

}


json 是一种数据格式,本质是一段字符串,json 格式和 js 对象结构一致。


window.JSON是一个全局对象,JSON。stringifyJSON.parse


获取当前页面 url 参数


传统方式,查找location.search


api,URLSearchParams


场景 5


  1. 讲 url 参数解析为 js 对象

  2. 手写数组 flatern

  3. 数组去重


代码:

// 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


  1. 手写深拷贝

  2. 使用RAF requestAnimateFrame

  3. 前端性能的优化


代码:


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收录,欢迎Starhttps://github.com/webVueBlog/WebFamily

发布于: 2021 年 02 月 08 日阅读数: 41
用户头像

魔王哪吒

关注

微信搜:程序员哆啦A梦 2018.05.08 加入

面向JavaScript爱好人员提供:Web前端最新资讯、原创内容、JavaScript、HTML5、Ajax、jQuery、Node.js、Vue.js、React、Angular等一系列教程和经验分享。 博客首发:http://www.dadaqianduan.cn/#/

评论

发布
暂无评论
记录一次BAT一线互联网公司前端JavaScript面试