什么是闭包
能够读取其他函数内部变量的函数就是闭包
1.提问:两个函数嵌套,一个函数能够访问另外一个函数内部变量的函数就形成了闭包,这个时候会造成内存泄漏吗?
答:不一定,只有内部函数被return,保存到外部,并且被执行的时候才造成内存泄漏
2. 闭包的实战应用
- 回调函数是闭包吗?
是闭包
function add(num1, num2, callback) {
var sum = num1 + num2
if(typeof callback === 'function') { // 通过类型判断是否是一个函数
callback(sum)
}
}
add(1, 2, function(sum) {
console.log(sum)
})
- 手写js方法, bind方法
var name ='s'
function a() {
console.log(arguments[0]+this.name + ',' + arguments[1] + arguments[2] );
// 我是nina,今年12岁
}
var b = {
name: "nina",
};
Function.prototype.newBind = function () {
if (typeof this !== "function") throw "caller must be a function";
let self = this;
let context = arguments[0];
let args = Array.prototype.slice.call(arguments, 1); // 得到bind上的除this指向的剩余参数
let fn = function () {
let newArgs = Array.prototype.slice.call(arguments); // 得到回调函数上面的所有参数
// 改变this指针指向,并且传递参数
self.apply(this instanceof self ? this : context, args.concat(newArgs));
};
fn.prototype = Object.create(self.prototype); // 维护原型
return fn;
};
a.newBind(b, "我是")("今年", "12岁");
- 防抖节流是闭包
function ajaxFn() {
console.log('发起请求');
}
// 防抖 只维护一个定时器,有新的进来,则清除旧定时器,再开始操作
function shake(fn, wait) {
let timer = null;
return function() {
if (timer) clearTimeout(timer);
timer = setTimeout(fn, wait);
};
}
window.addEventListener('resize', shake(ajaxFn, 2000))
// 节流 在定时器结束以后才能继续执行下一次操作
function throttle(fn, wait) {
let timer = null
return function() {
if(timer) return
timer = setTimeout(() => {
fn()
timer = null
}, wait)
}
}
window.addEventListener('scroll', throttle(ajaxFn, 2000))
- 单例模式是闭包
// 单例模式也称为单体模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点
// 比如:一个班级只有一个班主任,只有一个太阳,一个国家只有一个主席这些 “唯一” “便于访问(全局访问)” 的行为对象便称作是单例
let Person = function(name) {
this.name = name;
}
Person.prototype.getName = function() {
return this.name;
}
Person.getInstance = (function() {
let instance = null;
return function(name) {
if(!instance) {
instance = new Singleton(name)
}
return instance;
}
})()
let instance1 = Singleton.getInstance('why');
let instance2 = Singleton.getInstance('www');
console.log(instance1 === instance2); // 输出true
- 定时器穿参是闭包
function aa(a) {
return function() {
console.log(a)
}
}
setTimeout(aa(11), 1000)
- 利用闭包判断数据类型
function isType(type) {
return function(target) { // type Array
// 第一个 [object Array]
// 第二个 [object Object]
return `[object ${type}]` === Object.prototype.toString.call(target)
}
}
const isArray = isType('Array')
console.log(isArray([1,2,3])) // true
console.log(isArray({})) // false
- 封装私有变量和函数
function createPerson(name) {
var age = 0
return {
getName: function(){
return name
},
getAge: function() {
return age
},
setAge: function(newAge) {
age = newAge
}
}
}
var person = new createPerson('summer')
console.log(person.getName()) // 'summer'
console.log(person.getAge()) // 0
person.setAge(15)
console.log(person.getAge()) // 15
- 高阶函数
- 高阶函数除了可以接受函数作为参数以外,还可以将函数作为结果值返回
- 实现对一个数组的求和
function sum(arr) {
return arr.reduce((a, b) => a+b)
}
console.log(sum([1,2,3,4,5,6,7,8,9,10]) 55
- 但是如果不需要立刻取值,在后续的需求中再去取值,怎么做,这个时候可以不要直接返回求和以后的值,而是返回求和的函数
function lazy_sum(arr) {
let sum = function() {
return arr.reduce((a,b) => a+b)
}
return sum
}
let res = lazy_sum([1,2,3,4,5]) // 返回的是求和的函数
console.log(res()) // 15
- 迭代器(执行一次函数往下取一个值)
var arr = ['aa', 'bb', 'cc']
function inter(arr) {
var i = 0
return function () {
// 这个函数每次被执行 都会返回 arr 对应下标的元素
return arr[i++] || '数组值已遍历完'
}
}
var next = inter(arr)
console.log(next()) // 'aa'
console.log(next()) // 'bb'
console.log(next()) // 'cc'
console.log(next()) // '数组值已遍历完'
- 缓存
比如求和操作,如果没有缓存,每次调用都要重新计算,采用缓存,已经执行过的去查找,查找到了直接返回,不需要重新计算
var fn = (function() {
var cache = {} // 缓存对象
var calc = function (arr) { // 计算函数
return arr.reduce((a,b) => a+b)
}
return function() {
var args = Array.prototype.slice.call(arguments, 0) // 将arguments转化成数组
var key = args.join(',') // 将args用,连接成字符串
var result, tsum = cache[key]
if(tsum) {
// 如果缓存里面有值,则直接赋值给 result 并且返回
console.log(cache) // 打印查看
result = tsum
} else {
result = cache[key] = calc(args)
console.log('存入缓存', cache) // 打印查看
}
return result
}
})()
fn(1,2,3,4,5) // 存入缓存 {1,2,3,4,5: 15}
fn(1,2,3,4,5) // {1,2,3,4,5: 15}
fn(1,2,3,4,5,6) // 存入缓存 {1,2,3,4,5: 15, 1,2,3,4,5,6: 21}
fn(1,2,3,4,5,8) // 存入缓存 {1,2,3,4,5: 15, 1,2,3,4,5,6: 21, 1,2,3,4,5,8: 23}
fn(1,2,3,4,5,6) //{1,2,3,4,5: 15, 1,2,3,4,5,6: 21, 1,2,3,4,5,8: 23}