一、理解 apply、call、bind之间的区别(都是函数的方法)
说在前面
var name = '张三'
var age = 20
var obj = {
Name:this.name,
age:12,
myFn:function(){
console.log(this.name+'年龄'+this.age)
}
}
var obj1 = {
name:'李四',
age:45
}
obj.myfn() //undefined 年龄 12
-- 关于this指向 :
1、谁调用这个函数,this指向谁,this.age, obj调用 myFn ,所以this指向obj这个对象
2、this.name 中由于是全局调用name,所以this指向window
说了这么多,那么他们的作用的是什么呢????
obj.myFn.apply(obj1) //李四 年龄 45
obj.myFn.call(obj1) //李四 年龄 45
obj.myFn.bind(obj1)() //李四 年龄 45 //bind返回的事一个新的函数,必须调用他才会执行
如何理解?可以这么理解,person有个方法fullName调用this.firstName和this.lastName这两个变量,this指向person这个对象,但它是没有这两个变量的。
apply可以改变调用apply方法的函数(这里指的就是person.fullName这个函数)的this指向,现在person.fullName.apply(person1)就让this指向person1了,fullName方法就可以访问到这两个值,就成功输出。
1.1、apply()、call()、bind()传参的区别
var name = '张三'
var age = 20
var obj = {
Name:this.name,
age:12,
myFun:function(f,to){
console.log(this.name+'年龄'+this.age+'来自'+f+'去往'+to)
}
}
var db = {
name:'李四',
age:45
}
obj.myFun('北京','上海') //undefined年龄12来自北京去往上海
obj.myFun.call(db,'成都','上海'); // 李四年龄45来自成都去往上海
obj.myFun.apply(db,['成都','上海']); // 李四年龄45来自成都去往上海
obj.myFun.bind(db,'成都','上海')(); // 李四年龄45来自成都去往上海
obj.myFun.bind(db,['成都','上海'])(); // 李四年龄45来自成都,上海去往undefined
从上面四个结果不难看出:
1、 call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象
2、call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔
3、apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,[‘成都’, …, ‘string’ ])。
4、bind 除了返回是函数以外,它 的参数和 call 一样。
5、三者的参数不限定是 string 类型,允许是各种类型,包括函数 、 object 等等!
1.2、应用
利用apply()求最大值
var arr =[2,6,8,3,4,9,7,23,56,889];
console.log(Math.max.apply(arr,arr)) //第一个arr表示让arr借用max这个方法,第二个arr表示传给max的数据
//apply()所执行的操作:1.执行Math.max(1,2,3,5,4) 2.把内部的this改成arr
Math.max.apply(arr,arr)//889 apply 方法 求最大值
Math.max(arr)//NAN 这种方法不对
Math.max(...arr)//889 扩展运算符求最大值
二、防抖和节流
-
防抖:所谓防抖就是限制用户在一定时间内只能点击一次,执行最后一次
-
节流:所谓防抖就是限制用户在一定时间内只能点击一次,执行第一次
2.1、防抖
场景:在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。这些需求都可以通过函数防抖动来实现。尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导致页面卡顿,不如将多次计算合并为一次计算,只在一个精确点做操作。
一般的防抖会有immediate选项,表示是否立即调用。methods:{ //实现一个防抖函数的封装 debounce(fn,delay){ let timer = null; let that = this; return function(){ if(timer!=null){ clearInertval(timer) } timer = setTimeout(()=>{ fn.apply(that) },dalay) } } }
全部代码(demo)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <input type="text"> <script> var input = document.querySelector('input');//获取元素dom input.oninput = debounce(function() { console.log(this.value); },500) //fu是是搜索后要执行的事件 //delay是延迟的时间 function debounce(fn,delay){ let timer = null; return function() { let that = this; if(timer!==null){ clearTimeout(timer) } timer = setTimeout(() => { fn.apply(that)//改变this指向 }, delay); } } </script> </body> </html>
基于vue实现 防抖函数
<template> <div> <el-input v-model="form.name" @input="search"/> </div> </template>
<script> export default { data(){ form:{ name:'' }, timer:null, } }, methods:{ //防抖逻辑 debounce(fn,dalay){ //let timer = null; let that = this; return function(){ if(that.timer!==null){ clearTimeout(that.timer) } that.timer = setTimeout(()=>{ fn() },dalay) } }, //实现搜索的逻辑,以及请求ajax searchInfo(){ console.log(this.form.name) }, //用户输入时触发 search(){ //执行防抖逻辑 的函数 注意:结尾加()来执行函数 this.debounce(this.searchInfo,500)() } } </script>
2.2、节流
防抖和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
思路:每次触发事件时都判断当前是否有等待执行的延时函数。
methods:{ throttle(fn,delay){ let flag = true return function(){ if(flag){ setTimeout(()=>{ fn.call(this) flag = true },delay) flag = false } } } }
全部代码(demo)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div style="height:10000px"></div> <script> window.onscroll = throttle(()=>{ console.log(1); },1000) function throttle(fn,delay){ let flag = true return function(){ if(flag){ setTimeout(()=>{ fn.call(this) flag = true },delay) } flag = false } } </script> </body> </html>
三、事件循环
1.1、宏任务 微任务
-
宏任务:script(整体代码)、setTimeout、setInterval、setImmediate、I/O、UI rendering
-
微任务:promise、object.observe、MutationObserve
任务的优先级:promise.then->setTimeout->setImmediate
微任务一直在宏任务之后,当前宏任务执行完之后就会执行下一次循环,一次接着一次
console.log("log") setTimeout(()=>{ console.log("setTimeout") },0) new Promise((resove,reject)=>{ console.log('promise') resove('data') }).then(res=>{ console.log(res) }) console.log('log1')
执行顺序:log->promise->log1->data->setTimeout
遇到宏任务放一遍,先执行当前的宏任务,当前宏任务下遇到微任务先排队
console.log('task start'); setTimeout(()=>{ console.log('setTimeout1') },0) new Promise((resolve, reject)=>{ console.log('new Promise1') resolve() }).then(()=>{ console.log('Promise.then1') setTimeout(()=>{ console.log('setTimeout2') },0) new Promise((resolve, reject)=>{ console.log('new Promise2') resolve() }).then(()=>{ console.log('Promise.then2') }) }) console.log('task end'); //----------------------执行结果---------------------- // task start // new Promise1 // task end // Promise.then1 // new Promise2 // Promise.then2 // setTimeout1 // setTimeout2
宏任务微任务运行机制:
在了解了宏任务和微任务之后,整个Event Loop的流程图就可以用下面的流程图来概括:
**简而言之:**js是一门单线程语言,一次只能做一件事,单线程会导致事件的阻塞,所以js分了同步和异步,同步任务会立即执行执行栈中的任务,异步任务分配给浏览器管理,放置到任务队列中,当同步任务完成以后就会读取队列中的异步任务,拿到主线程中去执行,这样不断重复就是事件循环
-