一、什么是作用域?简单来说, 作用域就是变量起作用的范围. 想象一下, 你在家里喊"开饭啦!",只有家里的人能听见; 在小区里喊, 整个小区都能听见; 在大街上喊...呃, 可能会被当成神经病. JavaScript的作用域也是类似的道理. 1) 全局作用域全局作用域就像是在大街上喊话, 所有人都能听见 (所有代码都能访问).
但是要注意, 滥用全局变量是个坏习惯! 就像你不会在大街上喊你的银行卡密码一样, 太多全局变量容易造成命名冲突和难以维护. 2) 函数作用域函数作用域就像是在自己家里说话, 只有家里人能听见//全局变量, 在任何地方都能访问const globalMessage = "Hello World";function sayHello() {console.log(globalMessage); //可以访问全局变量}sayHello(); //输出"Hello World"console.log(globalMessage); //同样可以访问
3) 块级作用域块级作用域是ES6引入的, 用function familyMeeting() {const secret = "咱家存款有100万!"; //函数作用域内的变量console.log(secret); //可以访问}familyMeeting();// console.log(secret); //报错!外面访问不到secret
let和const声明的变量只在{}内有效.
if (true) {let blockScoped = "我在块里面";const alsoBlockScoped = "我也是";var notBlockScoped = "我不是块作用域"; //var没有块级作用域}console.log(notBlockScoped); //"我不是块作用域"// console.log(blockScoped); //报错// console.log(alsoBlockScoped); //报错
var没有块级作用域! 它只有函数作用域. 这也是为什么现在推荐用let和const. 二、作用域链
现在来点进阶的——作用域链. 想象你在公司里:
1. 先在自己工位找文件(当前作用域)
2. 找不到就去部门公共区域找(外层作用域)
3. 还找不到就去公司档案室找(更外层作用域)
4. 最后可能去总部找(全局作用域)
JavaScript查找变量也是这样一层层往外找的:作用域链是在函数定义时就确定好的, 不是调用时确定的! 这就是所谓的"静态作用域". 三、闭包闭包是JavaScript中最强大的特性之一, 也是面试必问的. 1) 闭包是指那些能够访问自由变量的函数(自由变量是指在函数中使用, 但既不是函数参数也不是函数局部变量的变量).当一个函数记住并访问它所在的词法作用域时, 就产生了闭包, 即使这个函数是在当前词法作用域之外执行. 示例:const company = "阿里"; //全局作用域function department() {const team = "前端"; //department函数作用域function myDesk() {const name = "小明"; //myDesk函数作用域console.log(name); //小明 (当前作用域)console.log(team); //前端 (上一层作用域)console.log(company); //阿里 (全局作用域)}myDesk();}department();
这里function outer() {const secret = "我爱JavaScript";function inner() {console.log(secret); //访问外部函数的变量}return inner;}const myFunc = outer();myFunc(); //"我爱JavaScript"
inner函数就是一个闭包, 因为它记住了outer的作用域, 即使outer已经执行完毕了. 2) 闭包的作用1-创建私有变量
3) 闭包的作用2-函数柯里化柯里化是把接受多个参数的函数变成接受单一参数的函数的过程function createCounter() {let count = 0; //这个变量外部无法直接访问return {increment: function() {count++;console.log(count);},decrement: function() {count--;console.log(count);}};}const counter = createCounter();counter.increment(); //1counter.increment(); //2counter.decrement(); //1// console.log(count); // 报错, count不可访问
4) 闭包的作用3-模块模式现代模块系统的基础就是闭包function multiply(a) {return function(b) {return a * b;};}const double = multiply(2);console.log(double(5)); // 10console.log(double(10)); // 20const triple = multiply(3);console.log(triple(5)); // 15
5) 闭包使用的注意事项闭包虽好, 也有风险, 内存泄漏风险: 因为闭包会保留对外部变量的引用, 所以如果不小心, 可能会导致内存无法被回收.示例:const myModule = (function() {const privateVar = "我是私有的";function privateMethod() {console.log(privateVar);}return {publicMethod: function() {privateMethod();}};})();myModule.publicMethod(); //"我是私有的"// myModule.privateMethod(); //报错// console.log(myModule.privateVar); //undefined
6) 闭包与this//不好的例子function leakMemory() {const hugeArray = new Array(1000000).fill("0");return function() {console.log("我保留了hugeArray的引用");};}const leakyFunc = leakMemory();//即使leakMemory执行完毕, hugeArray也不会被回收
答案是const obj = {name: "小明",sayName: function() {return function() {console.log(this.name);};}};obj.sayName()(); //输出什么?
undefined! 因为内部函数的this指向全局对象(严格模式下是undefined),不是obj. 解决方案: 保存this引用
或者使用箭头函数const obj = {name: "小明",sayName: function() {const self = this;return function() {console.log(self.name);};}};obj.sayName()(); //"小明"
const obj = {name: "小明",sayName: function() {return () => {console.log(this.name);};}};obj.sayName()(); // "小明"
![]() |
Austin Liu 刘恒辉
Project Manager and Software Designer E-Mail:lzhdim@163.com Blog:https://lzhdim.cnblogs.com 欢迎收藏和转载此博客中的博文,但是请注明出处,给笔者一个与大家交流的空间。谢谢大家。 |




浙公网安备 33010602011771号