美文网首页
JavaScript作用域原理

JavaScript作用域原理

作者: 庸者的救赎 | 来源:发表于2016-09-10 16:02 被阅读48次

问题

先看一个例子:

var name = "name";
function echo() {
  alert(name);
  var name = "anotherName";
  alert(name);
  alert(age);
}

echo();

大家觉得这个东西的输出是什么?

估计很多人会觉得是:

name
eve
[脚本出错]

大家的分析过程可能是这样的:

在echo中,第一次alert的时候,会去到全局变量name的值,而第二次值被局部变量name覆盖,所以第二次alert是”eve”.而age属性没有定义,所以脚本会出错.

然而,事实并非如此:

undefined
eve
[脚本出错]

可是why?

作用域链

想知道why,首先我们得先知道JavaScript中的作用域的原理以及什么是作用域链(scope chain).

在JavaScript权威指南里面有这么一句话说的很好: JavaScript中的函数是运行在他们被定义的作用域里,而不是他们被执行的作用域里.

在JavaScript中,作用域的概念和其他语言差不多,每次调用函数,就会进入一个函数内的作用域,当从函数返回以后,就返回调用前的作用域.任何执行上下文时刻的作用,都是由作用域链来实现的.大致过程如下:

  • 在一个函数被定义的时候,会将他定义时刻的scope chain链接到这个函数对象的[[scope]]属性.
  • 在一个函数对象被调用的时候,会创建一个活动对象,然后对于每一个函数的形参,都命名为该活动对象的命名属性,然后将这个活动对象做为此时的作用域链(scope chain)最前端,并将这个函数对象[[scope]]加入到scope chain中.

举个例子说明一下:

var func = function(a, b) {
  var name = "name";
  ... ...
}
func();

我们来分析一下上面这段代码.

在执行func的定义语句的时候,会创建一个这个函数对象的[[scope]]属性,并将这个[[scope]]属性,链接到定义它的作用域链上,此时因为func定义在全局环境,所以此时的[[scope]]只是指向全局活动对象window active object.

在调用func的时候,会创建一个活动对象,我们假设他为object,并创建实参对象(arguments),然后会给这个对象添加俩命名属性object.a,object.b;对于每一个在这个函数中申明的局部变量和函数定义,都作为该活动对象同名命名属性.

然后将调用参数赋值给形参,对于缺少的实参,赋值为undefined.

然后将这个活动对象作为scope chain的最前端,并将func的[[scope]]属性所指向的,定义func时候的顶级活动对象加入到scope chain.

有了上面的作用域链,在发生标识符解析的时候,就会逆向查询当前的scope chain列表每一个活动对象的属性,如果找到同名的就返回,招不到就标识undefined

注意,因为函数对象的[[scope]]属性是在定义一个函数的时候决定的,而非调用函数的时候,所以如下面的例子:

var name = "name";
function echo() {
  alert(name);
}

function env() {
  var name = "eve";
  echo();
}

env();

运行的结果是:

name

再来一个例子:

function factory() {
  var name = "x";
  var intro = function() {
    alert('Hello ' + name);
  }
  
  return intro;
}

function app(para) {
  var name = para;
  var func = factory();
  func();
}

app("eve");

当调用app的时候,scope chain是由:{window活动对象(全局)} -> {app的活动对象}组成.

在刚进入app函数体时,app的活动对象有一个arguments属性,两个值为undefined的属性:name和fund.和一个值为’eve'的属性para;此时scope chain如下:

[[scope chain]] = [
  {
    para : 'eve',
    name : undefined,
    func : undefined,
    arguments : []
  },{
    window call object
  }
]

当调用进入factory的函数体的时候,此时的factory的scope chain为:

[[scope chain]] = [
  {
    name : undefined,
    intro : undefined
  },{
    window call object
  }
]

请注意,此时的作用域链,并不包含app的活动对象.

在定义intro函数的时候,intro函数的[[scope]]为:

[[scope chain]] = [
  {
    name : 'x',
    intro ; undefined
  },{
    window call object
  }
]

从factory函数返回以后,在app体内调用intro的时候,发生了标识符解析,而此时的scope chain是:

[[scope chain]] = [
  {
    intro call object
  },{
    name : 'x',
    intro : undefined
  },{
    window call object
  }
]

因为scope chain中,并不包含factory活动对象,所以,name标识符解析的结果应该是factory活动对象中的name属性,也就是’x’.

所以运行结果应该是:

Hello x

所以请记住:
JavaScript中的函数运行在他们被定义的作用域里,而不是他们被执行的作用域里!!!

生命不息,折腾不止...
I'm not a real coder,but i love it so much!

相关文章

  • 作用域和闭包

    一、理解 JavaScript 的作用域、作用域链和内部原理 作用域 javascript 拥有一套设计良好的规则...

  • JavaScript作用域学习笔记

    作用域链 作用域的原理: ”JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.”...

  • JavaScript作用域原理

    问题 先看一个例子: 大家觉得这个东西的输出是什么? 估计很多人会觉得是: 大家的分析过程可能是这样的: 在ech...

  • 基于JavaScript作用域链的性能调优

    JavaScript作用域和作用域链,说起来很简单,但是细细分析,大有玄机。只能真正理解了作用域链原理,才能写出更...

  • JavaScript作用域学习笔记

    @(JS技巧)[JavaScript, 作用域] JavaScript作用域学习笔记 概念: 作用域就是变量与函数...

  • 07-JavaScript作用域和预解析

    JavaScript作用域 JavaScript中有全局作用域和局部作用域 相同作用域内不能有同名的变量和函数 不...

  • JavaScript 作用域和作用域链

    JavaScript 作用域 作用域就是变量与函数的可访问范围。在JavaScript中,变量的作用域有全局作用域...

  • 作用域、执行环境、作用域链

    作用域,之前有介绍过,JavaScript无块级作用域,只有函数作用域,简单点说就是JavaScript的作用域就...

  • 前端问(面试)题

    dom执行顺序 事件委托原理 原型链的作用,与作用域链的区别 js事件轮询原理、JavaScript的运行机制 结...

  • 一网打尽 JavaScript 的作用域

    JavaScript 的作用域包括:模块作用域,函数作用域,块作用域,词法作用域和全局作用域。 全局作用域 在任何...

网友评论

      本文标题:JavaScript作用域原理

      本文链接:https://www.haomeiwen.com/subject/ncwyettx.html