作用域

回顾:

编程语言最基本的功能是存储,只有把一个数据按一定的规则保存在内存中并且在后续的过程当中能够把数据拿出来,这样才能进行计算和后续的处理。数据的存取过程的规则叫做作用域

函数作用域:js对作用域的创建是基于函数的,没创建一个函数就会在当前作用域下创建一个新的作用域。作用域和作用域之间是可以相互嵌套,但不能重叠。在外部的作用域不能访问内部作用域,内部作用域可以访问外部作用域。例如酒店。

创建变量时js的解释器首先会看在当前作用域下是否有这样的一个变量存在。如果有就会忽略创建的过程。

变量的访问 对数据进行赋值的时候,解释器会询问在当前的作用域下是否有需要的变量或者属性,如果有就拿出来,如果没有就到外层的作用域查找,一直到全局作用域。

作用域的意义和用途

常规观念中,创建一个函数的过程和意义就是先声明一个函数然后在函数体中添加一些代码,在后续用到就调用这个函数。

js的作用域是基于函数的,每创建一个函数就创建一个作用域,被这个作用域包含的变量或者函数,在函数体外面访问不到。这种变量或者函数就叫做私有变量私有函数

所以函数的另外一个功能就是隐藏变量。通过创建函数来隐藏变量的技术会被大量使用。

开发人员两个最困的的问题性能起名

如果解决命名污染全局环境

思路:创建匿名函数直接运行

function (obj){}() // 匿名函数运行
//报错
function a(obj){}() // 改为声明函数运行
//还是报错 实际解析为 function a(){};()

函数声明语句和函数表达式语句区别:

把一个函数赋值给一个变量、参数或者属性的时候就是表达式语句,否则就是函数声明句。实质上是看整个代码开头,如果是function开头他就是函数声明语句,否则就是函数表达式语句。

只要不是function开头就是函数表达式语句。

  • 转化方式()

    // 把函数声明语句改为函数表达式语句
    (function(obj){})("实参")
    (function(obj){}("实参"))
    
  • 操作符方式 && || = ,

    操作符链接的是一个表达式,所以说当你使用操作符的时候,解释器它已经知道了后面你需要使用的是一个表达式。可以利用操作符的特性去消除函数表达式和函数声明语句的歧义。

    • 赋值操作符=

      如果你想创建一个变量,这个变量是通过复杂运算得到的。

      var a
      a=function(){}()
      
    • 与或操作符

      true && function(){}()
      false || function(){}()
      0,function(){}()
      

      打包工具中经常使用

  • 一元操作符 + - ~ !

    +function(o){ console.log(o)}(5) // 5 NaN
    -function(){}()
    ~function(){}()
    !function(){}()
    
  • new操作符 (不推荐,造成歧义)

    不传参数是否后面的()可以省略

    new function(){ console.log(123)} // 123 {}
    

性能比较:

一元操作符尤其是+ - ~ 性能偏低。要经过一次数字类型的转换。

立即执行的匿名函数专属名称叫 IIFE

IIFE作用:

  1. 解决当前作用域下命名污染问题

  2. 对整个程序性能提升,所在作用域比较大,查找速度快。

  3. 有利于压缩,传入的实参可用缩写

  4. 避免全局命名冲突。

    1. 对有冲突的名字通过最后的(实参)传入。
    2. 可以填坑,undefined被重置为true 。通过!function(undefined){}() 形参设为undefined 实参为空解决。
    3. 灵活加载第三方插件
  5. 可以保存闭包的状态

    for循环语句中发送异步请求时,可以使用立即执行匿名函数。

  6. 颠倒代码的运行顺序

    UMD通用模块规范

    function a(){
        console.log("a函数")
    }
    (function(obj){
        obj()
    })(a)
    

注意⚠️:

​ 前面添加分号 ; 防止代码压缩时发生错误。

总结:

​ js的作用域是基于函数的,每创建一个函数就生成一个新作用域。作用域的内部可访问外部,外部不能访问内部。作用域的存在可以避免名称的污染,但在创建作用域的过程当中它本身就已经污染了当前作用域。于是有了自执行匿名函数。

​ js作用域缺陷,循环语句中的定义变量i在for循环外也能访问。可使用自执行函数模拟块级作用域。

js块级作用域

  1. eveal 本身是函数

  2. with 设计时有问题,会导致性能下降,同时也会给调试代码造成困难

  3. try chatch

    声明的变量放到try语句中,以一个异常的形式抛出来,在chatch中接受这个变量。然后就成了块级作用域的变量可以使用。

    缺点:需要命名多个块级作用域下的变量时,就要使用各种嵌套的try chatch。性能有问题。

ES6中的let语句

​ 可以解决循环中定义变量外部访问的问题。

​ let声明出来的变量属于一个新的作用域,而不是在当前作用域下。变量和变量存在依赖关系时可能存在问题。