用代码解释代码,用函数证明函数
基本概念
高阶函数主要指:
- 这个函数中的参数是函数
- 这个函数的返回值是函数
before函数(装饰函数,AOP)
在执行一个函数之前去执行另一个函数
const f1 = ()=>{
console.log("正在执行任务...")
}
// 要在f1之前打印 开始...
Function.prototype.before = function(beforeFn){ // beforeFn叫回调函数
// 箭头函数中没有this
return ()=>{
beforeFn();
this(); // 谁调用了before函数,this就指谁
}
}
// f2是返回的新函数
const f2 = f1.before(()=>{
console.log("开始...")
})
f2()
高阶传参
const f1 = (...args)=>{
console.log("正在执行任务...",args)
}
Function.prototype.before = function(beforeFn){ // beforeFn叫回调函数
return (...args)=>{ // ...args 叫rest参数 用来接收1,2,3
beforeFn();
this(...args); // 把1,2,3继续传递给f1
}
}
const f2 = f1.before(()=>{
console.log("开始...")
})
f2(1,2,3)
包括函数
包括函数:在执行某个函数之前执行若干个函数,在执行某个函数之后执行若干个函数,事务函数
把之前的函数存起来,把之后的函数存起来,就把它存储到对象
let f1 = function(){
console.log("正在执行任务...")
}
let wrappers = [
{
// warpper
init(){
console.log("hello 1")
},
close(){
console.log("bye 1")
}
},
{
// warpper
init(){
console.log("hello 2")
},
close(){
console.log("bye 2")
}
}
]
// 定义一个函数,把上面两者结合起来
const work = (core,wrappers)=>{
// core是核心函数
wrappers.forEach(wrap=>{
wrap.init()
})
core()
wrappers.forEach(wrap=>{
wrap.close()
})
}
work(f1,wrappers)
让人想起的了函数柯里化
curring又称部分求值。一个curring的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数中被真正的需要求值的时候,之前传入的所有参数被一次性用于求值。
after函数
调用一个函数n次后,触发另一个函数,用after函数实现
const after = (times,fn)=>{
return ()=>{
if(--times === 0){
fn()
}
}
}
const f1 = after(3,()=>{
console.log("调用3次后才执行...")
})
f1()
f1()
f1()
多次调用才执行
我又想到了函数节流。。。。
在一定的时间内,函数只触发一次,能大大降低了频率问题
function throttle(fn, wait) {
let last = 0
return () => {
var now = +new Date().getMilliseconds();
if (now - last > wait) {
fn()
// console.log(now-last)
last = now;
} else {
// console.log(now-last)
console.log("什么情况")
}
}
}
const f3 = throttle(() => {
console.log('ZZZZ.....')
}, 500);
f3()
all函数
const fs = require("fs")
let content = {};
let index = 0;
fs.readFile("name.txt","utf8",(err,data)=>{
content['name'] = data
index++;
out()
})
fs.readFile("age.txt","utf8",(err,data)=>{
content['age'] = data
index++;
out()
})
function out(){
if(index == 2){
console.log(content)
}
}
const fs = require("fs")
let content = {};
const after = (times,fn)=>{
return ()=>{
if(--times === 0){
fn()
}
}
}
let newAfter = after(2,()=>{
console.log(content)
})
fs.readFile("./name.txt","utf8",(err,data)=>{
content['name'] = data
newAfter()
})
fs.readFile("./age.txt","utf8",(err,data)=>{
content['age'] = data
newAfter()
})
// 两者都成功才输出结果
emmmm…这让人想起了函数分时。。。。真的(〃‘▽’〃)
和节流函数的应用场景不同,当短时间内接受的数据过多过大,可能会有影响效率等问题,特别是如果我们需要在短时间内才页面中插入大量的DOM节点,那显然会让浏览器吃不消,这可能会引起浏览器的假死,所以我们需要进行分时函数,分批插入。
// 分时函数
var timeChunk = function (ary, fn, count) {
var timer;
var start = function () {
for (var i = 0; i < Math.min(count || 1, ary.length); i++) {
fn && fn(ary.shift())
}
};
return function () {
timer = setInterval(function () {
if (ary.length === 0) { // 如果全部节点都已经被创建好
return clearInterval(timer);
}
start();
}, 200); // 分批执行的时间间隔,也可以用参数的形式传入
};
};
var ary = [];
for (var i = 1; i <= 1000; i++) {
ary.push(i);
};
var renderFriendList = timeChunk(ary, function (item) {
console.log(item)
}, 8);
renderFriendList();
发布订阅
一种概念,订阅时把函数保存到一个容器中,发布时拿到这个容器并把里面的函数一一执行
订阅和发布之间没有关系
const fs = require("fs")
let content = {};
let e = { //订阅
arr:[],
on(fn){
this.arr.push(fn)
},
emit(){
this.arr.forEach(fn=>fn())
}
}
e.on(()=>{
// console.log("订阅了... ")
if(Object.keys(content).length === 2){
console.log(content)
}
})
e.emit() // 发布
fs.readFile("name.txt","utf8",(err,data)=>{
content["name"] = data;
e.emit() // 发布
})
fs.readFile("age.txt","utf8",(err,data)=>{
content["age"] = data;
e.emit() // 发布
})
同样的,既然可以保存函数到最后一起执行,又怎么可能不能保存多个呢
let e = {
arr:[],
on(fn){
this.arr.push(fn)
},
emit(){
this.arr.forEach(fn=>fn())
}
}
e.on(()=>{
console.log("哈哈1")
})
e.on(()=>{
console.log("哈哈2")
})
e.on(()=>{
console.log("哈哈3")
})
e.emit()
观察者模式
观察者模式分为观察者和被观察者(在被观察者中存储观察者),观察者模式是基于订阅和发布,但二者之间有关系
// 被观察者
class Subject{
constructor(){
this.arr = [] // 存储谁在观察者
this.state = "很开心"
}
attach(o){
this.arr.push(o)
}
setState(newState){
this.state = newState
this.arr.forEach(o=>o.update(newState))
}
}
// 观察者
class Observer{
constructor(name){
this.name = name;
}
update(newState){
console.log(this.name,"小宝宝状态:",newState)
}
}
let s = new Subject("小宝宝")
let o1 = new Observer("我")
let o2 = new Observer("我媳妇")
s.attach(o1) // 我 小宝宝状态: 不开心
s.attach(o2) // 我媳妇 小宝宝状态: 不开心
s.setState("不开心")
// console.log(s.state)
一点cones
惰性加载函数
有两种实现惰性载入的方式,第一种事函数在第一次调用时,对函数本身进行二次处理,该函数会被覆盖为符合分支条件的函数,这样对原函数的调用就不用再经过执行的分支了,我们可以用下面的方式使用惰性载入重写addEvent()。
function addEvent (type, element, fun) {
if (element.addEventListener) {
addEvent = function (type, element, fun) {
element.addEventListener(type, fun, false);
}
} else if(element.attachEvent){
addEvent = function (type, element, fun) {
element.attachEvent('on' + type, fun);
}
} else{
addEvent = function (type, element, fun) {
element['on' + type] = fun;
}
}
return addEvent(type, element, fun);
}
在这个惰性载入的addEvent()中,if语句的每个分支都会为addEvent变量赋值,有效覆盖了原函数。最后一步便是调用了新赋函数。下一次调用addEvent()的时候,便会直接调用新赋值的函数,这样就不用再执行if语句了。
第二种实现惰性载入的方式是在声明函数时就指定适当的函数。这样在第一次调用函数时就不会损失性能了,只在代码加载时会损失一点性能。一下就是按照这一思路重写的addEvent()。
var addEvent = (function () {
if (document.addEventListener) {
return function (type, element, fun) {
element.addEventListener(type, fun, false);
}
}
else if (document.attachEvent) {
return function (type, element, fun) {
element.attachEvent('on' + type, fun);
}
}
else {
return function (type, element, fun) {
element['on' + type] = fun;
}
}
})();
This`s all,thanks!