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


Posted in Javascript onJanuary 31, 2017

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

  • Javascript基础回顾之(一) 类型
  • Javascript基础回顾 (二) 作用域
  • 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作用域

  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中可以访问到其它两个环境中的所有变量,因为另外两个环境都是它的父执行环境。

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

我们可以发现,作用域链是由内向外扩展的,我们可以通过作用域访问外部环境的变量和函数,但是外部环境中访问不到内部环境的变量和函数。我们在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 = 0; i <= 10; i++)
{
 //
}
alert(i); // 11

延长作用域链

我们上面说了,在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 相关文章推荐
JQuery 图片延迟加载并等比缩放插件
Nov 09 Javascript
JavaScript的eval JSON object问题
Nov 15 Javascript
20个非常有用的PHP类库 加速php开发
Jan 15 Javascript
js输出列表实现代码
Sep 12 Javascript
JQuery判断子iframe何时加载完成解决方案
Aug 20 Javascript
js实现的复制兼容chrome和IE
Apr 03 Javascript
JS监听微信、支付宝等移动app及浏览器的返回、后退、上一页按钮的事件方法
Aug 05 Javascript
JS运动特效之同时运动实现方法分析
Jan 24 Javascript
详解基于node.js的脚手架工具开发经历
Jan 28 Javascript
在vue项目中使用codemirror插件实现代码编辑器功能
Aug 27 Javascript
vue中后端做Excel导出功能返回数据流前端的处理操作
Sep 08 Javascript
Vue router安装及使用方法解析
Dec 02 Vue.js
Javascript基础回顾之(一) 类型
Jan 31 #Javascript
jQuery使用siblings获取某元素所有同辈(兄弟姐妹)元素用法示例
Jan 30 #Javascript
AngularJS报错$apply already in progress的解决方法分析
Jan 30 #Javascript
JavaScript基础之AJAX简单的小demo
Jan 29 #Javascript
JavaScript Date 知识浅析
Jan 29 #Javascript
JavaScript实现时钟滴答声效果
Jan 29 #Javascript
Javascript中 带名 匿名 箭头函数的重要区别(推荐)
Jan 29 #Javascript
You might like
thinkphp文件引用与分支结构用法实例
2014/11/26 PHP
firefox 和 ie 事件处理的细节,研究,再研究 书写同时兼容ie和ff的事件处理代码
2007/04/12 Javascript
Mootools 1.2教程 滑动效果(Slide)
2009/09/15 Javascript
jquery遍历筛选数组的几种方法和遍历解析json对象
2013/12/13 Javascript
jQuery在iframe中无法弹出对话框的解决方法
2014/01/12 Javascript
JS 实现倒计时数字时钟效果【附实例代码】
2016/03/30 Javascript
浅谈jQuery 中的事件冒泡和阻止默认行为
2016/05/28 Javascript
JavaScript 中的12种循环遍历方法【总结】
2018/05/31 Javascript
jQuery事件blur()方法的使用实例讲解
2019/03/30 jQuery
jQuery中DOM常见操作实例小结
2019/08/01 jQuery
详解vue beforeRouteEnter 异步获取数据给实例问题
2019/08/09 Javascript
Nuxt.js实现一个SSR的前端博客的示例代码
2019/09/06 Javascript
vue简单封装axios插件和接口的统一管理操作示例
2020/02/02 Javascript
openlayers实现图标拖动获取坐标
2020/09/25 Javascript
利用Python脚本在Nginx和uwsgi上部署MoinMoin的教程
2015/05/05 Python
Python信息抽取之乱码解决办法
2017/06/29 Python
python SSH模块登录,远程机执行shell命令实例解析
2018/01/12 Python
浅谈python中字典append 到list 后值的改变问题
2018/05/04 Python
python实现给scatter设置颜色渐变条colorbar的方法
2018/12/13 Python
python中Lambda表达式详解
2019/11/20 Python
Django中的session用法详解
2020/03/09 Python
聊聊python中的循环遍历
2020/09/07 Python
基于python实现坦克大战游戏
2020/10/27 Python
详解Python中的文件操作
2021/01/14 Python
pycharm Tab键设置成4个空格的操作
2021/02/26 Python
如何利用CSS3制作3D效果文字具体实现样式
2013/05/02 HTML / CSS
环境科学专业个人求职信
2013/09/26 职场文书
汽车运用工程毕业生自荐信
2013/10/29 职场文书
大专学生推荐信范文
2013/11/19 职场文书
大学生怎样进行自我评价
2013/12/07 职场文书
导游词400字
2015/02/13 职场文书
怒海潜将观后感
2015/06/11 职场文书
2016年六一文艺汇演开幕词
2016/03/04 职场文书
详解python中[-1]、[:-1]、[::-1]、[n::-1]使用方法
2021/04/25 Python
golang三种设计模式之简单工厂、方法工厂和抽象工厂
2022/04/10 Golang
SQL中去除重复数据的几种方法汇总(窗口函数对数据去重)
2023/05/08 MySQL