翻译 Secrets of the JavaScript Ninja 边译边学(13)

Secrets of the JavaScript Ninja 边译边学(12)第3.1.1节 为什么JavaScript的功能性质如此重要?

3.1.1 Why is JavaScript’s functional nature important?
3.1.1 为什么JavaScript的功能性质如此重要?

It’s likely that if you’ve done any amount of scripting in a browser that you probably know all that we’re going to discuss in this section, but let’s go over it anyways just in case, and to make sure we’re all in the same vernacular.
如果你已经在浏览器端进行过大量脚本开发工作,那么你很可能已经知道我们将在本节讨论的大部分内容,但是为以防万一,我们还是先过一遍,确保我们在同一起跑线上。

  One of the reasons that functions and functional concepts are so important in JavaScript is that the function is the primary modular unit of execution. Except for inline script that runs while the markup is being evaluated, all of the script code that we’ll write for our pages will be within a function.
  在JavaScript中函数概念如此重要的其中一个原因是:函数是主要的模块化执行单元。除了在修饰评估时运行的内联脚本外,我们为页面编写的所有脚本代码都在函数中。

  NOTE
  Back in the Dark Ages, inline script was used to add dynamicity to pages via document.write() . These days, document.write() is considered a dinosaur and its use is not recommended. There are better ways to make pages dynamic, be they the use of server-side templating, or client-side DOM manipulation (or a healthy combination of both).
  在黑暗时代,人们使用内联脚本中的 *document.write()*语句向页面中动态添加信息。现在,*document.write()*语句被公认是臃肿不推荐的方式。有更好的方法可以使页面动态化,无论是使用服务器端模板,还是客户端DOM操作(或者两者以一种良性健康组合在一起)。

  Because most of our code will run as the result of a function invocation, we will see that having functions that are versatile and powerful constructs will give us a great deal of flexibility and sway when writing our code. We’ll spend the rest of this chapter examining just how the nature of functions as first-class objects can be exploited to our great benefit.
  由于大多数代码将作为函数调用的结果运行,所以在编写代码时,具有多功能和强大结构的函数将为我们提供更多的灵活性和对代码的控制能力。我们将在本章的余下部分中研究如何利用“函数是第一类对象”这一重要性质来获得巨大的利益。

  Now, that’s the second time that we’ve used the term “first class object”, and it’s an important concept, so before we go on, let’s make sure we know what it really means.
  这是我们第二次使用“第一类对象”这一概念了,这是一个重要的概念,在我们继续后面的讲述之前,先来确定一下它的真正含义。

  • created via literals

  • assigned to variables, array entries, and properties of other objects

  • passed as arguments to functions

  • returned as values from functions

  • possess properties that can be dynamically created and assigned

  • 通过文字创建

  • 可以赋值给变量,数组项以及其他对象的属性

  • 作为参数传递给函数

  • 作为函数的返回值

  • 可以动态传教和赋值

  Functions in JavaScript possess all of these capabilities, and are thus treated like any other object. Therefore, we say that they are first-class objects, just like any other object in the language.
  JavaScript中的函数拥有所有这些功能,所以可以将函数视为另一种对象。因此,我们说它是第一类对象,正如这门语言中其他对象一样。

  And more than just being treated with the same respect as other objects types, functions have a special capability in that they can be invoked. And that invocation is frequently discharged in an asynchronous manner.
  函数除了可以像其他对象类型那样使用,还有一个特殊的功能:可以被调用。而且该调用经常以异步的方式触发。

  Let’s talk a little about why that is.
  让我们来谈谈为什么会这样。

  THE BROWSER EVENT LOOP
  浏览器事件循环

If you’ve done any programming to create GUI (graphical user interface) desktop applications, you’ll know that most are written in a similar fashion:
如果你做过GUI(图形用户界面)桌面应用开发,你一定知道大多数程序是以如下方式编写的:

  • Set up the user interface

  • Enter a loop waiting for events to occur

  • Invoke handlers (also called listeners) for those events

  • 创建用户接口

  • 进入一个等待事件触发的循环

  • 触发为这些事件编写的处理函数(我们常常也称之为监听器)

  Well, programming for the browser is no different except that our code is not responsible for running the event loop and dispatching events; the browser handles that for us.
  好吧,浏览器的编程没有什么不同,只是我们的代码不用负责运行事件循环和调度事件;浏览器会为我们处理这些。

  Our responsibility is to set up the handlers for the various events that can occur in the browser. These events are placed in an event queue (a FIFO list, more on that later) as they occur, and the browser dispatches these events by invoking any handlers that have been established for them.
  我们的任务是为浏览器事件编写处理程序。这些事件存储在一个事件队列(一个FIFO列表,稍后会详细介绍)中,当它触发时,浏览器通过调用 为他们建立的处理函数将它们分发出去。

  Because these events happen at unpredictable times and in an unpredictable order, we say that the handling of the events, and therefore the invocation of their handling functions, is asynchronous.
  由于这些事件在不可预测的时间,以不可预测的顺序发生,因此我们认为事件处理以及调用事件处理函数是异步的。

  The types of events that can occur include:
  可能发生的事件类型包括:

  • Browser events, such as when a page is finished loading or when it is to be unloaded.

  • Network events, such as responses to an Ajax request.

  • User events, such as mouse clicks, mouse moves, or key presses.

  • Timer events, such as when a timeout expires or an interval fires.

  • 浏览器事件,比如当页面完成加载或者卸载。

  • 网络事件,比如一个Ajax请求的回应。

  • 用户操作事件,比如鼠标点击,鼠标移动或者按下键盘键。

  • 计时器事件,比如计时器超时或者触发间隔事件。

  The vast majority of our code executes as a result of such events. Consider the following:
  我们的绝大多数代码都是由于这些事件而执行的。考虑如下代码:

function startup(){
/* do something wonderful */
}
window.onload = startup;

  Here, we establish a function to serve as a handler for the load event. The establishing statement executes as part of the inline script (assuming it appears at top level and not within any other function), but the wonderful things that we’re going to do inside the function don’t execute until the browser finishes loading the page and fires off a load event.
  在以上代码中,我们创建一个函数作为load事件的处理函数。创建函数的语句作为内联脚本运行(假定它在最外层出现并且没有其他函数),但是我们在其中定义的执行过程直到浏览器完成加载,触发了load事件才会执行。

  In fact, we can simplify this to a single line if we like. Consider:
  实际上,我们甚至可以将以上代码简化为一条语句。如下:

window.onload = function() { /* do something wonderful */ };

  (If the notation used to create the function looks odd to you, be assured that we’ll be making it crystal clear in section 3.2.)
  (如果用于创建函数的符号在您看来很奇怪, 没关系,看完3.2节你就明白了)

Unobtrusive JavaScript
This approach of assigning a function, named or otherwise, to the *onload* property of the *window* instance may not be the way that you are used to setting up a load handler. You may be more accustomed to using the *onload* attribute of the < body > tag. Either approach achieves the same effect, but the *window.onload* approach is vastly preferred by JavaScript ninjas as it adheres to a popular principle known as *Unobtrusive JavaScript*. Remember when the advent of CSS pioneered moving style information out of the document markup? Few would argue that segregating style from structure was a bad move. Well, Unobtrusive JavaScript does the same thing for behavior; moving script out of the document markup. This results in pages having their three primary components: structure, style and behavior, nicely partitioned into their own locations. Structure is defined in the document markup, style in < style > elements or external stylesheets, and behavior in < script > blocks or external script files. You will not see any script embedded into document markup in the examples in this book unless it is to make a specific point or to vastly simplify the example.
低调的JavaScript
给*窗口*实例的*onload*属性赋值函数、命名或其他操作可能不是你以前使用的方式,你可能更适应使用< body >标签的*onload*属性方式赋值。 两种方式的效果是相同的,但是使用*window.onload*方式赋值是JavaScript忍者大力推荐的方法,因为它遵循了一个被称为“低调的JavaScript”的流行原则。 还记得CSS将样式信息从文档标记中移除吗? 没有人会质疑将样式从html布局结构中分离的好处。那么好吧,“低调的JavaScript”为浏览器端行为做了同样的工作:将脚本从文档标记中分离。 这样做的结果是将页面分为三个部分: html布局结构,样式和行为, 每个部分都完美地呆在自己的位置。 html布局结构在文档标记中定义,样式呆在< style >标记元素或者外部样式文件中,而行为在< script >块或者外部脚本文件中定义。 在本书的示例中,您将看不到任何嵌入到文档标记中的脚本,除非它是为了指出一个特定的点或简化代码示例。

  It’s important to note that the browser event loop is single-threaded. Every event that is placed into the event queue is handled in the order that it is placed onto the queue. This is known as a FIFO list (first-in, first-out), or perhaps a silo to the old-timers. Each event is processed in its own “turn” and all other events have to wait until the current event’s turn is over. Under no circumstances are two handlers executing simultaneously in separate threads.
  需要注意的是,浏览器事件循环是单线程的。队列中事件的执行顺序是按照该事件放入队列的顺序进行的。这就是人们熟知的FIFO队列(先进先出队列),或者就像以前的仓筒状。每个事件都有它自己的执行顺序,并且所有其他的事件都必须等待当前事件执行完毕才能执行。在任何情况下,两个处理程序都不可能同时单独执行线程。

  Think of a line at the bank. Everyone gets into a single line and has to wait their turn to be “processed” by the tellers. But with JavaScript, there’s only one teller window open! So the customers only get processed one at a time, as their turn comes. All it takes is one person, who thinks it’s appropriate to do their financial planning for the fiscal year while they are at the teller’s window (we’ve all run into them!), to gum up the whole works.
  想象一下在银行排队的一列,每个人都排在一列中等待出纳员处理。但是在JavaScript中,只有一个出纳窗口是开着的!所以在一个时间点,只有一个用户可以处理,该用户必须等排到他才能处理。这种处理方式导致只需要有一个人,当他在出纳窗口时,认为他可以花费一整年时间去做财政计划(我们都陷入等待中),我们的工作就完全堵塞了。

  This execution model, and ways of dealing with its challenges, is one that we’ll explore in
great depth in chapter 8.
  我们将在第8章深入研究这种执行模式以及如何解决以上问题的方法。

  A vastly simplified overview of this process is shown in figure 3.2.
  图3.2展示了该过程的一个简化版本。


图3.2 浏览器事件循环的一个简单版本,在一个单独的线程中,每个事件都在轮到自己处理时执行处理函数

  This concept is central to on-page JavaScript and is something we’ll see again and again throughout the examples of this book: code is set up in advance in order to execute at a later time. Except for in-line setup code, the vast majority of the code that we place onto a page is going to execute as the result of an event (in other words as part of the “Process event” box).
  以上概念是页面上的JavaScript的核心,它贯穿了本书所有的示例代码,我们将一次又一次看到它:代码先行,执行在后。除了内联创建代码,大多数代码都是先创建,而后在事件发生时才被当作处理事件执行(也就是作为事件队列的一部分)。

  It’s important to note that whatever browser mechanism is putting the events onto the queue is external to this event loop model. The processing necessary to determine when events have occurred and to push them onto the event queue does not participate in the thread that’s handling the events.
  需要注意的是,无论时哪个浏览器机制,将事件放入队列这一操作都处于事件循环模型的外部,而不会影响到事件循环模型本身的运行。确定事件发生时间并将其推送到事件队列所需的处理不会参与处理事件的线程的运行。

  For example, when the end user waves the mouse around on the page, the browser will detect these motions and push a bunch of mousemove events onto the event queue. The event loop will eventually come across these events and trigger any handlers established for that type of event.
  比如,当终端用户在页面中滑动鼠标时,浏览器会检测到这些鼠标移动操作并且把这些操作事件插入到队列尾部。事件循环最终会处理这些事件,调用为这一类事件编写的处理函数。

  Such event handlers are a case of a more general concept known as callback functions. Let’s explore that very important concept.
  这一类事件处理函数有一个更通用的概念,它们被称作回调函数.让我们探讨一下这个非常重要的概念。

THE CALLBACK CONCEPT
回调概念

Whenever we set up a function for something else to call at a later time, be it the browser or other code, we are setting up what is termed a callback. The term stems from the fact that we establish a function that some other code will later “call back” into at an appropriate point of execution.
当我们创建了一个函数以备将来调用(无论是在浏览器代码中还是在其他类型的代码中)时,我们就是在创建所谓的回调。这个术语源于这样一个事实:我们建立了一个函数,其他一些代码稍后将在适当的执行点“回调”到该函数中。

  We’ll find that callbacks are an essential part of using JavaScript effectively and we’re about to see a real-world example of how callbacks are used. But it’s a tad complex, so before we dive into it, let’s strip the callback concept completely naked and examine it in its simplest form.
  我们会发现回调是有效使用javascript的重要组成部分,后面会展示一个使用回调的真实例子。但它有点复杂,所以在深入研究它之前,让我们将回调概念完全剥离,并以最简单的形式来单独研究它。

  We’ll see callbacks used extensively as event handlers throughout the remainder of this book, but event handlers are just one example of callbacks; we can even employ callbacks ourselves in our own code. Here’s a completely useless example of a function that accepts a reference to another function as a parameter and calls that function as a callback:
  在本书的其余部分中,我们会看到大量将回调用作事件处理程序的例子,但是事件处理程序只是回调的其中一个示例,我们甚至可以在代码中使用自己定义的回调。如下所示为一个完全无用的函数,该函数接收了另一个函数的引用作为传入参数,并且使用回调方式调用了该函数。

function useless(callback) { return callback(); }

  But as useless as this example is, it clearly demonstrates the ability to pass a function as an argument to another function, and to subsequently invoke that function.
  但正如本例所示,它清楚地展示了JavaScript拥有将函数作为参数传递给另一个函数,并且在随后调用该函数的能力。

  We can test our useless function with:
  我们可以在如下代码中测试以上函数:

var text = 'Domo arigato!';
assert(useless(function(){ return text; }) === text,
"The useless function works! " + text);

  Here, we use the assert() testing function that we set up in the previous chapter to verify that the callback function is invoked and returns the expected value, which is in turn returned as the useless value. The result is shown in figure 3.3.
  在这里,我们使用了之前定义的*assert()*测试函数,来测试回调函数是否被调用并且返回了预期值。结果显示如图3.3所示。

图3.3 我们定义的无用函数可能没有什么用,但是它展示了函数可以被当作参数传递并且可以在未来的任何时间点触发调用

  That was really, really easy. And the reason is because of JavaScript’s functional nature that lets us deal with functions as first-class objects.
  这确实非常简单。而JavaScript的函数性质让我们可以将函数视为第一类对象。

  Now let’s consider a not-so-useless example, and compare it with using callbacks in a non-functional language.
  现在,让我们考虑一个“有点儿用的”示例,将它与在非函数性质语言中使用回调进行比较。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值