Javascript学习笔记之函数篇(六) : 作用域与命名空间


Posted in Javascript onNovember 23, 2014

在之前的介绍中,我们已经知道 Javascript 没有块级作用,只有函数级作用域。

function test() { // a scope

    for(var i = 0; i < 10; i++) { // not a scope

        // count

    }

    console.log(i); // 10

}

Javascript 中也没有显示的命名空间,这就意味着一切都定义在全局作用域中。每一次引用一个变量时,Javascript 会往上遍历整个全局作用域直到找到该变量。如果遍历完整个全局作用域仍然没有找到该变量,则抛出一个 ReferenceError 错误。

请输入图片描述

隐式全局变量

// script A

foo = '42';

// script B

var foo = '42'

上面的两个例子产生不一样的效果。第一个将在全局作用域中定义变量 foo,而第二个则在当前作用域定义变量 foo。
我们一定要注意,如果不使用关键字 var 将会带来意想不到的影响。

// global scope

var foo = 42;

function test() {

    // local scope

    foo = 21;

}

test();

foo; // 21

由于在函数 test 内没用 var 来定义变量 foo,所以将覆盖函数外部的全局变量 foo。尽管看上去不是什么大问题,但是如果有成千上万行代码时,这将是个难以追踪的 bug。

// global scope

var items = [/* some list */];

for(var i = 0; i < 10; i++) {

    subLoop();

}

function subLoop() {

    // scope of subLoop

    for(i = 0; i < 10; i++) { // missing var statement

        // do amazing stuff!

    }

}

上例中,外部的循环将会在执行第一次的时候就停止,这是因为 subloop 函数内部的变量 i 将会覆盖外部的全局变量 i。我们只需要在函数内部加上一个 var 就可以避免这个错误,所以我们在定义变量时一定不要忘记加上关键字 var。除非我们确实希望对外部的全局变量造成影响。

局部变量

Javascript 中局部变量只可以通过两个方式产生,一是通过关键字 var 来声明,一是作为函数的形参。

// global scope

var foo = 1;

var bar = 2;

var i = 2;

function test(i) {

    // local scope of the function test

    i = 5;

    var foo = 3;

    bar = 4;

}

test(10);

此时,函数 test 内部的变量 i 和 foo 是局部变量,而 bar 则会覆盖外部的全局变量 bar。

提升(Hoisting)

Javascript 将会提升变量声明,这就意味着 var 表达式和函数声明都将被提升到作用域的顶部。

bar();

var bar = function() {};

var someValue = 42;

test();

function test(data) {

    if (false) {

        goo = 1;

    } else {

        var goo = 2;

    }

    for(var i = 0; i < 100; i++) {

        var e = data[i];

    }

}

上面的代码在运行之前, var 表达式和函数 test 的声明都将提升至顶部,因此程序将正常运行并不会报错。

// var statements got moved here

var bar, someValue; // default to 'undefined'

// the function declaration got moved up too

function test(data) {

    var goo, i, e; // missing block scope moves these here

    if (false) {

        goo = 1;

    } else {

        goo = 2;

    }

    for(i = 0; i < 100; i++) {

        e = data[i];

    }

}

bar(); // fails with a TypeError since bar is still 'undefined'

someValue = 42; // assignments are not affected by hoisting

bar = function() {};

test();

由于 Javascript 没有块级作用域,这不仅将提升 var 表达式,同时也会使得 if 结构变得不够直观。
在上例中,尽管看上去 if 在对全局变量 goo 进行操作,实际上,由于变量 goo 被提升,所以修改的是局部变量。
如果没有对提升规则有所了解,你可能会认为下面的代码将会抛出 ReferenceError 错误。

// check whether SomeImportantThing has been initialized

if (!SomeImportantThing) {

    var SomeImportantThing = {};

}

当然上面的代码是没有错误的,因为在代码在运行前,var 表达式已经被提升到顶部。

var SomeImportantThing;

// other code might initialize SomeImportantThing here, or not

// make sure it's there

if (!SomeImportantThing) {

    SomeImportantThing = {};

}

这里要推荐下 @nightire 凡哥的博文 《理解 JavaScript(二)》,里面对提升的讲解非常透彻。
名称解析顺序

当尝试在一个函数作用域内访问一个 foo 变量时,Javascript 将会按照下面的顺序查找:

当前作用域内是否有 var foo 的定义。
函数形参中是否有 foo 变量。
函数自身的名称是否为 foo。
跳到外层定义域,再从第一部开始查找起。
命名空间

一个最常见的问题就是命名冲突,这是因为 Javascript 只有一个全局作用域所带来的。但这个问题可以通过匿名的外部函数解决。

(function() {

    // a self contained "namespace"

    window.foo = function() {

        // an exposed closure

    };

})(); // execute the function immediately

上例中的匿名函数被认为是表达式,所以它们会被执行。

( // evaluate the function inside the parentheses

function() {}

) // and return the function object

() // call the result of the evaluation

当然我们也可以用其他方式来调用函数表达式,不同的结构,但是同样的效果。

// A few other styles for directly invoking the 

!function(){}()

+function(){}()

(function(){}());

// and so on...

总结

建议大家使用匿名的外部函数来将代码封装到空间内,这样不仅可以解决命名空间的冲突,同时也有利于程序的模块化。
此外,使用全局变量不是一个好习惯,这将带来高成本的维护代价而且容易产生错误。

命名空间同类型、函数、变量、模板等都属于实体(entity)。
实体的主要的共性是,可以具有名称。(此外,标签也可以具有名称,但它不是实体。)
而命名空间作用域是作用域中的一类统称,和块作用域、类作用域、函数原型作用域、函数作用域(仅对标签有效)并列。命名空间内声明的名称在命名空间作用域中。全局名称被认为在隐含的全局命名空间作用域中。

命名空间作用确实就是作用域,但是,他又不同于简单的作用域,你可以分多次在多处声明同一个命名空间,但是里面的内容不能重定义,他们最终都会合成一个命名空间,就像std,到处宏定义

Javascript 相关文章推荐
jQuery 行级解析读取XML文件(附源码)
Oct 12 Javascript
ExtJS 下拉多选框lovcombo
May 19 Javascript
菜鸟javascript基础整理1
Dec 06 Javascript
javascript setTimeout和setInterval计时的区别详解
Jun 21 Javascript
jQuery获取attr()与prop()属性值的方法及区别介绍
Jul 06 Javascript
JS实现json的序列化和反序列化功能示例
Jun 13 Javascript
vue仿淘宝订单状态的tab切换效果
Jun 23 Javascript
解决vue处理axios post请求传参的问题
Mar 05 Javascript
详解如何使用webpack打包JS
Jun 21 Javascript
JS使用iView的Dropdown实现一个右键菜单
May 06 Javascript
vue实现瀑布流组件滑动加载更多
Mar 10 Javascript
JavaScript中Object、map、weakmap的区别分析
Dec 15 Javascript
Javascript学习笔记之函数篇(五) : 构造函数
Nov 23 #Javascript
Javascript学习笔记之函数篇(四):arguments 对象
Nov 23 #Javascript
Javascript学习笔记之 函数篇(三) : 闭包和引用
Nov 23 #Javascript
js实例属性和原型属性示例详解
Nov 23 #Javascript
JS常用函数使用指南
Nov 23 #Javascript
浅谈JSON和JSONP区别及jQuery的ajax jsonp的使用
Nov 23 #Javascript
理解jQuery stop()方法
Nov 21 #Javascript
You might like
ThinkPHP字符串函数及常用函数汇总
2014/07/18 PHP
phpword插件导出word文件时中文乱码问题处理方案
2014/08/19 PHP
PHP图片加水印实现方法
2016/05/06 PHP
php批量转换文件夹下所有文件编码的函数类
2017/08/06 PHP
PHP fclose函数用法总结
2019/02/15 PHP
slice函数的用法 之不错的应用
2006/12/29 Javascript
javascript当onmousedown、onmouseup、onclick同时应用于同一个标签节点Element
2010/01/05 Javascript
探索Emberjs制作一个简单的Todo应用
2012/11/07 Javascript
屏蔽IE弹出&quot;您查看的网页正在试图关闭窗口,是否关闭此窗口&quot;的方法
2013/12/31 Javascript
Javascript中的call()方法介绍
2015/03/15 Javascript
Jquery检验手机号是否符合规则并根据手机号检测结果将提交按钮设为不同状态
2015/11/26 Javascript
详解JavaScript基于面向对象之创建对象(2)
2015/12/10 Javascript
简单的JS时钟实例讲解
2016/01/13 Javascript
AngularJS入门心得之directive和controller通信过程
2016/01/25 Javascript
全屏js头像上传插件源码高清版
2016/03/29 Javascript
将List对象列表转换成JSON格式的类实现方法
2016/07/04 Javascript
详解Bootstrap的iCheck插件checkbox和radio
2016/08/24 Javascript
JS根据生日月份和日期计算星座的简单实现方法
2016/11/24 Javascript
详解weex默认webpack.config.js改造
2018/01/08 Javascript
React 源码中的依赖注入方法
2018/11/07 Javascript
搭建Vue从Vue-cli到router路由护卫的实现
2019/11/14 Javascript
JavaScript实现横版菜单栏
2020/03/17 Javascript
js实现简单的无缝轮播效果
2020/09/05 Javascript
django orm 通过related_name反向查询的方法
2018/12/15 Python
Django中如何防范CSRF跨站点请求伪造攻击的实现
2019/04/28 Python
基于Python安装pyecharts所遇的问题及解决方法
2019/08/12 Python
Python操作SQLite/MySQL/LMDB数据库的方法
2019/11/07 Python
python 实现socket服务端并发的四种方式
2020/12/14 Python
CSS去掉A标签(链接)虚线框的方法
2014/04/01 HTML / CSS
台湾网友喜爱的综合型网路购物商城:Yahoo! 奇摩购物中心
2018/03/10 全球购物
大学生表扬信范文
2014/01/09 职场文书
幼儿园六一儿童节文艺汇演主持词
2014/03/21 职场文书
三八妇女节演讲稿
2014/05/27 职场文书
公司委托书范本5篇
2014/09/20 职场文书
自主招生自荐信格式
2015/03/04 职场文书
Mysql 性能监控及调优
2021/04/06 MySQL