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

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

参数传递的问题

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

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

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

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

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

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

  

  1. 在堆中分配一块空间给person对象,并在栈中保存person在堆中的址址引用
  2. 复制堆中person的地址引用给person2(同样是在栈中)
  3. new Person()再次在堆中分配一块空间给person2对象,然后将栈中person2指向这个新的地址。
  4. 以后对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。而作用域链的下一个对象,来自包含的外部环境,一直延续到全局环境。所有函数的作用域链都可能延续到全局环境,这就是为什么全局变量可以在所有函数中访问的原因,并不是因为它叫全局变量,所以它就可以在所有函数中访问:) 并且,全局执行环境的环境变量始终是作用域链的最后一个对象。

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

var color = "blue";

function changeColor(){
var anotherColor = "red";
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
// 这里面可以访问到 color, anotherColor, tempColor
} // 这里只能访问到 anotherColor
swapColors();
} // 这里只能访问到color
changeColor();

  在上面的代码中,我们有3个执行环境,全局环境、changeColor的局部执行环境和swapColors的局部执行环境。全局环境中只有一个变量color和一个函数changeColor()。changeColor中有一个变量anotherColor和一个函数swapColors(),但是它可以访问到全局环境中的color。swapColors中有一个局部变量tempColor,该变量只不管是在changeColor还是全局环境中都不能被访问到,然后在swapColors中可以访问到其它两个环境中的所有变量,因为另外两个环境都是它的父执行环境。

  我们可以发现,作用域链是由内向外扩展的,我们可以通过作用域访问外部环境的变量和函数,但是外部环境中访问不到内部环境的变量和函数。我们在swapColors中用到了全局变量color,但是它也不是一下子就找到color的,它有一个由内向外的查找过程:

  1. 在当前执行环境内寻找叫color的局部变量,没有找到,向上升一级
  2. 在父级执行环境changeColor中去找叫color的变量,也没有找到,再向上升一级
  3. 在changeColor的父级中找到了color变量,直接拿过来使用。

块级作用域

  因为有着块级作用域(以花括号作为起始点)的存在,C#中这样的代码是编译不通过的。

static void Main(string[] args)
{
for(var i=0;i<=10;i++)
{
Console.Write(i);
}
Console.Write(i);
}

  我们在for循环以外已经访问不到i了。但是在Javascript情况就完全不一样了。

for (var i = ; i <= ; i++)
{
//
}
alert(i); //

延长作用域链

  我们上面说了,在Javascript中总共只有2种执行环境:全局和局部执行环境。但是我们可以用with和try-catch来延长作用域,由于平常使用场景较少,我们就拿with来举个例子好了。

function buildUrl() {
var qs = "?debug=true";
with(location){
var url = href + qs;
}
return url;
}

  在with的作用下,location这个变量被加到了作用域的最前端,所以所有location下的变量和方法都可以在with的这个范围内访问了。

  以上就是我们要讲的作用域的内容了,来总结一下吧:

  • 值类型按值传递,引用类型按引用传递。传递的行业和用变量拷贝的行业是一样的。
  • Javascript中有两种执行环境:全局和局部(函数)
  • 执行环境内有一个变量对象定义了该执行环境下能访问的变量和函数
  • 执行环境可以由内向外延伸一直延伸到全局的执行环境
  • Javascript没有块级作用域

  下一篇我们来看看Javascript中如果面向对象的编程,没有public, protected, private,等关键字的情况下,我们如何来实现public 和private 的属性? 又是如何实现static 类型的变量的? 不要走开,元旦假期之后,我们继续Javascript基础回顾之旅。

Javascript基础回顾 之(二) 作用域的更多相关文章

  1. Javascript基础回顾 之(三) 面向对象

    本来是要继续由浅入深表达式系列最后一篇的,但是最近团队突然就忙起来了,从来没有过的忙!不过喜欢表达式的朋友请放心,已经在写了:) 在工作当中发现大家对Javascript的一些基本原理普遍存在这里或者 ...

  2. Javascript基础回顾 之(一) 类型

    本来是要继续由浅入深表达式系列最后一篇的,但是最近团队突然就忙起来了,从来没有过的忙!不过喜欢表达式的朋友请放心,已经在写了:) 在工作当中发现大家对Javascript的一些基本原理普遍存在这里或者 ...

  3. JavaScript 基础回顾——对象

    JavaScript是基于对象的解释性语言,全部数据都是对象.在 JavaScript 中并没有 class 的概念,但是可以通过对象和类的模拟来实现面向对象编程. 1.对象 在JavaScript中 ...

  4. JavaScript基础回顾一(类型、值和变量)

    请看代码并思考输出结果 var scope = 'global'; function f(){ console.log(scope); var scope = 'local'; console.log ...

  5. JavaScript基础学习-函数及作用域

    函数和作用域是JavaScript的重要组成部分,我们在使用JavaScript编写程序的过程中经常要用到这两部分内容,作为初学者,我经常有困惑,借助写此博文来巩固下之前学习的内容. (一)JavaS ...

  6. JavaScript 基础学习(二)js 和 html 的结合方式

    第一种 使用一个标签 <script type="text/javascript"> js代码; </script> 第二种 使用 script 标签,引入 ...

  7. JavaScript 基础 学习 (二)

    JavaScript 基础 学习 节点属性 ​ 每一个节点都有自己的特点 ​ 这个节点属性就记录着属于自己节点的特点 1. nodeType(以一个数字来表示这个节点类型) ​ 语法:节点.nodeT ...

  8. JavaScript基础笔记(二)变量、作用域和内存问题

    变量.作用域和内存问题 一.基本类型和引用类型的值 基本类型值:简单的数据段 引用类型值:由多个值构成的对象 基本类型是按值访问的,引用类型是按引用访问的. 不能给基本类型的值添加属性,尽管不会报错. ...

  9. SQL Server-表表达式基础回顾(二十四)

    前言 从这一节开始我们开始进入表表达式章节的学习,Microsoft SQL Server支持4种类型的表表达式:派生表.公用表表达式(CTE).视图.内嵌表值函数(TVF).简短的内容,深入的理解, ...

随机推荐

  1. 安装.NET FRAMEWORK 4.5安装进度条回滚之后发生严重错误 代码0x80070643

    安装.NET FRAMEWORK 4.5安装进度条回滚之后发生严重错误  代码0x80070643 注意: 回滚完成后,不要急着点击完成 查看日志:用IE浏览器打开,最后红色部分就是要查看的部分,本人 ...

  2. Selenium Remote-Control架构

    Selenium Remote-Control(RC)是一个测试工具,它允许你编写基于JavaScript浏览器的Web UI自动化测试,它支持很多编程语言. Selenium RC包括两部分: 一个 ...

  3. make: *** [out/host/linux-x86/obj/EXECUTABLES/aidl_intermediates/aidl] 错误 1,make: *** [out/host/linux-x86/obj/lib/libESR_Portable.so] 错误 1

    错误3: g++: g++: selected multilib '32' not installed selected multilib '32' not installed make: *** [ ...

  4. [Javascript] 前端随笔

    做一个小功能时使用到的一点技术点记录下来: 1.在js中使用定时器: 这两个方法都可以用来实现在一个固定时间段之后去执行JavaScript.不过两者各有各的应用场景. 方 法 实际上,setTime ...

  5. LD算法获取字符串相似度

    一个如何识别相似语句的问题,于是上网找了找,一个叫Levenshtein Distance的算法比较简单,就写了段代码实现了一下,效果还不错. 这个算法是一个俄国人Lvenshtein提出的,用于计算 ...

  6. 删除流氓软件McAfee

    如何解决卸载McAfee时出现"处于托管模式时无法删除"问题 问题现象: 这几天在为客户终端换装杀毒软件时出现这么一个问题:在控制面板的添加或删除程序里面将"McAfee ...

  7. sql基础大全

  8. 承接 AutoCAD 二次开发 项目

    本人有多年的CAD开发经验,独立完成多个CAD二次开发项目.熟悉.net及Asp.net开发技术,和Lisp开发技术. 现在成立了工作室,独立承接CAD二次开发项目.结项后提供源码及开发文档,有需要的 ...

  9. DotnetCore Docker

    FROM microsoft/dotnet:1.0.0-preview2-sdk RUN mkdir /app WORKDIR /app COPY project.json /app RUN [&qu ...

  10. 给mac配置adb 路径

    给mac配置adb 路径 (1)找到对应的adb 所在路径 /Users/***/Library/Android/sdk/platform-tools (2)启动终端,输入 cd $Home (3)输 ...