JavaScript作用域学习笔记

变量声明语块

JavaScript使用关键字var来声明变量。变量可以包含任意类型的数据:数组、整数、浮点数、字符串等。

var str = "Hello World", //字符串
    arr = [],            //数组
    num = 123,           //数字
    name;                //仅声明,没有赋值

JavaScript是一种宽松型(loosely-typed)的语言也是一种动态语言,因为不需要指定变量的类型,并且即使在变量赋值以后,值的类型也可以通过赋予不同的类型值而改变。可以用var关键字同时声明多个变量并赋值,以逗号分隔。

对象字面量

对象字面量是指用 {} 括起来的一组以逗号分隔的属性所定义的对象。属性用冒号设置。对象字面量也可以包含数组,数组用 [] 括起来的一组以逗号分隔的成员。

var aboutMe = {
    name: "zchen9",
    study: "the Front-End Web",
    say: function(){
        console.log("Nice to see you!");
    }
}

变量作用域

在JavaScript中,唯一能定义变量作用域的语块就是函数。全局变量在函数外部定义,局部变量在函数内部定义。

如果在函数中声明局部变量的时候,忘记写var关键字,则创建的是全局变量。

《web单页应用》的举例特别有趣:

换种方式来看,函数就像监狱(prison),在函数中定义的变量就像囚犯(prisoner)。正如监狱限制囚犯不让他们从监狱的围墙逃跑,函数限定了局部变量不让他们逃脱函数之外。

var freeman = "I am free!";

function prison() {
    var prisoner = "I got caught !"
}

person();

console.log( freeman );      //"I am free!"
console.log( persioner );    //ERROR

变量提升

在JavaScript中,当变量声明时,声明会被提升到它所在的函数顶部,并被赋予undefined值。

function prison() {
    console.log(prisoner); // "undefined"
    var prisoner = "Now defined";
    console.log(prisoner); //"Now defined"
}
prison();

但是访问一个没有在局部或者全局声明过的变量,会导致JavaScript运行错误。

function prison(){
    console.log(prisoner); //ERROR
}
prison();

若变量在全局声明过,则返回值为全局变量值。

var freeman = "I am free"
function prison(){
    console.log(freeman); //"I am free"
}
prison();

若变量在全局声明过且在函数内又声明了一次,则返回值为局部变量值。

var freeman = "I am free"
function prison(){
    var freeman = "I'm in prison";
    console.log(freeman); //"I'm in prison"
}
prison();

高级变量提升

JavaScript引擎在进入作用域时,会对代码分为两轮处理。第一轮,初始化变量;第二轮,执行代码。

第一轮,JavaScript引擎分析代码,并做了以下三件事:

  • 声明并初始化函数参数。
  • 声明局部变量,包括将匿名函数赋给一个局部变量,但不初始化它们。
  • 声明并初始化函数。

执行环境和执行环境对象

执行环境由函数在执行时发生的所有事物组成。这和函数申明是分离的,因为函数声明描述了当前函数执行的时候会发生什么事情。执行环境是指函数的执行。

属于执行环境部分的变量和函数,被保存在执行环境对象中,执行环境对象是执行环境的ECMA标准的实现。在JavaScript中,执行环境对象是一种对象,每次使用变量其实就是在访问执行环境对象的属性。

作用域链

JavaScript引擎在执行环境对象中访问作用域内的变量,查找的顺序叫做作用域链。

当在查找变量的定义时,JavaScript引擎首先在局部执行环境对象上查找。如果没有定义,则跳出作用域链,到创建它的执行环境中去,并且在该执行环境对象中查找变量的定义,以此类推,直到找到定义或者到达全局作用域为止。

//在全局作用域里,设置chen
var chen = "I'm here";
//调用作用域: 全局
console.log(chen); // "I'm here" 匹配全局chen

function superchen(){

    //在superchen作用域里,设置chen
    var chen = "I'm not there";
    //调用作用域: 全局->superchen
    console.log(chen); //"I'm not there" 匹配superchen

    function prison(){
        var chen;
        //调用作用域: 全局->superchen->prison
        console.log(chen); 
    }
    prison(); //"undefined" 匹配prison
}
superchen();

全局变量和window对象

  • 浏览器的顶层对象是window对象
  • 在node.js中的顶层对象是global对象

window对象包含了很多属性,包括对象、方法(onload/onresize/alert/close…),DOM元素以及其他变量。所有这些属性使用语法window.property来访问。

当在浏览器中的JavaScript检查全局变量是否存在时,它是在window对象上查找的。

var global = "Global chen"; 
console.log(global);                   //"Global chen"
console.log(window.global);            //"Global chen"
console.log(global === window.global)  //true

自执行匿名函数

显式调用和自执行函数的对比(作用相同,都是创建一个函数然后立即调用它):

  • 显式调用

      var foo = function(){
          //do something
      };
      foo();
    
  • 自执行函数

      ( function() {
          //do something
      })();
    

自执行匿名函数被用来控制作用域,阻止变量泄露到代码中的其他地方。

自执行函数传递参数的方法:

(function(weather){

    var todayWeather = "Today is " + weather;
    console.log(todayWeather); //输出"Today is sunny"

})("sunny"); // 值sunny传递给匿名函数的第一个参数weather

一个很著名的组织变量被覆盖的例子为jQuery,其中jQuery和$变量是彼此别名。

(function($){
    console.log($);
})(jQuery);

//在函数的作用域里,$是jQuery对象。