对象转基本类型
//Symbol.toPrimitive的优先级最高let a = { valueOf(){ return 0; }, toString(){ return '1'; }, [Symbol.toPrimitive](){ return 2; }}1+a // =>3 '1' + a //=>'12'复制代码
四则运算符
1 + '1' //'11'2 * '2' //4[1,2] + [2, 1] //'1,22,1'//[1,2].toString() ->'1,2'//[2,1].toString() ->'2,1'//'1,2' + '2,1' = '1,22,1''a' + + 'b' //->"aNaN"//+ 'b' --->NaN//+ '1' ->1复制代码
== 操作符
[] == ![] // -> true 解析如下//[] 转成true ,然后取反变成 false [] == false 则 [] == ToNumber(false)则 [] == 0 则 ToPrimitive([]) == 0//[].toString() ->''则 '' == 0则 0 == 0 ---> true 复制代码
闭包
定义:函数A返回一个函数B,并且函数B中使用了函数A的变量,则函数B就被称为闭包
function A(){ let a = 1 function B(){ console.log(a) } return B }复制代码
经典面试题:循环中使用闭包解决var定义函数的问题for( var i=1;i<=5; i++ ){ setTimeout( function timer() { console.log( i ); }, i*1000);}由于setTimeout 是个异步函数,所以会先把循环全部执行完毕,这时 i 是6了;所以会输出一堆的6 复制代码
解决方法:* 1.使用闭包for (var i = 1; i <=5 ;i++ ){ ( function(j){ setTimeout(function timer() { console.log(j); }, j * 1000); })(i)}* 2.使用setTimeout的第三个参数for (var i = 1; i <= 5; i++){ setTimeout(function timer(j){ console.log(j) }, i*1000, i);}* 3.使用 let定义 i for (let i = 1; i <= 5; i++ ){ setTimeout( function timer(){ console.log( i ); },i*1000); }复制代码
深浅拷贝
let a = { age : 1}let b = a a.age = 2 console.log(b.age) //2 即 若给一个变量赋值一个对象,那么两者的值会是同一个引用,其中一方改变,另一方也会相应的改变。复制代码
解决以上问题方式如下:
浅拷贝: Object.assign 或 展开运算符 (浅拷贝只能解决第一层问题)
let a = { age : 1}let b = Object.assign({}, a) 或 let b = {...a}a.age = 2 console.log( b.age) //1复制代码
深拷贝 JSON.parse(JSON.stringify(object)
let a = { age : 1, jobs: { first:'FE' }}let b = JSON.parse(JSON.stringify(a)a.jobs.first = 'native'console.log(b.jobs.first) //FE 复制代码
![](https://user-gold-cdn.xitu.io/2019/1/1
模板化 在右Babel的情况下,可用直接使用ES6的模板化 commonJS是node独有的规范
防抖
在滚动事件中需要做一个复杂计算或者 实现一个按钮的方二次点击操作。
(频繁的事件回调中做复杂计算,很可能造成页面卡顿,所以不如将多次计算合并为一次计算,只在一个精确点做操作)
ps:防抖和节流 的作用是防止函数多次调用;区别:假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的情况 会每隔一定时间(参数wait)调用函数。
const debounce = (func, wait = 50) =>{ //缓存 一个定时器id let timer = 0 // return function(...args) { if(timer) clearTimeout(timer) timer = setTimeout(() =>{ func.apply(this, args) },wait) }}复制代码
- 搜索时,用户输入完最后一个字才调用查询接口, 延迟执行的 防抖函数
- 点赞 如star是,立马反馈是否star成功,使用立即执行的防抖函数
function debounce (func ,wait = 50 ,immediate = true){ let timer, context,args; const later = () =>setTimeOut(() => { timer = null if(!immediate){ func.apply(context,args) context = args = null } },wait) return function(...params) { if(!timer){ timer = later() if(immediate){ func.apply(this,params) }else{ context = this args = params } }else{ clearTimeout(timer) timer = later() } }}复制代码
总结:
- 对于按钮放点击来说的实现:如果函数是立即执行的,就立即调用,如果函数是延迟执行的,就缓存上下文和参数,放到延迟函数中去执行。一旦我开始一个定时器,只要我定时器还在,你每次点击我都重新计时。 一旦你点累,定时器时间到,定时器重置为null,就可以再次点击了。
- 对于延迟执行函数来说的实现:清除定时器id,如果是延迟调用就调用函数
节流
节流和防抖动的本质是不一样的。防抖动是 多次执行变成最后一次执行,节流是将多次执行变成每隔一段时间执行。
继承
class MyDate extend Date{ test(){ rreturn this.getTime() }}let myDate = new MyDate()myDate.test()复制代码
需要用Babel来编译代码 底层限制:若不是Date构造出来的实例,就不能调用Date的函数
function MyData(){ }MyData.prototype.test = function(){ retrun this.getTime()}let d = new Date()Object.setPrototypeOf(d,MyData.prototype)Object.setPrototypeOf(MyData.prototype,Date.prototype)即先创建父类实例 =》 改变实例原先的_proto_转而连接到子类的prototype=》子类的prototype的 _proto_改为父类的prototype通过这种方式实现的继承可以完美解决 JS底层限制复制代码
call ,apply ,bind区别
call和apply: 都是为了解决改变this的指向,只是传参的方式不同。 除了第一个参数外,call可以接收一个参数列表, apply只接受一个参数数组。 bind:作用也是解决改变this的指向,只是该方法返回一个函数。
let a = { value:1}function getValue(name ,age){ console.log(name) console.log(age) console.log(this.value) }getValue.call(a,'yck','24')getValue.apply(a,['yck','24'])复制代码
Promise 实现
Promise是Es6 新增的语法,解决了回调地狱的问题 看成一个状态机: 初始pending,通过函数resolve 和reject 将状态改成了 resolved和 rejected 状态,并且状态一旦改变就不能再次变化
then函数会返回一个Promise实例,并且该返回值是一个新的实例而不是之前的实例。因为Promise规范规定处理pending状态,其他状态是不可以改变的, 所以then本质上可以看成是 flatMap
Generator实现
Generator是ES6中新增的语法,都可以异步编程。
function* test(){ let a = 1+2; yield 2; yield 3;}let b = test();console.log(b.next()复制代码