目录
如果有小伙伴想要回顾之前的知识的话,点击下面的链接查看之前所有的学习栏目👇:
《大前端学习专栏:目录》
高阶函数
函数套着函数的现象,我们称外层函数是一个高阶函数。React中的高阶组件,其本质就是一个高阶函数。
函数作为参数
函数可以作为另一个函数的参数,这样可以使外层的函数可以更加灵活。
以下是两个将函数作为参数的实例,分别模拟了forEach
和filter
:
//forEach
function forEach(list, fn) {
for (let index = 0; index < list.length; index++) {
const element = list[index];
fn(element);
}
}
//测试
let list = [1, 2, 3];
forEach(list, (item) => console.log(item)); //1 2 3
filter
:
//filter
function filter(list, fn) {
let newList = [];
for (let index = 0; index < list.length; index++) {
const element = list[index];
if (fn(element)) {
newList.push(element);
}
}
return newList;
}
//测试
let list = [1, 2, 3];
let newList = filter(list, (item) => item === 1);
console.log(newList); // [1]
将参数变成函数这样写的好处在哪里?
将函数作为参数一大好处就是可以将一部分逻辑抽离出去,不必关注这部分的具体实现,方便各个模块解耦。
函数作为另一个函数的返回值
其本质是让一个函数去生成另外一个函数,使返回的函数拥有更强大的功能。
以下是一个基础的示例:
function makeFn() {
return function () {
console.log('hello');
};
}
let fn = makeFn();
fn();
// makeFn()(); 也可以这么调用
在Vue中,有一个API是$once
,表示监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除。我们这里使用高阶函数来模拟一下$once
:
//无论点击几次都只能支付一次
function $once(fn) {
let done = false;
return function (...args) {
if (done) return;
done = true;
return fn.apply(this, args);
};
}
const pay = $once(function (money) {
console.log(`我支付了${money}`);
});
pay(5); // 我支付了5
pay(5); //
pay(5); //
闭包
闭包(Closure):函数和其周围的状态(词法环境)的引用捆绑在一次形成闭包结构。
闭包结构可以在另一个作用域中调用一个函数的内部函数,并访问到内部函数作用域中的成员。
以下是一个简单的闭包结构:
function makeFn(){
let msg = "hello world";
return function(){
console.log(msg);
}
}
let fn = makeFn();
fn(); // hello world
以上示例中,内部函数引用了外部函数的msg
变量,导致外部函数调用完毕之后作用域无法释放,被内部函数所牵引,形成了一个闭包结构。
闭包的本质
函数在执行的时候会放到一个执行栈中,当函数执行完毕后会从执行栈中被移除,但是堆上的作用域成员因为被外部引用无法释放,因此内部函数依然可以访问到外部函数的成员。
闭包在开发中的应用
在Vue2.0源码中使用了很多的闭包技巧来保存变量,以下是Vue2.0源码中缓存函数结果的实现:
export function cached<F: Function> (fn: F): F {
const cache = Object.create(null)
return (function cachedFn (str: string) {
const hit = cache[str]
return hit || (cache[str] = fn(str))
}: any)
}
内部函数cachedFn
引用外部函数的局部变量cache
,形成了一个闭包结构。
Vue2.0源码中定义响应式对象:
export function defineReactive(
obj: Object,
key: string,
val: any
) {
const dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
//访问该值时,依赖收集
get: function reactiveGetter() {
dep.depend();
return val;
},
//设置该值时,派发更新
set: function reactiveSetter(newVal) {
val = newVal;
dep.notify();
},
});
}
内部函数get
和set
引用了外部函数的val
和dep
,形成了一个闭包结构,便于在之后的逻辑中重用这两个变量。
由此可见,使用闭包的好处是可以重用变量,节省多余代码。