JavaScript进阶(二)词法作用域与作用域链实例分析

本文实例讲述了JavaScript词法作用域与作用域链。分享给大家供大家参考,具体如下:

一、作用域

域表示的就是范围,即作用域,就是一个名字在什么地方可以使用,什么时候不能使用。想了解更多关于作用域的问题推荐阅读《你不知道的JavaScript上卷》第一章(或第一部分),从编译原理的角度说明什么是作用域。概括的说作用域就是一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量。

1.1 块级作用域

在C、Java、C#等编程语言中,下面的语法报错(伪代码)

{
  var num = 123;
  {
    console.log( num );  // num => 123
   }
}
console.log( num );  //报错

1.2 JS的词法作用域

所谓的词法(代码)作用域,就是代码在编写过程中体现出来的作用范围,代码一旦写好,不用执行,作用范围就已经确定好了,这个就是所谓的词法作用域。

在JS中词法作用域的规则:

  1. 函数允许访问函数外部的数据
  2. 整个代码结构中只有函数可以限定作用域
  3. 作用规则首先使用提升规则分析
  4. 如果当前作用域中有了名字了,就不考虑外面的名字

例子1:

var num = 123;
function foo(){
  console.log( num );
}
foo();  //123

例子2:

if( false ){
  var num = 123;
}
console.log( num ); //undefined

例子3:

var num = 123;
function foo () {
  var num = 456;
  function func(){
    console.log( num );
   }
   func();
}
foo();  //456

二、作用域链

只有函数才能制造作用域结构,那么只要是代码,至少有一个作用域,即全局作用域。

凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域,那么将这样的所有作用域列出来,可以有一个结构:函数内指向函数外的链式结构

例如:

function f1() {
  function f2() {
  }
}
var num = 456;
function f3() {
  function f4() {
  }
}

作用域链结构与DOM树结构很相似.

2.1 绘制作用域链

步骤:

  1. 看整个全局是一条链,即顶级链,记为0级链
  2. 看全局作用域中有什么成员声明,就以方格的形式绘制到0级链上
  3. 再找函数,只有函数可以限制作用域,因此从函数中引出新链,标记为1级链
  4. 然后在每一个1级链中再次往复刚才的行为

2.2 变量的访问(搜索)规则

  1. 首先看变量在第几条链上,在该链上看是否有变量的定义与赋值,如果有直接使用
  2. 如果没有到上一级链上找( n - 1 级链),如果有直接使用,停止继续查找。
  3. 如果还没有在此往上找… 直到全局链(0级),还没有就是is not defined
  4. 注意,切记:同级的链不可混合查找

绘制如下程序的作用域链

function f1() {
  var num = 123;
  function f2() {
    console.log( num );
   }
  f2();
}
var num = 456;
f1();  //123

函数f1 和变量 num=456, 在0级链,而f1下又可以展开1级链,1级链上有num=123和函数f2。程序f1()调用进入左边1级链,而f1中又调用了f2函数,f2函数中console.log(num)可以看作在2级链,此时,程序会向这一条链向上查找,首先2级链没有num,向上到达1级链,刚好1级链上有num=123,所以就直接使用123,程序最后的结果就是打印123.

2.3 如何分析代码

  1. 在分析代码的时候切记从代码的运行角度上来分析,如果代码给变量赋值了,一定要标记到图中
  2. 如果代码比较复杂,可以在图中表示代码的内容,有时候还要将原型图与作用域图结合起来分析

分析如下代码:

var num = 123;
function f1() {
  console.log( num );
}
function f2(){
  var num = 456;
  f1();
}
f2();  //123

作用域链图:

首先把num=123,函数f1,函数f2画在0级链上。f1中只有一句console,画出一条1级链,f2也下画出1级链,链上有num=456和函数调用语句f1();

调用f2(),进入f2函数的作用域链,而在f2中又调用了f1函数,程序进入f1的作用域链,所以console.log(num)会在此链上查找是否存在num,没有,继续向上一级链查找,刚好在0级链上找到了num=123,所以f1函数中的console.log(num)就是123.

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

(0)

相关推荐

  • JavaScript进阶(四)原型与原型链用法实例分析

    本文实例讲述了JavaScript原型与原型链用法.分享给大家供大家参考,具体如下: 一句话说明什么是原型:原型就是一个JavaScript对象,原型能存储我们的方法,构造函数创建出来的实例对象能够引用原型中的方法. 一.传统构造函数的问题 有如下代码 function Foo(){ this.sayHello = function(){ } } 由于对象是调用new Foo()所创建出来的,因此每一个对象在创建的时候,函数 sayHello 都会呗创建一次 那么有没一个对象都含有一个独立的,不

  • javascript 原型与原型链的理解及应用实例分析

    本文实例讲述了javascript 原型与原型链的理解及应用.分享给大家供大家参考,具体如下: javascript中一切皆对象,但是由于没有Class类的概念,所以就无法很好的表达对象与对象之间的关系了. 比如对象A与对象B之间,它们两个是相对独立的个体,互不干扰,对象A修改自身的属性不会影响到对象B. 虽然这很好,但是有一个问题,如果对象A与对象B都有一个方法 run() ,并且代码也一样,那对象A与对象B各自都独立拥有一份 run() 方法的完整代码,这是需要资源去保存的. 一旦我们程序中

  • JavaScript对象原型链原理解析

    这篇文章主要介绍了JavaScript对象原型链原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一个js对象,除了自己设置的属性外,还会自动生成proto.class.extensible属性,其中,proto属性指向对象的原型. 对象的属性也有writable.enumerable.configurable.value和get/set的配置方法. 对象的创建方式有三种: 一.使用字面量直接创建. 二.基于原型链创建. 分析上图,要点如

  • JS原型、原型链深入理解

    原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有"prototype"属性,函数对象有"prototype"属性,原型对象有"constructor"属性. 一.初识原型 在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript的对象中都包含了一个"[[Prototype]]"内部属性,这个属性所对应的就是该对象的原型. "[[Prototype

  • JavaScript进阶(一)变量声明提升实例分析

    本文实例讲述了JavaScript变量声明提升.分享给大家供大家参考,具体如下: 如下代码输出的结果是? var num = 123; function foo1(){ console.log( num ); //undefined var num = 456; console.log( num ); //456 } foo1(); Javascript代码执行分为两个大步: 预解析的过程 代码的执行过程 1.预解析与变量声明提升 程序在执行过程中,会先将代码读取到内存中检查,会将所有的声明在此

  • JavaScript从原型到原型链深入理解

    构造函数创建对象 我们先使用构造函数创建一个对象: function Person() { } var person = new Person(); person.name = 'Kevin'; console.log(person.name) // Kevin 在这个例子中,Person 就是一个构造函数,我们使用 new 创建了一个实例对象 person. 很简单吧,接下来进入正题: prototype 每个函数都有一个 prototype 属性,就是我们经常在各种例子中看到的那个 prot

  • 深入理解javascript原型链和继承

    在上一篇文章中,介绍了原型的概念,了解到在javascript中构造函数.原型对象.实例三个好基友之间的关系:每一个构造函数都有一个"守护神"--原型对象,原型对象心里面也存着一个构造函数的"位置",两情相悦,而实例呢却又"暗恋"着原型对象,她也在心里留存了一个原型对象的位置. javascript本身不是面向对象的语言,而是基于对象的语言,对于习惯了其他OO语言的人来说,起初有些不适应,因为在这里没有"类"的概念,或者说&q

  • JS继承--原型链继承和类式继承

    什么是继承啊?答:别人白给你的过程就叫继承. 为什么要用继承呢?答:捡现成的呗. 好吧,既然大家都想捡现成的,那就要学会怎么继承! 在了解之前,你需要先了解构造函数.对象.原型链等概念...... JS里常用的两种继承方式: 原型链继承(对象间的继承)类式继承(构造函数间的继承) 原型链继承: 复制代码 代码如下: //要继承的对象var parent={name : "baba" say : function(){ alert("I am baba");}} //

  • js原型链原理看图说明

    当初ECMAscript的发明者为了简化这门语言,同时又保持继承的属性,于是就设计了这个链表.. 在数据结构中学过链表不,链表中有一个位置相当于指针,指向下一个结构体. 于是乎__proto__也一样,每当你去定义一个prototype的时候,相当于把该实例的__proto__指向一个结构体,那么这个被指向结构体就称为该实例的原型. 文字说起来有点儿绕,看图说话 复制代码 代码如下: var foo = { x: 10, y: 20 }; 当我不指定__proto__的时候,foo也会预留一个这

  • JS原型和原型链原理与用法实例详解

    本文实例讲述了JS原型和原型链原理与用法.分享给大家供大家参考,具体如下: Javascript语言的继承机制一直很难被人理解. 它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承. Brendan Eich设计javascript之初是为了实现网页与浏览器之间交互的一种简单的脚本语言

  • JS学习笔记之原型链和利用原型实现继承详解

    本文实例讲述了JS学习笔记之原型链和利用原型实现继承.分享给大家供大家参考,具体如下: 原型链 原型链是一种关系,实例对象和原型对象之间的关系,关系是通过原型(__proto__)来联系的 实例对象中有__proto__,是对象,叫原型,不是标准的属性,浏览器使用,并且有的游览器不支持 构造函数中有prototype属性,也是对象,叫原型 注意 原型中的方法是可以互相访问的 实例代码 function Animal(name,age){ this.name=name; thia.age=age;

  • JavaScript进阶(三)闭包原理与用法详解

    本文实例讲述了JavaScript闭包原理与用法.分享给大家供大家参考,具体如下: 为了更好的理解,在阅读此文之前建议先阅读上一篇<JavaScript词法作用域与作用域链> 1.什么是闭包 闭包的含义就是闭合,包起来,简单的来说,就是一个具有封闭功能与包裹功能的结构.所谓的闭包就是一个具有封闭的对外不公开的,包裹结构,或空间. 在JS中函数构成闭包.一般函数是一个代码结构的封闭结构,即包裹的特性,同时根据作用域规则只允许函数访问外部的数据,外部无法访问函数内部的数据,即封闭的对外不公开的特性

随机推荐