一、第一部分
1.1值类型和引用类型
1.1.1值类型都是变量,通过在栈中进行存储,值类型在内存中所占的空间小;a=100,直接给a分配100的值空间;b=a,直接给b也分配一个100的值空间;因此a=200,直接修改a的值空间即为200;而b的值空间不变还是100
引用类型在内存中所占空间较大,一般通过栈和堆进行存储,给a={age:20},会在堆中申请一个内存地址,指向这个对象{age:20},;也就是说,a中存储的并不是一个对象,而是一个内存地址1,这个内存地址1,指向这个对象。把b=a,b也就指向这个内存地址1,;当b.age=21,b就把这个内存地址1改成了指向对象{age:21}了;因此此时的内存地址1已经被改成指向对象{age:21};a.age打印为改变后的对象{age:21}。
1..1.2 常见值类型
注意:使用const声明变量时必须赋值,要不然会报错
1.1.3常见引用类型
注意:函数也可当做一个特殊的类型
1.2 typeof运算符和深拷贝
1.2.1 typeof运算符
注意:typeof可以判断所有的值类型
也可以判断 函数类型和引用类型
引用类型只能识别到对象‘object’,具体还得使用其他方法
1.2.2 深拷贝
1.2.2.1 先看下浅拷贝:
//shanghai
也就是说上面的例子是符合引用类型的,改变了共同指向的内存地址指向的对象
1.2.2.2 深拷贝:不改变原来内存地址指向的对象
先使用typeof判断obj是null还是(数组和对象)
如果typeof obj是null不是(数组和对象),就返回obj;
如果instanceof 判断是数组,就返回[];否则返回{};
开始for循环遍历数组或者对象的每个值,要先hasOwnProperty过滤掉不是原型上的属性,保证key是obj自己本身 的属性,在对其属性进行一一深拷贝递归调用;
返回所得结果result:
1.3 变量计算
主要涉及到的有:字符串拼接、==、if语句和逻辑运算
1.3.1 字符串拼接
1.3.2 == 运算符
1.3.3 if语句和逻辑运算
真变量:!!a === true 的变量;
假变量:!!a ===false 的变量;
假变量都有哪些?
逻辑运算符
上面的例子:1.因为10是变量,所以继续看后面的,0 是假变量,所以返回0;
2.' '是假变量,继续看后面,'abc'是真变量,返回'abc';
改一下:
1.console.log(0&&) ;
//0
因为0是假变量,直接返回0
2.console.log('abc' || ' ') ;
//'abc'因为'abc'是真变量,直接返回'abc'
总结:
1.typeof能判断哪些类型?
答:1.识别所有值类型
2.识别函数
3.判断是否是引用类型
2.==何时用?===何时用?
答:除了判断==null之外,其他一律用===
3.值类型和引用类型的区别?
面试中会出题,比如:
其中x1=obj1.x是干扰判断,值类型赋值,直接分配内存空间;obj2.x=101,引用类型赋值,改变了与obj1共同指向的内存地址指向的对象,修改为{x:101}
4.手写深拷贝
答:首先判断是值类型还是引用类型;再注意判断obj是数组还是对象;递归
第二部分
题目:
1.如何精准判断一个变量是不是数组?
2.手写一个简易 的jQuery,考虑插件和扩展性
3.class的原型本质,怎么理解
知识点
1.class和继承
2.类型判断instanceof
3.原型和原型链
2.1 class
2.2 继承
extends、 super、扩展或重写
2.3 类型判断-instanceof
通过instanceof判断引用类型,前者是否是后者class构建出来的;或者说后者class是前者class构建出来的父类。
注意:Object是所有类的父类
2.4 原型
.prototype叫显式原型
_proto _叫隐式原型
原型关系:
1.每个class都有显式原型prototype
2.每个实例都有隐式原型_proto _
3.实例的_proto _指向对应class的prototype
基于原型的执行规则:
1.获取属性xiaoluo.name或执行方法xialuo.sayHi()
2.先在自身属性和方法上寻找
3.如果找不到则自动去_proto _中查找
2.5 原型链
再看instanceof
可以顺着xialuo能否对应到class的原型上的显式原型上;
比如,xialuo instanceof Studnet可以找到对应的显式原型Student.prototype;Student instanceof People可以找到对应的显式原型People.prototype;People instanceof Object可以找到对应的显式原型Object.prototype;
问题:
1.如何准确判断一个变量是不是数组?
答:使用instanceof,结合原型链图理解
2.手写一个简易的j Query,考虑插件和扩展性
答:
3.class的原型本质,怎么理解?
答:
1.原型和原型链的图示
2.属性和方法的执行规则
小结:
1.class和继承,结合上面手写jQuery的示例来理解
2.instanceof
3.原型和原型链:图示和执行规则
第三部分
题目:
1.this不同的使用场景
2.手写bind函数
3.实际开发中闭包的应用场景,举例
4.场景题:创建10个a标签,点击时候弹出对应的序号
知识点
作用域和自由变量、闭包、this
作用域
作用域分为:全局作用域、函数作用域、块级作用域
3.1作用域:
自由变量:一个变量在当前作用域没有被定义,但被使用了,那怎么办?可以向上级作用域,一层一层一次查找,直到找到为止,如果到全局作用域都还没找到,则报错 xx is not defined
3.2 闭包:
作用域应用的特殊情况,有两种表现:1.函数作为参数被传递2.函数作为返回值被返回
注:所有自由变量的查找,是在函数定义的地方,向上级作用域查找,而不是在执行的地方查找
因为fn()执行时,执行 create函数,create函数里面返回匿名函数打印a值,a值在匿名函数中没有找到,想上级 create函数查找a,找到了,a为100,因此打印a为100
因为执行print(fn)时,执行print函数,print函数里面执行fn(),执行fn()则执行fn函数,打印a值,a在fn函数中没有找到,向上级全局查找,找到了,a为100,则打印a为100
3.3 this
注意:this的取值是在函数执行的时候被调用,而不是在定义的时候
应用场景:作为普通函数被调用、使用call 、apply、bind被、作为对象方法被调用、在class方法中调用、箭头函数中
因为fn1()执行,打印的this是去全局的,所以fn1()执行打印window;而执行fn1.call(),call里面可以改变this指向,里面传入了对象{x:100},所以打印的就是这个对象{x:100};而执行fn2(),bind里面也可以改变this指向,里面传入了对象{x:200},就打印这个对象{x:200},注意一点,bind有条件:需要返回一个新的函数fn2,执行这个新函数才可以使用,而call直接调用就可以使用
eg2:
因为sayHi()作为zhangsan的sayHi()方法被执行,使以返回这个对象;而下面的wait()里面有一个setTimeout()执行,也就是说执行zhangsan.wait()时,如果没有setTimeout(),就返回的是zhangsan这个对象,有setTimeout()就相当于作为一个普通函数被执行,而不是作为一个方法被执行,是setTimeout本身触发的执行,而不是zhangsan,因此,打印的是this是window
eg3:
这个跟上面那个唯一不同的是setTimeout()里面有个箭头函数()=>{},而箭头函数的this是取它的上级作用域的值,因此跟zhangsan.waitAgain()方法跟zhangsan.sayHi()方法一样,取得是zhangsan这个对象
eg3:
constructor里面的this代表的是创建的实例,sayHi()方法里面的this代表就是zhangsan对象
题目解答:
1.this不同的使用场景
答:1、当做普通函数被调用 2.使用call、apply、bind 3.作为对象方法调用 4.在class的方法中调用 5.箭头函数
2.手写bind函数
答:
3.实际开发中闭包的应用场景,举例
//100
4.场景题:创建10个a标签,点击时候弹出对应的序号
因为i是全局变量,而
a.addEventListener('click', function(e){
e.preventDefault()
alert(i)
})
事件是当点击才会触发该事件,所以for循环非常快早已遍历到10,因此每当点击事件发生前,i就是10,因此点击0-10,都弹出10
修改:
将i改成局部变量,使其在for循环块级作用域里面,每次for循环的时候就会形成一个块,每次的值都会不一样
4.异步
题目:
1.同步和异步的区别是什么?
2.手写用Promise加载一张图片
3.前端使用异步的场景有哪些?
知识点
1.单线程和异步
2.应用场景
3.callback hell 回调地域和Promise
4.1 单线程和异步
-
js是单线程语言,只能同时做一件事
2.浏览器和node.js已支持js启动进程,如web worker
3.js和DOM渲染共用一个线程,因为Js可修改DOM结构
4.遇到等待(网络请求,定时任务)不能卡住,需要异步,回调cakkback函数
例子1:
异步:
先执行同步的100,遇到等待一秒才执行的setTimeout函数,但异步不会阻塞后面300的执行,因此先100,再300,再等待1秒执行200
// 100 300 200
例子2.
同步:会阻塞代码的执行,alert()没有完成,后面的就没法执行
//先100,弹出200警告框,点击确认之后才会打印300
4.2应用场景
1.网络请求,如ajax场景
2.定时任务,如setTimeout定时器
4.3回调地域 callback hell
4.3 Promise
问题:
4.4 手写promise加载图片示例
eg1:加载一张图片
eg2:加载多张图片
4.5异步应用场景
1.网络请求,如ajax图片加载
2.定时任务,如setTimeout
先1,遇到等待1秒执行的setTimeout异步不会阻塞后面代码,打印3,再遇到等待0秒,也就是立即执行的的setTimeout异步,但依然是异步,因此还是执行后面的代码5,然后再执行立即等待的4,然后再是2
小结
1.单线程和异步,异步和同步的区别
2.前端异步的应用场景:网络请求&定时任务
3.Promise解决callback hell嵌套的问题
js基础总结
内容:
1.变量的类型和计算
2.原型和原型链
3.作用域和闭包
4.异步和单线程
题目:
1.typeof能判断哪些类型
2.何时使用===何时使用==
3.值类型和引用类型的区别
4.手写深拷贝
知识点:
1.值类型 VS 引用类型,堆栈模型,深拷贝
2.typeof运算符
3.类型转换,truly和falsely变量
原型和原型链题目:
1.如何准确判断一个变量是不是数组?
2.手写一个简易 的 jQuery,考虑插件和扩展性
3.class的原型本质,怎么理解
原型和原型链-知识点
1.class和继承,结合手写jquery的示例来理解
2.instanceof
3.原型和原型链:图示
4.this的不同应用场景,如何取值?
5.手写bind函数
6.实际开发遇到的闭包问题(0-10对应弹出)
作用域和闭包-知识点
1.作用域和自由变量
2.闭包:两种常见方式
&自由变量查找规则
3.this
4.异步同步区别?
5.手写promise加载图片
6.前端使用异步场景
异步和单线程-知识点:
1.单线程和异步,异步和同步的区别
2.前端异步的应用场景:网络请求&定时任务
3.promise解决callback hell
第四部分 JS Web API
1.DOM
DOM操作
题目:
1.DOM是那种数据结构?
2.DOM操作的常用API?
3.attr和property的区别?
4.一次性插入多个DOM节点,考虑性能?
知识点
1.DOM本质
html解析出来的一棵树
2.DOM节点操作
先是获取DOM节点,再attribute,property
获取dom节点?
1.通过getElementById获取;
2.通过getElementsByTagName获取;
3.通过getElementsByClassName获取;
4.通过querySelectorAll获取;
dom节点的property:
dom节点的attr:
总结:
property
和attribute
区别:property
:修改对象属性,不会体现到html结构中;attribute
:修改html``属性,会改变
html结构,两者都有可能引起
DOM重新渲染,建议尽量用
property`
3.DOM结构操作
3.1新增/插入节点
新增
移动
3.2获取子元素列表,获取父元素
获取子元素列表:
const div1 = document.getElementById('div1')
const child = div1.childNodes
获取父元素
const div1 = document.getElementById('div1')
const parent = div1.parentNode
获取对应文本text节点:
3.3删除子元素
通过removeChild
:
div1.removeChild(child[0])
4.DOM性能
4.1DOM操作占用cpu较多,应避免频繁的DOM操作
4.2对DOM查询做缓存
4.3 将频繁操作改为一次性操作
DOM查询做缓存
//不缓存DOM查询结果
for(let i=0;i<document.getElementByTagName('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是那种数据结构?
答:树(DOM树)
2.DOM操作的常用API?
答:DOM节点操作和DOM结构操作、attribute和property
3.attr和property的区别?
答:attribute是修改html结构,会改变HTML结构;property是修改对象属性,不会体现到html结构中;两者都有可能引起DOM重新渲染
4.一次性插入多个DOM节点,考虑性能?
答:通过frag文档片段,要考虑到先缓存再一起插入
2.BOM
题目:
1.如何识别浏览器的类型
2.分析拆解url各个部分
知识点
navigatior和screen
通过navigatior.userAgent
来查看浏览器信息
通过screen
查看屏幕信息:
location
location.href
:网址
location.protocol
:协议
location.host
:域名
location.pathname
:获取待path
路径
location.search
:获取常用参数
location.hash
:#后面的内容
history
history.back()
:程序后退
'hiotory.forward()':程序前进
3.事件绑定
题目:
1.编写一个通用事件监听函数
2.描述事件冒泡的流程
3.无限下拉的图片列表,如何监听每个图片的点击?
知识点:
3.1 事件绑定
const btn=document.getElementById('btn1');
btn.addEventListener('click',event=>{ console.log('clicked')})
event.target:获取触发的元素;
event.preventDefault() :阻止默认行为
3.2事件冒泡
点击激活,触发p1点击事件,打印出“激活 取消”
因为p1>div1>body,先是p1事件打印“激活”,再冒泡到body,触发body的点击事件,打印“取消”
加上阻止冒泡e.stopPropagation()
3.3事件代理
优点:代码简洁;减少浏览器内存占用(子元素较多需要设置监听时,只需要在父元素上设置一次即可)
注意:有了
event.preventDefault()
阻止默认事件,点击某个<a href="#">a2</a>就不会跳转到#页面
3.4 编写一个通用的事件监听函数?注意this指向
描述事件冒泡的流程?
答:
1.基于DOM树形结构
2.事件会顺着触发元素向上冒泡3.应用场景:代理
无限下拉图片列表,如何监听每个图片的点击?
答:
1.利用事件代理;
2.用e.target获取触发元素
3.用matches来判断是否是触发元素
4.ajax
题目:
1.手写一个简易的ajax
2.跨域的常用实现方式
知识点:
1.XMLHttpRequest
2.状态码
3.跨域:同源策略、跨域解决方案
4.1XMLHttpRequest
在ajacx文件夹下的XMLHttpRequest文件夹下新建data文件夹,data文件夹下面再新建test.json;
访问
http://127.0.0.1:5500/ajax/XMLHttpRequest/data/test.json
实现get请求:
访问
http://127.0.0.1:5500/ajax/XMLHttpRequest/index.html
那么,POST请求:效果无法演示,代码如下:
0-(未初始化)还没有调用send()方法;
1-(载入)已调用send()方法,正在发送请求
2-(载入完成)send()方法执行完成,已经接收到全部响应内容
3-(交互)正在解析响应内容
4-(完成)响应内容解析完成,可以在客户端调用
xhr.staus
2xx-表示成功处理请求,如200;
3xx-需要重定向,浏览器直接跳转,如301 302 304;(301永久重定向,302临时重定向,304资源未被改变)
4xx-客户端请求错误,如404 403 (404 没有找到;403没有访问权限)
5xx-服务端错误(服务器程序有bug)
4.2 跨域
4.2.1什么是跨域?
1.ajax请求时,浏览器要求当前网页和server必须同源(安全)
2.同源:协议、域名、端口,三者必须一致
比如:
前端 :http://a.com:8080/;
后端:https://b.com/api/xxx;
4.2.2 加载图片css js可无视同源策略
1.<img src=跨域的图片地址/>
2.<link href=跨域的css地址/>
3.<script src=跨域的js地址></script>
4.<img /> 可用于统计大点,可使用第三方统计服务
5.<link /><script>可使用CDN,CDN一般都是外域
6.<script>可实现JSONP
注意:所有的跨域,都必须经过server端允许和配合;未经server端允许就实现跨域,说明浏览器有漏洞,危险信号
4.3JSONP
1.访问https://taobao.com,服务端一定会返回一个html文件吗?
答:不一定,服务端可以任意动态拼接数据返回,只要符合html格式要求;同理于<script src="https://taobao.com/getData.js" >
因此,<script>
可绕过跨域限制,服务端可以任意动态拼接数据返回;<script>就可以获得跨域的数据,只要服务端愿意返回
<script>标签加上username=xxx
改变callback函数
jQuery实现jsonp
CORS-服务端设置http-header
解答:
1.手写ajax?
答:
2.jQuery完整版ajax
补充
1.fetch
[https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch](https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch)
2.axios
执行get请求:
执行post请求:
执行多个请求:
axios API:
5.存储
题目:
描述cookie、localStorage、sessionStorsge区别?
知识点:
cookie
本身用于浏览器和server通讯,被“借用”到本地存储来,可用document.cookie="xxxx"
来修改。
缺点:存储大小,最大4kb;http请求时需要发送到服务端,增加请求数据量;只能用document.cookie="xxxx"
来修改,太过简陋
localStorage和sessionStorage
两个都是HTML5专门为存储而设计,最大可存5M;API简单易用:setItem和getItem;不会随着http请求被发送出去
localStorage和sessionStorage的区别?
1.localStorage数据会永久存储,除非代码或手动删除
2.sessionStorage数据只存在于当前会话,浏览器关闭则清空
3.一般用localStorage会更多一些
描述cookie、localStorage、sessionStorsge区别?
答:
1.容量:cookie是4kb容量,localStorage和sessionStorsge是5M容量
2.api易用性:cookie用
document.cookie="xxxx"
来修改,太过简陋;localStorage和sessionStorsge使用setItem和getItem3.是否跟随http请求发送出去:localStorage和sessionStorsge不会随着http请求被发送出去,因此在客户端(浏览区);而cookie是会随着HTTP请求被发送出去,值服务端
4.数据上的生命周期的不同:
Cookie 一般由服务器生成,可设置失效时间,如果在浏览器端生成cookie,默认是关闭后失效。
localStorage 除非被永久清除,否则永久保存。
sessionStorage 仅在当前会话会有效,关闭页面或浏览器后被清除
第五部分 开发环境
1.git
1.1常用git命令:
答:
(1)git add:把文件添加到git;
(2)git checkout xxx:如果发现修改错误,想还原到以前的状态;
(3)git commit -m "xxx":提交一行记录;
(4)git push origin master:推送到服务端
(5)git pull origin master:从服务端下载
(6)git branch:开分支
(7)git checkout -b xxx/git checkout xxx:切换分支
(8)git merge xxx:合并分支
2.调试工具
3.抓包
是移动端h5页,查看网络请求,需要用工具抓包,windows一般用fidder,Mac OS一般用charles。
抓包过程:
手机和电脑连同一个局域网,将手机道里道电脑上,
手机浏览网页,抓包;查看网络请求,网址代理,https
4.webpack
5.linux命令
ls 查看文件夹;clear清除;ll查看列表
第六部分 运行环境
1.网页是怎么加载的?
2.性能优化?
3.安全?
题目:
1.从输入url到渲染出页面的整个过程
2.window.onload
和DOMContentLoaded
的区别
知识点
1.加载资源的形式
2.加载资源的过程
3.渲染页面的过程
6.1.页面加载和渲染
6.1.1加载资源的形式有哪些
要加载:
(1)html代码
(2)媒体文件,如图片、视频等;
(3)javascript
、css
6.1.2.加载过程
(1)首先DNS解析:域名->IP地址
(2)浏览器根据IP地址向服务器发起http请求
(3)服务器处理http请求,并返回浏览器
6.1.3.渲染过程
(1)根据HTML代码生成DOM Tree
(2)根据CSS代码生成CSSOM
(3)将DOM Tree和CSSOM整合行程 Render Tree
(4)根据Render Tree渲染页面
(5)遇到<script>则暂停渲染,优先加载并执行JS代码,完成再继续
(6)直至把Render Tree渲染完成
示例:
根据html生成DOM树,根据CSS代码生成CSSOM,再将DOM Tree和CSSOM整合行程 Render Tree进行渲染;因为无css,所以渲染比较快
根据html生成DOM树,根据CSS代码生成CSSOM,再将DOM Tree和CSSOM整合行程 Render Tree进行渲染
思考:为何把css放在head中?
答:避免重复渲染;因为如果放在body里面的最后,前面html解析生成dom树,无css,因此不生成cssom,等整合render树渲染后又发现body下面的css,发起css解析生成cssdom,再render树解析,这样就重复了render树解析渲染
3.
加载过程:首先html解析生成dom树,渲染出
default
;遇到<script>
标签要暂停渲染,优先加载<script>
的js代码,将内容改成update by js
;然后再继续解析html代码,渲染p标签
思考:为何建议把js放到body最后?
答:示例2是为了讲解知识点,实际开发js位置不是很合理,应该把js代码放到body最后。因为示例2会导致渲染的时间过长,先是渲染出dom树,遇到script优先加载js代码后,又dom渲染了一遍,这样渲染的时间过长;应该把js代码放到body最后,把html渲染完后再执行script的渲染。
加载过程:首先解析html生成dom树,渲染出test,遇到img标签,这个不像script,不会阻塞dom渲染,一遍渲染img,一般继续dom渲染,可能图片比较大,渲染时间较长,会先空着,继续渲染下面的test,等img渲染完成,填充即可
6.1.4.window.onload
和DOMContetLoaded
区别
答:window.addEventListener('load',function (){ })
:页面的全部资源加载完才会执行,包括图片、视频
而document.addEventListener('DOMContentLoaded',function(){ })
:DOM渲染完即可执行,此时图片、视频可能还没加载完。
一般监听DOMContetLoaded
,不用等到图片加载完,加载完dom即可
eg:
图片较大还没加载完,dom已渲染完成,所有资源渲染完之后然后再是winow loader;多个图片的话,会按照实际的图片加载速度
6.2 性能优化
性能优化原则:
1.多使用内存、缓存或其他方法
2.减少CPU计算量,减少网络加载耗时
3.适用于所有编程的性能优化-空间换时间
如何入手?
1.让加载更快:
1.1减少资源体积:压缩代码(比如做webpack时,在生产环境下打包,代码体积会比在开发环境打包更小)
1.2 减少访问次数:合并代码(比如雪碧图),SSR
服务端渲染(服务端把页面以及显示内容一起渲染,不用前端通过ajax发送请求再返回数据渲染),缓存(比如有10个资源,不用缓存需要访问10次,用了缓存访问一次即可)
1.3使用更快的网络:CDN
2.让渲染更快:
2.1 CSS放在head,Js放在body最下面
2.2 尽早开始执行Js,用DOMContentLoaded触发
2.3 懒加载(图片懒加载,上滑加载更多)
2.4 对DOM查询进行缓存(DOM操作比较耗费性能,需要进行缓存)
2.5 频繁DOM操作,合并到一起插入DOM结构
2.6 节流throttle、防抖debounce
解释:
1.资源合并
2.缓存
静态资源加hash后缀,根据文件内容计算hash;
文件内容不变,则hash不变,url不变;url和文件不变,则会自动触发http缓存机制,返回304
bundle.[contenthash].js
文件变hash变,文件不变hash不变。比如上线一个新功能,代码中包括老内容和新内容,只有一个文件是新内容,那么老文件url和文件不变,会触发http缓存机制,返回304;新文件会计算新的hash新的url,自动下载。我们希望不变的触发缓存,变的重新计算下载
3.CDN
CDN是专门做静态文件的服务,也满足缓存的304机制
4.SSR
1.服务端渲染:将网页和数据一起加载,一起渲染
2.非SSR(前后端分离):先加载数据,再渲染数据
3.早先JSP、ASP、PHP都是,现在的vue、react SSR
5.懒加载
说明:比如新闻列表,我们希望图片不是一下子加载完,而是首先显示第一屏的图片,再是随着向上滑动,图片逐渐显示完。
abc.png是图片真实地址,当页面有10张图片,第一屏有5张图片,显示的是体积较小的预览图片preview.png,随着向上滑动(可由dom节点距离屏幕顶部top的值判断),再将图片真实地址赋值给dom节点
6.缓存DOM查询
不缓存dom查询结果
缓存dom查询结果
7.多个dom操作一起插入到dom结构
8.尽早开始js执行
window.addEventListener('load',function (){ })
:页面的全部资源加载完才会执行,包括图片、视频而
document.addEventListener('DOMContentLoaded',function(){ })
:DOM渲染完即可执行,此时图片、视频可能还没加载完。
6.3 防抖和节流
6.3.1 防抖debounce
监听一个输入框时,文字变化后触发change事件
直接用keyup事件,则会频繁触发change事件
防抖:用户输入结束或暂停时,才会触发change事件
机制原理:设置一个定时器,当定时器为空时,触发异步请求再请空,
比如输入‘12’;开始timer初始化为null,触发keyup监听事件,执行setTimeout()异步请求,5秒还没到,已输入2,触发监听事件,此时timer是有值的,timer有值,执行
clearTimerout(timer)
,清除了1的定时任务异步操作不打印,重新设置一个2的定时任务,等5秒到打印,timer重置null。
那么,封装一下:
6.3.2 节流
拖拽一个元素时,要随时拿到钙元素被拖拽的位置;
直接用drag事件,则会频繁触发,很容易导致卡顿
节流:无论拖拽速度多快,都会每隔100ms触发一次
封装一下:
6.4 安全
问题:常见的web前端攻击方式有哪些?
6.4.1 安全-xss攻击
示例:一个博客网站,我发表了一篇博客,其中嵌入<script>脚本,脚本内容是获取cookie,发送到我的服务器(服务器配合跨域),当发布了这篇博客,有人查看它,我就可以轻松收割访问者的cookie
这样访问者的信息就可以被获取到,可以通过跨域ajax等方式传到对方服务器
那么怎么预防?
6.4.2 xss
预防
替换特殊字符,如<
变为<
把>
变为>
<script>
变为<script>
直接显示,而不会作为脚本运行;
前端要替换,后端也要替换,都做总不会有错
这样会作为字符串被解析,而不会被运行
6.4.3 XSRF攻击
示例:小明正在网上购物,看中了某个商品,商品id是100.付费接口是xxx.com/pay?id=100,但没有任何验证;小王是攻击者,看重另外一个商品,id是200;那么小王向小明发送一封电子邮件,标题很吸引人,比如中奖了,但邮件正文隐藏着<img src=xxx.com.pay?id=200 />,小明已查看邮件,就帮小王买了id是200的商品。
XSRF预防?
1.使用post接口
2.增加验证:比如密码,短信验证码、指纹验证等。
- 在请求地址中添加takon验证
CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对。
面试题
1.var 和let const区别?
答:
var是es5语法,let、const是es6语法,var有变量提升;
var和let是变量,可修改;const是常量,不可修改;
let const有块级作用域(外面访问不到),var没有
2.typeof能判断哪些类型?
答:
- 值类型:
undefined``number
,string
,boolean
,symbol
- 引用类型:
object
(注意,typeof null === 'object') - function
3.列举强制类型转换和隐式类型转换
答:
强制:parseInt
、parseFloat
、toString
等;
隐式:if
、逻辑运算、==、+拼接字符串
4.手写深度比较,模拟lodash isEqual
答:
5.split()
和join()
的区别
答:
'1-2-3'.split('-')
//[1,2,3];
[1,2,3].join('-')
//'1-2-3'
6.数组的pop
、push
、unshift
、shift
分别做什么?
答:
功能是什么?
返回值是什么?
是否会对原数组造成影响?
[图片上传失败...(image-8a369d-1583225059539)]
【扩展】数组的api,有哪些是纯函数?
答:纯函数:1.不改变原数组(副作用),2.返回一个数组
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()
非纯函数:
psuh、pop shift、 unshift;
forEach;
some every;
reduce;
7.数组slice和splice区别
答:
- 功能区别(slice 切片,splice剪接)
- 参数和返回值
-
是否纯函数?
8.[10,20,30]map(parseInt)?
答:这里说的很清楚
https://blog.csdn.net/willard_cui/article/details/81504782
当忽略参数 radix , JavaScript 默认数字的基数如下:
如果 string 以 "0x" 开头,parseInt() 会把 string 的其余部分解析为十六进制的整数。
如果 string 以 0 开头,那么 ECMAScript v3 允许 parseInt() 的一个实现把其后的字符解析为八进制或十六进制的数字。
如果 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数。
9.ajax请求get和post区别?
答:1)get一般用于查询操作,post一般用于提交操作;
2)get参数拼接在url上,post放在请求体内(数据体积可更大);
3)安全性:post易于防止CSRF
10.函数call和apply的区别?
答:传参方式不同:
call是零散的传参方式,apply是以数组的形式
fn.call(this,p1,p2,p3)
;
fn.apply(this,arguments)
;
11.事件代理(委托)是什么?
答:
12.闭包是什么?有何特性?有何影响?
答:
1)回顾作用域和自由变量
2) 回顾闭包应用场景:作为参数被传入,作为返回值被返回;
3) 回顾:自由变量的查找,要再函数定义的地方(不是执行的地方)
4)影响:变量会常驻内存,得不到释放。闭包不要乱用
例1:
程序执行完
return a=a1+a2+a3;
a3、a2、a1、a被逐层释放例2:
闭包,函数作为返回值:
闭包例子中的 a=100不能被释放,因为作为返回值了
下面的a =200可以释放
例3:
闭包,函数作为参数:
a=100不能被释放,a=200可以被释放
13.如何阻止事件冒泡和默认行为?
答:阻止事件冒泡:event.stopPropagation()
和阻止默认行为:event.preventDefault()
14.查找、添加、删除、移动DOM节点的方法?
答:
15.如何减少DOM操作?
1)缓存DOM查询结果(比如查询dom的list,可以先把list查出来保存,不要每次用再去查dom);
2)多次DOM操作,合并到一次插入(比如文档片段片段)
16.解释jsonp
原理,为何不是真正的ajax
?
答:ajax
是通过XMLHttpRequest
实现,jsonp
是通过javascript标签
实现;
1)浏览器的同源策略(服务端没有同源策略)和跨域
2)哪些html标签能绕过跨域?
3)jsonp原理
定义一个全局函数,访问一个js文件,返回数据
17.document.load和read的区别?
答:18.==和===区别?
答:
1)==会尝试类型转换
2)=== 严格相等
3)==使用场景?在==null时,其他都有===
19.函数声明和函数表达式的区别?
答:
函数声明:function fn(){}
;
函数表达式:const fn = function(){}
;
函数声明会在代码执行前预加载,(相当于变量提升),而函数表达式不会
函数声明:
//30
函数表达式:
因为是var sum,存在变量提升,没有被赋值,所以是undefined;但不是function
20.new Object()和Object.create()的区别?
答:
首先,{}等同于new Object(),原型Object.prototype;
其次,Object.create(null)没有原型;Object.create({...})可指定原型
21.this场景?
//1
//undefined
22.关于作用域和自由变量的场景题?
答:23.判断字符串以字母开头,后面字母数字下划线,长度6-30
答:const reg=/^[a-zA-Z]\w{5,29}$/
小写英文字母:/^[a-z]+$/
英文字母:/^[a-zA-Z]+$/
日期格式 2019-12-1 (\d表示查找数字):/^\d{4}-/d{1,2}-\d{1,2}$/
用户名(\w 元字符用于查找单词字符,单词字符包括:a-z、A-Z、0-9,以及下划线, 包含 _ (下划线) 字符):/^[a-zA-Z]\w{5,17}$/
简单的IP地址匹配 127.0.0.1(\d表示查找数字):/\d+\.\d+\.\d+\.\d+/
24.关于作用域和自由变量的场景题?
答:
25.手写字符串trim保证浏览器兼容性
答:
25.获取多个数字中的最大值
答:
26.如何用js继承?
答:
1.class继承
2.prototype继承
27.如何捕获js中的异常?
答:
高风险处:
其他地方:
27.什么是JSON?
答:1.json是一种数据格式,本质是一段字符串;
2.json格式和js对象结构一致,对js语言更友好
3.window.JSON是一个全局对象:JSON.stringify(对象转化成字符串)和JSON.parse(字符串转化成对象)
//json里面不能用单引号
28.获取当前页面url参数
答:1.传统方式:查找location.search
2.新api:URLSearchParams
29.将url参数解析为js对象
答:1.传统
2.新api:
30.手写数组flatern,考虑多层级
答:1.递归
2.reduce
3.toString & split
4.join & split
5.es6扩展运算符
[].concat(...[1, 2, 3, [4, 5]]); // [1, 2, 3, 4, 5]
31.数组去重
答:
1.传统方式:遍历元素逐个比较,去重
2.set(无序,不能重复),效率高
32.手写深拷贝
注意:Object.assign不是深拷贝,是浅层的
因此,注意Object.assign是浅拷贝
33.介绍RAF requestAnimationFrame
答:
1.要想动画流畅,更新频率要60帧/s,即16.67ms更新一次视图
2.setTimeout要手动控制频率,而RAF浏览器会自动控制
3.后台标签或隐藏iframe中,RAF会暂停,而setTimeout依然执行
代码演示:
1.用setTimeout方法:时间需要自己计算控制
2.用
requestAnimationFrame
方法,无需自己控制时间,推荐如何性能优化,从哪几方面考虑?
答:1.原则:多使用内存、缓存、减少计算、减少网络请求
2.方向:加载页面,页面渲染,页面操作流畅度