词法作用域 VS 动态作用域 VS 立即执行函数

2024-06-12 16:08

本文主要是介绍词法作用域 VS 动态作用域 VS 立即执行函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • 词法作用域

    JS 词法作用域是由你在写代码时将变量和块作用域写在哪里来决定

  • 动态作用域

 JS动态作用域和this机制息息相关。它的作用域诗是在运行的过程中确定

var a = 1;function foo() {var a = 2;console.log( this.a );
}foo(); // 1

从上面的代码,我们可以看出:foo中打印a的值不是由写代码的位置确定的,而是取决于foo执行的位置

  • 区别

    • 词法作用域是在写代码或者说定义时确定的,而动态作用域是在运行时确定的。(this 也是!)
    • 词法作用域关注函数在何处声明,而动态作用域关注函数从何处调用。
最小授权原则

最小授权原则是指在软件设计中,应该最小限度地暴露必要内容,而将其他内容都“隐藏”起来,比如某个模块或对象的 API 设计。

这个原则可以延伸到如何选择作用域来包含变量和函数。如果所有变量和函数都在全局作 用域中,当然可以在所有的内部嵌套作用域中访问到它们。但这样会破坏前面提到的最小 特权原则,因为可能会暴漏过多的变量或函数,而这些变量或函数本应该是私有的,正确 的代码应该是可以阻止对这些变量或函数进行访问的。

例如:

function doSomething(a) {b = a + doSomethingElse( a * 2 );console.log( b * 3 );}function doSomethingElse(a) { return a - 1;
}var b;
doSomething( 2 ); // 15

在这个代码片段中,变量 b 和函数 doSomethingElse(..) 应该是 doSomething(..) 内部具体 实现的“私有”内容。给予外部作用域对 b 和 doSomethingElse(..) 的“访问权限”不仅 没有必要,而且可能是“危险”的,因为它们可能被有意或无意地以非预期的方式使用, 从而导致超出了 doSomething(..) 的适用条件。更“合理”的设计会将这些私有的具体内容隐藏在 doSomething(..) 内部,

例如:

function doSomething(a) { function doSomethingElse(a) {return a - 1; }var b;b = a + doSomethingElse( a * 2 );console.log( b * 3 );
}
doSomething( 2 ); // 15

现在,b 和 doSomethingElse(..) 都无法从外部被访问,而只能被 doSomething(..) 所控制。 功能性和最终效果都没有受影响,但是设计上将具体内容私有化了,设计良好的软件都会 依此进行实现。

规避冲突

当我们的程序代码逐渐多起来,难免会出现变量冲突。那么如何规避冲突就显得额外重要。

函数可以把标识符严谨的"隐藏"起来,外部无法访问到,利用这个特性我们可以很好的规避冲突。

function foo() {var a = 1;
}function bar() {var a = 2;
}

foo和bar中定义了相同的变量a,但是却不会相互造成影响。因为函数可以很好的把标识符"隐藏"起来。

  • 全局命名空间

变量冲突的一个典型例子存在于全局作用域中。当程序中加载了多个第三方库时,如果它 们没有妥善地将内部私有的函数或变量隐藏起来,就会很容易引发冲突。
这些库通常会在全局作用域中声明一个名字足够独特的变量,通常是一个对象。这个对象 被用作库的命名空间,所有需要暴露给外界的功能都会成为这个对象(命名空间)的属 性,而不是将自己的标识符暴漏在顶级的词法作用域中。

例如:

var myLibrary = {name: 'echo',getName: function() {console.log( this.name );}
}


立即执行函数(IIFE)

var a=2;(function foo() { var a=3;console.log( a ); // 3
})();console.log( a ); // 2

该函数是以()开始,不是以关键词function开始,因此IIFE是函数表达式

函数名对 IIFE 当然不是必须的,IIFE 最常见的用法是使用一个匿名函数表达式。虽然使 用具名函数的 IIFE 并不常见,但它具有以下优势:

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

因此具名函数的 IIFE 也是一个值得推广的实践。

  • 另一种表达形式
(function() {}())
  • 参数传递

IIFE 也可以和其他形式的函数一样实现参数的传递(多说一句:参数传递是按值传递)。

(function foo(a) {console.log(a);
})(3);

这个模式的另外一个应用场景是解决 undefined 标识符的默认值被错误覆盖导致的异常(虽 然不常见)。将一个参数命名为 undefined,但是在对应的位置不传入任何值,这样就可以 保证在代码块中 undefined 标识符的值真的是 undefined:

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

IIFE 还有一种变化的用途是倒置代码的运行顺序,将需要运行的函数放在第二位,在 IIFE 执行之后当作参数传递进去。尽管这种模式略显冗长,但有些人认为它更易理解。

var a=2;(function IIFE( def ) { //参数的处理def( window );
})(function def( global ) {//逻辑运算var a=3;console.log( a ); // 3 console.log( global.a ); // 2
});

这篇关于词法作用域 VS 动态作用域 VS 立即执行函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1054692

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数

函数式编程思想

我们经常会用到各种各样的编程思想,例如面向过程、面向对象。不过笔者在该博客简单介绍一下函数式编程思想. 如果对函数式编程思想进行概括,就是f(x) = na(x) , y=uf(x)…至于其他的编程思想,可能是y=a(x)+b(x)+c(x)…,也有可能是y=f(x)=f(x)/a + f(x)/b+f(x)/c… 面向过程的指令式编程 面向过程,简单理解就是y=a(x)+b(x)+c(x)

jenkins 插件执行shell命令时,提示“Command not found”处理方法

首先提示找不到“Command not found,可能我们第一反应是查看目标机器是否已支持该命令,不过如果相信能找到这里来的朋友估计遇到的跟我一样,其实目标机器是没有问题的通过一些远程工具执行shell命令是可以执行。奇怪的就是通过jenkinsSSH插件无法执行,经一番折腾各种搜索发现是jenkins没有加载/etc/profile导致。 【解决办法】: 需要在jenkins调用shell脚

利用matlab bar函数绘制较为复杂的柱状图,并在图中进行适当标注

示例代码和结果如下:小疑问:如何自动选择合适的坐标位置对柱状图的数值大小进行标注?😂 clear; close all;x = 1:3;aa=[28.6321521955954 26.2453660695847 21.69102348512086.93747104431360 6.25442246899816 3.342835958564245.51365061796319 4.87