开发者社区> 天飞> 正文


简介: 春节闲点,可以安心的梳理一下以前不是很清楚的东东。。 看的是以下几个URL: http://javascriptissexy.




JavaScript Variable Scope and Hoisting Explained

jan. 31 2013 86

In this post, we will learn JavaScript’s variable scope and hoisting and all the idiosyncrasies of both.

We must understand how variable scope and variable hoisting work in JavaScript, if want to understand JavaScript well. These concepts may seem straightforward; they are not. Some important subtleties exist that we must understand, if we want to thrive and excel as JavaScript developers.

  • Receive Updates

Variable Scope
A variable’s scope is the context in which the variable exists. The scope specifies from where you can access a variable and whether you have access to the variable in that context.

Variables have either a local scope or a global scope.

Local Variables (Function-level scope)
Unlike most programming languages, JavaScript does not have block-level scope (variables scoped to surrounding curly brackets); instead, JavaScript has function-level scope. Variables declared within a function are local variables and are only accessible within that function or by functions inside that function. See my post on Closures for more on accessing variables in outer functions from inner functions.

Demonstration of Function-Level Scope

  var name = "Richard";
  function showName () {
  var name = "Jack"; // local variable; only accessible in this showName function​
  console.log (name); // Jack​
  console.log (name); // Richard: the global variable

No Block-Level Scope

  var name = "Richard";
  // the blocks in this if statement do not create a local context for the name variable​
  if (name) {
  name = "Jack"; // this name is the global name variable and it is being changed to "Jack" here​
  console.log (name); // Jack: still the global variable​
  // Here, the name variable is the same global name variable, but it was changed in the if statement​
  console.log (name); // Jack
  • If You Don’t Declare Your Local Variables, Trouble is Nigh
    Always declare your local variables before you use them. In fact, you should use JSHint to check your code for syntax errors and style guides. Here is the trouble with not declaring local variables:

      // If you don't declare your local variables with the var keyword, they are part of the global scope​
      var name = "Michael Jackson";
      function showCelebrityName () {
      console.log (name);
      function showOrdinaryPersonName () {
      name = "Johnny Evers";
      console.log (name);
      showCelebrityName (); // Michael Jackson​
      // name is not a local variable, it simply changes the global name variable​
      showOrdinaryPersonName (); // Johnny Evers​
      // The global variable is now Johnny Evers, not the celebrity name anymore​
      showCelebrityName (); // Johnny Evers​
      // The solution is to declare your local variable with the var keyword​
      function showOrdinaryPersonName () {
      var name = "Johnny Evers"; // Now name is always a local variable and it will not overwrite the global variable​
      console.log (name);
  • Local Variables Have Priority Over Global Variables in Functions
    If you declare a global variable and a local variable with the same name, the local variable will have priority when you attempt to use the variable inside a function (local scope):


      var name = "Paul";
      function users () {
      // Here, the name variable is local and it takes precedence over the same name variable in the global scope​
      var name = "Jack";
      // The search for name starts right here inside the function before it attempts to look outside the function in the global scope​
      console.log (name);
      users (); // Jack

Global Variables
All variables declared outside a function are in the global scope. In the browser, which is what we are concerned with as front-end developers, the global context or scope is the window object (or the entire HTML document).

  • Any variable declared or initialized outside a function is a global variable, and it is therefore available to the entire application. For example:
      // To declare a global variable, you could do any of the following:​
      var myName = "Richard";
      // or even​
      firstName = "Richard";
      // or ​
      var name; //​
  • If a variable is initialized (assigned a value) without first being declared with the var keyword, it is automatically added to the global context and it is thus a global variable:
      function showAge () {
      // Age is a global variable because it was not declared with the var keyword inside this function​
      age = 90;
      console.log(age);// ​
      showAge (); // 90​
      // Age is in the global context, so it is available here, too​
      console.log(age); // 90

    Demonstration of variables that are in the Global scope even as they seem otherwise:

      // Both firstName variables are in the global scope, even though the second one is surrounded by a block {}. ​
      var firstName = "Richard";
      var firstName = "Bob";
      // To reiterate: JavaScript does not have block-level scope​
      // The second declaration of firstName simply re-declares and overwrites the first one​
      console.log (firstName); // Bob

    Another example

      for (var i = 1; i <= 10; i++) {
      console.log (i); // outputs 1, 2, 3, 4, 5, 6, 7, 8, 9, 10;​
      // The variable i is a global variable and it is accessible in the following function with the last value it was assigned above ​
      function aNumber () {
      // The variable i in the aNumber function below is the global variable i that was changed in the for loop above. Its last value was 11, set just before the for loop exited:​
      aNumber (); // 11​
  • setTimeout Variables are Executed in the Global Scope
    Note that all functions in setTimeout are executed in the global scope. This is a tricky bit; consider this:


      // The use of the "this" object inside the setTimeout function refers to the Window object, not to myObj​
      var highValue = 200;
      var constantVal = 2;
      var myObj = {
      highValue: 20,
      constantVal: 5,
      calculateIt: function () {
      setTimeout (function () {
      console.log(this.constantVal * this.highValue);
      }, 2000);
      // The "this" object in the setTimeout function used the global highValue and constantVal variables, because the reference to "this" in the setTimeout function refers to the global window object, not to the myObj object as we might expect.​
      myObj.calculateIt(); // 400​
      // This is an important point to remember.
  • Do not Pollute the Global Scope
    If you want to become a JavaScript master, which you certainly want to do (otherwise you will be watching Honey Boo Boo right now), you have to know that it is important to avoid creating many variables in the global scope, such as this:


      // These two variables are in the global scope and they shouldn't be here​
      var firstName, lastName;
      function fullName () {
      console.log ("Full Name: " + firstName + " " + lastName );

    This is the improved code and the proper way to avoid polluting the global scope

      // Declare the variables inside the function where they are local variables​
      function fullName () {
      var firstName = "Michael", lastName = "Jackson";
      console.log ("Full Name: " + firstName + " " + lastName );

    In this last example, the function fullName is also in the global scope.

Variable Hoisting
All variable declarations are hoisted (lifted and declared) to the top of the function, if defined in a function, or the top of the global context, if outside a function.

It is important to know that only variable declarations are hoisted to the top, not variable initialization or assignments (when the variable is assigned a value).

Variable Hoisting Example:

  function showName () {
  console.log ("First Name: " + name);
  var name = "Ford";
  console.log ("Last Name: " + name);
  showName ();
  // First Name: undefined​
  // Last Name: Ford​
  // The reason undefined prints first is because the local variable name was hoisted to the top of the function​
  // Which means it is this local variable that get calls the first time.​
  // This is how the code is actually processed by the JavaScript engine:​
  function showName () {
  var name; // name is hoisted (note that is undefined at this point, since the assignment happens below)​
  console.log ("First Name: " + name); // First Name: undefined​
  name = "Ford"; // name is assigned a value​
  // now name is Ford​
  console.log ("Last Name: " + name); // Last Name: Ford​

Function Declaration Overrides Variable Declaration When Hoisted
Both function declaration and variable declarations are hoisted to the top of the containing scope. And function declaration takes precedence over variable declarations (but not over variable assignment). As is noted above, variable assignment is not hoisted, and neither is function assignment. As a reminder, this is a function assignment: var myFunction = function () {}.
Here is a basic example to demonstrate:

  // Both the variable and the function are named myName​
  var myName;

  function myName () {
  console.log ("Rich");
  // The function declaration overrides the variable name​
  console.log(typeof myName); // function
  // But in this example, the variable assignment overrides the function declaration​
  var myName = "Richard"; // This is the variable assignment (initialization) that overrides the function declaration.​
  function myName () {
  console.log ("Rich");
  console.log(typeof myName); // string

It is important to note that function expressions, such as the example below, are not hoisted.

  var myName = function () {
  console.log ("Rich");

In strict mode, an error will occur if you assign a variable a value without first declaring the variable. Always declare your variables.

Be good. Sleep well. And enjoy coding.



Understand JavaScript Closures With Ease

feb. 2 2013 202

Closures allow JavaScript programmers to write better code. Creative, expressive, and concise. We frequently use closures in JavaScript, and, no matter your JavaScript experience, you will undoubtedly encounter them time and again. Sure, closures might appear complex and beyond your scope, but after you read this article, closures will be much more easily understood and thus more appealing for your everyday JavaScript programming tasks.

This is a relatively short (and sweet) post on the details of closures in JavaScript. You should be familiar with JavaScript variable scope before you read further, because to understand closures you must understand JavaScript’s variable scope.

  • Receive Updates


What is a closure?
A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain. The closure has three scope chains: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function’s variables, and it has access to the global variables.

The inner function has access not only to the outer function’s variables, but also to the outer function’s parameters. Note that the inner function cannot call the outer function’s argumentsobject, however, even though it can call the outer function’s parameters directly.

You create a closure by adding a function inside another function.
A Basic Example of Closures in JavaScript:

function showName (firstName, lastName) {

var nameIntro = "Your name is ";
    // this inner function has access to the outer function's variables, including the parameter
function makeFullName () {
return nameIntro + firstName + " " + lastName;

return makeFullName ();


showName ("Michael", "Jackson"); // Your name is Michael Jackson

Closures are used extensively in Node.js; they are workhorses in Node.js’ asynchronous, non-blocking architecture. Closures are also frequently used in jQuery and just about every piece of JavaScript code you read.
A Classic jQuery Example of Closures:

$(function() {

var selections = []; 
$(".niners").click(function() { // this closure has access to the selections variable
selections.push (this.prop("name")); // update the selections variable in the outer function's scope


Closures’ Rules and Side Effects

  1. Closures have access to the outer function’s variable even after the outer function returns:
    One of the most important and ticklish features with closures is that the inner function still has access to the outer function’s variables even after the outer function has returned. Yep, you read that correctly. When functions in JavaScript execute, they use the same scope chain that was in effect when they were created. This means that even after the outer function has returned, the inner function still has access to the outer function’s variables. Therefore, you can call the inner function later in your program. This example demonstrates:


    function celebrityName (firstName) {
        var nameIntro = "This celebrity is ";
        // this inner function has access to the outer function's variables, including the parameter
       function lastName (theLastName) {
            return nameIntro + firstName + " " + theLastName;
        return lastName;
    var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.
    // The closure (lastName) is called here after the outer function has returned above
    // Yet, the closure still has access to the outer function's variables and parameter
    mjName ("Jackson"); // This celebrity is Michael Jackson

  2. Closures store references to the outer function’s variables; they do not store the actual value. 
Closures get more interesting when the value of the outer function’s variable changes before the closure is called. And this powerful feature can be harnessed in creative ways, such as this private variables example first demonstrated by Douglas Crockford:

    function celebrityID () {
        var celebrityID = 999;
        // We are returning an object with some inner functions
        // All the inner functions have access to the outer function's variables
        return {
            getID: function ()  {
                // This inner function will return the UPDATED celebrityID variable
                // It will return the current value of celebrityID, even after the changeTheID function changes it
              return celebrityID;
            setID: function (theNewID)  {
                // This inner function will change the outer function's variable anytime
                celebrityID = theNewID;
    var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.
    mjID.getID(); // 999
    mjID.setID(567); // Changes the outer function's variable
    mjID.getID(); // 567: It returns the updated celebrityId variable

  3. Closures Gone Awry
Because closures have access to the updated values of the outer function’s variables, they can also lead to bugs when the outer function’s variable changes with a for loop. Thus:


    // This example is explained in detail below (just after this code box).
    function celebrityIDCreator (theCelebrities) {
        var i;
        var uniqueID = 100;
        for (i = 0; i < theCelebrities.length; i++) {
          theCelebrities[i]["id"] = function ()  {
            return uniqueID + i;
        return theCelebrities;
    var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
    var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
    var stalloneID = createIdForActionCelebs [0];

console.log(; // 103

    In the preceding example, by the time the anonymous functions are called, the value of i is 3 (the length of the array and then it increments). The number 3 was added to the uniqueID to create 103 for ALL the celebritiesID. So every position in the returned array get id = 103, instead of the intended 100, 101, 102.

    The reason this happened was because, as we have discussed in the previous example, the closure (the anonymous function in this example) has access to the outer function’s variables by reference, not by value. So just as the previous example showed that we can access the updated variable with the closure, this example similarly accessed the i variable when it was changed, since the outer function runs the entire for loop and returns the last value of i, which is 103.

    To fix this side effect (bug) in closures, you can use an Immediately Invoked Function Expression (IIFE), such as the following:

    function celebrityIDCreator (theCelebrities) {
        var i;
        var uniqueID = 100;
        for (i = 0; i < theCelebrities.length; i++) {
            theCelebrities[i]["id"] = function (j)  { // the j parametric variable is the i passed in on invocation of this IIFE
                return function () {
                    return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array
                } () // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.
            } (i); // immediately invoke the function passing the i variable as a parameter
        return theCelebrities;
    var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
    var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
    var stalloneID = createIdForActionCelebs [0];
console.log(; // 100
    var cruiseID = createIdForActionCelebs [1];
console.log(; // 101














  • 问题的核心


现在我们定义了一个函数(function foo(){}或者var foo = function(){}),函数名后加上一对小括号即可完成对该函数的调用,比如下面的代码:








  • 一波未平一波又起










如果想要了解更多,可以参考ECMA-262-3 in detail. Chapter 5. Functions.














  • 无论何时,给立即执行函数加上括号是个好习惯












  • 立即执行函数与闭包的暧昧关系









  • 我为什么更愿意称它是“立即执行函数”而不是“自执行函数”


IIFE的称谓在现在似乎已经得到了广泛推广(不知道是不是原文作者的功劳?),而原文写于10年,似乎当时流行的称呼是自执行函数(Self-executing anonymous function),接下去作者开始为了说明立即执行函数的称呼好于自执行函数的称呼开始据理力争,有点咬文嚼字,不过也蛮有意思的,我们来看看作者说了些什么。





  • 最后的旁白:模块模式











  1. ECMA-262-3 in detail. Chapter 5. Functions. – Dmitry A. Soshnikov
  2. Functions and function scope - Mozilla Developer Network
  3. Named function expressions - Juriy “kangax” Zaytsev
  4. JavaScript Module Pattern: In-Depth - Ben Cherry
  5. Closures explained with JavaScript - Nick Morgan
  6. ~~~~~~~








//Note that the item in the click method's parameter is a function, not a variable.
//The item is a callback function
$("#btn_1").click(function() {
  alert("Btn 1 Clicked");



var friends = ["Mike", "Stacy", "Andy", "Rick"];

friends.forEach(function (eachName, index){
console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick







//The anonymous function is not being executed there in the parameter. 
//The item is a callback function
$("#btn_1").click(function() {
  alert("Btn 1 Clicked");

匿名函数将延迟在click函数的函数体内被调用,即使没有名称,也可以被包含函数通过 arguments对象访问。

当作为参数传递一个回调函数给另一个函数时,回调函数将在包含函数函数体内的某个位置被执行,就像回调函数在包含函数的函数体内定义一样。这意味着回调函数是闭包的,想更多地了解闭包,请参考作者另一个贴子Understand JavaScript Closures With Ease。从所周知,闭包函数可以访问包含函数的作用域,所以,回调函数可以访问包含函数的变量,甚至是全局变量。




// global variable
var allUserData = [];

// generic logStuff function that prints to console
function logStuff (userData) {
    if ( typeof userData === "string")
    else if ( typeof userData === "object")
        for (var item in userData) {
            console.log(item + ": " + userData[item]);



// A function that takes two parameters, the last one a callback function
function getInput (options, callback) {
    allUserData.push (options);
    callback (options);


// When we call the getInput function, we pass logStuff as a parameter.
// So logStuff will be the function that will called back (or executed) inside the getInput function
getInput ({name:"Rich", speciality:"JavaScript"}, logStuff);
//  name: Rich
// speciality: JavaScript


//Global variable
var generalLastName = "Clinton";

function getInput (options, callback) {
    allUserData.push (options);
// Pass the global variable generalLastName to the callback function
    callback (generalLastName, options);



function getInput(options, callback) {

    // Make sure the callback is a function
    if (typeof callback === "function") {
    // Call it, since we have confirmed it is callable



// Define an object with some properties and a method
// We will later pass the method as a callback function to another function
var clientData = {
    id: 094545,
    fullName: "Not Set",
    // setUserName is a method on the clientData object
    setUserName: function (firstName, lastName)  {
        // this refers to the fullName property in this object
      this.fullName = firstName + " " + lastName;

function getUserInput(firstName, lastName, callback)  {
    // Do other stuff to validate firstName/lastName here

    // Now save the names
    callback (firstName, lastName);

在下面的示例代码中,当clientData.setUserName被执行时,this.fullName不会设置clientData 对象中的属性fullName,而是设置window 对象中的fullName,因为getUserInput是一个全局函数。出现这种现象是因为在全局函数中this对象指向了window对象。

getUserInput ("Barack", "Obama", clientData.setUserName);

console.log (clientData.fullName);// Not Set

// The fullName property was initialized on the window object
console.log (window.fullName); // Barack Obama


我们可以通过使用 Call 或 Apply函数来解决前面示例中的问题。到目前为止,我们知道JavaScript中的每一个函数都有两个方法:Call和Apply。这些方法可以被用来在函数内部设置this对象的内容,并内容传递给函数参数指向的对象。

Call takes the value to be used as the this object inside the function as the first parameter, and the remaining arguments to be passed to the function are passed individually (separated by commas of course). The Apply function’s first parameter is also the value to be used as the thisobject inside the function, while the last parameter is an array of values (or the arguments object) to pass to the function.  (该段翻译起来太拗口了,放原文自己体会)


//Note that we have added an extra parameter for the callback object, called "callbackObj"
function getUserInput(firstName, lastName, callback, callbackObj)  {
    // Do other stuff to validate name here

    // The use of the Apply function below will set the this object to be callbackObj
    callback.apply (callbackObj, [firstName, lastName]);


// We pass the clientData.setUserName method and the clientData object as parameters. The clientData object will be used by the Apply function to set the this object

getUserInput ("Barack", "Obama", clientData.setUserName, clientData);

// the fullName property on the clientData was correctly set
console.log (clientData.fullName); // Barack Obama

我们也可以使用Call 函数,但在本例中我们使用的Apply 函数。


function successCallback() {
    // Do stuff before send

function successCallback() {
    // Do stuff if success message received

function completeCallback() {
    // Do stuff upon completion

function errorCallback() {
    // Do stuff if error received





var p_client = new Db('integration_tests_20', new Server("", 27017, {}), {'pk':CustomPKFactory});, p_client) {
    p_client.dropDatabase(function(err, done) {
        p_client.createCollection('test_custom_key', function(err, collection) {
            collection.insert({'a':1}, function(err, docs) {
                collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
                    cursor.toArray(function(err, items) {
                        test.assertEquals(1, items.length);

                        // Let's close the db


  1. 命名并定义你的函数,然后传递函数名作为回调,而不是在主函数的参数列表里定义一个匿名函数。
  2. 模块化:把你的代码划分成一个个模块,这样你可以空出一部分代码块做特殊的工作。然后你可以将这个模型引入到你的大型应用程序中。




  • 避免重复代码 (DRY—Do Not Repeat Yourself)
  • 在你需要更多的通用功能的地方更好地实现抽象(可处理各种类型的函数)。
  • 增强代码的可维护性
  • 增强代码的可读性
  • 有更多定制的功能




// First, setup the generic poem creator function; it will be the callback function in the getUserInput function below.
function genericPoemMaker(name, gender) {
    console.log(name + " is finer than fine wine.");
    console.log("Altruistic and noble for the modern time.");
    console.log("Always admirably adorned with the latest style.");
    console.log("A " + gender + " of unfortunate tragedies who still manages a perpetual smile");

//The callback, which is the last item in the parameter, will be our genericPoemMaker function we defined above.
function getUserInput(firstName, lastName, gender, callback) {
    var fullName = firstName + " " + lastName;

    // Make sure the callback is a function
    if (typeof callback === "function") {
    // Execute the callback function and pass the parameters to it
    callback(fullName, gender);


getUserInput("Michael", "Fassbender", "Man", genericPoemMaker);
// Output
/* Michael Fassbender is finer than fine wine.
Altruistic and noble for the modern time.
Always admirably adorned with the latest style.
A Man of unfortunate tragedies who still manages a perpetual smile.

因为getUserInput 函数只处理用户数据的输入,我们可以传递任何回调函数给它。例如我们可以像这样传递一个greetUser函数。

function greetUser(customerName, sex)  {
   var salutation  = sex && sex === "Man" ? "Mr." : "Ms.";
  console.log("Hello, " + salutation + " " + customerName);

// Pass the greetUser function as a callback to getUserInput
getUserInput("Bill", "Gates", "Man", greetUser);

// And this is the output
Hello, Mr. Bill Gates

和上一个例子一样,我们调用了同一个getUserInput 函数,但这次却执行了完全不同的任务。



  • 异步执行(例如读文件,发送HTTP请求)
  • 事件监听和处理
  • 设置超时和时间间隔的方法
  • 通用化:代码简洁 



Vue 集成腾讯地图基础api Demo集合(基础地图引入与展示模块,地址逆解析,3D/2D切换 ,位置服务,mark标记)
Vue 集成腾讯地图基础api Demo集合(基础地图引入与展示模块,地址逆解析,3D/2D切换 ,位置服务,mark标记)
7 0
Vue 仿钉钉流程图(流程节点绘制 vue+Ant【如果用其他UI库需要替换几个组件】 附 demo)
# [这里是git地址](
6 0
数据类型-数值和字符串 | 学习笔记
5 0
作用域及作用域链 | 学习笔记
5 0
9 0
代码注释 | 学习笔记
9 0
求数组元素的和 | 学习笔记
6 0
15 0
函数的返回值 | 学习笔记
6 0
函数当作参数2 | 学习笔记
6 0