这次写的不只是函数作用域,而是。。。。

简介: 这次写的不只是函数作用域,而是。。。。

思维导图

e15370f693644576840a1f81a7fd45d1_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


函数中的作用域

函数作用域定义

函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内使用及复 用(事实上在嵌套的作用域中也可以使用)

var x = -1;
function foo() {
    var a = 0;
    function fxx() {
        var b = 1
    }
    let c = 2;
}
复制代码


  • 全局作用域内包含 foo函数,变量x。
  • foo函数的函数作用域包含变量a,c和函数fxx。但是还可以访问外部作用域的变量(全局作用域)
  • fxx函数的函数作用域包含变量b,但是还可以访问外部作用域的变量(foo函数作用域和全局作用域)


总结 被包含的“小作用域”可以访问外侧“大作用域”。


最小授权

创建一个函数就会产生一个函数作用域,那么外部作用域就无法访问函数作用域内部的变量和函数,这样就达到了:“私有化变量”的目的。


规避冲突

1. 全局命名空间

变量冲突的一个典型例子存在于全局作用域中。当程序中加载了多个第三方库时,如果它 们没有妥善地将内部私有的函数或变量隐藏起来,就会很容易引发冲突。

这些库通常会在全局作用域中声明一个对象。这个对象被用作库的命名空间,所有需要暴露给外界的功能都会成为这个对象(命名空间)的属性,而不是将自己的标识符暴漏在顶级的词法作用域中。


2. 模块管理

通过依赖管理器的机制将库的标识符显式地导入到另外一个特定的作用域中。

核心 就是为变量和函数都创建各自的作用域,这样“各扫门前雪”,不会共享作用域,避免命名冲突。


函数作用域

函数名污染作用域

创建一个函数就会产生一个函数作用域,内部的变量和函数就会被“私有化”,但是这样也会在外部作用域增加一个函数名,使外部作用域改变(多控制了一个刚刚声明的函数名)。


立即函数调用(IIFO)

(function foo() {
    var a = 0;
    function fxx() {
        var b = 1
    }
    let c = 2;
})()
(function foo() {
    var a = 0;
    function fxx() {
        var b = 1
    }
    let c = 2;
}())
复制代码


上面这2种写法都是立即调用函数。立即调用函数是一个函数表达式,不是函数声明(代码最左侧function开头的才是),这样函数名就不是外部作用域所包含的,而是函数内部包含的。

b978f7a745134b30bc7b5a565f0e7a2a_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png


如果将一段代码中的任意一部分拿出来用函数进行包裹,会改变这段代码的含义,其中的 this、return、break 和 contine 都会 发生变化。所以不用随便使用IIFO去切割代码。


匿名函数表达式

函数表达式可以是匿名的, 而函数声明则不可以省略函数名,不然你是不是声明个寂寞。

setTimeout(function () {
    console.log("I waited 1 second!");
}, 1000);
复制代码


  1. 匿名函数在栈追踪中不会显示出有意义的函数名,使得调试很困难。
  2. 如果没有函数名,当函数需要引用自身时只能使用已经过期的 arguments.callee 引用, 比如在递归中。另一个函数需要引用自身的例子,是在事件触发后事件监听器需要解绑自身。
  3. 匿名函数省略了对于代码可读性 / 可理解性很重要的函数名。一个描述性的名称可以让 代码不言自明。


行内函数表达式

行内函数表达式有不有名字都无所谓,但是始终给函数表达式命名是一个最佳实践。

setTimeout(function myFunction() {
    console.log("I waited 1 second!");
}, 1000);
复制代码


undefined标识符

这点你肯定觉得神奇,undefined是一个标识符,也就是说它可以作为一个变量(容器),让我们给他赋新值。

undefined = true; // 给其他代码挖了一个大坑!绝对不要这样做!
(function IIFE(undefined) {
    var a;
    if (a === undefined) { 
        console.log("Undefined is safe here!");
    }
})();
复制代码


块作用域

块作用域是一个用来对之前的最小授权原则进行扩展的工具,将代码从在函数中隐藏信息 扩展为在块中隐藏信息。

for (var i = 0; i < 10; i++) {
    setTimeout(function () {
        console.log(i) // 全是10
    }, 0)
}
复制代码


这是为什么,每次循环我们都声明了一个匿名函数到事件队列,但是这些声明的函数都是共享的一个作用域,等到for循环执行完毕,i = 10,那么这些函数就该执行了,自然都会去找这个i = 10的麻烦。

但是在我们使用了块作用域就不一样了,先卖个关子,在let那讲。


with

with 从对象中创建出的作用域仅在 with 声明中而非外部作用域中有效,但是在这个创建的作用域中也可以声明全局变量(就是漏掉var 去声明变量a = 1 这个a 就跑到全局作用域去了)。


try/catch

JavaScript 的 ES3 规范中规定 try/catch 的 catch 分句会创建一个块作 用域,其中声明的变量仅在 catch 内部有效。

try {
    undefined(); // 执行一个非法操作来强制制造一个异常
}
catch (err) {
    console.log(err); // 能够正常执行!
}
console.log(err); // ReferenceError: err not found
复制代码


let

let 关键字可以将变量绑定到所在的任意作用域中(通常是 { .. } 内部)。换句话说,let 为其声明的变量隐式地了所在的块作用域。


let循环

for (let i = 0; i < 10; i++) {
    setTimeout(function () {
        console.log(i) // 1-9
    }, 0)
}
复制代码

每次循环都会产生一个块级作用域,和变量i的当前值进行绑定。这样每个块级作用域绑定的变量就是不一样的值,最后每个匿名函数会去对应绑定的块级作用域中找到对应的变量i。


垃圾回收

了解闭包的都应该知道,闭包保存着对外部函数作用域的变量引用。这样即使外部函数销毁了,但是变量被闭包引用着,就不会被回收销毁。使用块级作用域就可以避免这种情况。

function process(data) {
    // 在这里做点有趣的事情
}
// 在这个块中定义的内容可以销毁了!
{
    let someReallyBigData = { .. };
    process(someReallyBigData);
}
var btn = document.getElementById("my_button");
btn.addEventListener("click", function click(evt) {
    console.log("button clicked");
}, /*capturingPhase=*/false);
复制代码


const

声明的变量是常量,但是如果是对象,那么这个对象的引用是常量,但是对象不是常量。还是可以添加删除属性,方法什么的。


块级作用域的替代方案

try/catch的catch分句就会产生一个块作用域。当你讲代码转换为ES6之前就可能看到它了。


隐式和显式作用域

let 作用域或 let 声明

let(a = 2) {
    console.log(a); // 2
}
console.log(a); // ReferenceError
复制代码


隐式使用会劫持一个已经存在的作用域 let声明(显示使用)会创建一个显示的作用域并与其进行绑定。

注意 let 声明并不包含在 ES6 中。官方的 Traceur 编译器也不接受这种形式的代码。



目录
相关文章
|
6月前
|
编译器 C++
c++关于命名空间内变量和函数及全局变量的使用和作用域
c++关于命名空间内变量和函数及全局变量的使用和作用域
105 1
|
12天前
|
存储 缓存 JavaScript
哪些情况适合使用块级作用域,哪些情况适合使用函数作用域?
【10月更文挑战第29天】块级作用域和函数作用域在不同的场景下各有优势,合理地选择和运用这两种作用域可以使JavaScript代码更加清晰、高效和易于维护。在实际开发中,需要根据具体的业务需求、代码结构和编程模式来决定使用哪种作用域,或者在适当的情况下结合使用两者,以达到最佳的编程效果。
|
12天前
|
JavaScript 前端开发
块级作用域和函数作用域有什么区别?
【10月更文挑战第29天】块级作用域和函数作用域在JavaScript中各有特点和用途。块级作用域提供了更精细的变量控制,有助于避免变量提升和意外的全局变量污染等问题;而函数作用域则在函数封装和模块化编程等方面有着重要的应用。在实际开发中,需要根据具体的需求和场景合理地选择使用哪种作用域来声明变量和组织代码。
|
1月前
|
JavaScript 前端开发
作用域和作用域链及预解析
作用域和作用域链及预解析
20 4
|
2月前
|
Java
作用域
作用域
19 2
|
2月前
C 作用域详解
在 C 语言中,作用域决定了变量和函数的可见性和生命周期,包括块作用域、函数作用域、文件作用域和全局作用域。块作用域内的变量仅在块内有效,函数作用域内的变量在整个函数内有效,文件作用域内的全局变量和函数在整个文件内有效,而全局作用域内的变量和函数在整个程序运行期间有效。作用域的优先级遵循局部变量优先的原则,局部变量会遮蔽同名的全局变量。变量的生命周期分为局部变量(函数调用时创建和销毁)、全局变量(程序开始时创建和结束时销毁)以及静态变量(整个程序期间有效)。理解作用域有助于避免命名冲突和错误,提高代码的可读性和可维护性。
|
6月前
|
JavaScript 前端开发 Python
函数与作用域
编程中的函数与作用域概念。函数是可重用的代码块,能提高代码的可读性、可维护性和复用性。基础用法包括定义、调用和返回值。高级用法涉及函数嵌套、匿名函数(lambda函数)和装饰器。装饰器能在不修改原函数代码的情况下添加功能。 作用域决定了变量的可见范围,从内到外是局部、嵌套、全局和内置作用域。闭包是能访问外部函数变量的内部函数,即使外部函数执行完毕,闭包仍能保留其状态。闭包常用于实现特殊功能,如记忆化和延迟执行。 立即执行函数表达式(IIFE)是JavaScript中的模式,用于创建私有作用域和防止变量污染全局。IIFE常用于封装变量、避免命名冲突以及实现模块化和函数作为参数传递。
|
Linux 网络架构
暂时性死区以及函数作用域
暂时性死区以及函数作用域
172 0
|
设计模式 自然语言处理 JavaScript
一篇文章帮你真正理解javascsript作用域闭包
一篇文章帮你真正理解javascsript作用域闭包
85 0
|
存储 缓存 JavaScript
深入理解作用域和闭包(下)
深入理解作用域和闭包(下)
深入理解作用域和闭包(下)