函数作用域链

  基本任何变成语言都有作用域的概念,即各种变量的可见性和生命周期,通俗来说,就是变量在什么地方可以被调用,什么地方不可以被调用。此处是js的函数作用域链的概念理解,结合培训班和网上搜索。

  1、全局作用域, 局部作用域

    全局作用域:处于全局作用域的变量为全局变量,在代码中的任何地方都可被可视,即在任何地方都可被调用。

    常见情况有以下几种:

     (1)最外层函数和最外层定义的变量拥有全局作用域。

     (2)所有未声明而直接赋值的变量拥有全局作用域。

     (3)所有window对象拥有全局作用域。

   2.局部作用域:

    仅在一定的代码段,才可见(才可被调用),常见的是函数内部声明的变量。

var a = 1;
function fun1(){
    var b = 2;
    mn = 123;
    function fun2(){
        var c = 3;
    }
}

/*  a: 全局,最外层函数之外定义的变量。  fun1:全局,最外层函数  b:局部,函数内定义  mn:全局,未声明而直接赋值,为window对象属性,属于全局作用域  fun2:局部,在函数内声明的函数  c:局部,函数内声明*/

   3.作用域链:

    

 在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。

  当一个函数创建后,它的作用域链会被创建此函数的作用域中可访问的数据对象填充。例如定义下面这样一个函数:

function add(num1,num2) {
    var sum = num1 + num2;
    return sum;
}

  在函数add创建时,它的作用域链中会填入一个全局对象,该全局对象包含了所有全局变量,如下图所示(注意:图片只例举了全部变量中的一部分):

  函数add的作用域将会在执行时用到。例如执行如下代码:

var total = add(5,10);

  执行此函数时会创建一个称为“运行期上下文(execution context)”的内部对象,运行期上下文定义了函数执行时的环境。每个运行期上下文都有自己的作用域链,用于标识符解析,当运行期上下文被创建时,而它的作用域链初始化为当前运行函数的[[Scope]]所包含的对象。

  这些值按照它们出现在函数中的顺序被复制到运行期上下文的作用域链中。它们共同组成了一个新的对象,叫“活动对象(activation object)”,该对象包含了函数的所有局部变量、命名参数、参数集合以及this,然后此对象会被推入作用域链的前端,当运行期上下文被销毁,活动对象也随之销毁。新的作用域链如下图所示:

  在函数执行过程中,没遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从作用域链头部,也就是从活动对象开始搜索,查找同名的标识符,如果找到了就使用这个标识符对应的变量,如果没找到继续搜索作用域链中的下一个对象,如果搜索完所有对象都未找到,则认为该标识符未定义。函数执行过程中,每个标识符都要经历这样的搜索过程。

  简单总结: 以函数为例:函数定义时(或被声明时),此时会生成一个全局执行期上下文,函数每次执行都会产生一个自己唯一的执行期上下文,然后和之前函数定义产生的执行期上下文形成栈式作用域链,全局位于栈底,而刚生成上下文位于栈顶,访问时,优先访问栈顶执行期上下文,若没有目标,再往下寻找,依次规则,一般全局变量都是被最后访问的,所以尽量少定义全局变量。

  4.作用域的代码优化:

  访问变量在作用域链的位置越深,访问之越耗时,所以尽量少的定义全局变量,解决方法:如果一个跨作用域的对象被引用了一次以上,则先把它存储到局部变量里再使用

  

function changeColor(){
    var doc=document; /*用一个局部变量储存全局的document,从而提高访问效率,当该种情况重复次数多时效果会很明显*/
    doc.getElementById("btnChange").onclick=function(){
        doc.getElementById("targetCanvas").style.backgroundColor="red";
    };
}

  5.改变作用域链的方法:

    1.with(){ 此处的作用域为with参数指定的}

      当调用with时会产生一个新的执行期上下文,然后插入到当前作用域链的栈顶处,导致结果是仅仅with快代码某些变量访问快了,但所有局部作用域被挤到第二个位置,局局作用域的变量访问变慢,最后的结果是访问代价更高,所以with尽量能不用就别用。

    2.catch{},

      try-catch语句中,当try中代码报错时会跳到catch块中,此时会将异常对象放到作用域链的栈顶,所有局部作用域被挤到第二个位置,导致性能降低,解决方法:将异常对象作为形参传入处理函数即可。

  6.预编译过程创建的AO对象,就是执行期上下文,联系着理解感觉挺好。

  预编译过程:

  

 *JS执行三部曲:

  1.语法分析:

    通篇扫描,校验低级语法错误,即一眼就可看出的错误。

  2.预编译:

  (1)预编译前奏:

    a)imply\globle 暗示全局变量 ,任何变量未经声明就赋值,此变量为全局对象window所有;

      var a = b = 0;

    ==>

      var a = 0;

      b = 0;(从右向左)

    连续赋值,优先级是从右向左

    b)一切声明的全局变量,此变量为全局对象window所有。

  (2)正式预编译:

    a)说明:

    Activation object = {

      a: undefined,

      b: function b(){}

    }

    即执行上下文。

    b)预编译四部曲:

      1.创建AO对象

      2.找形参 和 变量的声明,将形参 和 声明的变量 作为AO的属性,并赋值为undefined;(注:函数声明不属于变量的声明, 赋值操作也不属于变量的声明)

      3.将传递值和形参值统一

      4.在函数体里找函数声明,值赋予函数体

    3.解释执行(即一行一行执行)。

      执行过程中,若在函数外赋值操作,则进行这样的操作:

      //例如:a = 100; -> window.a = 100;与AO中同名的变量不冲突,一个局部,一个全局

参考资料:http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html

时间: 2024-08-10 19:08:21

函数作用域链的相关文章

javascript篇-----函数作用域,函数作用域链和声明提前

在一些类似C语言的编程语言中,花括号内的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的(也就是我们不能在代码段外直接访问代码段内声明的变量),我们称之为块级作用域,然而,不同于这类型的编程语言,javascript是没有块级作用域.取而代之的,javascript使用的是块级作用域:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的. 在如下的所示的代码中,在不同位置定义了变量 i . j 和 k ,它们都在同一个作用域内——这三个变量在函数体内均是有定义

javascript函数作用域链 词法作用域

在开发语言中常见的作用域规则有  块级作用域和词法作用域 作用域 顾名思义就是起作用的区域  定义一变量后 ,可以在此范围作用的区域 一.块级作用域就是用一个块结构分割变量的访问区域  块即{ } 代表语言有C 系列语言 二.词法作用域就是变量的作用范围,在书写代码时就已经决定作用的范围,与运行时无关 特点:分割作用域只有函数 变量名提升 函数名提升 函数的声明比变量的声明优先级高 function a(){ } var a; alert(a);//打印出a的函数体 var a; functio

1、JavaScript高级之函数作用域链

作用域链: JavaScript的每个函数function都有自己的作用域,使用Active Object(简称AO)活动对象来保存,在相互嵌套的函数中形成了作用域链,如图: 作用域链就是从里到外的AO链 变量的寻找: 函数fn3中使用的变量,如在fn3作用域内寻找不到,则往外层fn2作用域寻找,以此类推,直到全局对象window 代码演示: var c = 5; function t1(){ var d = 6; function t2(){ var e = 7; var d = 3;//如果

关于JS里的函数作用域链的总结

在JavaScript中,函数的作用域链是一个很难理解的东西.这是因为JavaScript中函数的作用域链和其他语言比如C.C++中函数的作用域链相差甚远.本文详细解释了JavaScript中与函数的作用域链相关的知识,理解这些知识可以帮助你在处理闭包的时候避免一些可能出现的问题. 在JavaScript中,函数可以让你在一次调用中执行一系列的操作.有多种方式来定义一个函数,如下: 1.函数声明: function maximum(x, y) { if (x > y) return x; els

js中函数的创建和调用都发生了什么?执行环境,函数作用域链,变量对象

博客搬迁,给你带来的不便,敬请谅解! http://www.suanliutudousi.com/2017/11/26/js%E4%B8%AD%E5%87%BD%E6%95%B0%E7%9A%84%E5%88%9B%E5%BB%BA%E5%92%8C%E8%B0%83%E7%94%A8%E9%83%BD%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F%E6%89%A7%E8%A1%8C%E7%8E%AF%E5%A2%83%E5%87%B

js的变量,变量作用域,作用域链

变量声明: 使用var关键字声明,如果使用没有声明的变量,则JS会自动声明此变量根据变量作用域.如果变量只声明为赋值,则此时值是undefined.重复声明变量,在JS不会报错,会依据最后一次的声明来处理变量. 变量作用域: 一个变量的作用域是,程序代码定义这个变量的区域,全局变量在程序代码内任何地方都可以访问. 包括在{}函数,对象内的变量(属性)成为局部变量. 在函数体内定义的变量成为局部变量,作用域也是局部,函数参数也是局部变量. 他们只在函数体内有意义. 在函数体内,局部变量优先于全局变

关于Javascript作用域及作用域链的总结

本文是根据以下文章以及<Javascript高级程序设计(第三版)>第四章相关内容总结的. 1.Javascript作用域原理,地址:http://www.laruence.com/2009/05/28/863.html 2.JavaScript 开发进阶:理解 JavaScript 作用域和作用域链,地址:http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html 在介绍有关作用域的内容之前,先来介绍

浅谈js中函数作用域问题(一)

本人学习js时间并不长,前几天,写一段js代码时,在js指定一个按钮事件的匿名函数中加入一个同级函数,具体代码可见如下: var mainOb=document.getElementById("divObject");                                        var start=document.getElementById("start"); var a=10; start.onclick=funcrion(){ functi

JavaScript系列----作用域链和闭包

1.作用域链 1.1.什么是作用域 谈起作用域链,我们就不得不从作用域开始谈起.因为所谓的作用域链就是由多个作用域组成的.那么, 什么是作用域呢? 1.1.1作用域是一个函数在执行时期的执行环境. 每一个函数在执行的时候都有着其特有的执行环境,ECMAScript标准规定,在javascript中只有函数才拥有作用域.换句话,也就是说,JS中不存在块级作用域.比如下面这样: function getA() { if (false) { var a = 1; } console.log(a); /