闭包定义:闭包在函数被定义时被创建,能否形成闭包在于内部函数是否能够直接访问到外界的作用域,与外界的作用域环境捆绑在一起形成的一个包就叫做闭包;
广义闭包:每当创建一个函数就形成一个闭包。
实用闭包:函数环境捆绑外部环境形成一个闭包,形不形成闭包,关键在于这个函数能够之后访问到外部函数的作用域。(一个函数和对其周围状态的引用捆绑在一起)
闭包:在函数被定义时就已经形成闭包。(函数环境捆绑外部环境形成一个包 --> 闭包 --> 这个函数 --> 闭包函数)
高阶函数: 闭包和回调函数任意一个使用在函数上,都是高阶函数。
高阶函数: 具有更强的封装性和集成性,高阶函数要比普通函数处理更多的问题。
此时function fn函数也是一个闭包函数;为什么呢?因为MDN上介绍闭包的定义是:一个函数和对其周围状态(词法环境 lexical environment)的引用捆绑在一起形成的一个包就叫做闭包;也就是说,闭包可以让你在一个内层函数中访问到其外层函数的作用域;
那么此时function fn函数存在全局作用域中,fn函数能够访问当全局作用域,并且与全局作用域的引用捆绑在一起,fn函数可以任意访问到全局作用域;所以此时fn函数就是一个闭包函数,只是一种广义上的闭包形式;
function fn() {}
相比对上面的例子,下面的例子就能更形象的解释广义上的闭包形式;此时test函数是fn函数的内部函数,test函数与fn函数作用域的引用捆绑在一起,并且test函数能够直接访问到fn函数的作用域,所以此时test函数也是一个广义上的闭包函数,这种形式利于开发功能的集成与开发;
function fn() {
function test() {}
}
函数test中参数fn是一个回调函数,判断函数是否是一个闭包,其实也是很简单;MDN上指明:在JavaScript中,每当创建一个函数,闭包就会在函数创建的同时被创建出来;也就是说闭包是在函数被声明的时候就已经确定,并且闭包能够让一个内层函数访问到其外层函数的作用域;
所以test函数中fn回调函数能够访问到test函数的作用域吗?与test函数作用域的引用捆绑在一起吗?很显然是fn回调函数是访问不到test函数的作用域的**,因为对于fn函数来说,它的作用域链上保存着自己函数的AO执行期上下文环境和全局执行期上下文环境GO;所以fn回调函数并不能够访问到test函数的作用域,所以并不符合闭包的定义,所以此时fn回调函数并不是一个闭包函数**;通过实验,fn函数也并不能访问到test函数中的变量a,所以fn回调函数并不是闭包;
function test(fn) {
var a = 1;
fn();
}
test(function() {
console.log(a);
});
实用的闭包,也体现出闭包的现象;此时fn函数中声明一个函数fn2,根据广义性闭包的定义,我们可以知道fn2函数就是一个闭包函数,fn2函数与fn函数作用域的引用捆绑在一起,并且fn2函数能够访问到fn函数的作用域,所以fn2函数是一个闭包函数;但是呢?知道这些还不够。。。
fn函数执行完成后,通过return关键字终止fn函数的执行,并且将函数fn2返回出去(fn2函数此时并未执行,并且返回的不仅是fn2函数,因为fn2函数是闭包函数,所以返回的是fn2函数 + fn函数作用域[[scope]]);在全局环境下,声明变量fn3,将fn函数执行后的返回值赋值给全局变量fn3。那么全局变量fn3保存的是什么呢?变量fn3其实保存的就是fn2函数的引用,所以执行fn3,此时执行的就是fn2闭包函数,打印变量a其实打印的就是闭包函数fn2捆绑fn函数作用域引用中的变量a;
可能中间有个疑问,就是fn函数执行完成,那么它的作用域链和作用域不都销毁的吗?为什么fn2能够依旧能够访问到fn函数的作用域呢?MDN上说:**一个函数和对其周围状态(当前作用域 lexical environment)的引用捆绑在一起形成的一个包就叫做闭包;**闭包是一种现象,虽然fn函数执行完成后作用域销毁,但是fn2函数是闭包函数与fn函数的作用域引用捆绑在一起,导致fn函数AO执行期上下文对象被fn2函数牢牢捆绑,使得函数fn函数AO执行期上下文对象不释放,所以在全局环境下执行fn3变量,依然可以访问到fn作用域中变量a;这种现象只是发挥了闭包的功能;
function fn() {
var a = 1;
function fn2() {
a++;
console.log(a);
}
return fn2;
}
var fn3 = fn();
fn3();
如果说你不通过关键字return将fn2函数返回出去,那么fn2函数现在也是闭包函数,只是没有发挥闭包函数的功能,展现出不闭包函数的能力;
function fn() {
var a = 1;
function fn2() {
a++;
console.log(a);
}
}
fn();
公有变量: public variable
私有变量: private variable
虽然功能上完成,但是有一个小的瑕疵;此时变量a应该是属于Compute类的私有变量,不能够被外界访问的到;但是呢,现在的这种操作,导致变量a可以被外界访问的到,变量a现在属于公有变量;那么怎么操作才能变成Compute类的私有变量呢?
class Compute {
constructor() {
this.a = 1;
}
add(number) {
return this.a + number;
}
miuns(number) {
return this.a - number;
}
}
var computed = new Compute();
console.log(computed.a); // 1
console.log(computed.add(200)); // 201
借助闭包函数;立即执行函数执行后返回Compute类,立即执行函数的作用就是创建一个私有的作用域;此时变量a是立即函数作用域中的变量,而Compute类中的add、miuns函数都是闭包函数,与立即执行函数形成作用域引用捆绑在一起;当在全局环境中实例化Computed对象时,此时打印compute.a为undefined,是因为变量a是Compute类的私有变量,外界访问不到,只有Compute类内部访问的到;而由于add、minus函数是闭包函数,所以在全局环境下依旧能够访问到变量a,并且实现功能;
var Compute = (function() {
var a = 1;
class Compute {
add(number) {
return a + number;
}
minus(number) {
return a - number;
}
}
return Compute;
})();
var computed = new Compute();
console.log(computed.a); // undefined
console.log(computed.add(200)); // 201
javaScript位运算符
// 写一个函数,参数是初始数字,函数提供加减方法和获取值的方法,加减方法参数是每一次都要加减的值
function Calculate(initialNumber) {
// var _initialNumber = Number(initialNumber) >>> 0; 看javaScript位运算符
var _initialNumber = Number(initialNumber);
function add(a){
_initialNumber += a;
}
function minus(b) {
_initialNumber -= b;
}
function getValue(){
return _initialNumber;
}
return {
add:add,
minus:minus,
getValue:getValue
}
}
回调函数: a函数的参数是b函数 ---> b函数就是a函数的回调函数(callback function);
callback: 将任务分为两个部分 ---> 先进行一部分任务, 后进行一部分任务。
函数的调用叫做call, 回调函数的调用callback。
回调的函数意义在于将所有任务分为主函数任务和回调函数任务。
/**
* 回调函数fn --> test ---> 在什么时候都可以执行, fn回调函数不受test函数内部程序的影响。
* 主函数test和回调函数fn完美隔离
* test函数在执行的时候(未执行的回调函数),fn函数在实际参数给形式参数赋值的时候被定义
* test函数执行期 --> 定义另一个函数
**/
function test(num, callback) {
num += 1;
callback && callback(num);
}
test(1, function fn(){
console.log(num);
})
// 回调函数不影响函数本身的返回值情况
function test(count, callback) {
var newCount = count + 1;
var callbackCount = count + 100;
callback && callback(callbackCount);
return newCount;
}
var newCount = test(123, function (callbackCount) {
console.log(callbackCount);
})
console.log(newCount);
// 回调函数做功能性扩展
// 主函数计算 +_*/
// 回调函数,接收外界参数a,参数b,参数type,result,dateTime进行打印日志。
function Calculate(callback) {
return function (a, b, type) { // 为什么需要的返回一个函数呢?利用闭包的作用,因为回调函数一般是静态的,所以只执行一次就好,保存好就行。
var _a = Number(a),
_b = Number(b),
_type = type,
_res = null,
dateTime = new Date();
switch (type) {
case '+':
res = a + b;
break;
case '-':
res = a - b;
break;
case '*':
res = a * b;
break;
case '/':
res = a / b;
break;
default:
break;
}
// 执行回调函数
callback && callback(a, b, type, res, dateTime);
}
}
// Calculate其实相当于工具函数。
var cal = Calculate(function (a, b, type, res, dateTime) {
console.log(`${dateTime} ${a} ${type} ${b} = ${res}`)
});
cal(1, 2, '+');
cal(2, 3, '-');
cal(3, 4, '/');
cal(4, 5, '*');
function fn(validate) {
return function (a, b, type) {
var _a = Number(a),
_b = Number(b),
type = type,
res = null;
var { isError, errMsg } = validate(_a, _b);
if (isError) {
throw new Error(`${errMsg}`);
}
console.log(_a);
console.log(_b);
switch (type) {
case '+':
return res = _a + _b;
case '-':
return res = _a - _b;
case '*':
return res = _a * _b;
case '/':
return res = _a / _b;
default:
break;
}
}
}
console.log(fn(validate)(101, 100, '*'));
// callback验证函数
function validate(a, b) {
if (a < 100) {
return {
isError: true,
errMsg: '参数a必须大于100'
}
}
return {
isError: false,
errMsg: ''
}
}