作用域和闭包

作用域

指代码当前上下文,控制着变量和函数的可见性和生命周期。一个变量的作用域是在程序源代码中定义时决定的。不同作用域下同名变量不会冲突。

作用域可以分为:全局作用域模块作用域函数作用域(局部作用域)、块级作用域

全局作用域

  • 直接写在 Script 标签中的 js 代码都在全局作用域。在全局作用域下声明的变量叫做全局变量(在块级外部定义的变量)

  • 全局作用域在全局的任何位置下都可以使用;全局作用中无法访问到局部作用域中的变量

  • 所有window对象的属性拥有全局作用域

    补充:

    var和function命令声明的全局变量和函数是window对象的属性和方法

    let命令、const命令、class命令声明的全局变量,不属于window对象的属性

    为什么let,const,class命令声明的全局变量不属于window对象属性呢?

    let 命令、const 命令和 class 命令声明的变量之所以不属于window对象属性,是因为它们是使用块级作用域在不同的作用域中声明的。在 ES6 以前,JavaScript 中只有全局作用域和函数作用域,变量的作用域只能局限于函数内或作为全局变量。全局变量是指声明在全局作用域下的变量,它们会被添加到 window 对象中。但是,ES6 引入了块级作用域,可以使用 let、const 和 class 命令在不同块级作用域中声明变量,这些变量不能被添加到 window 对象中,因为它们不是全局变量,而是块级作用域中的变量。所以,这些变量不属于 window 对象的属性,也不会影响全局作用域。

    以下为代码示例:

<script>
	const A = 1
	var c = 3
	function fun () {
		const b = 2
		console.log(A)
		console.log(b)
	}
	fun() // 1 2
	console.log(A) // 1
	console.log(b) // b is not defined
	console.log(window.A) // undefined
	console.log(window.c) // 3
</script>

模块作用域

早期 js 语法中没有模块的定义,随着脚本越来越复杂,模块化方案(AMD、CommonJS、UMD、ES6模块等)应运而生。通常一个模块就是一个文件或者一段脚本,而每个模块拥有其自己的作用域。

函数作用域

  • 函数作用域会随着函数的执行而被创建,当其执行完毕后,其作用域会被销毁。每次执行函数都会创建一个新的作用域,其是相互独立的。
  • 函数作用域内可访问全局变量,函数外无法访问函其内部变量。
  • 函数作用域内操作变量时,会先在自身作用域中寻找,有就直接使用,没有就向上一作用域中寻找,直到找到全局作用域,如果全局作用域中仍然没有找到,就会报错(这个过程就是作用域链)。
可参考上面的示例进行理解

块级作用域

  • 在花括号{}中的语句都属于一个块,在块中使用let和const声明的变量,外部无法访问,这种作用域规则叫块级作用域。
  • 通过var声明的变量或者非严格模式下创建的函数声明没有块级作用域
function fun () {
	const A = 1
	{
		var b = 2
		const C = 3
	}
	console.log('A=', A)
	console.log('b=', b)
	console.log('C=', C)
}
fun() // A = 1; b = 2; C is not defined
// 严格模式
'use strict'; // 开启严格模式
{
	function fun () {
		console.log('开启严格模式')
	}
}

fun() // fun is not defined

补充:严格模式

JavaScript中的“严格模式”(strict mode)是一种可选的执行模式,它为代码定义了更强的语法和错误捕获机制,并禁用了一些不安全或不推荐使用的特性。

在JavaScript中,默认情况下,代码执行时处于“非严格模式”(non-strict mode),这意味着代码在运行时会尽可能地灵活适应开发人员的写法,但也会带来一些问题,例如:

  • 一些变量可能无意中被赋值给全局对象(比如window对象),这会导致变量污染或不必要的冲突。
  • 一些不安全或不推荐使用的特性可能会导致代码行为出乎意料,例如with语句和eval函数。
  • 一些常见错误可能不会被捕获,例如内部函数引用的变量名与外部变量重名时,代码可能出现错误但却不报错。

相比之下,严格模式则强制执行更严格的语法和错误捕获机制,以避免这些问题,并提供更好的代码质量保证。在JavaScript中,你可以通过以下代码开启严格模式:

'use strict'; // 开启严格模式

当上述语句出现在脚本文件的顶部或函数体的顶部时,该代码片段将在严格模式执行,否则将在非严格模式中执行。

作用域链指如果在当前作用域中没有查到值,就会像上级作用域查询,直到全局作用域,这样一直查找过程所形成的链条就被称之为作用域链

闭包

闭包是什么呢?它是指函数 A 内创建了函数 B,B 可以使用 A 中的变量,则 B 被称为闭包。

闭包的特征

  • 闭包可以访问外部函数中的变量:内部函数可以访问在外部函数中定义的变量和参数。
  • 外部函数的变量和参数保持在内存中: 由于闭包可以访问外部函数的变量和参数,当外部函数返回后,这些变量和参数不会被销毁。它们将被保存在内存中,因为闭包仍然引用它们。
  • 闭包可以更新外部变量的值: 闭包可以使用在外部函数中定义的变量和参数,并更改它们的值。这种行为可以被认为是非常强大和危险的,因为它可以产生意想不到的副作用,并且使代码难以理解和调试。
  • 闭包可以封装私有变量: 闭包可以用于创建“私有变量”,这些变量只能在闭包内部访问。这种行为可以使代码更加安全,因为它可以防止外部代码意外地修改闭包内部的变量。
  • 闭包可以用于在函数间共享状态: 由于闭包可以访问外部函数的变量和参数,它们可以被用于在函数之间共享状态,这对于避免全局变量和更好地管理应用程序状态非常有用

闭包代码示例

function makeCounter() {
  let count = 0; // 定义一个局部变量

  function counter() {
    return ++count; // 访问了父函数中的自由变量 count
  }

  return counter;
}

let counter1 = makeCounter();
console.log(counter1()); // 输出 1
console.log(counter1()); // 输出 2

let counter2 = makeCounter();
console.log(counter2()); // 输出 1
console.log(counter2()); // 输出 2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值