您的当前位置:首页正文

JavaScript进阶系列—作用域与命名空间

时间:2023-12-01 来源:欧塔娱乐网

  • 隐式的全局变量

  • 局部变量

  • 变量声明提升(Hoisting)

  • 名称解析顺序

  • 命名空间

  • 结论

  • 尽管 JavaScript 支持一对花括号创建的代码段,但是并不支持块级作用域; 而仅仅支持 函数作用域。

    function test() { // 一个作用域 for(var i = 0; i < 10; i++) { // 不是一个作用域 // count } console.log(i); // 10}

    译者注:如果 return 对象的左括号和 return 不在一行上就会出错。

    (注意: 如果不是在赋值语句中,而是在 return 表达式或者函数参数中,{...} 将会作为代码段解析, 而不是作为对象的字面语法解析。如果考虑到 自动分号插入,这可能会导致一些不易察觉的错误。)

    // 译者注:下面
    输出 undefinedfunction add(a, b) { return a + b;}console.log(add(1, 2));

    JavaScript 中没有显式的命名空间定义,这就意味着所有对象都定义在一个全局共享的命名空间下面。

    每次引用一个变量,JavaScript 会向上遍历整个作用域直到找到这个变量为止。 如果到达全局作用域但是这个变量仍未找到,则会抛出 ReferenceError 异常。

    隐式的全局变量

    // 脚本 Afoo = '42';// 脚本 Bvar foo = '42'

    上面两段脚本效果不同。脚本 A 在全局作用域内定义了变量 foo,而脚本 B 在当前作用域内定义变量 foo。

    再次强调,上面的效果完全不同,不使用 var 声明变量将会导致隐式的全局变量产生。

    // 全局作用域var foo = 42;function test() { // 局部作用域 foo = 21;}test();foo; // 21

    在函数 test 内不使用 var 关键字声明 foo 变量将会覆盖外部的同名变量。 起初这看起来并不是大问题,但是当有成千上万行代码时,不使用 var 声明变量将会带来难以跟踪的 BUG。

    // 全局作用域var items = [/* 数组 */];for(var i = 0; i < 10; i++) { subLoop();}function subLoop() { // subLoop 函数作用域 for(i = 0; i < 10; i++) { // 没有使用 var 声明变量 // 干活 }}

    外部循环在第一次调用 subLoop 之后就会终止,因为 subLoop 覆盖了全局变量 i。 在第二个 for 循环中使用 var 声明变量可以避免这种错误。 声明变量时绝对不要遗漏 var 关键字,除非这就是期望的影响外部作用域的行为。

    局部变量

    JavaScript 中局部变量只可能通过两种方式声明,一个是作为函数参数,另一个是通过 var 关键字声明。

    // 全局变量var foo = 1;var bar = 2;var i = 2;function test(i) { // 函数 test 内的局部作用域 i = 5; var foo = 3; bar = 4;}test(10);

    foo 和 i 是函数 test 内的局部变量,而对 bar 的赋值将会覆盖全局作用域内的同名变量。

    变量声明提升(Hoisting)

    JavaScript 会提升变量声明。这意味着 var 表达式和 function 声明都将会被提升到当前作用域的顶部。

    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]; }}

    上面代码在运行之前将会被转化。JavaScript 将会把 var 表达式和 function 声明提升到当前作用域的顶部。

    // var 表达式被移动到这里var bar, someValue; // 缺省值是 'undefined'// 函数声明也会提升function test(data) { var goo, i, e; // 没有块级作用域,这些变量被移动到函数顶部 if (false) { goo = 1; } else { goo = 2; } for(i = 0; i < 100; i++) { e = data[i]; }}bar(); // 出错:TypeError,因为 bar 依然是 'undefined'someValue = 42; // 赋值语句不会被提升规则(hoisting)影响bar = function() {};test();

    没有块级作用域不仅导致 var 表达式被从循环内移到外部,而且使一些 if 表达式更难看懂。

    在原来代码中,if 表达式看起来修改了全局变量 goo,实际上在提升规则被应用后,却是在修改局部变量。

    如果没有提升规则(hoisting)的知识,下面的代码看起来会抛出异常 ReferenceError。

    // 检查 SomeImportantThing 是否已经被初始化if (!SomeImportantThing) { var SomeImportantThing = {};}

    实际上,上面的代码正常运行,因为 var 表达式会被提升到全局作用域的顶部。

    var SomeImportantThing;// 其它一些代码,可能会初始化 SomeImportantThing,也可能不会// 检查是否已经被初始化if (!SomeImportantThing) { SomeImportantThing = {};}

    译者注:在 Nettuts+ 网站有一篇介绍 hoisting 的文章,其中的代码很有启发性。

    // 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则var myvar = 'my value'; (function() { alert(myvar); // undefined var myvar = 'local value'; })();

    名称解析顺序

    JavaScript 中的所有作用域,包括全局作用域,都有一个特别的名称 this 指向当前对象。函数作用域内也有默认的变量 arguments,其中包含了传递到函数中的参数。比如,当访问函数内的 foo 变量时,JavaScript 会按照下面顺序查找:

    1. 当前作用域内是否有 var foo 的定义。

    2. 函数形式参数是否有使用 foo 名称的。

    3. 函数自身是否叫做 foo。

    4. 回溯到上一级作用域,然后从 #1 重新开始。

    命名空间

    只有一个全局作用域导致的常见错误是命名冲突。在 JavaScript中,这可以通过 匿名包装器 轻松解决。

    (注意: 自定义 arguments 参数将会阻止原生的 arguments 对象的创建。)

    (function() { // 函数创建一个命名空间 window.foo = function() { // 对外公开的函数,创建了闭包 };})(); // 立即执行此匿名函数

    匿名函数被认为是 表达式;因此为了可调用性,它们首先会被执行。

    ( // 小括号内的函数首先被执行function() {}) // 并且返回函数对象() // 调用上面的执行结果,也就是函数对象

    有一些其他的调用函数表达式的方法,比如下面的两种方式语法不同,但是效果一模一样。

    // 另外两种方式+function(){}();(function(){}());

    结论

    推荐使用匿名包装器(译者注:也就是自执行的匿名函数)来创建命名空间。这样不仅可以防止命名冲突, 而且有利于程序的模块化。

    另外,使用全局变量被认为是不好的习惯。这样的代码容易产生错误并且维护成本较高。

    小编还为您整理了以下内容,可能对您也有帮助:

    ::是什么意思啊?

    ::是c++中的运算符。

    ::是运算符中等级最高的,它分为三种:全局作用域符,类作用域符,命名空间作用域符。

    1)全局作用域符。

    “::”指明了成员函数所属的类。如:M::f(s)就表示f(s)是类M的成员函数。  

    2)类作用域符。

    例:声明了一个类A,类A里声明了一个成员函数void f(),但没有在类的声明里给       出f的定义,那么在类外定义f时, 就要写成void A::f(),表示这个f()函数是类A的成员函数。

    3)命名空间作用域符。

    表示引用成员函数及变量。

    扩展资料:

    作用

    1、全局作用域符号:当全局变量在局部函数中与其中某个变量重名,那么就可以用::来区分。

    2、作用域符号::的前面一般是类名称,后面一般是该类的成员名称,C++为了避免不同的类有名称相同的成员而采用作用域的方式进行区分

    如:A,B表示两个类,在A,B中都有成员member。那么A::member就表示类A中的成员member;B::member就表示类B中的成员member。

    3、"::"是作用域限定符或者称作作用域运算符或者作用域操作符(scope operator).例如命名空间

    "::"作用:namespace::name。

    参考资料来源:百度百科——::

    ::是什么意思啊?

    ::是c++中的运算符。

    ::是运算符中等级最高的,它分为三种:全局作用域符,类作用域符,命名空间作用域符。

    1)全局作用域符。

    “::”指明了成员函数所属的类。如:M::f(s)就表示f(s)是类M的成员函数。  

    2)类作用域符。

    例:声明了一个类A,类A里声明了一个成员函数void f(),但没有在类的声明里给       出f的定义,那么在类外定义f时, 就要写成void A::f(),表示这个f()函数是类A的成员函数。

    3)命名空间作用域符。

    表示引用成员函数及变量。

    扩展资料:

    作用

    1、全局作用域符号:当全局变量在局部函数中与其中某个变量重名,那么就可以用::来区分。

    2、作用域符号::的前面一般是类名称,后面一般是该类的成员名称,C++为了避免不同的类有名称相同的成员而采用作用域的方式进行区分

    如:A,B表示两个类,在A,B中都有成员member。那么A::member就表示类A中的成员member;B::member就表示类B中的成员member。

    3、"::"是作用域限定符或者称作作用域运算符或者作用域操作符(scope operator).例如命名空间

    "::"作用:namespace::name。

    参考资料来源:百度百科——::

    javascript 中 “ !function(){}() ” 是什么意思?

    ( function(){…} )()和( function (){…} () )是两种javascript立即执行函数的常见写法,要理解立即执行函数,需要先理解一些函数的基本概念。

    函数声明:

    function fnName () {…};使用function关键字声明一个函数,再指定一个函数名,叫函数声明。

    函数表达式:

    var fnName = function () {…};使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。

    匿名函数:

    function () {}; 使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。

    函数声明和函数表达式不同之处在于:

    一、Javascript引擎在解析javascript代码时会‘函数声明提升'(Function declaration Hoisting)当前执行环境(作用域)上的函数声明。

    而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式。

    二、函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用。

    在function前面加!、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果,而()、!、+、-、=等运算符,都将函数声明转换成函数表达式。

    消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。

    扩展资料

    作用:

    javascript中没用私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉。

    根据javascript函数作用域链的特性,可以使用这种技术可以模仿一个私有作用域,用匿名函数作为一个“容器”。

    “容器”内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。

    JQuery使用的就是这种方法,将JQuery代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域中调用JQuery代码时,可以达到保护JQuery内部变量的作用。

    javascript中: (function(){})();如何理解?

    javascript中: (function(){})()是匿名函数,主要利用函数内的变量作用域,避免产生全局变量,影响整体页面环境,增加代码的兼容性。

    (function(){})是一个标准的函数定义,但是没有复制给任何变量。所以是没有名字的函数,叫匿名函数。没有名字就无法像普通函数那样随时随地调用了,所以在他定义完成后就马上调用他,后面的括号()是运行这个函数的意思

    扩展资料

    函数声明:使用function声明函数,并指定函数名。 

    function setFn() {    // coding   }

    函数表达式:使用function声明函数,但未指定函数名,将匿名函数赋予一个变量。

    var setFn = function() {    // coding}

    匿名函数:使用function关键字声明函数,但未指定函数名。匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等。

    function() {    // coding}

    参考资料:百度百科 - javascript

    欧塔娱乐网还为您提供以下相关内容希望对您有帮助:

    如何更好的理解javascript变量类型以及变量作用域

    JS中变量的作用域相对与JAVA、C这类语言显得更自由,一个很大的特征就是JS变量没有块级作用域,函数中的变量在整个函数都中有效,运行下面代码: &lt;SCRIPT LANGUAGE="JavaScript" type="text/javascript"&gt; //定义一个输出函...

    为什么命名空间作用域是最大的作用域?全局命名空间不是更大吗?_百度知...

    如果你只是std::cout这样的话,那么仅仅是在这条语句有效。所以命名空间的作用于需要看你在上面地方声明使用命名空间。

    如何编写高质量JS代码_基础知识

    javascript核心作用域规则很简单,被精心设计,且很强大。有效地使用javascript需要掌握变量作用域的一些基本概念,并了解一些可能导致难以捉摸的、令人讨厌的问题的极端情况。1.1尽量少用全局变量javascript很容易在全局命名空间中创建变量。创建全局...

    “::”是什么作用域和关系?

    表示作用域,和所属关系。::是运算符中等级最高的,它分为三种:1、global scope(全局作用域符),用法(::name)2、class scope(类作用域符),用法(class::name)3、namespace scope(命名空间作用域符),用法(namespa...

    命名空间的含义是什么?

    一个嵌套命名的空间即是一个嵌套作用域——其作用域嵌套在包含它命名空间内部。嵌套命名空间中的名字遵循常规规则:外围命名空间中声明的名字被嵌套命名空间声明中同一名字的声明所屏蔽。嵌套命名空间内部定义的名字局部于该命名空间。外围命名...

    let和var声明变量有什么不同

    但此时它的值是undefined。4、全局作用域内的声明:在全局作用域中,使用var声明的变量会成为全局对象的属性(在浏览器中是window对象)。而使用let声明的变量则不会,这有助于避免全局命名空间污染。

    javascript 中“!function(){}() ” 是什么意思?

    一、Javascript引擎在解析javascript代码时会‘函数声明提升'(Function declaration Hoisting)当前执行环境(作用域)上的函数声明。而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式。二...

    前端作用域冲突是什么意思

    前端作用域冲突是指在JavaScript中出现了同名变量造成的变量覆盖或者函数覆盖的情况。这种情况出现在多个JavaScript代码段使用了相同的变量名或者函数名的时候。在编写JavaScript代码的时候,我们需要注意避免这种情况的发生,否则会...

    javascript中: (function(){})();如何理解?

    javascript中: (function(){})()是匿名函数,主要利用函数内的变量作用域,避免产生全局变量,影响整体页面环境,增加代码的兼容性。(function(){})是一个标准的函数定义,但是没有复制给任何变量。所以是没有名字的函数,...

    Python 中作用域与命名空间的问题?

    同一个代码块(作用域)里, 同一个变量的作用域只能是同一种或者说同一个变量只能来自同一个作用域, 不能是一会是局部变量然后又变成全局变量;i = i + 1 首先前面的'i='表明了i是一个局部变量(没有global声明, ...

    Top