let、var声明变量的区别以及预解析、作用域的详解

本文详细比较了JavaScript中let和var的区别,包括作用域的不同(块级与函数作用域)、let的不可预解析和不可重定义特性,以及作用域链和预解析的概念。还探讨了块级作用域和局部作用域的差异以及预解析的规则和注意事项。
摘要由CSDN通过智能技术生成

一、let、var的区别

	let、以及var都是声明变量的

1.作用域不同

var是函数作用域(伪全局作用域),而let是块级作用域。在块级作用域内声明的变量无法在块级作用域外面使用

2.let不能在定义之前访问该变量,但是var可以在定义之前访问(let不会预解析)。

var声明的变量,变量提升(预解析了),let不会预解析


console.log(a)//预解析只声明不复制 undefined
var a = 10//var 后面的变量,变量提升了(也就是预解析了)
console.log(a)//10
console.log(b)//报错(Uncaught ReferenceError: 
let  = 20;

3.let不能被重新定义,但是var是可以的


let a = 10
let a = "你好"//报错(Uncaught SyntaxError: Identifier 'a' has already been declared)let后面的变量不能重新定义,var可以
console.log(a)
var b = 10
var b = "你好"
console.log(b);

二、作用域

1.什么是作用域?

作用域:作用域就是变量(变量名、函数名)生效的区域,作用域又分为全局作用域(window就是全局作用域)以及局部作用域(私有作用域,函数里面就是私有作用域)。

2、变量定义、使用、赋值机制,作用域链

变量定义:变量定义在哪一个作用域里面,就只能在当前作用域(包括子级作用域)里面使用,上一级作用域无法使用

变量使用:当需要使用一个变量,会首先在自己的作用域里面查找,如果有就直接使用,没有就去上一级作用域查找,如果还没有,再去上一级查找,直到查找到window(全局作用域)依然没有,就报错 (Uncaught ReferenceError: xxx is not defined)

作用域链:就是变量一层一层向上查找的过程

变量赋值:当需要给一个变量赋值时,会在自己作用域里面查找,有就赋值(注意和变量声明区分开),没有就去上一级查找,直到window都没有,就把这个变量定义为全局变量再赋值

// 全局作用域
var a //在全局作用域声明一个a变量
a = 5//变量赋值 
c //变量声明必须加上关键词let var const(常量),没有就报错
d = 6//变量赋值会先查找(查找到window)如果没有,就声明(相当于使用var,但是不会预解析)再赋值
function fn() {
    var b=6//私有作用域声明一个变量
    console.log(a)//变量使用,在自己作用域查找,没有去上一级查找(作用域链),找到了a为5返回5
}
fn()
console.log(b)//错误全局中没有b变量,b变量是定义在function里面的,是fn私有的,全局调用报错

3、块级作用域和局部作用域区别

块级作用域:只要{}没有和函数结合在一起, 那么应该"块级作用域"

局部作用域:函数后面{}中的的作用域, 我们称之为"局部作用域"

1、在块级作用域中通过var定义的变量是全局变量

2、在局部作用域中通过var定义的变量是局部变量

3、let只要在{}里面就是局部变量

4、无论是在块级作用域还是在局部作用域, 省略变量前面的let或者var就会变成一个全局变量(省略之后,赋值号必须要,不然报错表示直接使用not defined,省略之后不会进行预解析,相当于给window增加了一个属性名和属性值)

//块级作用域{}
{
    var a = 1; // 全局变量
    let b = 2; // 局部变量
    c = 3; // 全局变量
}

 
//函数后的{} 局部作用域
function test() {
    console.log(d)//undefined
    var d = 4; // 局部变量
    let e = 5; // 局部变量
    console.log(f)//报错(Uncaught ReferenceError: f is not defined)
    f = 6; // 全局变量 不会预解析
    console.log(f)//6 赋值了
}
console.log(a)//打印1
console.log(b)//报错
console.log(c)//打印3
test()//执行函数test,里面的变量才会预解析,将de解析为局部变量,f为全局变量,(变量污染,里面的变量会污染全局),不执行f变量会出错
console.log(d)//报错
console.log(e)//报错
console.log(f)//打印6

三、预解析

1、什么是预解析??

  在初始化页面加载时,js会先加载function以及变量,但是不会加载变量赋值的过程。预解析是在加载这个页面就进行了(全局预解析),之后再加载代码。

  变量预解析以及函数预解析,函数声明以及变量声明会提到当前作用域前面,但是不会调用和赋值

  当变量名和函数名重名时,会预解析函数(权重大),函数名不要和变量重名,重名以函数为准

  全局作用域预解析:在页面打开就进行了,只解析全局的内容

   私有作用域预解析:执行的时候才会内部预解析,函数内部预解析,只属于函数内部

问题:一旦函数的形参和定义的私有变量重名,是先预解析还是先形参赋值?

  因为函数是单独进行预解析的,所以在函数执行的时候,会先进行形参赋值,然后再进行预解析

fn()//打印,函数预解析权重大于变量预解析
console.log(fn)//这里的fn是一个函数
var fn = 66
console.log(fn)//fn赋值为66
fn()//报错(Uncaught TypeError: fn is not a function),fn已经变成一个变量了,不是函数
function fn() {//该fn函数是声明式函数
    console.log("我是fn函数")
}
fn1()//赋值式函数不能先调用,因为预解析时,将fn1当成了一个变量,没有赋值
var fn1 = function(a){//该fn1函数是赋值式函数
    console.log("我是fn1函数")
    console.log(a)//函数形参和私有变量重名,先进行形参赋值,再预解析,所以打印4
    var a = 10
    console.log(a)//打印10
}
fn1(4)//打印
 
/* 
        1、预解析函数fn 
        2、变量fn预解析 然后fn1预解析
        3、fn(),函数执行,内部预解析
        4、打印fn函数(函数名加括号表示执行,不加括号表示函数)
        5、fn赋值为66
        6、打印fn,66
        7、执行fn()函数,错误(以下步骤出现错误,直接忽略,可以加try catch)
        8、fn1() 错误,没有这个函数,但是有fn1变量,打印fn1不会报错undefined
        9、fn1赋值为函数,赋值式函数
        10、fn1(4),函数执行,内部预解析
            a、给形参a传递参数4,(形参赋值)
            b、预解析里面的变量a
            c、打印我是fn1函数
            d、4
            e、变量a赋值
            f、打印10        
    */

2、预解析的无节操

            1.预解析的时候不管你的条件是否成立,都要把里面的代码进行预解析。
            
            2.预解释的时候只预解释”=”左边的,右边的值,不参与预解析

            3.自执行函数:定义和执行一起完成了。

             4.函数体中return下面的代码虽然不再执行了,但是需要进行预解析;

            5.函数声明和变量声明都会被提升。函数名不要和变量重名,重名以函数为准,在预解析的时候,如果名字已经声明过了,不需要从新的声明,但是需要重新的赋值;
Kotlin中的let是一个拓展函数,它可以更优雅地处理可空变量问题。当我们使用let函数时,它会将调用它的对象作为参数传递给lambda表达式,并且在lambda表达式中可以对该对象进行操作。这样可以避免对可空变量进行null检查,提高代码的可读性和简洁性。同时,let函数还可以在链式调用中使用,使代码更加清晰。 具体来说,let函数有以下特点: 1. 它将调用它的对象作为lambda表达式的参数。 2. 在lambda表达式中可以对该对象进行操作。 3. let函数的返回值是lambda表达式的返回值。 通过使用let函数,我们可以在处理可空变量时,避免使用繁琐的null检查,提高代码的可读性和简洁性。同时,let函数还可以与其他拓展函数一起使用,例如apply、run等,以便更灵活地处理对象。 总结来说,Kotlin中的let函数是一个非常实用的拓展函数,可以更优雅地处理可空变量问题,并且在链式调用中提高代码的可读性和简洁性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [十三、Kotlin进阶学习:内联函数let、also、with、run、apply的用法。](https://blog.csdn.net/weixin_43936741/article/details/125996295)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Kotlin要点之一 | let](https://blog.csdn.net/jxq1994/article/details/103007338)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值