基础

2015-07-21  本文已影响129人  tsyeyuanfeng

提升(Hoisting)

  1. 简介

In JavaScript, functions and variables are hoisted. Hoisting is JavaScript's behavior of moving declarations to the top of a scope(the global scope or the current function scope).

That means that you are able to use a function or a variable before it has been declared, or in other words: a function or variable can be declared after it has been used already.

  1. 示例
    2.1 变量提升
foo = 2;
var foo;
// is implicitly understood as:
var foo;
foo = 2;

2.2 函数提升

hoisted(); //logs "foo"
function hoisted() {
      console.log("foo");
}

作用域和作用域链(Scope and Scope Train)

  1. 作用域
    任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域局部作用域两种。
  1. 作用域链
    在JavaScript中,函数也是对象。函数对象和其他对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。

函数的作用域链是由一系列的对象(函数的活动对象+0个到多个的上层函数的活动对象+最后的全局对象)组成的。在函数执行的时候,会按照先后顺序从这些对象的属性中寻找函数体中用到的标识符的值。函数会在定义时将它们各自所处环境(全局上下文或者函数上下文)的作用域链存储到自身的[[scope]]内部属性中。

全局对象:JavaScript引擎在脚本开始执行之前就会创建全局对象,并添加一些预定义的属性。在脚本中定义的全局变量也会成为全局对象的属性。

活动对象:当JavaScript引擎调用函数时,被调用的函数会创建一个新的活动对象。所有在函数内部定义的局部变量、传入函数的命名参数和arguments对象都会作为这个活动对象的属性。这个活动对象加上该函数的[[scope]]内部属性中存储的作用域链就构成了本次函数调用的作用域链。

全局函数作用域链

var x = 10;
var y = 0;

function testFn(i){
      var x = true;
      y = y + 1;
      alert(i);  
}
testFn(10);
执行 testFn(10) 时的作用域链
内部函数作用域链
function outerFn(i, j) {
    var x = i + j;
    return innerFn(x) {
        return i + x;
    }
}

var func1 = outerFn(5, 6);
var func2 = outerFn(10, 20);
alert(func1(10)); //返回15
alert(func2(10)); //返回20
执行函数func1(10)时的作用域链
执行函数func2(10)时的作用域链
问题:一个活动对象在函数执行时被创建,但在函数执行完毕后会不会被销毁?
 function outerFn(x) {
     return x * x;
 }
 var y = outerFn(2);

如果函数没有内部函数,则在该函数执行时,当前活动对象会被添加到该函数的作用域链的最前端.作用域链是唯一引用这个活动对象的地方.当函数退出时,活动对象会被从作用域链上删除,由于再没有任何地方引用这个活动对象,则它随后会被垃圾回收器销毁.

  function outerFn(x) {
      
      //在outerFn外部没有指向square的引用
      function square(x) {
          return x * x;
      }
      
      //在outerFn外部没有指向cube的引用
      function cube(x) {
          return x * x * x;
      }

      var temp = square(x);
      return temp / 2;
  }
  var y = outerFn(5);

在这种情况下,函数执行时创建的活动对象不仅添加到了当前函数的作用域链的前端,而且还添加到了内部函数的作用域链中.当该函数退出时,活动对象会从当前函数的作用域链中删除,活动对象和内部函数互相引用着对方,outerFn函数的活动对象引用着嵌套的函数对象square和cube,内部函数对象square和cube的作用域链中引用了outerFn函数的活动对象.但由于它们都没有外部引用,所以都将会被垃圾回收器回收.

  function outerFn(x) {
      //内部函数作为外部函数的返回值被引用到了外部
      return innerFn() {
          return x * x;
      }
  }
  //引用着返回的内部函数
  var square = outerFn(5);
  square();

例子2:

 var square;
 function outerFn(x) {
     //通过全局变量引用到了内部函数
     square = function innerFn() {
         return x * x;
     }
 }
 outerFn(5);
 square();

在这种情况下,outerFn函数执行时创建的活动对象不仅添加到了当前函数的作用域链的前端,而且还添加到了内部函数innerFn的作用域链中(innerFn的[[scope]]内部属性).当外部函数outerFn退出时,虽然它的活动对象从当前作用域链中删除了,但内部函数innerFn的作用域链仍然引用着它. 由于内部函数innerFn存在一个外部引用square,且内部函数innerFn的作用域链仍然引用着外部函数outerFn的活动对象,所以在调用innerFn时,仍然可以访问到outerFn的活动对象上存储着的变量x的值。

  1. 作用域链和代码优化
function changeColor() {
    document.getElementById("btnChange").onclick = function() {
       document.getElementById("targetCanvas").style.backgroundColor = "red";
    }
}

改成:

function changeColor() {
    var doc = document;
    doc.getElementById("btnChange").onclick = function() {
        doc.getElementById("targetCanvas").style.backgroundColor = "red";
    } 
}

分号自动添加机制(ASI)

  1. 不用使用分号结尾的语句
for(;;;) {}

while(true) {}

do{
    a--;
}wile(a > 0);
if(true) {}
switch(a) {
    case 0:break;
    default:;
}
function func(x) {return x;}

var f = function f(x) {return x};
  1. 分号的自动添加

数据类型(Data Type)

在 ECMAScript 中,变量可以存在两种类型的值,即原始值引用值
原始值
存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。
引用值
存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存处。
ECMA-262把术语类型(type)定义为值的一个集合,每一种原始类型定义了它包含的值的范围及其字面量的表示形式。
ECMAScript有5种原始类型(primitive type),即Undefined, Null, Boolean, Number和String。
在许多语言中,字符串都被看作引用类型,而非原始类型,因为字符串的长度是可变的。ECMAScript 打破了这一传统。
ECMAScript 提供了 typeof 运算符来判断一个值是否在某种类型的范围内。可以用这种运算符判断一个值是否表示一种原始类型:如果它是原始类型,还可以判断它表示哪种原始类型。

函数(Function)

对象(Object)

  1. 封装 - 把相关的信息(无论数据或方法)存储在对象中的能力
  2. 聚合 - 把一个对象存储在另一个对象内的能力
  3. 继承 - 由另一个类(或多个类)得来类的属性和方法的能力
  4. 多态 - 编写能以多种方法运行的函数或方法的能力
  1. 工厂方法
  2. 构造函数方式
  3. 原型方式
  4. 混合的构造函数/原型方式
  5. 动态原型方法
  6. 混合工厂方式
  1. 对象冒充
  2. call方法
  3. apply方法
  4. 原型链
  5. 混合方式
上一篇 下一篇

猜你喜欢

热点阅读