本文主要记录工作时JS问题或经验
new Array()的遍历问题
new Array(3)这样得出的稀疏数组不能够进行遍历,需要进行特殊的转义才能够遍历
webkitTransitionEnd监听Transition动画结束事件
css3的过渡属性transition,在动画结束时,也存在结束的事件:webkitTransitionEnd; 注意:transition,也仅仅有这一个事件。
1 | $el.addEventListener('webkitTransitionEnd', handler, false); |
css3的动画animation也能够监听事件,分别在开始和结束时都能够监听到animationstart,animationend
阻止冒泡问题
stopPropagation()
方法既可以阻止事件冒泡,也可以阻止事件捕获
stopImmediatePropagation()
和 stopPropagation()
的区别在,后者只会阻止冒泡或者是捕获。 但是前者除此之外还会阻止该元素的其他事件发生,但是后者就不会阻止其他事件的发生
arguments的问题
arguments
不是一个真正的数组,是一个类数组,无法使用shift
等数组方法,只能使用Array.prototype.slice.apply(arguments)
转换成真正的数组,或者使用[].shift.call(arguments)
之类的方法来调用数组的方法
img作为数据统计的问题
img经常进行用于数据上报,做用户埋点什么的,消息已读什么的,就是将query放在图片的请求地址上就可以了。需要注意在使用img进行http请求时,img对象需要存储在闭包里,避免函数执行后,里面的变量在http请求完成前被销毁,导致发送不成功,会造成请求丢失的问题
例子(使用闭包封装img变量,避免销毁):
1 | var tracker = (function() { |
js数组的push实际是进行复制
下面类似v8的实现源码:
1 | Array.prototype.arrayPush = function() { |
arguments.callee就是执行函数本身,常用是递归
flex布局左右分配规则
1 | <div class="test"> |
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto
- flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
- flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
- flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小,占据固定空间。
script也有onload
1 | var script = document.createElement('script'); |
img懒加载完美版
js实现图片加载前显示loading,先设置一个自定义属性,然后img的src先指向loading的位置,然后每个img依次使用new Image()
,预加载好自定义属性里的图片地址,待预加载好了的时候,使用onload将img的地址指向真正的src
实例:
1 | <div> |
1 | // 节流函数 |
倒序遍历:
1 | for (var l = arr.length - 1; l >= 0; l--) { |
关于es6中的WeakSet和WeakMap:
(Weak都是为了对象解决引用内存的问题)
- WeakMap 只能用Object作为key,不能用基本数据类型比如字符串作为key
- WeakMap 中的key是弱引用
- WeakMap 没有size
- WeakMap 不支持遍历
没有size和不支持遍历的原因是,由于Weak内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此ES6规定Weak不可遍历
Map 的一个最大弊端就是它会导致作为key的对象增加一个引用,因此导致GC无法回收这个对象,如果大量使用object作为Map的key会导致大量的内存泄露
WeakMap就是为了解决这个问题,在WeakMap中对作为key的对象是一个弱引用,也就是说,GC在计算对象引用数量的时候并不会把弱引用计算进去。这样当一个对象除了WeakMap没有其他引用的时候就会被GC
自定义事件的创造和触发(version >= ie9)
1 | // 建立自定义事件创造函数 |
注意,手动触发事件更好的做法是 IE 下用 fireEvent,标准浏览器下用 dispatchEvent 实现
可以使用array.length = 0来清空一个数组
客户端的cookies和服务端的session
http是一种无状态的协议,就是收到一个请求,就返回一个响应,而不关心请求者的身份。cookie就是在用户端保存请求信息的机制,每次请求的时候请求头都会携带cookie
cookie是一个分号分隔的多个key-value的字段,它也存在于本地的加密文件里,但只有浏览器能够操作它,本地打开是加密后文件
cookie有几个字段:
- name:代表cookie的名称
- domain:cookie生效的域名,有作用域概念的,比如说二级域名能够使用一级域名的cookie,但不能使用其他二级域名,也不能操作所处的三级域名的cookie
- path:cookie的生效路径,同一域名下又不同路径的cookie,也是无法操作的
- expires:cookie的过期时间,如果不设置这个字段的话,就会在浏览器关闭的时候这个cookie就会被删除。expires的值是标准的日期格式,一般使用new Date().toUTCString()的值
- HttpOnly:由服务端进行设定,并且用户端无法更改这个cookie,防止XSS恶意修改cookie,HttpOnly并不能绝对防止XSS
- 删除对应的cookie只有将某条cookie(其他字段一样)的过期时间改成已经过去的时间,即可删除
session机制:
- session是服务端保存请求信息的机制,记录请求者身份
- 一般由服务端接到请求后,由服务端生成一个sessionID,然后将这个id写进请求用户端的cookie里,并且设定HttpOnly,这样每次客户端请求的时候,携带sessionID来请求,就能识别用户身份
- 生成的sessionID并不一定要种在cookie里,也可以放在请求参数里,或者在http的请求头里开辟一个token字段
- 一般都是在Response的Raw里Set-Cookie可以查看到服务端往客户端写cookie的操作
标准写法:
1 | document.cookie = 'name=yuuhei;domain=baidu.com;path=/;expires=Mon, 26 Nov 2018 12:11:10 GMT' |
关于使用async/await进行请求的使用方法
使用了async的函数一般默认返回都是返回一个Promise对象
async/await会由于写法的不同产生的请求时机也会不一样,如以下代码:
首先定义一个模拟的请求函数:
1 | // 模拟promise请求 |
接着写执行函数:
1 | const mainStep = async function() { |
注意以上代码,他们只会p1请求完毕只会,才会再请求p2,p2请求完毕后才会请求p3,这是一个同步的代码,并不是并行的代码,如果需要并行则需要改成以下代码:
1 | const mainAll = async function() { |
当然也可以使用PromiseAll处理并发,换个方式:
1 | const mainPromiseAll = async function() { |
async/await同样可以使用Promise.all进行错误捕获:
await的Promise.all的错误捕获之一,统一在Promise.all中的catch捕获
1 | const doRequest1 = async () => { |
await的Promise.all的错误捕获之二,分别在里面的Promise都进行try-catch处理,Promise.all里的catch就不会再执行
1 | const doRequest1 = async () => { |
不使用Promise.all也可以达到效果的同步代码:
1 | async function doRequest() { |
以上代码p1、p2和p3并行完成后才会继续走next后的代码
在请求头使用Token
在使用JSON Web Token作为单点登录的验证媒介时,为保证安全性,建议将JWT的信息存放在HTTP的请求头中,并使用https对请求链接进行加密传输
当在进行跨域请求的时候,如自定义请求头,如添加token字段,属于复杂请求,那么HTTP请求会发出一个预检请求,即OPTIONS请求,访问服务器是否允许该请求,如果没有进行设置,服务器会返回403 Forbidden,需要在服务端也要定义好自定义的请求头字段,才能响应响应预检请求,ajax工具可以在beforeSend进行对xhr.setRequestHeader进行设置请求头
数组的indexOf也可以用来判断对象的位置,传入对象引用的内存地址就能够找到
1 | let obj1 = {a: 1, b: 2}; |
String类型也可以使用slice,用法与Array差不多
JS标准快排原理
1 | function quickSort(array) { |
- 递归赋值
1 | /** |
iOS下调用元素的focus方法,input元素不聚焦问题
注意,在iOS有一个兼容问题,就是如果在钩子函数里调用$el.focus(),或异步调用$el.focus()的话,是不成功的。
google有以下解释:
iOS将只允许在其他元素上绑定函数来触发focus事件,如果第一个函数调用栈是由非编程触发的事件,调用setTimeout开始一个新的调用堆栈,IOS的安全机制开始阻止你触发input元素的focus事件。在函数里使用异步也是不行的
钩子函数无法触发例子:
1 | export default { |
上述代码应该很多人会这么做,需求背景时:点击某个元素,弹框出现,然后自动聚焦在弹框的input上
异步函数无法触发例子:
1 | export default { |
既然钩子函数无法触发,那就使用点击事件,其中visable对应v-show和v-if都是一样的,然后在异步调用focus
以上两个例子在真机上,安卓是可以的,iOS是失效的。
那怎么办,暂时找到的唯一方法:使input元素一直存在于页面中,只是对用户不可见而已(位移移出可视区域),然后调用focus,iOS才能生效
成功例子:
1 | export default { |
在iOS中,所有异步调用setTimeout,Promise,nextTick等,调用focus()均无效
超长String转Number后会有问题
注意:Number类型超过16位的数字会变成0,且第16位会加一
在对String进行转换为Number时需要注意长度问题
使用递归格式化用于级联列表的格式
从后台返回的树状列表结构,一般是这种形式:
1 | [ |
而级联组件一般的格式就是如此,但由于后端返回来的字段各种各样,前端也需要进行一个数据的格式化,需要将格式变为以下如此:
1 | [ |
可以发现,这类数据都有着重复的规律,这种就可以使用递归进行遍历:
1 | // 递归处理树状结构 |
关于document.write的问题
document.write写入的内容是叠加的,不是覆盖写入,可以做一些如下操作:
1 | document.write('<link rel="stylesheet" type="text/css" href="css/reset.css">') |
可以动态加载注入外部的css文件
前端生成的base64图片或文件如何下载
前端下载的原理是通过在a标签上定义href和download属性,download在同源里可以设置下载的文件名,然后通过自定义时间触发a标签的点击,就可以进行下载了:
1 | // data可以为base64,或后端的文件地址 |
前端生成pdf并下载
可以通过html2canvas和jsPDF配合生成下载,具体教程可以网上搜