看到题目,想到考点
JS知识体系面试知识点
变量类型和计算
javascript允许直接对变量赋值而无需事先声明,但声明变量是一个良好的习惯。
命名
1. 类:以大写字母开头
2. 方法:以小写字母开头
3. 常量名全部大写
4. 通常运算符 ( = + - * / ) 前后需要添加空格
JS中分号的使用
一行开头是括号
或者方括号
的时候加上分号就可以了,其他时候全部不需要
var let const的区别?
1.使用var声明的变量,函数作用域,且存在变量提升现象;在全局作用域中声明会成为window对象的属性。
2.使用let声明的变量,块作用域(函数作用域的子集),不存在变量提升;在全局作用域中声明不会成为window对象的属性。
3.使用const声明基本和let相同,声明变量时必须初始化变量,修改变量时会报错。若const引用对象,则此对象内部属性可以修改。
var o1={b:1}
实现了在堆内存中创建了一个对象{b:1},o1则存储了该对象在堆内存中的地址,即o1是一个指针,指向{b:1};
箭头函数=>
在箭头函数内使用this,是在使用它的上级作用域的this
值类型和引用类型
值类型存储直接保存在栈内存中,值与值之间是独立存在,修改一个变量不会影响其他的变量。
1.数值(number):整数和小数(比如1和3.14)
2.字符串(string):文本(比如Hello World),JavaScript里用 \(反斜杠)对字符进行转义。
3.布尔值(boolean):表示真伪的两个特殊值,即true(真)和false(假)
4.undefined:表示“未定义”或不存在,即由于目前没有定义,所以此处暂时没有任何值
5.null:表示空值,即此处的值为空。
6.sumbol:表示符号,即此处的值为空。
7.Object:表示符号,即此处的值为空。
引用类型存储是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间。变量保存的是对象的内存地址(对象的引用)。
- 对象(object):各种值组成的集合。(如果两个变量保存的是同一个对象引用,当一个通过一个变量修改属性时,另一个也会受到影响),JavaScript中所有变量都是某种类型的对象。
//值类型
let a=1
let b=a
b=100
console.log(a)//1
//引用类型
let a={age:25}//{}是一个对象
let b=a
b.age=100
console.log(a.age)//100
手写深拷贝
Object.assign不是深拷贝!!!
/**
* 深拷贝
*/
const obj1 = {
age: 20,
name: 'xxx',
address: {
city: 'beijing'
},
arr: ['a', 'b', 'c']
}
const obj2 = deepClone(obj1)
obj2.address.city = 'shanghai'
obj2.arr[0] = 'a1'
console.log(obj1.address.city)
console.log(obj1.arr[0])
/**
* 深拷贝
* @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)) {//hasOwnProperty:拥有自身的属性
// 递归调用!!!
result[key] = deepClone(obj[key])
}
}
// 返回结果
return result
}
类型判断
typeof 能判断哪些类型?
考点:JS的变量类型
(1)typeof能识别所有值类型
(2)识别函数:function
(3)判断是否是引用类型:object
字符串拼接
const a = 100 + 10 //110
const 100 + parseInt('10')//110
const b = 100 + '10'//'10010'
const c = true + '10'//'true10 '
==何时使用 = = = 何时使用 = ===
考点:强制类型转换
//只有判断==null之外,其余一律用===
100 == '100'//true
0 == ''//true
0 == false //true
false == ''//true
null == undefined //true
逻辑运算
if语句和逻辑运算
const n = 100;
!n//false
!!n//true
原型和原型链
原型和原型链
1.每个class都有显示原型属性:prototype
:构造函数有一个prototype属性,指向实例对象的原型对象
2.每个实例都有隐式原型:__ proto__,
:指向该实例对象对应的原型对象
3.原型对象有一个constructor属性
: 指向该原型对象对应的构造函数
4.获取属性或者执行方法时,先在自身属性和方法中寻找,如果找不到则自动去proto__中寻找,最终都继承于Object对象,Object对象的没有原型,如果在Object原型中依然没有找到,则返回 null
class的原型本质,怎么理解?
instanceof
isPrototypeOf()
方法用于测试一个对象是否存在于另一个对象的原型链上。
instanceof
运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype
属性。
如何准确判断一个变量是不是数组
a instanceof Array
class和继承
//父类:People
class People{
constructor(name){
this.name=name
};
eat(){
console.log(`${this.name}要吃东西`)
};
};
//子类:Student
class Student extends People {
constructor(name,number){
super(name)
this.number=number
};
sayHi(){
//建议这么写输出
console.log(
`姓名 ${this.name}上课要说你好`
);
//还可以用字符串拼接的方式
console.log(
'姓名' + this.name +',学号'+this.number
);
};
};
//子类:Teacher
class Teacher extends People{
constructor(name,major){
super(name)
this.major = major
};
teacher(){
console.log(`${this.name}教授${this.major}`)
}
}
//通过类声明对象/实例
const luojing = new Student('罗京',21907010041)
console.log(luojing.name)
console.log(luojing.number)
console.log(luojing.sayHi())
luojing.eat()
//通过类声明对象/实例
const zhangsan = new Teacher('zhangsan','数学')
console.log(zhangsan.name)
console.log(zhangsan.number)
console.log(zhangsan.teacher())
zhangsan.eat()
//隐式原型和显示原型
console.log(luojing.__proto__)//__proto__:隐式原型
console.log(Student.prototype )// prototype:显示原型
console.log(luojing.__proto__===Student.prototype)//true
手写一个简易的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 ele =this[i]
fn(elem)
};
};
on(type,fn){
return this.each(elem =>{
elem.addEventListener(type,fn,false)
});
};
};
//插件
jQuery.prototype.dialog=function(){
alert(info)
}
//复写(造轮子)
class myJQuery extends jQuery{
constructor(selector){
super(selector)
}
//扩展自己的方法
addClass(className){
};
style(data){
}
}
作用域和闭包
this
this取什么值是在函数执行的时候确认的,不是在定义的地方确认的
this的不同应用场景,如何取值
使用场景:
1.作为普通函数被调用
2.使用call apply bind
3.作为对象方法被调用
4.在class方法中被调用
5.箭头函数
//场景1
function fn1(){
console.log(this)
}
fn1()//windows
fn1.call({x:100})//x:100
const fn2 = fn1.bind({x:200})
fn2()//{x:200}
//场景2
const zhangsan={
name:'张三'
sayHi(){
//this即是当前zhangsan对象
console.log(this)
};
wait(){
setTimeout(function(){
//this===window
console.log(this)
})
}
}
//场景3
const zhangsan={
name:'张三'
sayHi(){
//this即是当前对象
console.log(this)
};
waitAgain(){
setTimeout(()=>{
//this即当前对象
console.log(this)
})
}
}
手写bind函数
function fn1(a,b){
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)
//模拟bind
Function.prototype.bind1=function(){
//将参数拆解为数组
const args=Array.prototype.slice.call(arguments)
//获取this(数组第一项)
const t = args.shift()
//fn1.bind(...)中的fn1
const self =this()
//返回一个函数
return function(){
return self.apply(t,args)
}
}
实际开发中闭包的应用场景,举例说明
//创建10个'<a>'标签,点击时弹出对应的序号
let a
for(let 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)
}
作用域和自由变量
一个变量在当前作用域没有定义,但被使用了,向上级作用域,一层一层依次寻找,直至找到为止。如果到了全局作用域都没有找到,则报错xx is not defined
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()
闭包
作用域应用的特殊情况,有两种表现:
1.函数作为参数被传递。
2.函数作为返回值被返回
。
//函数作为返回值
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
//闭包:自由变量的查找,是在函数定义的地方,向上级作用域查找,不是在执行的地方!
开发中闭包的应用
隐藏数据
做一个简单的cache工具
//闭包隐藏数据,只提供API
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'))
异步和单线程
同步和异步的区别?
JS是单线程语言,只能同时做一件事,异步是解决单线程的等待问题。每个异步都需要加一个callback函数。异步不会阻塞代码执行。
手写Promise加载一张图片
//promise图片加载
console.log('start')
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='https://baike.baidu.com/pic/%E5%88%98%E4%BA%A6%E8%8F%B2/136156/21228350/f703738da977391281207810f6198618367ae2b9'//图片地址
loadImg(url).then(img=>{
console.log(img.width)
return img
}).then(img=>{
console.log(img.heught)
}).catch(ex=>console.error(ex))
前端使用异步的场景
//setTimeout笔试题,输出顺序13452
console.log(1)
setTimeout(function(){
console.log(2)
},1000)
console.log(3)
setTimeout(function(){
cnsole.log(4)
},0)
console.log(5)
应用场景
网络请求,如ajax图片加载
定时任务,如setTimeout
//ajax
console.log('start')
$.get('./data1.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(1)
setTimeout(function(){
console.log(2)
},1000)
console.log(3)
//定时器setInterval,(循环)
console.log(1)
setInterval(function(){
console.log(2)
},1000)
console.log(3)
callback和Promise
//callback hell,嵌套,有很多层
//获取第一份数据
$.get(url1,(data1)=>{
console.log(data1)
//获取第二份数据
$.get(url2,(data2)=>{
console.log(data2)
//获取第三份数据
$.get(url3,(data3)=>{
console.log(data3)
//还可以获取更多数据
})
})
})
// Promise,通过then就变成按顺序执行
function getData(url){
return new Promise((resolve,reject)=>{
$.ajax({
url,
success(data){
resolve(data)
},
error(err){
reject(err)
}
})
})
}
const url1='/data1.json'
const url2='/data2.json'
const url3='/data3.json'
getData(url1).then(data1=>{
console.log(data1)
return getData(url2)
}).then(data2=>{
sonsole.log(data2)
return getData(url3)
}).then(data3=>{
console.log(data3)
}).catch(err=>console.error(err))
JS-Web-API
DOM是哪种数据结构
树形结构
DOM操作的常用API
DOM节点操作,DOM结构操作
Vue和React框架应用广泛,封装了DOM操作。api:应用程序编程接口(预先定义好的函数)
DOM
DOM的本质
DOM是一棵树型结构。
DOM的节点操作
获取DOM节点
//获取DOM节点
//方式一:通过 id 获取一个元素节点
var div1 = document.getElementById("box1");
//方式二:通过 标签名 获取 元素对象的集合 以数组形式存储,所以有s
var arr1 = document.getElementsByTagName("div");
//方式三:通过 类名 获取 元素节点数组,所以有s
var arr2 = document.getElementsByClassName("hehe");
//方式四:返回指定选择器的所有元素对象。".box"说明"box"是一个类;"#box"说明"box"是一个id
var randomName=document.querySelectorAll('.box');
//返回指定选择器的第一个元素对象。".box"说明"box"是一个类;"#box"说明"box"是一个id h5以上才支持
var randomName=document.querySelector('.box');
//获取html元素
var htmlEle = document.documentElement;
//获取body元素
var bodyEle = document.body;
属性
attr和property的区别
property修改对象属性,不会体现在html结构中(尽量使用)。attribute修改html属性,会改变html结构。两者都有可能引起DOM重新渲染
property
通过修改js对象的属性来改变页面样式的一种形式,不会体现到html结构中(尽量使用)
const pList =document.querySelectorAll('p')
const p= pList[0]
//property形式
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)
attribute
直接修改html标签的属性,会改变html结构(尽量不要用)
//attribute形式
const pList = document.querySelectorAll('p')
const p=plist[0]
p.getAttribute('data-name')
p.setAttribute('data-name','imooc')
p.getAttribute('style')
p.setAttribute('style','font-size:30px')
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操作非常“昂贵”,避免频繁的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查询
}
对DOM查询做缓存
一次性插入多个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)
BOM
问题:
如何识别浏览器的类型?
如何拆解url各个部分?
//navigator
const ua =navigator.userAgent
const isChrome =ua.indexOf('Chrome')
console.log(isChrome)
//screen
console.log(screem.width)
sonsole.log(screen.height)
//location
console.log(location.href)
console.log(location.protocol)
console.log(location.pathname)
console.log(location.search)
console.log(location.hash)
//history
history.back()
history.forward()
navigator
浏览器信息
screen
屏幕信息
location
地址信息
history
前进后退
事件
题目:
编写一个通用的事件监听函数?
描述事件冒泡的流程?
事件冒泡是基于DOM树形结构,事件会顺着触发元素往上冒泡,应用场景就是事件代理
无限下拉的图片列表,如何监听 每个图片的点击?
使用事件代理,用e.target获取触发元素。用matches来判断是否是触发元素
事件绑定
const btn = document.getElementById('btn1')
btn.addEventListener('click',event=>{
console.log('clicked')
})
//通用的绑定函数
function bindEvent(elem,type,fn){
elem.addEventListener(type,fn)
}
//通过id来绑定
const a= document.getElementById('link1')
bindEvent(a,'click',event =>{
event.preventDefault()//阻止默认行为
alert('clicked')
})
事件冒泡
当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window
const p1 =document.getElementById('p1')
const body = document.body
bindEvent(p1,'click',e=>{
e.stopPropagation()//阻止事件冒泡
alert('激活')
})
bindEvent(body,'click',e=>{//在body处监听事件
alert('取消')
})
事件代理
基于事件冒泡
<div id="div1">
<a herf="#">a1</a>
<a herf="#">a2</a>
<a herf="#">a3</a>
<a herf="#">a4</a>
</div>
<button>
点击增加一个a标签
</button>
function bindEvent(elem,type,selector,fn){
if(fn==null){
fn=selector
selector=null
}
}
elem.addEventListener(type,event=>{
const target = event.target
if(selector){
//代理绑定
if(target.matches(selector)){
fn.call(target,event)
}
}else{
//普通绑定
fn.call(target,event)
}
})
//普通绑定
const a= document.getElementById('link1')
bindEvent(a,'click',event =>{
event.preventDefault()//阻止默认行为
alert('clicked')
})
//代理绑定
const div1 = document.getElementById('div1')
bindEvent(div1,'click','a',event=>{
event.preventDefault()//阻止默认行为
alert(target.innerHTML)
})
ajax
手写一个简易的ajax-
function ajax(url,successFn){
const xhr = new XMLHttpRequest()
xhr.open("GET",url,true)
xhr.onreadystatechange = function(){
if(xhr.readState === 4){
if(xhr.status === 200){
alert(xhr.responseText)
}
}
}
xhr.send(null)
}
XMLHttpRequsetAPI
//get请求
const xhr =new XMLHttpRequest()
xhr.open("GET","/api",true)// true代表异步请求;
xhr.onreadystatechange = function(){
//这里的函数是异步执行,可参考之前JS基础中的异步模块
if(xhr.readState === 4){
if(xhr.status === 200){
alert(xhr.responseText)
}
}
}
xhr.send(null)】
//post请求
const xhr =new XMLHttpRequest()
xhr.open("POST","/login",true)// true代表异步请求;
xhr.onready·tatechange = function(){
//这里的函数是异步执行,可参考之前JS基础中的异步模块
if(xhr.readState === 4){
if(xhr.status === 200){
alert(xhr.responseText)
}
}
}
xhr.send(null)
状态码
xhr.readyState
0:(未初始化)还没有调用send()方法
1:(载入)已调用send()方法,正在发送请求
2:(载入完成)send()方法执行完成,已经接收到全部响应内容
3:(交互)正在解析响应内容
4:(完成)响应内容解析完成,可以在客户端调用
xhr.status
2xx:表示成功处理请求,如200
3xx:需要重定向,浏览器直接跳转,如301 302 304
4xx:客户端请求错误,如404 403
5xx:服务器端错误
跨域
同源策略,跨域解决方案
ajax请求时,浏览器要求当前网页和server必须同源(安全)
同源:协议,域名,端口。三者必须一致。加载图片css js可无视同源策略。
//可以无视同源策略
<img src=跨域的图片地址,可用于统计打点,可使用第三方统计服务/>
<link href=跨域的css地址,可使用CDN(CDN一般都是外域)/>
<script src=跨域的js地址,可实现JSONP></script>
跨域:所有的跨域,都必须经过server端允许和配合,未经server端允许就实现跨域,说明浏览器有漏洞
跨域的常用的实现方式
JSONP原理,CORS
JSONP
/*
<script>可绕过跨域限制
服务器可以任意动态拼接数据返回,所以,<script>就可以获得跨域的数据,只要服务端愿意返回
*/
<script>
window.callback = function(data){
console.log(data)
}
<script src="http://localhost:8002/jsonp.js?username=xxx"><script>
callback(
{name:'xxx'}
)
CORS
服务器设置 http header
//第二个参数填写允许跨域的域名称,不建议直接写“*”
response.setHeader("Access-Control-Allow-Origin","http://localhost:8011");
response.setHeader("Access-Control-Allow-Headers","X-Request-With");
response.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
//接收跨域的cookie
response.setHeader("Access-Control-Allow-Credentials","true");
常用的ajax插件
JQuery Fetch Axios(推荐)
存储
描述cookie localStorage sessionStorage区别
cookie
本身用于浏览器和server通讯,被“借用”到本地存储来,只能用document.cookie = '...'来修改,最大存储4kb,http请求时需要发送到服务端,增加请求数据量。
lacalStorage和sessionStorage
HTML5专门为存储而设计,最大可存5M,API简单易用setItem getItem。不会随着http请求被发送出去。
localStorage数据永久存储,除非代码或手动删除(使用较多)。sessionStorage数据只存在于当前会话,浏览器关闭则清空。
开发环境
git
git add .//添加
get checkout xxx//还原
git commit -m "xxx"//提交
git push origin master//推送
git pull origin master//下载
git branch//开发分支
git checkout -b xxx/git checkout xxx// 切换分支
git merge xxx //合并分支
chrome调试工具
Elements:DOM结构
Console debugger Network:资源加载
Application:存储
抓包
fiddler(windows) charles(macos)
webpack和babel
babel是一个JS编译器,用来转换最新的JS语法,比如把ES6, ES7等语法转化成ES5语法,从而能够在大部分浏览器中运行。
webpack是一个打包工具,打包js文件,css文件,图片,html等等,它可以分析整个项目的文件结构,确认文件之间的依赖,比如一个js文件引入了另一个js文件。在这个过程中可以合成js,压缩,加入hash等,最终生成项目文件。
linux命令
ssh 用户名@IP地址 //本地登录线上
ls //查看文件夹
ls -a //查看隐藏文件
mkdir //创建文件夹
rm -rf 文件夹 //删除文件夹
mv //修改文件名
cp //拷贝
touch //新建文件
运行环境
页面加载过程
从输入url到渲染出页面的整个过程
-
加载资源的形式
html代码 媒体文件 js和css
-
加载资源的过程
(1) DNS解析:域名->ip地址
(2) 浏览器根据ip地址向服务器发起http请求
(3) 服务器处理http请求,并返回给浏览器
-
渲染页面的过程
(1) 根据HTML代码生成DOM Tree
(2) 根据CSS代码生成CSSOM (建议把css放在head中就是为了避免重复渲染)
(3) 将DOMTree和CSSOM整合形成Render Tree
(4) 根据Render Tree渲染页面
(5) 遇到
window.onload和DOMContentLoaded的区别
window.onload资源全部加载完才能执行,包括图片
DOMContentLoaded渲染完即可,图片可能尚未下载
性能优化
加载资源优化
多使用内存,缓存或其他方法。减少cpu计算量,减少网络加载耗时。适用于所有编程的性能优化 - 空间换时间。加载更快:(1)减少资源体积:压缩代码。(2)减少访问次数:合并代码,ssr服务器端渲染,缓存。(3)使用更快的网络:CDN
渲染优化
渲染更快:(1)CSS放在head,JS放在body最下面。(2)尽早开始执行JS,用DOMContentLoaded触发。(3)懒加载(图片懒加载,上滑加载更多)(4)对DOM查询进行缓存。(5)频繁DOM操作,合并到一起插入DOM结构。(6)节流throttle防抖debounce
缓存:(1)静态资源加hash后缀,根据文件内容计算hash。(2)文件内容不变,则hash不变,则url不变。(3)url和文件不变,则回自动触发http缓存机制,返回304.
CDN:
SSR:服务器端渲染:将网页和数据一起加载,一起渲染。非SSR(前后端分离):先加载网页,在加载数据,在渲染数据。
懒加载:先加载预览图,当图片出现在屏幕时再加载图片
缓存DOM查询:DOM查询慢,JS执行很快
手写防抖(定时器)
//监听一个而输入框,文字变化后触发change事件
//直接用keyup事件,则会频繁触发change事件
//防抖:用户输入结束或暂时时,才会触发change事件(定时器)
function debounce(fn,delay = 500){
//timer是闭包中的
let timer =null
return function(){
if(timer){
clearTimeout(timer)
}
timer=setTimeout(()=>{
fn.apply(this,arguments)
timer = null
},delay)
}
}
手写节流(定时器)
//拖拽一个元素时,要随时拿到该元素被拖拽的位置
//直接用drag事件,则会频繁触发,很容易导致卡顿
//节流:无论拖拽速度多块,都会每间隔100ms触发一次
function throttle(fn,delay =100){
let timer=null
return function(){
if(timer){
return
}
timer = setTimeout(()=>{
fn.apply(this,arguments)
timer = null
},delay)
}
}
安全
常见的web前端攻击方式有哪些?
xss跨站请求攻击:嵌入
xsrf跨站请求伪造:使用post接口,增加验证
题目汇总:
typeof能判断那些类型?
何时使用===何时使用==?
值类型和引用类型的区别?
手写深拷贝?
如何准确判断一个变量是不是数组?
手写一个简易的额jQuery,考虑插件和扩展性
class的原型本质,怎么理解?
this的不同应用场景,如何取值?
手写bind函数
实际开发中闭包的应用场景,举例说明
同步和异步的区别是什么?
手写用promise加载一张图片
前端使用一部的场景有哪些?
DOM是哪种数据结构?
DOM操作的常用api
attr和property的区别
一次性插入多个DOM节点,考虑性能
如何识别浏览器的类型
分析拆解url各个部分
编写一个通用的事件监听函数
描述事件冒泡的流程
无限下拉图片列表,如何监听每个图片的点击
手写一个简易的ajax
跨域的常见实现方式
描述cookie localStorage sessionStorage区别
从输入url到渲染出页面的整个过程
window.onload和DOMContentLoaded的区别?
前端常见的性能优化方案
手写节流和防抖
Web前端常见的安全攻击方式和预防
简历:简洁明了,突出个人技能和项目经验。个人把博客,开源作品放在简历中。简历不要造假,保证能力上的真实性(不要用精通)
- 如何看待加班:像借钱,救急不救穷。
- 千万不要挑战面试官,不要反考面试官。
- 学会给面试官惊喜,证明你能想到更多,做得更好,但不要太多。
- 遇到不会的问题,说出你知道的部分即可,但别岔开话题
- 谈谈你的缺点:说一下最近在学什么。
面试真题
typeof返回哪些类型
基本类型:undefined string number boolean symbol
引用类型:object (null===‘object’)
函数:function
列举强制类型转换的隐式类型转换
强制:parseInt parseFloat toString
隐式:if 逻辑运算 == +拼接字符串
手写深度比较,模拟lodash isEqual
/*实现如下效果:
const obj1={a:10,b:{x:100,y:200}}
const obj2={a:10,b:{x:100,y:200}}
isEqual(obj1,obj2)===true
*/
//判断是否为对象或数组
function isObject(obj){
return typeof obj === 'ojject' && obj !==null
}
function isEqual(obj1,obj2){
//为值类型时(参与equal的一般不是函数)
if (!isObject(obj1) || !isObject(obj2)){
return obj1 === obj2
}
//为同一对象时
if(obj1 === obj2){
return true
}
//当两个为不同对象时(包含数组)
//先取出obj1和obj2的keys,比较个数
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length){
return false
}
//以obj1为基准,和obj2递归比较
for(let key in obj1){
//比较当前key的val (递归)
const res - isEqual(obj1[key],obj2[key])
if(!res){
return false
}
//全相等
return turn
}
}
split()和join()的区别
‘1-2-3’.split(’-’) // [1,2,3]
[1,2,3].join(’-’) // ‘1-2-3’
数组的pop push unshift shift分别做什么
功能是什么?返回值是什么?是否会对原数组造成影响?
pop抛除数组最后一位,并返回最后一位
shift在数组前面删除一维数组,并返回抛出的那位
push往后追加一个数组,返回数组长度
unshift在数组最前面插入一个 ,返回数组长度
纯函数:不改变源数组,返回一个新数组
concat map filter slice
非纯函数:push pop shift unshift forEach some every reduce
slice和splice的区别
功能:slice-切片 splice-剪接
参数和返回值:
是否纯函数:slice是纯函数,splice是非纯函数
[10,20,30].map(parseInt)返回结果是什么?
map的参数和返回值
parseInt参数和返回值
ajax请求get和post的区别?
get一般用于查询操作,post一般用于用户提交操作
get参数拼接在url上,post放在请求体内(数据体积可更大)
安全性:post易于防止CSRF
函数call和apply的区别
fn.call(this,p1,p2,p3)
fn.apply(this,arguments)
事件代理(委托)是什么?
const p1 = document.getElementById('P1')
const body = document.body
bindEvent(p1,'click',e =>{
e.stopPropagation()//注释掉这一行,来体会事件冒泡
alert('激活')
})
bindEvent(body,'click',e =>{
alert('取消')
})
闭包是什么?有什么特性?有什么负面影响?
回顾作用域和自由变量
回顾闭包应用场景:作为参数被传入,作为返回值被返回
回顾:自由变量的查找,要在函数定义的地方(而非执行的地方)
影响:变量回常驻内存,得不到释放。闭包不要乱用。(没法判断未来是否会用到)
如何阻止事件冒泡和默认行为?
event,stopPropagation()
event.preventDefault()
查找,添加,删除,移动DOM节点的方法
如何减少DOM操作?
缓存DOM查询结果
多次DOM操作,合并到一次插入
解释jsonp的原理,为何它不是真正的ajax?
浏览器的同源策略(服务端没有同源策略)和跨域
哪些html标签能绕过跨域?
jsonp的原理
document load和ready的区别
load:页面的全部资源加载完才会执行,包括图片,视频等
ready:DOM渲染完即可执行,此时图片,视频还可能没有加载完
函数声明和函数表达式的区别?
函数声明function fn(){ } 函数声明会在代码执行前预加载,而函数表达式不会
函数表达式const fn = function(){ }
new Object()和Object.create()的区别
{ }等同于new Object( ) 原型都是Object.prototype
Object.create(null)没有原型,Object.create({ … })可指定原型
关于this的场景题
关于作用域和自由变量的场景题
//问题
let i
for(i =1;i<=3;i+=){
setTimeout(function(){
console.log(i)
},0)
}
判断字符串以字母开头,后面字母数字下划线,长度6-30
const reg = /1\w{5,29}$/
学习正则表达式规则
手写常见的正则表达式
关于作用域和自由变量的场景题-2
//看代码的时候先跳过函数内部,等到执行的时候再看函数内部
let a = 100
function test(){
alert(a)
a=10
alert(a)
}
test()
alert(a)
手写字符串trim方法,保证浏览器兼容性
String.prototype.trim=function(){
return this.replace(/^\s+/,'').replace(/\s+$/,'')
}
//原型,this,正则表达式
如何获取多个数字中的最大值
function max(){
const nums = Array.prototype.slice.call(arguments)//变为数组
let max = 0
nums.forEach(n=>{
if(n>max){
max=n
}
})
return max
}
如何用JS实现继承
class继承
prototype继承
如何捕获JS程序中的异常?
//手动捕获异常
try{
//todo
}catch(ex){
console.error(ex)//手动捕获catch
}finally{
//todo
}
//自动捕获异常
window.onerror = function (message,source,lineNom,colNom,error){
//第一,对跨域的js,如CDN的,不会有详细的报错信息
//第二,对于压缩的js,还要配合sourceMap反查到未压缩代码的行,列
}
什么是JSON?
json是一种数据格式,本质是一段字符串。
{
"name":"张三",
"info":{
"single":true,
"age":30,
"city":"北京"
},
"like":["篮球","音乐"]
}
json格式和js对象结构一致,对js语言更友好
window.JSON是一个全局对象: JSON.stringify JSON.parse
获取当前页面url参数
传统方式,查找location.search
新API.URLSearchParams
将url参数解析为JS对象
//传统方式,分析search
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(){
const res = {}
const pList = new URLSearchParams(location.search)
pList.forEach((val,key)=>{
res[key] = val
})
return res
}
手写数组flatern,考虑多层级
function flat(arr){
//验证arr中,还有没有深层数组[1,2,[3,4]]
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 ([1,2,[3,4,[10,20,[100,200]]],5])
console.log(res)
数组去重
- 传统方式: 遍历元素挨个比较,去重
- 使用set,考虑计算效率
//传统方式: 遍历元素挨个比较,去重
function unique(arr){
const res = []
arr.forEach(item=>{
if(res.indexOf(item)<0){
res.push(item)
}
})
return res
}
const res = unique([30,10,10,20,40,30])
console.log(res)
//使用set方式,,set时无序结构,不能重复
function unique(arr){
const set = new Set(arr)
return [...set]//...是解构
}
const res = unique([30,10,10,20,40,30])
console.log(res)
介绍一下RAF requestAnimateFrame
要想动画流畅,更新频率要达到60帧/s,即16.67ms更新一次视图
setTimeout要手动控制频率,而RAF浏览器会自动控制
后台标签或隐藏iframe中,RAF会暂停,而setTimeout依然执行
前端性能如何优化?一般从哪几个方面考虑?
原则:多使用内存,缓存,减少计算,减少网络请求
方向:加载页面,页面渲染,也买你操作流畅度
a-zA-Z ↩︎