文章出處

事出有因

為何選擇event loop?

Event Loop是一種推進無阻塞I/O(網絡、文件或跨進程通訊)的軟件模式。傳統的阻塞編程也是用一樣的方式,通過function來調用I/O。但進程會在該I/O操作結束前卡住不動,下面的這段偽代碼可以演示阻塞I/O的情況:

var post = db.query('SELECT * FROM posts where id = 1');  // 這行后面的指令都無法執行,除非等到這行指令執行完畢
doSomethingWithPost(post);
doSomethingElse();

這里都發生了什么呢?當數據庫查詢被執行時,整個進程/線程都閑置下來,等待查詢操作的響應,我們稱之為阻塞。數據查詢的響應可能要花費上千個CPU周期,這段時間內,整個進程都處于不可用狀態。進程在數據庫操作完成前就白白等待下去,浪費了讓它來著手處理其它客戶端請求的機會。

這種編程方式將不允許你來并行處理I/O(例如同時執行另一個數據庫查詢或與遠程web服務進行通訊),導致調用棧被凍結了,傻傻等候數據庫服務的回應。

這里有兩個可實現的解決方案能讓進程在這種等候期間保存忙碌——創建更多的調用棧,或者使用事件回調。

Why?

Why the event loop?

The Event Loop is a software pattern that facilitates non-blocking I/O (network, file or inter-process
communication). Traditional blocking programming does I/O in the same fashion as regular function
calls; processing may not continue until the operation is complete. Here is some pseudo-code that
demonstrates blocking I/O:
1 var post = db.query('SELECT * FROM posts where id = 1');
2 // processing from this line onward cannot execute until the line above completes
3 doSomethingWithPost(post);
4 doSomethingElse();
What is happening here? While the database query is being executed, the whole process/thread
idles, waiting for the response. This is called blocking. The response to this query may take many
thousands of CPU cycles, rendering the entire process unusable during this time. The process could
have been servicing other client requests instead of just waiting for the database operation to
complete.
Programming in this way does not allow you to parallelize I/O (such as performing another database
query or communicating with a remote web service). The call stack becomes frozen, waiting for the
database server to reply.
This leaves you with two possible solutions to keep the process busy while it’s waiting: create more
call stacks or use event callbacks.
原文

 

方案1: 創建更多調用棧

為了讓你的進程能操作更多并發I/O,你需要擁有更多并發的調用棧。因此你可以使用線程或者一些協作式多任務的方案,例如協同程序(coroutines)、fibers、延續機制(continuations)等等。對多線程并發模型存在難以配置、理解和編譯的問題,主要是因為同步訪問和修改共享狀態的復雜性。你無法知道一個你運行的線程何時才從執行中結束,從而導致奇怪的、難以重現的各種bug。

另外,當你使用的棧超過一個時,使用協作式多任務是場鬧劇,每一個執行的“線程”都會被明確要求刪除自己,來給其它并行的“線程”讓路,這樣雖能舒緩同步需求,但也讓程序員失去了對線程調度的控制,從而讓事情變的復雜并有滋生錯誤的風險。

Solution 1: Create more call stacks

In order for your process to handle more concurrent I/O, you have to have more concurrent call
stacks. For this, you can use threads or some kind of cooperative multi-threading scheme like coroutines,
fibers, continuations, etc.
The multi-threaded concurrency model can be very difficult to configure, understand and debug,
mainly because of the complexity of synchronization when accessing and modifying shared state;
you never know when the thread you are running is going to be taken out of execution, which can
lead to bugs that are strange and difficult to reproduce.
On the other hand, cooperative multi-threading is a “trick” where you have more than one stack,
and each “thread” of execution explicitly de-schedules itself to give time to another parallel “thread”
of execution. This can relax the synchronization requirements but can become complex and errorprone,
since the thread scheduling is left in the hands of the programmer.
原文

 

方案2:使用事件驅動I/O

事件驅動I/O方案指的是由你注冊回調函數,當與之相關的I/O事件發生時,觸發該回調。

一個事件回調是一個函數,可以在某些有意義的事件(比如“從數據庫查詢得到的結果是有效的”)發生的時候該函數便會被觸發。

在之前的示例中,我們可以做一番修改,讓其使用事件回調:

callback = function(post) {
doSomethingWithPost(post); // 只有當db.query函數返回響應的時候才會執行
};
db.query('SELECT * FROM posts where id = 1', callback);
doSomethingElse(); // 該指令會獨立執行,無關db.query函數調用返回的狀態

這里我們定義了一個函數,它在數據庫操作完畢的時候會被觸發。接著把該函數作為回調參數放到數據庫查詢操作中,這樣一來,“數據查詢結果是有效的就執行回調”就成了數據庫操作的一個責任。你可以用一個內聯的匿名函數來寫的更緊湊些:

db.query('SELECT * FROM posts where id = 1',
function(post) {
doSomethingWithPost(post); // 只有當db.query函數返回響應的時候才會執行
}
);
doSomethingElse(); // 該指令會獨立執行,無關db.query函數調用返回的狀態

當db.query()執行的時候,進程可以自由地繼續運行doSomethingElse()、繼續為新的客戶端請求提供服務。

Solution 2: Use event-driven I/O

Event-driven I/O is a scheme where you register callbacks to be invoked when an interesting I/O
event happens.
An event callback is a function that gets invoked when something significant happens (e.g. the result
of a database query is available.)
To use event callbacks in the previous example, you could change it to:
1 callback = function(post) {
2 doSomethingWithPost(post); // this will only execute when the db.query function\
3 returns.
4 };
5 db.query('SELECT * FROM posts where id = 1', callback);
6 doSomethingElse(); // this will execute independent of the returned status of the\
7 db.query call.
Here you are defining a function to be invoked when the database operation is complete, then passing
this function as a callback argument to the db query operation. The db operation becomes responsible
for executing the callback when the result is available.
You can use an inline anonymous function to express this in a more compact fashion:
1 db.query('SELECT * FROM posts where id = 1',
2 function(post) {
3 doSomethingWithPost(post); // this will only execute when the db.query functi\
4 on returns.
5 }
6 );
7 doSomethingElse(); // this will execute independent of the returned status of the\
8 db.query call.
While db.query() is executing,the process is free to continue running doSomethingElse(), and even
service new client requests.
原文

 

在很長一段時間,在C系統編程社區“hacker”里都知悉事件驅動編程是擴展服務來操作多個同步連接的最好方法——內存使用更高效(更少的上下文環境需保存),等待時間更少(更少的上下文轉換)。
該知識點逐漸滲透到其它平臺和社區——像Ruby的 EventMachine, Perl的AnyEvent和Python的Twisted等,都是非常知名的event loop操作機制。
貼士:關于更多事件驅動服務機制的信息,可以查閱http://en.wikipedia.org/wiki/Reactor_pattern
執行一個使用事件驅動框架的應用,需要了解框架特效以及框架特性庫。打個比方,當你使用了Event Machine,你必須避免使用Ruby-land上所有可用的同步庫(也就是說大部分的庫)。
為了獲取非阻塞的好處,你會被限制使用那些Event Machine的特性庫。如果你使用了那些阻塞的庫,你的程序將無法最佳地進行擴展,因為event loop不斷地被阻塞了,可能導致I/O事件執行過程的延時,進而讓你的程序變慢,違背了你使用事件驅動I/O的初衷。
Node從一開始就被創造為一種無阻塞I/O服務平臺,這意味著你可以期許一切建立在Node上的東西都能是無阻塞的(包括一些具體、明確的異常處理)。由于JS本身小巧也不不強制要求任何I/O操作(他不像C、Ruby或者Python那樣有標準的I/O庫),Node才可以從零開始建立起來。

For quite some time, the C systems-programming “hacker” community has known that event-driven
programming is the best way to scale a server to handle many concurrent connections. It has been
known to be more efficient regarding memory: less context to store, and time: less context-switching.
This knowledge has been infiltrating other platforms and communities: some of the most wellknown
event loop implementations are Ruby’s Event Machine, Perl’s AnyEvent and Python’s
Twisted, and some others.

Tip: For more info about event-driven server implementations, see http://en.wikipedia.org/wiki/Reactor_-
pattern⁴.
Implementing an application using one of these event-driven frameworks requires frameworkspecific
knowledge and framework-specific libraries. For example: when using Event Machine, you
must avoid using all the synchronous libraries available in Ruby-land (i.e. most libraries). To gain
the benefit of not blocking, you are limited to using libraries that are specific for Event Machine. If
you use blocking libraries, your program will not be able to scale optimally because the event loop is
constantly being blocked, which may delay the processing of I/O events and makes your application
slow, defeating the original purpose of using event-driven I/O.
Node has been devised as a non-blocking I/O server platform from day one, which means that you
should expect everything built on top of it to be non-blocking (with some specific and very explicit
exceptions). Since JavaScript itself is very minimal and does not impose any way of doing I/O (it
does not have a standard I/O library like C, Ruby or Python), Node has a clean slate to build upon.
原文

 

為何采用JavaScript?

Ryan Dahl 開始執行該項目時是采用C語言來搭建平臺的,但他很快意識到,若要維持好回調之間的上下文關系會非常復雜,并會導致代碼結構不合理。于是他轉向了Lua,Lua有著一些阻塞I/O的庫,這樣混合著阻塞和非阻塞的方式會讓大部分開發者摸不著頭腦,從而阻礙他們來搭建可擴展的應用,因此Lua也不是干這事的理想的語言。

于是Dahl開始考慮JS了,JS有閉包也有經典的函數,讓它實在地、出色地匹配了事件型I/O編程的需求。

閉包指的是繼承了外部封閉環境的變量的函數,當一個函數回調執行它的時候,它能魔幻般地記住它之前聲明過的上下文環境,包括該上下文環境和其它父級上下文環境中的變量。這種強大的特性是使得Node在各種編程社區中取得成功的重要原因。讓我們來看一些它出色的運行方式:

在web瀏覽器中,如果你想監聽某個事件,比如點擊一個按鈕,你可以類似這么做:

var clickCount = 0;
document.getElementById('mybutton').onclick = function() {
clickCount ++;
alert('Clicked ' + clickCount + ' times.');
};

或者使用jQuery:

var clickCount = 0;
$('button#mybutton').click(function() {
clickCount ++;
alert('Clicked ' + clickCount + ' times.');
});

在這兩個示例中,我們都把回調函數作為一個參數來指派,它會在相關事件(如示例是按鈕被點擊)發生后被執行。該點擊事件的處理函數可以訪問其聲明處作用域內的每一個變量。
也就是說,該點擊事件處理函數可以訪問在其父級閉包作用域內聲明的變量clickCount。
在這里我們使用了一個全局變量“clickCount”,用來保存用戶在不同時刻點擊按鈕的數量。我們也可以避免使用全局變量——把它放到一個閉包內即可,讓變量clickCount只能在我們創建的閉包內被訪問:

(function() {
var clickCount = 0;
$('button#mybutton').click(function() {
clickCount ++;
alert('Clicked ' + clickCount + ' times.');
});
})();

在第七行我們讓函數在其定義后便立即執行,如果你對這種方式感到陌生,倒是無須擔心,后續我們會介紹到這種模式。

在這里你無需操心同步的問題了——你完全可以保證你的回調函數在其結束前不會被中斷。

Why JavaScript?

Ryan Dahl began this pet project of his by building a platform that was programmable in C, but he
soon realized that maintaining the context between callbacks was too complicated and could lead
to poorly structured code. He then turned to Lua.
Lua already has several blocking I/O libraries and the mix of blocking and non-blocking could
confuse average developers and prevent many of them from building scalable applications, so Lua
wasn’t ideal either. Dahl then thought to JavaScript.
JavaScript has closures and first-class functions, making it indeed a powerful match with evented
I/O programming.
Closures are functions that inherit the variables from their enclosing environment. When a function
callback executes it will magically remember the context in which it was declared, along with all
the variables available in that context and any parent contexts. This powerful feature is at the heart
of Node’s success among programming communities. Let’s see a little bit of this goodness in action:
In the web browser, if you want to listen for an event, a button click for instance, you may do
something like:
1 var clickCount = 0;
2 document.getElementById('mybutton').onclick = function() {
3 clickCount ++;
4 alert('Clicked ' + clickCount + ' times.');
5 };
or, using jQuery:
⁴http://en.wikipedia.org/wiki/Reactor_pattern
Why? 7
1 var clickCount = 0;
2 $('button#mybutton').click(function() {
3 clickCount ++;
4 alert('Clicked ' + clickCount + ' times.');
5 });
In both examples we assign or pass a function as an argument. This function will then be executed
later once the relevant event (button clicking in this case) happens. The click handling function has
access to every variable in scope at the point where the function is declared, i.e. practically speaking,
the click handler has access to the clickCount variable, declared in the parent closure.
Here we are using a global variable, “clickCount”, where we store the number of times the user has
clicked a button. We can also avoid having a global variable accessible to the rest of the system,
by wrapping it inside another closure, making the clickCount variable only accessible within the
closure we created:
1 (function() {
2 var clickCount = 0;
3 $('button#mybutton').click(function() {
4 clickCount ++;
5 alert('Clicked ' + clickCount + ' times.');
6 });
7 })();
In line 7 we are invoking a function immediately after defining it. If this is strange to you, don’t
worry! We will cover this pattern later.
Here you don’t have to worry about synchronization: your callback function will not be interrupted
until it returns - you have that guarantee.
原文

 

我要如何擺脫對JS的恐懼并試著愛上它?

JavaScript有其優勢也有其劣勢,它是由Netscape公司的Brendan Eich在1995年創建的,當初為了盡快用到新版的Netscape瀏覽器上,JS被設計的很匆忙。因此JS雖然有很多很好甚至很贊的地方,也存在某些不好的地方。 本書內容不會涉及比較JS的“好”和“壞”,(當然我們只會使用那些“好”的部分來書寫示例。)如果你想了解更多此話題信息,可以閱讀由O’Reilly出版的Douglas Crockford的《JavaScript, The Good Parts》一書。

盡管JS有其缺點,但也超乎預料地迅速流行起來,成為了瀏覽器語言的無冕之王。在那時候,JS主要是用來檢測和操作HTML文檔、創建動態的客戶端web應用。在1998年年末,萬維網路聯盟(W3C)標準化了文檔對象模型(DOM),以及在用戶端檢測、操作HTML文檔的API。由于JS相較DOM API存在怪異模式和抗衡,也因為它在不同瀏覽器渲染引擎上存在兼容問題(甚至在同渲染引擎上的不同產品有時也會存在兼容問題),導致JS很快得到了壞名聲。

盡管在一些開發者社區對JS還是口誅筆伐,JS還是日漸被廣泛采納。無論好壞,JS在當下已成為了世界上最多被開發的編程語言,而且開發數量還在增長中。 如果你獲悉過一門語言的出色特性,比如原型繼承、函數閉包等等,而且你懂得如何避免或繞過一門語言中的瑕疵處,那么JS會是讓你用起來很舒心的一門語言。

函數聲明風格

在JS中,函數可以通過多種方式來進行聲明,最簡單的方式就是這樣匿名地聲明它:

function() {
console.log('hello');
}

這里我們聲明了一個函數,但這樣做沒啥用處,因為它沒被調用到。而且因為它沒有名字,所以我們也沒辦法調用它。
不過我們可以這樣調用一個匿名函數:

(function() {
console.log('hello');
})();

此處我們讓函數在聲明后便立即執行起來。注意我們在整個函數聲明體的外部包裹了一對括號。

我們也可以這樣給函數命名:

function myFunction () {
console.log('hello');
}

我們給這個命名函數起了個名字叫“myFunction”,myFunction在其聲明的作用域內部是可以被調用的:

myFunction();

在內部作用域中也可以:

 function myFunction () {
 console.log('hello');
 }

 (function() {
 myFunction();
 })();

由于JS把函數作為一級對象來處理,因此我們可以把一個函數指派給一個變量:

 var myFunc = function() {
 console.log('hello');
 }

現在這個函數等值于這個myFunc變量,我們可以指派這個函數給其它變量:

var myFunc2 = myFunc;

并像其它函數那樣調用它們:

 myFunc();
 myFunc2();

我們可以結合全部技巧, 把一個命名函數存儲在一個變量中:

 var myFunc2 = function myFunc() {
 console.log('hello');
 }
 myFunc2();

(注意我們在myFunc本身所處的作用域外是不能訪問到myFunc的)
我們可以使用一個變量或函數的名稱來作為其它函數的參數:

 var myFunc = function() {
 console.log('hello');
 }

 console.log(myFunc);

如果在其它地方我們用不上它,可以直接以內聯的形式聲明:

 console.log(function() {
 console.log('hello');
 });
How I Learned to Stop Fearing and Love JavaScript

JavaScript has good and bad parts. It was created in 1995 by Netscape’s Brendan Eich, in a rush to
ship the latest version of the Netscape web browser. Due to this rush some good, even wonderful,
parts got into JavaScript, but also some bad parts.
This book will not cover the distinction between JavaScript good and bad parts. (For all we know, we
will only provide examples using the good parts.) For more on this topic you should read Douglas
Crockford book named “JavaScript, The Good Parts”, edited by O’Reilly.

In spite of its drawbacks, JavaScript quickly - and somewhat unpredictably - became the de-facto
language for web browsers. Back then, JavaScript was used primarily to inspect and manipulate
HTML documents, allowing the creation the first dynamic, client-side web applications.
In late 1998, the World Wide Web Consortium (W3C), standardized the Document Object Model
(DOM), an API devised to inspect and manipulate HTML documents on the client side. In response
to JavaScript’s quirks and the initial hatred towards the DOM API, JavaScript quickly gained a
bad reputation, also due to some incompatibilities between browser vendors (and sometimes even
between products from the same vendor!).
Despite mild to full-blown hate in some developer communities, JavaScript became widely adopted.
For better or for worse, today JavaScript is the most widely deployed programming language on
planet Earth – and growing.
If you learn the good features of the language - such as prototypical inheritance, function closures,
etc. - and learn to avoid or circumvent the bad parts, JavaScript can be a very pleasant language to
work in.
Function Declaration Styles
A function can be declared in many ways in JavaScript. The simplest is declaring it anonymously:
1 function() {
2 console.log('hello');
3 }
Here we declare a function, but it’s not of much use, because we do not invoke it. What’s more, we
have no way to invoke it as it has no name.
We can invoke an anonymous function in-place:
1 (function() {
2 console.log('hello');
3 })();
Here we are executing the function immediately after declaring it. Notice we wrap the entire
function declaration in parenthesis.
We can also name functions like this:
1 function myFunction () {
2 console.log('hello');
3 }
Here we are declaring a named function with the name: “myFunction”. myFunction will be available
inside the scope in which it’s declared

1 myFunction();
and also within inner scopes:
1 function myFunction () {
2 console.log('hello');
3 }
4
5 (function() {
6 myFunction();
7 })();
A result of JavaScript treating functions as first-class objects means we can assign a function to a
variable:
1 var myFunc = function() {
2 console.log('hello');
3 }
This function is now available as the value of the myFunc variable.
We can assign that function to another variable:
1 var myFunc2 = myFunc;
And invoke them just like any other function:
1 myFunc();
2 myFunc2();
We can mix both techniques, having a named function stored in a variable:
1 var myFunc2 = function myFunc() {
2 console.log('hello');
3 }
4 myFunc2();
(Note though, we cannot access myFunc from outside the scope of myFunc itself!)
We can then use a variable or a function name to pass variables into functions like this:

1 var myFunc = function() {
2 console.log('hello');
3 }
4
5 console.log(myFunc);
or simply declare it inline if we don’t need it for anything else:
1 console.log(function() {
2 console.log('hello');
3 });
原文

 

函數是一級對象
事實上,在JS里其實不存在“二級對象”。JS是徹頭徹尾的面向對象語言,幾乎JS上所有的東西都是一個對象。就此而言,一個函數就是一個對象,一個你可以設置其屬性,可以把它放到參數中,或者返回它們的對象
示例:

 1  var scheduler = function(timeout, callbackfunction) {
 2  return function() {
 3  setTimeout(callbackfunction, timeout)
 4  }
 5  };
 6 
 7  (function() {
 8  var timeout = 1000; // 1 second
 9  var count = 0;
10  var schedule = scheduler(timeout, function doStuff() {
11  console.log(++ count);
12  schedule();
13  });
14  schedule()
15  })();

在這個小例子中我們創建了一個函數并保存到一個叫做scheduler的變量中(第一行),該函數返回了一個啟動了定時器的函數,定時器會在某個數量的毫秒(第三行)過后執行所給的函數。這個定時器會在定時變量所指定的1秒的延遲后執行回調函數。

在第九行我們聲明了一個會在第十五行立即執行的函數,這是一個JS中很常用的創建新作用域的辦法。在該作用域內,我們創建了兩個變量:timeout(第八行)和 count(第九行)。注意這些變量無法在作用域外部被訪問到。
接著看第十行,我們調用了scheduler函數,并把timeout的值和名為doStuff的函數分別作為第一個和第二個參數傳入。我們把它返回的函數保存到局部變量schedule中,我們會在后面調用它(第十四行),來觸發setTimeout定時器。當超時發生時,函數會增加變量count的值并記錄它,接著再一次調用schedule。

所以在這個小示例中我可以看到:函數可作為參數來傳遞;函數可以用來創建作用域;函數可以異步地執行回調并返回函數。我們也在這里提出了封裝(向外部作用域隱藏局部變量)和遞歸(在函數的結尾調用它自己)的概念。

在JS中你甚至可以設置和訪問一個函數的屬性,例如這樣:

var myFunction = function() {
// do something crazy
};
myFunction.someProperty = 'abc';
console.log(myFunction.someProperty);
// #=> "abc"

JS毋庸置疑是一門強大的語言,如果你還沒準備好,那么你應該開始學習并擁抱它那些出色的地方。

Functions are first-class objects

In fact, there are no second-class objects in JavaScript. JavaScript is the ultimate object-oriented
language, where (almost) everything is indeed, an object. As that, a function is an object where you
can set properties, pass it around inside arguments and return them.
Example:
1 var scheduler = function(timeout, callbackfunction) {
2 return function() {
3 setTimeout(callbackfunction, timeout)
4 }
5 };
6
7 (function() {
8 var timeout = 1000; // 1 second
9 var count = 0;
10 var schedule = scheduler(timeout, function doStuff() {
11 console.log(++ count);
12 schedule();
13 });
14 schedule()
15 })();
16
17 // "timeout" and "count" variables
18 // do not exist in this scope.
In this little example we create a function and store it in a variable called “scheduler” (starting on
line 1). This function returns a function that sets up a timer that will execute a given function within
a certain number of miliseconds (line 3). This timeout will schedule a callback function to be called
after a time delay of 1 second, as specified by the timeout variable.
Why? 11
In line 9 we declare a function that will immediately be executed in line 15. This is a normal way
to create new scopes in JavaScript. Inside this scope we create 2 variables: “timeout” (line 8) and
“count” (line 9). Note that these variables will not be accessible to the outer scope.
Then, on line 10, we invoke the scheduler function, passing in the timeout value as first argument
and a function called doStuff as second argument. This returns a function that we store in the local
schedule variable, which we later invoke (line 14), provoking the setTimeout to be called. When the
timeout occurs, this function will increment the variable count and log it, and also call the schedule
all over again.
So in this small example we have: functions passed as argument, functions to create scope, functions
to serve as asynchronous callbacks and returning functions. We also here present the notions of
encapsulation (by hiding local variables form the outside scope) and recursion (the function is calling
itself at the end).
In JavaScript you can even set and access attributes in a function, something like this:
1 var myFunction = function() {
2 // do something crazy
3 };
4 myFunction.someProperty = 'abc';
5 console.log(myFunction.someProperty);
6 // #=> "abc"
JavaScript is indeed a powerful language, and if you don’t already, you should learn it and embrace
its good parts.
原文

 

JSHint
本書不會涉及該部分內容,但JS的確存在某些我們應當全力避免的缺陷部分。就我而言,JSHint便是非常好用一款工具,它能分析你的JS文件并輸出系列錯誤和警告信息,從而告知你一些已知的腳本錯誤,比如直接使用了全局域變量(沒有var的變量),比如凍結了在遞歸的回調中使用的變量。還有其它一些有用的功能。
JHLint可以通過如下命令進行安裝:

$ npm install -g jshint

如果你沒有事先安裝過Node,可查閱本書的NPM章節。

然后我們可以通過如下命令運行JHLint:

$ jshint myfile.js

你也可以在你的home文件夾中定制一個.jshintrc 文件來定義該工具應服從的規則。更多關于JSHint的信息你可以到 http://www.jshint.com/docs/ 查閱官方文檔。

JSHint

It’s not to be covered here, but JavaScript indeed has some bad parts, and they should be avoided at
all costs.
One tool that’s proven invaluable to me is JSHint. JSHint analyzes your JavaScript file and outputs a
series of errors and warnings, including some known misuses of JavaScript, such as using globallyscoped
variables (like when you forget the “var” keyword), and freezing values inside iteration that
have callbacks that use them, and many others that are useful.
JHLint can be installed using
1 $ npm install -g jshint
If you don’t have Node installed see section about NPM⁵.
and can be run from the command line like this:
⁵index.html#npm
Why? 12
1 $ jshint myfile.js
You can also define what rules this tool should obey by defining and customizing a .jshintrc inside
your home directory. For more information on JSHint plrease refer to the official documentation at
http://www.jshint.com/docs/⁶.
原文

 

JavaScript版本

JavaScript是其本身名字“ECMAScript”的一個標準,它已經歷了好幾次迭代。 Node支持V8腳本引擎所支持的一切——ECMA3和部分ECMA5。

在github wiki頁面上有友好地列出那些被支持的ECMA5部分,你可以到這里查閱: https://github.com/joyent/node/wiki/5-Mozilla-Features-Implemented-in-V8

JavaScript versions

JavaScript is a standard with its own name - ECMAScript - and it has gone through various iterations.
Currently Node natively supports everything the V8 JavaScript engine supports ECMA 3rd edition
and parts of the new ECMA 5th edition.
These parts of ECMA 5 are nicely documented on the following github wiki page: https://github.com/joyent/node/wiki/5-Mozilla-Features-Implemented-in-V8⁷
原文

donate


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


全站熱搜
創作者介紹
創作者 大師兄 的頭像
大師兄

IT工程師數位筆記本

大師兄 發表在 痞客邦 留言(0) 人氣()