JavaScript词法作用域的简单介绍

news/2024/7/3 0:54:55

by Michael McMillan

迈克尔·麦克米兰(Michael McMillan)

JavaScript词法作用域的简单介绍 (An easy intro to Lexical Scoping in JavaScript)

Lexical scoping is a topic that frightens many programmers. One of the best explanations of lexical scoping can be found in Kyle Simpson’s book You Don’t Know JS: Scope and Closures. However, even his explanation is lacking because he doesn’t use a real example.

词法作用域是一个使许多程序员感到恐惧的话题。 词法作用域的最佳解释之一可以在Kyle Simpson的《 You't Know JS:范围和闭包》一书中找到。 但是,甚至没有他的解释,因为他没有使用真实的例子。

One of the best real examples of how lexical scoping works, and why it is important, can be found in the famous textbook, “The Structure and Interpretation of Computer Programs” (SICP) by Harold Abelson and Gerald Jay Sussman. Here is a link to a PDF version of the book: SICP.

关于词法作用域如何工作以及为什么它很重要的最好的真实例子之一,可以在Harold Abelson和Gerald Jay Sussman着名的教科书《计算机程序的结构和解释》(SICP)中找到。 这是该书的PDF版本的链接: SICP 。

SICP uses Scheme, a dialect of Lisp, and is considered one of the best introductory computer science texts ever written. In this article, I’d like to revisit their example of lexical scoping using JavaScript as the programming language.

SICP使用Scheme,这是Lisp的方言,被认为是有史以来最好的入门计算机科学课本之一。 在本文中,我想回顾他们使用JavaScript作为编程语言的词法作用域示例。

我们的例子 (Our example)

The example Abelson and Sussman used is computing square roots using Newton’s method. Newton’s method works by determining successive approximations for the square root of a number until the approximation comes within a tolerance limit for being acceptable. Let’s work through an example, as Abelson and Sussman do in SICP.

Abelson和Sussman使用的示例是使用牛顿方法计算平方根。 牛顿法的工作原理是确定一个数字的平方根的逐次逼近,直到逼近在可接受的公差范围内。 让我们来看一个例子,就像Abelson和Sussman在SICP中所做的那样。

The example they use is finding the square root of 2. You start by making a guess at the square root of 2, say 1. You improve this guess by dividing the original number by the guess and then averaging that quotient and the current guess to come up with the next guess. You stop when you reach an acceptable level of approximation. Abelson and Sussman use the value 0.001. Here is a run-through of the first few steps in the calculation:

他们使用的示例找到2的平方根。首先对2的平方根进行猜测,即1。通过将原始数字除以猜测值,然后对该商和当前猜测值求平均值,可以改善此猜测值。提出下一个猜测。 当您达到可接受的近似水平时,您将停止。 Abelson和Sussman使用值0.001。 这是计算的前几个步骤的贯穿过程:

Square root to find: 2First guess: 1Quotient: 2 / 1 = 2Average: (2+1) / 2 = 1.5Next guess: 1.5Quotient: 1.5 / 2 = 1.3333Average: (1.3333 + 1.5) / 2 = 1.4167Next guess: 1.4167Quotient: 1.4167 / 2 = 1.4118Average: (1.4167 + 1.4118) / 2 = 1.4142

And so on until the guess comes within our approximation limit, which for this algorithm is 0.001.

依此类推,直到猜测在我们的近似极限之内,该近似极限为0.001。

牛顿方法JavaScript函数 (A JavaScript Function for Newton’s Method)

After this demonstration of the method the authors describe a general procedure for solving this problem in Scheme. Rather than show you the Scheme code, I’ll write it out in JavaScript:

在对该方法进行了演示之后,作者在Scheme中描述了解决此问题的一般过程。 我没有用Scheme代码显示,而是用JavaScript编写出来:

function sqrt_iter(guess, x) {  if (isGoodEnough(guess, x)) {    return guess;  }    else {    return sqrt_iter(improve(guess, x), x);  }}

Next, we need to flesh out several other functions, including isGoodEnough() and improve(), along with some other helper functions. We’ll start with improve(). Here is the definition:

接下来,我们需要充实其他几个函数,包括isGoodEnough()和Improvement()以及其他一些辅助函数。 我们将以改良()开始。 这是定义:

function improve(guess, x) {  return average(guess, (x / guess));}

This function uses a helper function average(). Here is that definition:

此函数使用辅助函数average()。 这是定义:

function average(x, y) {  return (x+y) / 2;}

Now we’re ready to define the isGoodEnough() function. This function serves to determine when our guess is close enough to our approximation tolerance (0.001). Here is the definition of isGoodEnough():

现在我们准备定义isGoodEnough()函数。 此函数用于确定我们的猜测何时足够接近我们的近似公差(0.001)。 这是isGoodEnough()的定义:

function isGoodEnough(guess, x) {  return (Math.abs(square(guess) - x)) < 0.001;}

This function uses a square() function, which is easy to define:

此函数使用square()函数,该函数易于定义:

function square(x) {  return x * x;}

Now all we need is a function to get things started:

现在,我们需要的是一个可以开始的功能:

function sqrt(x) {  return sqrt_iter(1.0, x);}

This function uses 1.0 as a starting guess, which is usually just fine.

此函数使用1.0作为开始猜测,通常很好。

Now we’re ready to test our functions to see if they work. We load them into a JS shell and then compute a few square roots:

现在,我们准备测试我们的功能,看看它们是否有效。 我们将它们加载到JS shell中,然后计算一些平方根:

> .load sqrt_iter.js> sqrt(3)1.7321428571428572> sqrt(9)3.00009155413138> sqrt(94 + 87)13.453624188555612> sqrt(144)12.000000012408687

The functions seem to be working well. However, there is a better idea lurking here. These functions are all written independently, even though they are meant to work in conjunction with each other. We probably aren’t going to use the isGoodEnough() function with any other set of functions, or on its own. Also, the only function that matters to the user is the sqrt() function, since that’s the one that gets called to find a square root.

这些功能似乎运行良好。 但是,这里有一个更好的主意。 这些功能都是独立编写的,即使它们旨在相互配合工作也是如此。 我们可能不会将isGoodEnough()函数与任何其他函数集一起使用,或者单独使用。 另外,对用户而言唯一重要的函数是sqrt()函数,因为该函数被调用来查找平方根。

块作用域隐藏助手功能 (Block Scoping Hides Helper Functions)

The solution here is to use block scoping to define all the necessary helper functions within the block of the sqrt() function. We are going to remove square() and average() from the definition, as those functions might be useful in other function definitions and aren’t as limited to use in an algorithm that implements Newton’s Method. Here is the definition of the sqrt() function now with the other helper functions defined within the scope of sqrt():

此处的解决方案是使用块作用域来在sqrt()函数的块内定义所有必需的辅助函数。 我们将从定义中删除square()和average(),因为这些函数在其他函数定义中可能很有用,并且不限于在实现牛顿方法的算法中使用。 这是sqrt()函数的定义,以及在sqrt()范围内定义的其他辅助函数:

function sqrt(x) {  function improve(guess, x) {    return average(guess, (x / guess));  }  function isGoodEnough(guess, x) {    return (Math.abs(square(guess) - x)) > 0.001;  }  function sqrt_iter(guess, x) {    if (isGoodEnough(guess, x)) {      return guess;    }    else {      return sqrt_iter(improve(guess, x), x);    }  }  return sqrt_iter(1.0, x);}

We can now load this program into our shell and compute some square roots:

现在我们可以将该程序加载到我们的shell中并计算一些平方根:

> .load sqrt_iter.js> sqrt(9)3.00009155413138> sqrt(2)1.4142156862745097> sqrt(3.14159)1.772581833543688> sqrt(144)12.000000012408687

Notice that you cannot call any of the helper functions from outside the sqrt() function:

请注意,您不能从sqrt()函数外部调用任何辅助函数:

> sqrt(9)3.00009155413138> sqrt(2)1.4142156862745097> improve(1,2)ReferenceError: improve is not defined> isGoodEnough(1.414, 2)ReferenceError: isGoodEnough is not defined

Since the definitions of these functions (improve() and isGoodEnough()) have been moved inside the scope of sqrt(), they cannot be accessed at a higher level. Of course, you can move any of the helper function definitions outside of the sqrt() function to have access to them globally as we did with average() and square().

由于这些函数的定义(improve()和isGoodEnough())已移至sqrt()范围内,因此无法在更高级别访问它们。 当然,您可以将任何辅助函数定义移到sqrt()函数之外,以像对average()和square()那样全局访问它们。

We have greatly improved our implementation of Newton’s Method but there’s still one more thing we can do to improve our sqrt() function by simplifying it even more by taking advantage of lexical scope.

我们极大地改进了牛顿方法的实现,但是还有更多事情可以做,以通过利用词汇范围进一步简化sqrt()函数来改进我们的sqrt()函数。

用词法范围提高清晰度 (Improving Clarity with Lexical Scope)

The concept behind lexical scope is that when a variable is bound to an environment, other procedures (functions) that are defined in that environment have access to that variable’s value. This means that in the sqrt() function, the parameter x is bound to that function, meaning that any other function defined within the body of sqrt() can access x.

词法作用域背后的概念是,当变量绑定到环境时,在该环境中定义的其他过程(函数)可以访问该变量的值。 这意味着在sqrt()函数中,参数x绑定到该函数,这意味着sqrt()主体内定义的任何其他函数都可以访问x。

Knowing this, we can simplify the definition of sqrt() even more by removing all references to x in function definitions since x is now a free variable and accessible by all of them. Here is our new definition of sqrt():

知道了这一点,我们可以通过删除函数定义中对x的所有引用来进一步简化sqrt()的定义,因为x现在是一个自由变量,并且所有变量都可以访问。 这是我们对sqrt()的新定义:

function sqrt(x) {  function isGoodEnough(guess) {    return (Math.abs(square(guess) - x)) > 0.001;  }  function improve(guess) {    return average(guess, (x / guess));  }  function sqrt_iter(guess) {    if (isGoodEnough(guess)) {      return guess;    }    else {      return sqrt_iter(improve(guess));    }  }  return sqrt_iter(1.0);}

The only references to parameter x are in computations where x’s value is needed. Let’s load this new definition into the shell and test it:

对参数x的唯一引用是在需要x值的计算中。 让我们将这个新定义加载到外壳中并对其进行测试:

> .load sqrt_iter.js> sqrt(9)3.00009155413138> sqrt(2)1.4142156862745097> sqrt(123+37)12.649110680047308> sqrt(144)12.000000012408687

Lexical scoping and block structure are important features of JavaScript and allow us to construct programs that are easier to understand and manage. This is especially important when we begin to construct larger, more complex programs.

词法作用域和块结构是JavaScript的重要功能,使我们能够构建更易于理解和管理的程序。 当我们开始构建更大,更复杂的程序时,这一点尤其重要。

翻译自: https://www.freecodecamp.org/news/lexical-scoping-in-javascript-e876cd221b74/


http://lihuaxi.xjx100.cn/news/238074.html

相关文章

非对称加密及RSA算法

想知道更多关于区块链技术知识&#xff0c;请百度【链客区块链技术问答社区】 链客&#xff0c;有问必答&#xff01;最近在学习区块链相关的知识&#xff0c;发现其保证去中心化的一个重要的手段就是基于密码学中的非对称加密。 何为非对称加密&#xff1f; 在回答这个问题之前…

处理器拦截器(HandlerInterceptor)详解

处理器拦截器&#xff08;HandlerInterceptor&#xff09;详解 编程界的小学生 关注 2017.04.06 15:19* 字数 881 阅读 657评论 0喜欢 4简介SpringWebMVC的处理器拦截器&#xff0c;类似于Servlet开发中的过滤器Filter&#xff0c;用于处理器进行预处理和后处理。 应用场景1、日…

saltstack实现haproxy+keepalived负载均衡+高可用(二)

一键部署haproxykeepalived实现负载均衡高可用 实验环境&#xff1a; &#xff01;&#xff01;&#xff01;&#xff01; 特别注意&#xff1a; www.westos.org为test1的minion名字 test1: 172.25.1.11 nginx master minion test2: 172.25.…

python学习day3

1丶 用户先进行登陆如果用户名在文件中且用户密码也正确就登陆成功调用购物车函数&#xff0c;如果用户用户名输入正确密码错误&#xff0c;提示用户密码错误且重新输入&#xff0c;如果用户 输入用户名不存在&#xff0c;提示用户是否创建该用户&#xff0c;调用注册函数。 1.…

以太坊交易事件,日志的理解

想知道更多关于区块链技术知识&#xff0c;请百度【链客区块链技术问答社区】 链客&#xff0c;有问必答&#xff01;Ethereum transation event and log 以太坊交易事件的功能有三个&#xff1a; 用于返回智能合约执行过程中的返回值到用户界面同步触发前端用户界面事件便宜的…

Phpcms V9手机门户设置教程:怎么用PC V9做手机网站

一、在PHPcms V9管理后台设置手机门户 1.1、开启手机网站。位置&#xff1a;模块 》手机门户 》 添加手机站点&#xff0c;具体设置可参照截图&#xff1a; 填写站点名和LOGO文件相对位置&#xff0c;绑定用于手机网站的二级域名m.cmsyou.com&#xff0c;域名以http://开头。 1…

中年妇女xxx_2019年国际妇女节庆祝活动

中年妇女xxxBehind the scenes幕后花絮 Becoming Women Techmakers Ambassador成为女性技术制造者大使 In 2018 I decided to start my remote freelance career and to continue to expand my Full Stack Developer knowledge by finishing the last FreeCodeamp projects f…

WPF查找子控件和父控件方法

原文:WPF查找子控件和父控件方法public List<T> GetChildObjects<T>(DependencyObject obj, string name) where T : FrameworkElement{DependencyObject child null;List<T> childList new List<T>();for (int i 0; i < VisualTreeHelper.GetCh…