深入理解前端全局作用域、局部作用域、作用域链以及执行上下文、变量污染、变量提升、函数提升以及let、const、var这些重要的概念

182 阅读13分钟

首先我们在 JavaScript 中,理解全局作用域、局部作用域、作用域链以及执行上下文是非常重要的。这些概念帮助我们掌握变量的可访问性和相关的执行机制。

1. 全局作用域

  • 定义:全局作用域指的是在代码的最外层定义的变量和函数,任何地方都可以访问这些变量和函数。

  • 使用:在浏览器环境中,全局作用域的对象是 window。例如,在全局作用域中定义的变量可以通过 window.variableName 访问。

  • 示例

    var globalVar = "I am global";
    
    function globalFunction() {
      console.log(globalVar); // accessible everywhere
    }
    
    globalFunction(); // 输出: I am global
    

2. 局部作用域

  • 定义:局部作用域是指在函数内部定义的变量和函数只能在该函数内部访问

  • 作用:使用局部作用域来封装变量,避免命名冲突和全局变量的污染。

  • 示例

    function localFunction() {
      var localVar = "I am local";
      console.log(localVar); // 可以访问
    }
    
    localFunction(); // 输出: I am local
    // console.log(localVar); // 报错: Uncaught ReferenceError: localVar is not defined
    

3. 作用域链

  • 定义:作用域链是指在 JavaScript 中,访问变量时会首先查找当前作用域的变量,如果没有找到,则向上查找外部作用域,直到全局作用域为止

  • 机制:如果在某个作用域内找不到变量,JavaScript 引擎会依次向上查找,形成一个链式结构。

  • 示例

    var outerVar = "I am outside";
    
    function outerFunction() {
      var innerVar = "I am inside";
    
      function innerFunction() {
        console.log(innerVar); // 访问内层作用域
        console.log(outerVar); // 访问外层作用域
      }
    
      innerFunction();
    }
    
    outerFunction(); // 输出: 
                     // I am inside
                     // I am outside
    

4. 执行上下文

  • 定义:执行上下文是 JavaScript 中代码执行的环境。每当函数被调用时,会创建一个新的执行上下文。

  • 类型

    • 全局执行上下文:当 JavaScript 引擎首次加载代码时会创建一个全局执行上下文。
    • 函数执行上下文:每次调用函数时,都会创建一个新的函数执行上下文。
  • 组成

    • 变量对象(Variable Object) :存储该上下文中的所有变量和函数声明。
    • 作用域链:当前执行上下文中的作用域链,用于查找变量。
    • this 值:指向当前执行上下文的 this 绑定对象。
  • 示例

    function exampleFunction() {
      var localVariable = "Hello!";
      console.log(localVariable);
    }
    
    exampleFunction(); // 创建一个新的函数执行上下文
    

小结

  • 全局作用域:在整个代码中都可访问的作用域。
  • 局部作用域:函数内部定义的作用域,仅在该函数内部可访问。
  • 作用域链:查找变量的机制,从当前作用域向上查找外层作用域。
  • 执行上下文:代码执行时的环境,包含变量对象、作用域链和 this 值。

5. 前端变量污染

理解

  • 变量污染是指全局作用域中的变量被意外修改或覆盖,导致程序行为不符合预期的现象。在前端中,尤其在使用 JavaScript 时,若多个脚本共用全局作用域,变量名重复可能会引发此问题。

解决问题

  • 使用模块化编程和 ES6 的模块系统可以有效避免变量污染。通过将变量封装在模块内部,只有通过显式的导出(export)和导入(import)方式来使用,能够限制全局命名空间的使用,从而减少变量污染的风险。

6. 变量提升

理解

  • 变量提升是指在 JavaScript 中,变量声明(var 和函数声明)会被提升到其作用域的顶部。无论变量声明的位置在哪里,整个作用域都会将变量提升到顶部。这意味着在使用变量之前,可以在代码中先声明它。

示例

console.log(x); // undefined (没有报错)
var x = 5;
console.log(x); // 5

解决问题

  • 了解变量提升有助于开发者理解变量的作用域,提高代码的可读性。使用 let 和 const 不会被提升,可以声明变量的位置提前避免在代码未执行前访问变量的问题。

7. 函数提升

理解

  • 函数提升是指函数声明会被提升到其作用域的顶部,允许在函数定义之前调用该函数。

示例

myFunction(); // 输出 'Hello'
function myFunction() {
  console.log('Hello');
}

解决问题

  • 函数提升确保了函数可以在定义之前被调用,这使得代码组织更加灵活,但也需要注意函数表达式(使用 var 或 let 定义的函数)不会被提升。

8. letconst 和 var 的区别

  • var

    • 权限:函数作用域。
    • 提升:变量提升(可以在声明之前访问,但值为 undefined)。
    • 解决问题:可以在函数内部声明变量,但在全局作用域中会造成命名冲突和变量污染。
  • let

    • 权限:块作用域(如 {})。
    • 提升:变量提升,但不初始化(在声明之前访问将抛出 ReferenceError)。
    • 解决问题:减少全局变量,避免变量冲突,提供更强的作用域控制。
  • const

    • 权限:块作用域。
    • 提升:与 let 类似(也不初始化)。
    • 特点:声明常量,声明后不能被重新赋值。
    • 解决问题:提供了更清晰的语义,指示开发者该变量应该保持不变。

小结

在现代 JavaScript 开发中,推荐优先使用 let 和 const 而非 var,以获得更好的作用域控制和代码可读性。

特别的 JavaScript 的严格环境

在 JavaScript 中,严格模式(Strict Mode)是一种限制 JavaScript 语法和行为的特性。使用严格模式可以帮助开发者编写健壮和安全的代码。严格模式可以使 JavaScript 代码在执行时表现得更加严格,从而防止常见的编码错误和不安全的行为。

如何启用严格模式

严格模式可以通过在脚本或函数的开始处添加 'use strict'; 字符串来启用。可以在全局作用域或在特定函数中使用。

1. 全局启用严格模式

'use strict';

function myFunction() {
  // 在这里都是严格模式
  let x = 3.14;
}

2. 函数级启用严格模式

function myFunction() {
  'use strict';
  // 只有在这个函数内部是严格模式
  let y = 3.14;
}

严格模式的特点和好处

  1. 禁止使用未声明的变量

    • 在严格模式中,如果尝试使用未声明的变量,会抛出错误。
    'use strict';
    x = 20; // ReferenceError: x is not defined
    
  2. 禁止删除变量、函数或参数

    • 在严格模式中,不能使用 delete 操作符删除变量、函数或参数。
    'use strict';
    var x = 10;
    delete x; // SyntaxError: Delete of an unqualified identifier in strict mode.
    
  3. 禁止重复参数名

    • 函数的参数不能有相同的名称。
    'use strict';
    function sum(a, a, c) { // SyntaxError: Duplicate parameter name not allowed in this context
        return a + a + c; 
    }
    
  4. 禁止使用 this 关键字指向全局对象

    • 在严格模式下,函数的 this 关键字不再指向全局对象 window,而是 undefined(在调用时没有明确绑定)。
    'use strict';
    function f() {
        console.log(this); // undefined
    }
    f();
    
  5. 给 this 赋值为 undefined

    • 在严格模式下,this 的值如果没有被绑定将是 undefined,而不是全局对象。
  6. 限制某些语法

    • 严格模式下禁止使用某些不推荐使用的特性,如 with 语句。
    'use strict';
    with (Math) { // SyntaxError: Strict mode code may not include a with statement
        x = cos(2);
    }
    
  7. 改进的错误处理

    • 严格模式可以使错误更显式。试图使用一些不安全的行为会引发错误,帮助开发者更快地发现问题。

适用范围

  • 全局范围:通过在文件的第一行添加 'use strict';,整个文件都处于严格模式。
  • 函数范围:通过在函数内部添加 'use strict';,只有该函数内部有效。

小结

JavaScript 的严格模式是一种提高代码安全性和可维护性的机制。通过限制某些行为和语法,严格模式帮助开发者更快地发现潜在错误,写出更健壮的代码。推荐在编写 JavaScript 代码时使用严格模式,尤其是在较大的项目中,能有效减少错误和提升开发质量。

this的理解

this关键字是一个非常重要但也容易引起混淆的概念。this的值取决于函数调用的方式,并且在不同的上下文中可能有不同的含义。下面是一些关键点来帮助深刻理解this**:**

  1. 全局作用域中的this

    • 在浏览器环境中,如果代码不是在严格模式下运行,全局作用域中的this指向window对象。
    • 如果是在严格模式('use strict';)下,全局作用域中的thisundefined
  2. 函数调用模式

    • 当一个函数作为普通函数被调用时(即没有被绑定到任何对象上),this通常指向全局对象(非严格模式)或undefined(严格模式)。
  3. 方法调用模式

    • 当函数作为某个对象的方法被调用时,this会绑定到这个对象。
    • 例如,在对象的方法中,this通常指的是该对象本身。
  4. 构造函数模式

    • 使用new关键字调用函数时,this绑定到新创建的对象实例。
    • 这种情况下,this指向新创建的对象,并且该对象将成为函数执行结果的对象。
  5. 箭头函数中的this

    • 箭头函数不绑定自己的this值;它们继承自外层(通常是父级)作用域的this值。
    • 这意味着箭头函数内部的this值取决于定义时所在的上下文,而不是调用时的上下文。
  6. 显式绑定this

    • 可以使用.bind().call().apply()方法来显式地设置函数执行时的this值。
    • 这些方法允许开发者明确指定this应该指向哪个对象。

理解this的关键在于记住它的值是由函数调用的方式决定的,而不是函数定义的位置。这对于编写可重用和灵活的代码至关重要。在实际开发中,特别是在处理事件处理器、回调函数或者类的方法时,正确理解和管理this是非常重要的。

大总结

JavaScript 是一种灵活、强大的编程语言,说简单也不简单,js里面太多概念需要深刻去理解和关联/串联起来思考了。比如如下 JavaScript 使用严格模式带来的好处:

  1. 消除不安全的引用

    • 在严格模式下,试图引用未声明的变量会导致一个 ReferenceError 错误。
    • 严格模式禁止对全局对象的属性进行无意的赋值。
  2. 限制变量声明

    • 在严格模式下,不允许重复声明同一个变量或函数。
    • 不能在函数内部使用 eval 或者间接引用 (arguments.callee 和 arguments.caller)。
  3. 提升代码安全性

    • 禁止使用 with 语句,这有助于避免潜在的命名冲突问题。
    • 对象不可配置的属性不能被删除(除非该属性是可配置的)。
  4. 改进调试体验

    • 严格模式会强制执行更严格的解析规则,比如不允许八进制字面量。
    • 在严格模式下,未使用的参数将引发警告。
  5. 性能优化

    • 在某些情况下,启用严格模式可以使 JavaScript 引擎更好地优化代码执行。
    • 减少了不必要的操作,提高了代码的执行效率。
  6. 其他限制

    • 严格模式还限制了某些特殊的、容易出错的操作,如对未定义变量的赋值。

上面谈及的概念也做个总结

全局作用域(Global Scope)

  • 定义:全局作用域是 JavaScript 运行时的最外层作用域,在全局作用域中声明的变量和函数可以被整个代码所访问。
  • 特性:在浏览器中,全局作用域的对象是 window,全局变量可以通过 window.variableName 访问。
  • 注意:过多的全局变量可能会导致命名冲突和变量污染。

局部作用域(Local Scope)

  • 定义:局部作用域是指在函数内部或代码块({})中定义的作用域,只能在该函数或代码块内部访问。
  • 特性:局部作用域能够有效管理变量,避免变量污染。

3. 作用域链(Scope Chain)

  • 定义:作用域链是 JavaScript 查找变量时的机制。当访问变量时,JavaScript 首先查找当前作用域的变量,如果找不到,就逐层向上查找外层作用域,直到全局作用域。
  • 特性:这种机制确保了可以在嵌套函数中访问外层函数的变量。

4. 执行上下文(Execution Context)

  • 定义:执行上下文是代码执行时的环境,它可以理解为来自不同执行环境的状态信息的集合。

  • 类型

    • 全局执行上下文:代码首次执行时创建的上下文。
    • 函数执行上下文:每次调用函数时创建的新的上下文。
  • 组成:包括变量对象(存储所有变量和函数声明)、作用域链和 this 绑定。

5. 变量污染(Variable Pollution)

  • 定义:变量污染是指全局作用域中的变量被意外修改或覆盖,通常是因为全局变量和局部变量命名冲突导致的。
  • 影响:可能引发逻辑错误和不可预测的行为。
  • 解决方案:使用局部作用域、模块化开发和 ES6 的模块导入导出来避免。

6. 变量提升(Variable Hoisting)

  • 定义:变量提升是指变量声明会被提升到其作用域的顶部。在访问变量之前,你可以在代码中先声明它。
  • 特性:只有声明会被提升,赋值不会提升。

7. 函数提升(Function Hoisting)

  • 定义:函数声明会被提升到其作用域的顶部,可以在定义之前调用。
  • 区别:函数表达式(例如,通过 var 定义的函数)不会被提升。

8. letconst 和 var 的区别

  • var

    • 权限:函数作用域。
    • 提升:变量提升(未初始化时为 undefined)。
    • 特性:不支持块作用域,容易导致变量污染。
  • let

    • 权限:块作用域(如 {})。
    • 提升:变量提升,但不初始化(访问会报错)。
    • 特性:防止变量重复报名,减少全局变量的使用。
  • const

    • 权限:块作用域。
    • 提升:与 let 相同。
    • 特性:声明常量,确保在后续代码中该变量无法被重新赋值。

完结。每次学习一遍基础概念基础知识总结一下就感觉有不一样的理解,红宝石书也是写的很多细节概念没有去深入过对比过,这次一并总结几个常用常问的概念~卷王不是终点,不断学习才是终点!!!