这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战
hi 我是小十七_,今天是大年初八,之前整理了一些关于闭包的比较基础的知识,在这里分享给大家。
闭包运用的是封装原理,父函数内部的变量不被暴露,通过子函数访问。
function foo(){
var a = 2;
// 声明一个函数,bar 作为内存栈中的函数对象的引用,指向内存堆的一个内存地址,储存着函数对象的实例
function bar(){
// 可以访问上级作用域中的变量
console.log(a);
}
// 返回 bar 引用的函数对象本身
return bar;
}
// bar 和 baz 指向堆中的同一个内存地址
var baz = foo();
// 在全局作用域下执行 baz,
baz();
js 引擎有垃圾回收机制,会自动释放不用变量的内存,不过全局变量,需要等所有代码执行完毕,或者关闭网页时被销毁
每一个数据都有对应的内存空间,基础数据类型(number,string,boolean,null,undefined,symbol)保存在栈内存中(不完全是,比如 string 在一些情况保存在堆中),有固定的大小,符合堆栈的规则,后进先出
基础类型的值都是不可变的,比如说字符串,如果对字符串进行操作,一定是返回了一个新的字符串
number 类型是唯一的一种数字类型,用的是双精度64位二进制浮点数
symbol 类型是唯一并且不可修改的,var aaa = Symbol('biaoshifu'); typeof aaa === 'symbol' 可以用作对象的 key 值,下面是一个 symbol 的例子:
Symbol("foo") !== Symbol("foo")
const foo = Symbol()
const bar = Symbol()
typeof foo === "symbol"
typeof bar === "symbol"
let obj = {}
obj[foo] = "foo"
obj[bar] = "bar"
JSON.stringify(obj) // {}
Object.keys(obj) // []
Object.getOwnPropertyNames(obj) // []
Object.getOwnPropertySymbols(obj) // [ foo, bar ]
与此对应的有引用数据类型 object,var bar = function(){},bar 作为一个存在栈里的引用,指向一个内存地址,存储着堆内存的实例。
var bar = { aaa: 111 };
var foo = bar;
foo.aaa = 222;
console.log(bar.aaa); // 222
内部函数在它定义的作用域以外被调用,他还可以访问原来作用域的变量,调用的时候就发生了闭包。
// setTimeout
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
// setTimeout 的回调函数会先被加入到一个任务队列里,等待所有的代码执行完成后再执行,执行的顺序按照 setTimeout 传入的延迟时间排
// 所以 for 循环执行完后,setTimeout 的回调函数才会开始执行,这个时候全局变量 i 已经变成 6 了。
// 改进方法 1
for (var i=1; i<=5; i++) {
(function(i){
setTimeout( function timer() {
console.log( i );
}, i*1000 );
})(i)
}
// 在 window 下调用回调函数时,他可以保持对他定义时的父作用域的引用,父作用域作为参数保存了i,于是可以正常的打印i
// 改进方法 2
for (let i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i );
}, i*1000 );
}
// 利用 let 块作用域的特点,let 在 for 循环中,每次迭代都会新声明一个变量,且值为上一次迭代结束时的值。
模块也是闭包的一个典型例子
- 有一个最外层的封闭函数,并且至少被调用一次
- return 出至少一个内部函数作为 api,函数保持对之前作用域的引用,形成闭包,可以访问模块内部的变量。
function CoolModule() {
var something = "cool";
var another = [1, 2, 3];
function doSomething() { console.log( something );}
function doAnother() {console.log( another.join( " ! " ) );}
return {doSomething: doSomething, doAnother: doAnother};
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3
将模块外部包裹一个自执行函数,形成单例模式(一共有23种设计模式)
es6 中从语言本身添加了对于模块的支持,一个模块一个文件,使用 import export 引入 导出 模块
// 第一种
export var a = 1;
// 或者
var a = 1;
export {a};
// 对应
import {a} from './xxx';
// 第二种
var a = 1;
export default a;
// 对应
import a from './xxx';