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/

JavaScript词法作用域的简单介绍相关推荐

  1. 再探Javascript词法作用域

    写在前面的话:每个人都会犯错--有时候'孰能无过,过而能改,善莫大焉',有时候知道自己错了却没有机会更改.其实,错了并不仅仅是错了,做错了,除了及时改正和弥补之外,最重要的是为自己犯的错承担所有责任. ...

  2. JavaScript词法作用域和动态作用域

    2019独角兽企业重金招聘Python工程师标准>>> 作用域 作用域是指代码中定义变量的区域. 作用域规定了如何查找变量,也就是确定当前代码执行对变量的访问权限 JavaScrip ...

  3. jsp对象的四大作用域的简单介绍

    对象的作用域 page作用域(名字有的说pageContext有的说page,个人觉得是第一个) request作用域 session作用域 application作用域 在jsp中提供了四种作用域, ...

  4. javascript的数据类型的简单介绍

    目录 1.umber 数字类型 2,String字符串类型 3.字符串转义符 4.字符串长度 5.字符串拼接 6.布尔型Boolean 7.Undefined和 Null 1.umber 数字类型 I ...

  5. javascript的词法作用域

    这个概念是js中相当基础也是极为重要的,很多想当然的错误或感觉怪异的问题都是和这个东西有关.所以,本文主要说下这个名词的概念以及讨论下他牵扯出来的有关变量.函数.闭包的问题. 由变量开始谈 习惯性先来 ...

  6. JavaScript夯实基础系列(一):词法作用域

      作用域是一组规则,规定了引擎如何通过标识符名称来查询一个变量.作用域模型有两种:词法作用域和动态作用域.词法作用域是在编写时就已经确定的:通过阅读包含变量定义的数行源码就能知道变量的作用域.Jav ...

  7. JavaScript之词法作用域和动态作用域

    作用域 作用域是指程序源代码中定义变量的区域. 作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限. JavaScript 采用词法作用域(lexical scoping),也就是静态作 ...

  8. javascript基础拾遗——词法作用域

    本来是想写js面向对象笔记(二)关于封装的,但是在敲实例代码的时候,发现对作用域这个东西的概念有点模糊,翻阅了犀牛后,有点感觉了,就想着先记录下此时的感受. 之所以取名叫做词法作用域,是这个概念是js ...

  9. 如何延长作用域链_通过实例理解javaScript中的this到底是什么和它的词法作用域...

    最近,听到李笑来说,讲解编程的过程中,举例子很重要. 而且,我最近看的各种javaScript工具书中的例子,也都有点复杂. 所以啊,我试着举一些简单又直观的例子,与各位苦学javaScript的同学 ...

最新文章

  1. crontab的用法
  2. 8Manage PPM助力中投证券 项目管理向数字化转型
  3. 计算机专业学习资料总结(~持续更新中)
  4. Eclipse代码自动补全
  5. 每日程序C语言25-查找100以内的素数
  6. 前端学习(3192):react第一个案例
  7. 从运维角度浅谈MySQL数据库优化,中小企业DBA必会
  8. 查看当前Linux系统的发行版本命令详解
  9. Java多线程——线程范围内共享变量和ThreadLocal
  10. Java之戳中痛点 - (5)switch语句break不能忘以及default不同位置的用法
  11. Daily Scrum M2 11-19
  12. 获取手机IMEI和UID
  13. 用友U8审批流相关开发
  14. 学计算机仓库管理一定打字吗,仓库管理员要会电脑吗?需要哪些电脑操作呢?...
  15. 各自然带代表植被_植被带气候
  16. nodejs对PDF合并的几种方法
  17. IOS9 未受信任的企业级开发者 没有信任按钮 解决
  18. android 图片浏览 app 排行版,安卓图片浏览软件哪个好_安卓图片浏览app推荐_图片浏览app软件排行...
  19. ADB安装电视应用市场
  20. 工业视觉检测入门——如何设计一个合适的检测方案?(需求分析+光学设计+检测算法+UI交互+后端数据)

热门文章

  1. Angular7中引用外部JS文件
  2. 微信小程序如何进行登录授权和获取用户信息
  3. go语言服务器连接mysql_go语言原生连接数据库
  4. python爬取哔哩哔哩视频_荐爬取哔哩哔哩中的cosplay小视频
  5. 4 RACMulticastConnection 连接类
  6. iOS网络缓存扫盲篇--使用两行代码就能完成80%的缓存需求
  7. 【Android】ViewPager实现无限循环滚动
  8. Linux上重启服务的正确命令
  9. Word文档使用密码加密
  10. getLocationInWindow getLocationOnScreen getLeft , getTop, getBottom,getRight