zoukankan      html  css  js  c++  java
  • 作用域和闭包

    作用域是根据名称查找变量的一套规则。

    当一个块或函数嵌套在另一个块或函数中时,就发生了作用域嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直至找到该变量,或抵达最外层的作用域(全局作用域)为止。

    如果RHS查询所在嵌套的作用域中遍寻不到所需的变量,引擎就会抛出ReferenceError异常。

    function foo(a){
        console.log(a + b)
        b = a
    }
    

    当引擎执行LHS查询时,如果在顶层(全局作用域)中无法找到目标变量,全局作用域中就会创建一个具有该名称的变量,并将其返还给引擎,前提是程序运行在非”严格模式“下。

    function foo(a){
        b = a
        console.log(a + b)
    }
    

    作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用LHS查询;如果目的是获取变量的值,就会使用RHS查询。

    词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时,会保持作用域不变(大部分情况是这样的)。

    作用域查找会在找到第一个匹配的标识符时停止。在多层的嵌套作用域中可以定义同名的标识符,这叫”遮蔽效应“(内部的标识符”遮蔽“了外部的标识符)。

    欺骗词法作用域会导致性能下降。

    eval():在严格模式下,eval()在运行时有其自己的词法作用域,意味着其中的声明无法修改所在的作用域。

    with

    JavaScript引擎会在编译阶段进行数项的性能优化。其中有些优化依赖于能够根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。但是如果引擎在代码中发现了eval或with,它只能简单地假设关于标识符位置的判断都是无效的,因为无法在词法分析阶段明确知道eval会接受什么代码,这些代码会如何对作用域进行修改,也无法知道传递给with用来创建新词法作用域的对象内容到底是什么。

    最悲观的情况是如果出现了eval或with,所有的优化可能都是无意义的,因此最简单的做法是完全不做任何优化。

    块作用域的用处:变量的声明应该距离使用的地方越近越好,并最大限度地本地化。

    • with关键字:用with从对象中创建出的作用域仅在with声明中而非外部作用域有效。
    • try/catch的catch分句会创建一个块作用域,其中声明的变量仅在catch内部有效。

    提升是指声明会被视为存在于其所出现的作用域的整个范围内。但是使用let进行的声明不会再块作用域中进行提升。声明的代码被运行之前,声明并不”存在“。

    变量和函数在内的所有声明都会在任何在任何代码执行前首先被处理。

    函数优先:函数声明和变量声明都会被提升。但是一个值得注意的细节(这个细节可以出现有多个”重复“声明的代码中)是函数会首先被提升,然后才是变量。

    闭包是基于词法作用域书写代码时所产生的的自然结果。

    闭包:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行的。

    function foo(){
        let a = 1
        return function(){
            console.log(a)
        }
    }
    let bar = foo()
    bar() //1 --这就是闭包
    

    函数在定义时的词法作用域以外的地方被调用。闭包是的函数可以继续访问定义时的词法作用域。当然无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时都可以观察到闭包。

    function foo(){
        let a = 1
        function baz(){
            console.log(a)
        }
        bar(baz)
    }
    function bar(fn){
        fn()
    }
    foo()//1
    

    无论通过何种手段将内部函数传递到所在的词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处被执行这个函数都会使用闭包。

    本质上无论何时何地,如果将函数(访问它们各自的词法作用域)当做第一级的值类型并到处传递,你就会看到闭包在这些函数中的应用。

    循环和闭包

    品品这六段代码,有点意思...

    for(var i = 1;i <= 5;i++){
        setTimeout(function timer(){
            console.log(i)
        },i * 1000)
    }
    
    for(var i = 1;i <= 5;i++){
        (function(){
            setTimeout(function timer(){
                console.log(i)
            },i * 1000)
        })()
    }
    
    for(var i = 1;i <= 5;i++){
        (function(){
            var j = i
            setTimeout(function timer(){
                console.log(j)
            },j * 1000)
        })()
    }
    
    for(var i = 1;i <= 5;i++){
        (function(j){
            setTimeout(function timer(){
                console.log(j)
            },j * 1000)
        })(i)
    }
    
    for(var i = 1;i <= 5;i++){
        let j = i
        setTimeout(function timer(){
            console.log(j)
        },j * 1000)
    }
    
    for(let i = 1;i <= 5;i++){
        setTimeout(function timer(){
            console.log(i)
        },i * 1000)
    }
    
  • 相关阅读:
    php原生PHPExcel插件导表(附表格合并,加粗居中及加边框换行操作)
    VMware虚拟机基于contos 7 搭建lnmp环境全过程
    菜鸟学git的基本命令及常见错误
    为什么要写blog????
    JavaScript高级程序设计--函数小记
    jsapi微信支付
    Javascript模式小记(一)
    图片的增删查
    图形的滚动及颜色区域的拉移
    网页中的宽与高
  • 原文地址:https://www.cnblogs.com/zhenjianyu/p/13257622.html
Copyright © 2011-2022 走看看