`
dyllove98
  • 浏览: 1379177 次
  • 性别: Icon_minigender_1
  • 来自: 济南
博客专栏
73a48ce3-d397-3b94-9f5d-49eb2ab017ab
Eclipse Rcp/R...
浏览量:38218
4322ac12-0ba9-3ac3-a3cf-b2f587fdfd3f
项目管理checkList...
浏览量:78433
4fb6ad91-52a6-307a-9e4f-816b4a7ce416
哲理故事与管理之道
浏览量:131597
社区版块
存档分类
最新评论

Javascript Context和Scope的一些学习总结

 
阅读更多

1.1.1 摘要

在我们学习Javascript过程中,常常会遇到作用域(Scope)和执行上下文(Context)等概念。其中,执行上下文与this关键字的关系密切。

有面向对象编程经验的各位,对于this关键字再熟悉不过了,因此我们很容易地把它和面向对象的编程方式联系在一起,它指向利用构造器新创建出来的对象;在ECMAScript中,也支持this,然而, 正如大家所熟知的,this不仅仅只用来表示创建出来的对象。

在接下来的博文我们讲介绍Javascript的作用域和执行上下文,以及它们的异同之处。

目录

1.1.2 正文

执行环境(Execution context)也称为“环境”是Javascript中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。

看到了执行环境的定义有点头昏了,简而言之“每个执行环境都有一个与之关联的变量对象”;这里我们有一个疑问就是这个变量对象是怎样定义的呢?

接下来,让我们看一下变量对象的定义,具体实现如下:

/**
 * Execution context skeleton.
 */
activeExecutionContext = {
    // variable object.
    VO: {...},
    this: thisValue
};

通过上面的伪代码我们知道对象字面量activeExecutionContext,它包含一个变量对象VO和this属性。

这说明了this与上下文的可执行代码类型有关,其值在进入上下文阶段就确定了,并且在执行代码阶段是不能改变的(关于this使用可以阅读《Javascript this 的一些学习总结》)。

作用域(Scope)控制着变量和参数的可见性及生命周期。

简而言之,执行环境是基于对象的,而作用域是基于函数的。

作用域

我们将通过一个例子介绍作用域的使用,首先,我们定义了一个函数FooA()和FooB,示例代码如下:

/**
 * Defines a function.
 */
var FooA = function(){
    var a = 1;
    var FooB = function(){
        var b = 2;
        console.log(a, b); // outputs: 1, 2
    }
    console.log(a, b); // Error! b is not defined
}
FooA();

在示例中,第二个log输出变量为未定义,这是由于在Javascript中定义在函数里面的参数和变量在函数外部是不可见的,而在一个函数内部任何位置定义的参数和变量,在该函数内部任何地方都是可见的。

执行环境

首先,我们定义了对象字面量o,它包含一个属性x和方法m(),示例代码如下:

/**
 * Defines a literal object.
 * @type {Object}
 */
var o = {
    x:23,
    m: function(){
        var x = 1;
        console.log(x, this.x); // outputs 1, 23
    }
}
o.m();

示例中的两个变量和属性x都能被访问,但它们被访问的方式是截然不同,在log中访问第一个x是通过作用域方式访问了本地变量x,而this.x是通过执行上下文方式访问对象o的属性x,因此输出值也不尽相同。

上下文问题

接下来,我们修改一下前面的例子,在方法m()中添加一个函数f(),示例代码如下:

/**
 * Defines a literal object.
 * @type {Object}
 */
var o = {
    x:23,
    m: function(){
        var x = 1;
        var f = function(){
            console.log(x, this.x); // outputs 1, undefined
        }
        f();
    }
}
o.m();

上面,我们通过调用方法m()来输出x的值,由于方法m()的具体实现是通过调用函数f()来实现。

当我们调用对象o的方法m()时,发现this.x是未定义的。

这究竟是什么原因呢?回忆前面的例子,由于方法m()获取了对象o的上下文,所以this是指向对象o的,难道是函数f()没有获取对象o的上下文,因此它不清楚this指向哪个对象?

首先让我们回顾一下函数和方法以及属性和变量的区别:方法和对象关联,如:object.myMethod = function() {},而函数非对象关联:var myFunc = function {};同样属性也是对象关系的,如:object.myProperty = 23,而变量:var myProperty = 23。

因为我们提到上下文是基于对象的,所以函数f()不能获取对象o的执行上下文。

我们是否可以让函数f()获取对象o的执行上下文呢?我们仔细地想一下,既然函数f()不清楚this指向的对象,那么可以直接调用对象的属性就OK了。

/**
 * Fixs broken context issue.
 * @type {Object}
 */
var o = {
    x:23,
    m: function(){
        var x = 1;
        var f = function(){
            console.log(x, o.x); // outputs 1, 23
        }
        f();
    }
}
o.m();

我们在函数f()中直接调用对象o的属性x,这样函数f()就无需获取执行上下文直接调用对象的属性了。

现在,我们又遇到一个新的问题了,如果对象不是o而是p,那么我们就需要修改函数f()中的对象了,更严重的情况就是我们没有办法确定具体是哪个对象,示例代码如下:

/**
 * Defines a literal object.
 * @constructor
 */
var C = function(){}
C.prototype = {
    x:23,
    m: function(){
        var x = 1;
        var f = function(){
            console.log(x, this.x); // outputs 1, undefined
        }
        f();
    }
}
var instance1 = new C();
instance1.m();

上下文实例问题

上面,我们定义了函数C和它的原型对象,而且我们可以通过new方式创建C对象实例instance1,按照前面的方法解决Broken Context问题,具体实现如下:

/**
 * Defines a literal object.
 * @constructor
 */
var C = function(){}
C.prototype = {
    x:23,
    m: function(){
        var x = 1;
        var f = function(){
            console.log(x, instance1.x); // outputs 1, undefined
        }
        f();
    }
}
var instance1 = new C();
instance1.m();

如果我们在创建一个C的对象实例instance2,那么我们就不能指定函数f()中的对象了。

其实,this是对象实例的抽象,当实例有多个甚至成千上百个的时候,我们需要通过this引用这些对象实例。

因此,指定对象方法不能有效解决Broken Context问题,我们还是需要使用this来引用对象,前面我们讲到由于函数f()没有获取对象o的执行上下文,因此它不清楚this指向哪个对象,所以输出this.x未定义,那么我们是否可以让函数f()获取对象的执行上下文。

跨作用域的上下文

我们想想既然方法是基于对象的,而且可以获取对象的执行上下文,那么我们直接把f()定义为方法好了。

现在,我们在C对象原型中定义方法f(),示例代码如下:

/**
 * Defines a literal object.
 * @constructor
 */
var C = function(){}

C.prototype = {
    x:10,
    m: function(){
        var x = 1;
        this.f();
    },
    f: function(){
        console.log(x, this.x); // Reference ERROR!!
    }
}
var instance1 = new C();
instance1.m();

好啦,我们在C对象原型中定义方法f(),那么方法f()就可以获取对象的执行上下文。

现在,我们在Firefox运行以上代码,结果输出Reference ERROR,这究竟是什么原因呢?我们想了一下问题出于变量x中,由于方法f()不能获取方法m()的作用域,所以变量x不在方法f()中。

使用上下文解决作用域问题

我们处于两难的境地方法f()既要获取执行上下文,又要获取方法m()的作用域;如果我们要获取方法m()的作用域,那么我们需要把方法f()定义在m()中。

接下来,我们把方法f()定义在m()中,具体实现如下:

/**
 * Defines a literal object.
 * @constructor
 */
var C = function(){}
C.prototype = {
    x:23,
    m: function(){
        var x = 1;
        this.f = function(){
            console.log(x, this.x); // outputs 1, 23
        }
        this.f();
    }
}
var instance1 = new C();
instance1.m();

现在我们通过this调用方法f(),它现在可以获取对象instance1执行上下文,并且也可以获取方法m()的作用域,所以方法f()可以获取属性和变量x的值。

使用作用域解决上下文问题

接下来,继续看一个例子,我们要在函数setTimeout()中调用方法onTimeout(),具体定义如下:

/**
 * setTimeout function with Broken Context issue
 * @type {Object}
 */
var o = {
    x:23,
    onTimeout: function(){
        console.log("x:", this.x);
    },
    m: function(){
        setTimeout(function(){
            this.onTimeout(); // ERROR: this.onTimeout is not a function
        }, 1);
    }
}
o.m();

同样在函数setTimeout()中调用方法onTimeout()失败,我们知道这是由于方法onTimeout()不能获取对象执行上下文。

我们知道在方法m()中可以获取对象执行上下文,所以可以通过临时变量引用this指向的对象,实例代码如下:

/**
 * Fixs setTimeout function with Broken Context issue.
 * @type {Object}
 */
var o = {
    x:23,
    onTimeout: function(){
        console.log("x:", this.x); // outputs 23
    },
    m: function(){
        
        // Keeps instance reference.
        var self = this;
        setTimeout(function(){
           // Gets m scrope. 
            self.onTimeout();
        }, 1);
    }
}
o.m();

上面,我们通过临时变量self保存了this的引用,由于setTimeout()函数可以获取m()的作用域,所用我们可以通过self. onTimeout()的方式调用onTimeout()方法。

1.1.3 总结

本博文通过介绍执行上下文和作用域的异同、this的使用以及变量对象,让我们加深对Javascript 语言特性的理解。

首先,我们介绍了执行上下文和this的的关系,并且执行上下文是具有对象的;然后,介绍了作用域使变量在作用域范围内可见,并且作用域是基于函数的。

我们通过具体的例子介绍了在不同的作用域和执行上下文中,对this和变量的影响加深了作用域和执行上下文的理解,从而帮助我们更好的阅读和编写代码。

参考

6
6
分享到:
评论
2 楼 leaow567 2013-06-07  
饿。。。。是我看错了。。。
1 楼 leaow567 2013-06-07  
文章开头把“变量对象”和“对象”混淆了,会有歧义

相关推荐

    深入浅析JavaScript中的作用域和上下文

    javascript中的作用域(scope)和上下文(context)是这门语言的独到之处,这部分归功于他们带来的灵活性。每个函数有不同的变量上下文和作用域。这些概念是javascript中一些强大的设计模式的后盾。然而这也给开发人员...

    Programming JavaScript Applications

    Interfaces, JavaScript Style Guide, Lambdas, Method Chaining and Fluent APIs, Method Context, Named Parameters, Node Modules, Object Factories, Partial Application and Currying, Plugins, Principles ...

    JavaScript-Scope-Context-Coloring:在基于JSLint和CodeMirror构建的语法突出显示和范围着色之间切换的实验

    一个基于JSLint和CodeMirror并受Douglas Crockford启发JavaScript实验,用于在语法突出显示和范围着色之间切换。 ## ,欢迎提出要求! :) ###在语法突出显示和范围上下文着色之间切换 ###实时范围上下文着色

    JavaScript提高网站性能优化的建议(二)

    从HTTP请求到页面渲染几个方面对提高网站性能提出了几点建议,本文是学习Steve Sounders的另外一本书《高性能网站建设进阶指南》之后,从JavaScript性能的角度进行总结概括,诸君共勉。 JavaScript性能是实现高性能...

    CoffeeScript.Accelerated.JavaScript.Development.2nd.Edition

    Functions, Scope, and Context Chapter 3. Collections, Iteration, and Destructuring Chapter 4. Classes, Prototypes, and Inheritance Chapter 5. Web Applications with jQuery and Backbone.js Chapter 6. ...

    JavaScript 作用域链解析

    JavaScript中有Scope(作用域),Scope chain(作用域链),Execute context(执行上下文),Active Object (活动对象),Dynamic Scope(动态作用域),Closure(闭包)这些概念,要理解这些概念,我们从静态和动态两个方面去...

    scopex:基于angular-expressions的javascript表达式编译器

    安装npm install --save scopex用法var ScopeX = require ( 'scopex' )var scope = new ScopeX ( context )var normalExpressionResult = scope . parse ( '1 + 1' ) // 2var scopeExpressionResult = scope . parse...

    一文扫清对 JavaScript 中的疑惑

    一文扫清对 JavaScript 中的疑惑 文章目录一文扫清对 ... EC(Execution Context)【执行环境 / 执行上下文】 和 Scope【作用域】 是一个东西吗 ? 如果不是区别在哪 ?4. 什么是词法作用域 ? 和作用域有什么关系 ?5. VO

    rescope:加载和作用域任何外部JavaScript并按需重新加载作用域

    加载和作用域所有外部JavaScript,并根据需要重新加载作用域。 例如,假设这是我们要加载的js url的列表,这些列表保存在libs变量中: 资产/lib/bootstrap.native/main/bootstrap-native.min.js 资产/lib/...

    spring-web-2.5.jar

    org.springframework.web.context.request.RequestScope.class org.springframework.web.context.request.ServletRequestAttributes.class org.springframework.web.context.request.ServletWebRequest.class org....

    图文详解Javascript中的上下文和作用域

    位于最顶端或最外层的上下文称为全局上下文(global context),全局上下文取决于执行环境,如Node中的global和Browser中的window: 需要注意的是,上下文与作用域(scope)是不同的概念。Js本身是单线程的,每当有...

    javascript bind绑定函数代码

    代码如下: dom.bind = function(fn,context){ //第二个参数如果你喜欢的话,也可以改为thisObject,scope, //总之,是一个新的作用域对象 if (arguments.length < 2 && context===undefined) return fn; var

    轻松5句话解决JavaScript的作用域

    javascript 程序的每一个字节都是在这个或那个运行上下文(execution context)中执行的。你可以把这些上下文想象为代码的邻居,它们可以给每一行代码指明:从何处来,朋友和邻居又是谁。没错,这是很重要的信息,...

    JavaScript AOP编程实例

    本文实例讲述了JavaScript AOP编程。分享给大家供大家参考。具体如下: /* // aop({options}); ...// Version: 1.0 // Simple aspect oriented ... context: myObject, // scope context of the target function. targe

    JavaScript之AOP编程实例

    本文实例讲述了JavaScript之AOP编程。分享给大家供大家参考。具体如下: /* // aop({options}); ...// Version: 1.0 // Simple aspect oriented ... context: myObject, // scope context of the target function. targe

    Javascript-Intermediate-Aug-2015:2015年8月

    中级Java脚本 2015年8月 Guru Coding Academy的Javascript中级动手项目 Projects - Rock Paper Scissors - Guessing Game Concepts Used - Events - Functions and Objects ...- Scope & Context

    VA_X_Setup1940

    Context-insensitive Highlight Active Word feature for unparsed files (such as text files) as well as strings and comments in parsed files. Enabled via Options | Advanced | Refactoring | Automatically...

Global site tag (gtag.js) - Google Analytics