Javascript基础回顾之(二) js作用域

本来是要继续由浅入深表达式系列最后一篇的,但是最近团队突然就忙起来了,从来没有过的忙!不过喜欢表达式的朋友请放心,已经在写了:) 在工作当中发现大家对Javascript的一些基本原理普遍存在这里或者那里的一知半解,所以决定先花一些时间整理一下这些基础知识和大家分享。 后面会附上培训用的PPT。刚开始是打算写一篇的,但是后来写着写着就发现越来越多,所以决定还是写一个系列吧。本系列所有内容都是涉及Javascript基础的,没有时髦的玩意儿,但是我相信这些基础的东西会有助于你理解那些有趣的东西的。

  本篇是你必须知道的Javascript系列第二篇,我们主要来看看Javascript中变量作用域的问题。主要涉及以下内容 :

参数传递的问题
什么是作用域和作用域链
块级作用域
延长作用域链
参数传递的问题

  在Javascript中所有的参数传递都是按值传递的。也就是说把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,而引用类型值的传递,就如同引用类型变量的复制一样。

  —— 《Javascript 高级程序设计》 第三版

function addTen(num) { num += 10; return num; } var count = 20; var result = addTen(count); alert(count) // 20 alert(result) // 30

我想关于值类型的传递应该很简单,所以我们就不多说了,重点来看看引用类型的值传递。

function setName(person) { person.name = "Jesse"; } var person = new Object(); setName(person); alert(person.name); //Jesse

这个函数把我们外部对象的值也一起改变了,这一点和C#也是一样的。因为函数里面的person所指向的地址和外部person所指向的地址是一样的。下面的行为也是和C#一样的,即如果在函数内容把参数指向另外一个对象,不会对外部对象产生影响 。

function setName(person) { person.name="Jesse" person = new Object(); person.name = "Another Jesse"; } var person = new Object(); setName(person); alert(person.name); //Jesse

关于引用类型的拷贝,给初学者一张图解。

Javascript基础回顾之(二) js作用域

在堆中分配一块空间给person对象,并在栈中保存person在堆中的址址引用

复制堆中person的地址引用给person2(同样是在栈中)

new Person()再次在堆中分配一块空间给person2对象,然后将栈中person2指向这个新的地址。

以后对person2的更改不会对person产生影响 

什么是作用域和作用域链

  我们知道在JavaScript中有局部变量和全局变量,某个函数里面的局部变量不能在另一个函数中被访问(暂且避开闭包不谈)。这就是作用域起的作用,因为变量只在它所在的那个函数里面起作用。

  每一个函数都有自己的执行环境,而每一个执行环境都有一个与之相关联的变量对象, 这个环境中所有变量和函数就保存在这个变量中。除了函数有自己的执行环境以外,我们还有一个最大的全局执行环境,而我们所熟知的window就是这个全局执行环境的变量对象,因为所有的全局变量和函数都是作为window的属性和方法创建的。每个环境中的所有代码执行完后,该环境被随之销毁,保存在其中的所有变量和函数也随之销毁。对于全局执行环境来说,关闭浏览器或者退出页面,那么这个全局的执行环境也就被销毁了。

  但是,我在这个函数里面是不是只能访问这个函数里面的变量呢?也许大家都知道,还有全局变量,全局变量可以被任意函数(Javascript没有像public, private, protected 这样的关键字),或者任意引入到页面的js访问到。这个就是我们常说的作用域链。作用域链的作用就是保证对执行环境有权访问的所有变量和函数进行有序访问。为什么说有序访问呢? 来看一看下面这段代码:

var color = "blue"; function alertColor() { var color = "red"; alert(color); } alertColor(); // red alert(color); // blue

大家知道如果局部变量和全局变量同名的话,全局变量会被覆盖,但是也不是真正的覆盖,只是在当前这个函数里面被覆盖而已,我们在外部依旧可以正常使用的。这里就涉及到一个执行环境有序访问的问题。

  作用域链的最前端永远是当前执行代码所在环境的变量对象,对于我们的alertColor而言,就是它自己的活动对象。 所有函数的活动对象都包含一个初始值,那就是我们的arguments。而作用域链的下一个对象,来自包含的外部环境,一直延续到全局环境。所有函数的作用域链都可能延续到全局环境,这就是为什么全局变量可以在所有函数中访问的原因,并不是因为它叫全局变量,所以它就可以在所有函数中访问:) 并且,全局执行环境的环境变量始终是作用域链的最后一个对象。

  我们来看个复杂一点的例子:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwgspg.html