`

(转)JavaScript中的作用域

 
阅读更多
很多(JavaScript)开发者都在讨论"作用域",但它是什么?它们在JavaScript中的任何地方!我发现很多年轻的开发者不知道作用域是什么。他们中大多数人可以用jQuery做一些很酷的东西。但只是停留在从网上复制一些代码片段,修改一下他们懂的地方并且粘贴到一个JavaScript文件中的程度。

了解什么是作用域和你能用它做什么是很重要的,我希望能帮你更好地理解它。我讲的大多数内容都来自我为一些年轻开发者们做了很多次的演讲。(JavaScript基础教程)

什么是作用域

让我们从全局作用域开始。从你开始写JavaScript的第一刻起,你的代码就处于全局作用域下。全局作用域可以从任何地方访问。除此之外你可以在全局作用域下通过函数创建更小的作用域。局部作用域无法从全局作用域访问,只能从相同作用域(或者叫函数)访问。全局作用域可以从任何地方访问!

全局作用域

当一些东西没有定义在一个函数中时他就被定义在了全局作用域下。只在一个JavaScript文件中定义一些变量,他们就在全局作用域中。

JavaScript 例子

var myGlobalScope = 'Global scope value';
console.log('myGlobalScope: ' + myGlobalScope);
// "myGlobalScope: Global scope value"
局部作用域

局部作用域通过一个函数创建。其中定义的任何东西都只能在其内部访问,或者它的子作用域(在那个函数内的函数)。在一个局部作用域中,你可以访问全局作用域或者父级作用域(父级函数)!

JavaScript 例子

var myGlobalScope = 'Global scope value';

function myLocalScopeFunction() {
  var myLocalScope = 'myLocalScope value';

  console.log('----- start of local scope in function -----');
  console.log('myGlobalScope: ' + myGlobalScope);
  console.log('myLocalScope: ' + myLocalScope);
  console.log('----- end of local scope -----');
}

myLocalScopeFunction();
console.log('----- start of Global scope in function -----');
console.log('myGlobalScope: ' + myGlobalScope);
console.log('myLocalScope: ' + myLocalScope);
console.log('----- end of Global scope -----');
Console输出的结果:

"----- start of local scope in function -----"
"myGlobalScope: Global scope value"
"myLocalScope: myLocalScope value"
"----- end of local scope -----"
"----- start of Global scope in function -----"
"myGlobalScope: Global scope value"
"ReferenceError: myLocalScope is not defined
    at domigov.js:15:57
    at https://static.jsbin.com/js/prod/runner-3.39.15.min.js:1:13926
    at https://static.jsbin.com/js/prod/runner-3.39.15.min.js:1:10855"
这个例子中,正如你上面看到的当你试图在全局作用域下获取定义在子作用域中的变量时将会报错。

为什么在JavaScript中使用作用域?

在JavaScript中,你可以在不同作用域中使用相同的变量名定义变量,它不会被重写。如果你想改变变量的值只需要直接改变而不用重新定义一遍。

不重写

(function() {
    var var1 = 'variable 1 value in parent scope';

    (function() {
        // Scope 1
        var var1 = 'variable 1 value in scope 1';
        console.log('scope 1: ' + var1); // Is 'variable 1 value in scope 1'
    }());

    (function() {
        // Scope 2
        var var1 = 'variable 1 value in scope 2';
        console.log('scope 2: ' + var1); // Is 'variable 1 value in scope 2'
    }());

    console.log('Parent scope: ' + var1); // Is 'variable 1 value in parent scope'

}());
Console输出的结果:

"scope 1: variable 1 value in scope 1"
"scope 2: variable 1 value in scope 2"
"Parent scope: variable 1 value in parent scope"
重写

(function() {
    var var1 = 'variable 1 value in parent scope';

    (function() {
        // Scope 1
        var1 = 'variable 1 value in scope 1';
        console.log('scope 1: ' + var1); // Is 'variable 1 value in scope 1'
    }());

    (function() {
        // Scope 2
        var1 = 'variable 1 value in scope 2';
        console.log('scope 2: ' + var1); // Is 'variable 1 value in scope 2'
    }());

    console.log('Parent scope: ' + var1); // Is 'variable 1 value in parent scope'

}());
console.log('Global scope: ' + var1); // Is undefined
Console输出的结果:

"scope 1: variable 1 value in scope 1"
"scope 2: variable 1 value in scope 2"
"Parent scope: variable 1 value in scope 2"
"ReferenceError: var1 is not defined
    at zodaqo.js:19:57
    at https://static.jsbin.com/js/prod/runner-3.39.15.min.js:1:13926
    at https://static.jsbin.com/js/prod/runner-3.39.15.min.js:1:10855"
如果你想防止自己不小心在不同作用域改变变量的值,只需要在函数体开始部分声明你的变量。

变量提升

这也被称作变量提升。它将防止你在意外情况下重写你的变量。(注:个人理解作者这里写的变量提升并不是我们平时说的变量提升的现象,简单地理解成手动提前声明变量就好。)

变量提升例子

// Global scope
(function() {
  // Parent scope
  var var1, var2, var3;

  var1 = 'variable 1 value in parent scope';
  var2 = 'variable 2 value in parent scope';
  var3 = 'variable 3 value in parent scope';

    (function() {
      var var1, var2, var3;
      // Scope 1
      var1 = 'variable 1 value in scope 1';
      var2 = 'variable 2 value in scope 1';
      var3 = 'variable 3 value in scope 1';
      console.log('scope 1: ' + var1);
      console.log('scope 1: ' + var2);
      console.log('scope 1: ' + var3);
    }());

  console.log('Parent scope: ' + var1);
  console.log('Parent scope: ' + var2);
  console.log('Parent scope: ' + var3);

}());
Console输出的结果:

"scope 1: variable 1 value in scope 1"
"scope 1: variable 2 value in scope 1"
"scope 1: variable 3 value in scope 1"
"Parent scope: variable 1 value in parent scope"
"Parent scope: variable 2 value in parent scope"
"Parent scope: variable 3 value in parent scope"
当然,如果这真是你的代码,你永远都不需要这么做。但是假如你正在写一个更大规模的JavaScript应用,非常推荐这么做!

然而,一些有经验的开发者出于一些原因不赞成这么做。但根据我的经验,它能防止很多问题!

私有 & 公有

使用过其他库或框架并且看过它们源码的开发者知道有些方法你可以在你自己的代码中使用,但是这些方法内部更小的函数则不能使用或访问。

这是因为存在私有和公有函数(属性)。公有函数可以从其他作用域访问到。私有函数只能从相同作用域访问,因此它对于其他作用域或父作用域是隐藏的。

公有和私有函数被用作JavaScript模块。一个模块很容易创建,只需通过一个"立即执行函数表达式"定义变量。这些模块可以在你的JavaScript任何地方使用。如果你想了解更多模块模式相关内容,转向Todd Motto的“精通模块模式”这篇文章!

JavaScript模块

var JavaScriptModule = (function () {
  var javascriptModuleObject, privateFunction;

  privateFunction = function () {
    return 'privateFunction is called!';
  };

  javascriptModuleObject = {
    methodOne:  function () {
      return 'methodOne is called!';
    },
    methodTwo:  function () {
      return 'methodTwo is called!';
    }
  };

  return javascriptModuleObject;

})();
私有函数

当创建一个模块后我们现在可以发现私有和公有的区别。如果我们尝试获取私有函数将会报错。

var JavaScriptModule = (function () {
  var javascriptModuleObject, privateFunction;

  privateFunction = function () {
    return 'privateFunction is called!';
  };

  javascriptModuleObject = {
    methodOne:  function () {
      return 'methodOne is called!';
    },
    methodTwo:  function () {
      console.log(privateFunction());
      return 'methodTwo is called!';
    }
  };

  return javascriptModuleObject;

})();


console.log(JavaScriptModule.privateFunction());
Console输出的结果:

"TypeError: JavaScriptModule.privateFunction is not a function
    at dowazit.js:23:55
    at https://static.jsbin.com/js/prod/runner-3.39.15.min.js:1:13926
    at https://static.jsbin.com/js/prod/runner-3.39.15.min.js:1:10855"
这个函数只有模块内部可以访问。无法从外部访问!因此私有函数对内部逻辑比如复杂的计算,错误处理等等非常有用。

公有函数

对于我们的模块,公有函数(也叫方法)可以从模块外部获取。

var JavaScriptModule = (function () {
  var javascriptModuleObject, privateFunction;

  privateFunction = function () {
    return 'privateFunction is called!';
  };

  javascriptModuleObject = {
    methodOne:  function () {
      return 'methodOne is called!';
    },
    methodTwo:  function () {
      console.log(privateFunction());
      return 'methodTwo is called!';
    }
  };

  return javascriptModuleObject;

})();


console.log(JavaScriptModule.methodOne());
console.log(JavaScriptModule.methodTwo());
Console输出的结果:

"methodOne is called!"
"privateFunction is called!"
"methodTwo is called!"
公有函数可以在你的模块外执行。例如你以Google Maps API为例。它是指全部公有和私有方法的集合。

var map;
function initMap() {
  map = new google.maps.Map(document.getElementById('map'), {
    center: {lat: -34.397, lng: 150.644},
    zoom: 8
  });
}
Map()方法是一个公有函数!这个公有函数执行了许多私有函数。所以你只需要调用一个函数然后它将帮你处理好剩下的事情!

如何创建私有和公有函数

如果你看了样例模块会发现它很简单!如果一个函数被返回了就将变为公有的。否则,它会保持私有!在模块中存在javascriptModuleObject对象,这个对象通过return语句被返回。

所以如果我们想让其它函数也变成公有的我们只要将它添加到这个对象上就可以了。我们可以将这个函数添加到对象内,也可以这样

javascriptModuleObject.newPublicFunction = function() {}
大多数情况下我喜欢这样创建我得模块:

var JavaScriptModule = (function () {
  var javascriptModuleObject, privateFunction;

  privateFunction = function () {
    return 'privateFunction is called!';
  };

  javascriptModuleObject = {};

  javascriptModuleObject.newPublicFunction = function() {
    return 'new public functions!';
  };

  javascriptModuleObject.methodOne = function() {
    return 'methodOne is called!';
  };

  javascriptModuleObject.methodTwo = function() {
    console.log(privateFunction());
      return 'methodTwo is called!';
  };

  return javascriptModuleObject;

})();


console.log(JavaScriptModule.methodOne());
console.log(JavaScriptModule.methodTwo());
console.log(JavaScriptModule.newPublicFunction());
Console输出的结果:

"methodOne is called!"
"privateFunction is called!"
"methodTwo is called!"
"new public functions!"
这样创建它是因为对哪个函数是公有或私有的可读性非常好。看一眼你就能发现所有绑定在对象上(会被返回)的函数都将成为公有的,剩下的将成为私有的!
分享到:
评论

相关推荐

    javascript变量作用域

    JavaScript 首先在当前作用域中查找变量,如果没有定义,则会继续查找上一层作用域,直到找到全局对象。在上面的代码中,我们定义了一个全局变量 rain,并在 rainman 函数中调用 inner 函数, inner 函数中没有定义 ...

    JavaScript中作用域链的概念及用途讲解

    接着查找`fruit`,不在当前作用域,于是继续在全局作用域中找到`fruit`并使用。 而在全局环境中,当尝试访问`fruit`和`color`时,由于全局作用域的变量对象只包含`fruit`,所以`color`会导致一个`undefined`的错误...

    理解JavaScript变量作用域.pdf

    理解JavaScript变量作用域.pdf 本人还有几十本经典javascript书籍以及无数javascript资料,要的加我qq 568094881,本人网址:www.maoshanhai.com

    javascript执行环境,作用域理解

    在 JavaScript 中,执行环境和作用域是两个非常重要和基本的概念,理解了这两个概念对于 JavaScript 中很多脚本的运行结果就能明白其中的道理了。执行环境是一个概念,一种机制,用来完成 JavaScript 运行时在作用域...

    深入理解JavaScript作用域和作用域链

    JavaScript作用域是编程中至关重要的概念,它规定了变量和函数的可见性及生命周期。JavaScript主要有两种作用域:全局作用域和局部作用域。 全局作用域是指在代码的任何位置都可以访问的变量或函数,这通常包括在最...

    JavaScript作用域原理

    首先,全局作用域是最外层的作用域,任何在这个作用域中声明的变量在整个脚本中都是可访问的。如果在函数外部声明一个变量,那么这个变量就是全局变量。全局变量在整个程序的生命周期中都存在,直到页面关闭才会消失...

    深化理解javascript作用域其次篇之词法作用域和动态作用域_.docx

    深化理解javascript作用域其次篇之词法作用域和动态作用域_ 深化理解javascript作用域其次篇之词法作用域和动态作用域,是javascript中非常重要的一部分。理解词法作用域和动态作用域对javascript的编程至关重要。...

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

    JavaScript中,作用域和上下文是理解代码执行逻辑的关键概念。作用域指的是变量和函数的可见性和生命周期,而上下文则关乎`this`关键字的值,它指示了当前代码执行的环境。 **作用域(Scope)** 1. **全局作用域**...

    javascript的作用域和块级作用域概念理解.doc

    javascript的作用域和块级作用域概念理解.doc

    05-JavaScript作用域.pdf

    JavaScript作用域是指在JavaScript代码中,变量、常量、对象和函数能够访问的范围。在编程中,变量和函数的使用都受到作用域的限制,决定了它们能够在哪些代码块中被引用。作用域有助于防止变量命名冲突,也使得程序...

    理解JavaScript作用域和作用域链

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理。今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript。任何程序...

    JavaScript作用域示例详解_.docx

    因此,当调用`inner()`时,它可以在`func`的作用域中找到`funcX`变量。 5. 作用域链的动态性 虽然作用域链在函数定义时就已创建,但其内容可以随着执行过程而改变。例如,通过闭包,函数可以访问在其外部定义的变量...

    Javascript的词法作用域分析.pdf

    这意味着,在方法执行时,Javascript引擎不会去寻找当前作用域中的变量,而是根据定义时的作用域来确定变量的值。 词法作用域的特点是,它取决于源码,而不是执行时的环境。这意味着,词法作用域可以在编译时被确定...

    JavaScript: 函数与作用域深入解析及应用场景

    内容概要:本文详细介绍了 JavaScript 中的函数与作用域。首先解释了函数的基本概念,包括函数声明、函数表达式、匿名函数、箭头函数、函数参数与返回值。接着讨论了 JavaScript 中的全局作用域、局部作用域、块级...

    Web前端面试题目JavaScript(作用域,原型。原型链,闭包,封装函数).txt

    前端面试题,包含JavaScript的闭包,作用域,原型,原型链,上下文环境以及DOM,BOM封装函数深度克隆,以及一些常见的·JS问题,试题简单但是容易混淆,作为前端工程师必考题

    深入理解JavaScript作用域共12页.pdf.zip

    JavaScript作用域是编程中至关重要的概念,尤其是在JavaScript这种动态类型的脚本语言中。它规定了变量、函数以及其它标识符的可见性和生命周期,是代码组织和管理的关键元素。本资料"深入理解JavaScript作用域共12...

    Javascript中的作用域及块级作用域

     javascript的变量作用域,与平时使用的类C语言不同,例如C#中的代码: static void Main(string[] args) { if(true) { int number=10; } Console.WriteLine(number); }  这段代码进行编译,是无法通过的,...

    javascript 闭包、匿名函数、作用域链

    JavaScript中的闭包、匿名函数和作用域链是编程中至关重要的概念,它们是理解JavaScript运行机制的关键。在本文中,我们将深入探讨这三个概念,并通过实际示例来展示它们的运用。 首先,我们来讨论“闭包”。闭包是...

Global site tag (gtag.js) - Google Analytics