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

本来是要继续由浅入深表达式系列最后一篇的,但是最近团队突然就忙起来了,从来没有过的忙!不过喜欢表达式的朋友请放心,已经在写了:) 在工作当中发现大家对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

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

  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 = 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基础回顾之旅。

(0)

相关推荐

  • 微信小程序本作用域下调用全局JS详解及实例

    微信小程序本作用域下调用全局JS详解 本地wxml文件 <view> app版本:{{version}} </view> 本地js文件 var app; Page({ data:{ }, onLoad:function() { app = getApp(); this.setData({version:app.globalData.appName}); } }) 全局js文件 //app.js App({ globalData:{ appName:"hcoder"

  • js中作用域的实例解析

    首先,要了解一下作用域的概念:作用--读.写,域--范围或空间.作用域:可用来进行读.写操作的范围或者空间. 其次,再来看看浏览器读取js文件(script标签内的内容)的步骤. 第一步:预解析-----根据var function 把可能用到的参数或函数找出来,放在内存里(这相当于放到仓库里面) 例1: <script> alert(a) var a=1 function fn1(){alert(2)} </script> 根据var和function可以找到:a  fn1 预解

  • JavaScript的作用域和块级作用域概念理解

    作用域永远都是任何一门编程语言中的重中之重,因为它控制着变量与参数的可见性与生命周期.讲到这里,首先理解两个概念:块级作用域与函数作用域. 什么是块级作用域呢? 任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域. 函数作用域就好理解了(*^__^*) ,定义在函数中的参数和变量在函数外部是不可见的. 大多数类C语言都拥有块级作用域,JS却没有.请看下文demo: //C语言 #include <stdio.h> void mai

  • js变量以及其作用域详解

    一.变量的类型 Javascript和Java.C这些语言不同,它是一种无类型.弱检测的语言.它对变量的定义并不需要声明变量类型,我们只要通过赋值的形式,可以将各种类型的数据赋值给同一个变量.例如: 复制代码 代码如下: i=100;//Number类型 i="variable";//String类型 i={x:4};//Object类型 i=[1,2,3];//Array类型 JS的这种特性虽然让我们的编码更加灵活,但也带来了一个弊端,不利于Debug,编译器的弱检测让我们维护冗长的

  • JS作用域闭包、预解释和this关键字综合实例解析

    本文实例分析了JS作用域闭包.预解释和this关键字.分享给大家供大家参考,具体如下: var number = 2; var obj = {number : 5, fn1 : ( function() { this.number *= 2; number=number*2; var number=3; return function() { this.number *= 2; number*=3; alert(number); } } )() }; var fn1 = obj.fn1; ale

  • 网易JS面试题与Javascript词法作用域说明

    调用对象位于作用域链的前端,局部变量(在函数内部用var声明的变量).函数参数及Arguments对象都在函数内的作用域中--这意味着它们隐藏了作用域链更上层的任何同名的属性. 2010年9月14日,我去参加网易网页工程师招聘会,应聘JS工程师职位.有幸参加笔试,然后有幸栽在笔试,呵呵.废话少说,抓出音响极深的一题重新研究研究. 题目大概是:写出如下代码的输出结果并进行分析 复制代码 代码如下: var tt = 'aa'; function test(){ alert(tt); var tt

  • JS中作用域和变量提升(hoisting)的深入理解

    作用域(Scoping) 对于Javascript初学者来说,一个最迷惑的地方就是作用域:事实上,不光是初学者.我就见过一些有经验的javascript程序员,但他们对scope理解不深.javascript作用域之所以迷惑,是因为它程序语法本身长的像C家族的语言.我对作用域的理解是只会对某个范围产生作用,而不会对外产生影响的封闭空间.在这样的一些空间里,外部不能访问内部变量,但内部可以访问外部变量. c语言的变量分为全局变量和局部变量,全局变量的作用范围是任何文件和函数访问(当然,对于非变量定

  • javascript 函数及作用域总结介绍

    在js中使用函数注意三点:1.函数被调用时,它是运行在他被声明时的语法环境中的: 2.函数自己无法运行,它总是被对象调用的,函数运行时,函数体内的this指针指向调用该函数的对象,如果调用函数时没有明确指定该对象, this 默认指向 window ( strict 模式除外,本文不涉及 strict 模式): 3.函数是一种带有可执行代码的对象类型数据. 一.声明函数 1.使用 function 关键字 复制代码 代码如下: function myfun(a,b){ //声明名为myfun的函

  • JavaScript的变量作用域深入理解

    在学习JavaScript的变量作用域之前,我们应当明确几点: a.JavaScript的变量作用域是基于其特有的作用域链的. b.JavaScript没有块级作用域. c.函数中声明的变量在整个函数中都有定义. 1.JavaScript的作用域链 首先看下下面这段代码: 复制代码 代码如下: <script type="text/javascript" language="javascript"> var rain = 1; function rain

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

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

  • Javascript基础回顾之(三) js面向对象

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

  • Javascript基础回顾之(一) 类型

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

  • JavaScript事件学习小结(二)js事件处理程序

    相关阅读: JavaScript事件学习小结(五)js中事件类型之鼠标事件 http://www.jb51.net/article/86259.htm JavaScript事件学习小结(一)事件流 http://www.jb51.net/article/86261.htm javaScript事件学习小结(四)event的公共成员(属性和方法) http://www.jb51.net/article/86262.htm JavaScript事件学习小结(二)js事件处理程序 http://www

  • Javascript基础知识(二)事件

    Event对象:(event对象是window对象的属性,当事件发生时,同时产生event对象,事件结束,event对象消失) IE中:window.event;//获取对象 DOM中:argument[0];//获取对象 IE中Event对象常用的属性方法: 1.clientX:事件发生时,鼠标指针在客户区(不包括工具栏,滚动条等)的X坐标: 2.clientY:事件发生时,鼠标指针在客户区(不包括工具栏,滚动条等)的Y坐标: 3.keyCode:对于keyCode事件,指示按下的键的Unic

  • JavaScript 基础篇(一)

    JavaScript 基础篇(一) 基础篇 Javascript:一.数据类型和值 javascript:允许使用3种基本类型数据--------数字.字符串.布尔值,此外还支持两种小数据类型null(空)和undefine(未定义). javascript:还支持符合数据类型-对象(object),javascript中对象分两种,一种对象表示的已命名的值的无序集合,另一种表示有 编号的值的有序集合.其实有序集合就是数组(Array). javascript:还定义了另一种特殊对象----函数

  • JavaScript基础语法之js表达式

    本文将详细介绍javascript表达式,表达式分为原始表达式和复杂表达式.一般,关于javascript基础语法,人们听得比较多的术语是操作符和语句.但,其实还有一个术语经常使用,却很少被提到,这就是javascript表达式(expression). 原始表达式(primary exression) 原始表达式是表达式的最小单位--它不再包含其他表达式.javascript中的原始表达式包括this关键字.标识符引用.字面量引用.数组初始化.对象初始化和分组表达式 PrimaryExpres

  • javascript基础进阶_深入剖析执行环境及作用域链

    执行环境 执行环境定义了变量或函数有权访问的其他函数,决定了他们各自的行为.每个执行环境都有一个与之关联的变量对象. 变量对象 环境中定义的所有变量和函数都保存在这个对象中. 全局执行环境 全局执行环境是最外围的一个执行环境.在web浏览器中,全局执行环境被认为是Window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的. 每个函数都有自己的执行环境. 作用域链 当代码在一个环境中执行,会创建变量对象的一个作用域链. 用途:保证对执行环境有权访问的所有变量和函数有序访问.

  • JavaScript 基础表单验证示例(纯Js实现)

    验证思路 监听每个input控件的焦点离开(onblue),当焦点离开时调用验证函数,验证后修改第三栏文字,显示符合/不符合并返回true/false 实现代码: index.html <!DOCTYPE html> <html> <head> <title></title> <pnk rel="stylesheet" href="css.css" rel="external nofollo

  • JavaScript 基础函数_深入剖析变量和作用域

    函数定义和调用 定义函数,在JavaScript中,定义函数的方式如下: function abs(x){ if(x >=0){ return x; }else{ return -x; } } 上述abs() 函数的定义如下: function 指出这是一个函数定义; abs 是函数的名称: (x) 括号内列出函数的参数,多个参数以,分隔: {...}之间的代码是函数体,可以包含若干语句,甚至可以没有任何语句. 注意:函数体内部的语句在执行时,一旦执行到return 时,函数就执行完毕,并将结果

随机推荐