当前位置: 代码网 > it编程>编程语言>Javascript > 你真的了解JavaScript的作用域与闭包吗

你真的了解JavaScript的作用域与闭包吗

2024年05月19日 Javascript 我要评论
一、作用域1.作用域总体来说就是根据名称查找变量的一套规则。js查找变量的方式有两种:lhs和rhs。lhs(left hand side)大致可以理解为给某个变量赋值,在赋值符(=)的左边;rhs(

一、作用域

1.作用域总体来说就是根据名称查找变量的一套规则。js查找变量的方式有两种:lhs和rhs。

lhs(left hand side)大致可以理解为给某个变量赋值,在赋值符(=)的左边;rhs(right hand side)是指找到某个变量的源值,如在赋值符(=)的右边。

举个例子说明,对于 a = 10; a = b; 这两个语句。

var a 这里就是lhs查询,查找当前作用域是否有a这个变量来给a进行赋值,如果有则进行赋值;如果没有,在非严格模式下js引擎会自动创建一个变量a来进行赋值;在严格模式下js引擎会抛出referenceerror错误。

a = b;这个语句,对于b这个变量是进行rhs查询,查找b这个变量的值。如果成功查询,则进行赋值操作;如果查询失败,则抛出referenceerror错误。如果查询成功,但是对这个变量的操作有问题,就会抛出typeerror错误。如b只是一个普通的变量,却当成函数使用(b())。

2.js是基于词法作用域的。指在你写代码时将变量和函数写在哪里决定的,而不是调用顺序决定的。

举个例子

var num = 1;
function a() {
    console.log(num); // 1
}
function b() {
    var num = 2;
    a();
}
b();

当按照顺序执行b()时,在b函数作用域中定义了一个值为2的num变量,然后在b函数中调用a函数,来到a函数的作用域进行调用(而不是在b函数作用域),找到全局变量num = 1。

3.不同作用域间是互相不会影响的,重复定义变量是不会发生错误的。

举个例子

var i = 1;
var j = 2;
function a(){
    var i = 3;
    var j = 4;
    console.log(i,j); // 3,4
}
function b(){
    var i = 5;
    var j = 6;
    console.log(i,j); // 5,6
}
console.log(i,j);// 1,2
a();
b();

4.for循环中,var声明的变量在全局作用域能够访问,并且值是退出循环的那个值;let声明的变量会产生块作用域,它将值重新绑定到循环迭代中。

for(var i = 0;i<5;i++){
    console.log(i); // 0 1 2 3 4
}
console.log(i); // 5
 
for(let i = 0;i<5;i++){
    console.log(i); // 0 1 2 3 4
}
console.log(i); // referenceerror

5.每个作用域都会发生提升操作;函数声明会发生提升,但是函数表达式不会被提升;

b() // typeerrorn() // referenceerror,因为不会变量提升// 函数声明function a(){    console.log(num); // undefined    var num = 1;}// 函数表达式var b = function n(){    console.log('b');}

6.函数声明首先被提升,变量声明在后;

函数首先被提升可以认为是函数的优先级高于变量,重复声明的变量会被函数替代。但是其他函数声明可以覆盖之前的函数声明。

b() // typeerror
n() // referenceerror,因为不会变量提升
// 函数声明
function a(){
    console.log(num); // undefined
    var num = 1;
}
// 函数表达式
var b = function n(){
    console.log('b');
}

二、闭包

1.闭包是基于词法作用域书写代码时的必然结果。

不论函数在哪里执行,函数都会在代码书写的位置(即函数本身在哪)来进行作用域链的查找。当函数进行作用域查找时,就形成了闭包。

也可以说是当函数被当作值进行传递的时候(如参数传递、返回值等),就会形成闭包。

举个例子

function foo() {
    var a = 2;
    function bar() {
        console.log(a);
    }
    return bar;
}
var a = 3;
var baz = foo();
baz(); // 2 

当foo函数执行时,会返回bar函数,当bar函数在全局作用域执行时,会沿着bar函数定义的地方开始进行rhs查找a变量的值。这时就产生了闭包。闭包的产生准确来说应该是函数在函数本身所处作用域之外执行,对于iife(立即执行函数)来说虽然创建了闭包,但是却没有真正使用闭包,因为立即执行函数的执行作用域是函数定义的作用域,虽然是闭包,但是没有使用闭包的功能。

闭包的功能:函数在所处作用域之外执行时,会沿着函数定义时的作用域链进行查找。

var a = 1;
// 立即执行函数
(
    function(){
        console.log(a);
     }
)();

2.循环与闭包

观察下面的例子

for(var i = 0;i<3;i++){
    settimeout(()=>{
        console.log(i);
    },i*500)
}

输出结果为 3 3 3,并不是想象中的 0 1 2。因为在for循环结束时,settimeout开始执行,在执行的时候进行变量 i 的rhs查询,但是变量 i 只能在全局作用域中查到,这是变量 i 的值是退出循环的值 i = 3;

可能与你的想象结果有些许不同,如果要输出 0 1 2 该怎么做?

(1).使用iife形成闭包是否可行?

for (var i = 0; i < 3; i++) {
    (function () {
        settimeout(() => {
            console.log(i);
        }, i*500)
    })()
}

结果依然是 3 3 3。因为虽然使用iife形成了闭包,但是在闭包中并没有 i 的值,还会沿着作用域链查找到全局 i = 3;

(2).在iife使用一个变量临时保存 i 值或传参

for (var i = 0; i < 3; i++) {
    (function () {
        var j = i;
        settimeout(() => {
            console.log(j);
        }, i*500)
    })()
}
 
// 或
for (var i = 0; i < 3; i++) {
    (function (i) {
        settimeout(() => {
            console.log(i);
        }, i*500)
    })(i)
}

结果为 0 1 2,第一个for循环闭包中有变量 j ,第二个for循环有形参 i ,都不会到达全局作用域。

(3).使用 let 生成块作用域

for(let i = 0;i<3;i++){
    settimeout(()=>{
        console.log(i);
    },i*500)
}

结果为 0 1 2,let声明的变量会包含在块作用域中,并且作为循环变量时,每一次的迭代都会声明一次变量并将变量结果保存,当访问变量时,由于闭包的作用,会得到每次迭代的值。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注代码网的更多内容!

(0)

相关文章:

  • javascript基础数据类型转换教程示例

    数值型转换为字符串类型方式说明案例tostring()转成字符串var num =1; alert ( num.tostring());string()强制转换 转成字符串var …

    2024年05月19日 编程语言
  • javascript数据类型基础示例教程

    js中的输入输出语句方法说明归属alert(msg)浏览器弹出警示框浏览器console.log(msg)浏览器控制台打印输出信息浏览器prompt(info)浏览器弹出输入框,用…

    2024年05月19日 编程语言
  • JavaScript入门初体验书写方式

    javascript历史布兰登艾奇( brendan eich ,1961年~)。神奇的大哥在1995年利用10天完成 javascript 设计。网景公司最初命名为 livesc…

    2024年05月19日 编程语言
  • JavaScript中的内置对象介绍

    一、对象的概念对象是一种特殊的数据类型(object)。拥有一系列的属性和方法。分为内置对象和自定义对象。二、string对象string对象用来支持对于字符串的处理。1、属性le…

    2024年05月19日 编程语言
  • JavaScript中变量的作用域详解

    一、变量的分类在javascript中变量分为两种:全局变量局部变量二、变量的作用域1、局部变量的作用域局部变量:在函数内部定义的变量称为局部变量,其作用域为该函数内部,在该函数外…

    2024年05月19日 编程语言
  • JavaScript自定义函数用法详解

    javascript中的函数分为两种:系统函数和自定义函数,这里主要讲解自定义函数。自定义函数1、语法:注意:传入的参数是可选的。例如:<!doctype html>&…

    2024年05月19日 编程语言

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com