(无聊)JavaScript中的作用域

JavaScript中的作用域

  • 最近已经开始试着做一些轻量级的开发了,也发现了自己的很多基础问题,最近几天再试着做一点点整理
  • 首先JavaScript中的作用域是真的让我头疼啊,以前C里面的作用域说白了就是一个块级作用域的问题。。。ES5和ES6不是很统一,就让我很头大。。
  1. 全局作用域
    当你在文档中(document)编写 JavaScript 时,你就已经在全局作用域中了。JavaScript 文档中(document)只有一个全局作用域。定义在函数之外的变量会被保存在全局作用域中。全局作用域里的变量能够在其他作用域中被访问和修改。
  2. 局部作用域
    定义在函数中的变量就在局部作用域中。并且函数在每次调用时都有一个不同的作用域。这意味着同名变量可以用在不同的函数中。因为这些变量绑定在不同的函数中,拥有不同作用域,彼此之间不能访问。
  3. 块语句
    块级声明包括if和switch,以及for和while循环,和函数不同,它们不会创建新的作用域。在块级声明中定义的变量从属于该块所在的作用域。
    ECMAScript 6 引入了let和const关键字。这些关键字可以代替var。
    和var关键字不同,let和const关键字支持在块级声明中创建使用局部作用域。
  4. 上下文
    上下文就涉及到我最喜欢又最讨厌的this了
    在全局作用域中,上下文总是window对象
    如果作用域定义在一个对象的方法中,上下文就是这个方法所在的那个对象
lass User {
    logName() {
        console.log(this);
    }
}
 
(new User).logName(); // logs User {}
  • 那么一个函数的作用域到底是什么呢?
    在一个函数被调用的时候,函数的作用域才会存在。此时,在函数还没有开始执行的时候,开始创建函数的作用域:
      函数作用域的创建步骤:
    1.函数形参的声明。
    2.函数变量的声明
    3.普通变量的声明。
    4.函数内部的this指针赋值
执行环境的创建阶段有三件事:
	1. 创建变量对象
	2. 创建作用域链
	3. 设置上下文(this)的值
  • 【讨论闭包】
    什么叫闭包?
    函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机学文献中称为闭包
    所以,所有JavaScript函数都是闭包,他们都是对象,都关联到作用域链
    我们要理解几个关键概念
    • 函数的执行依赖于变量作用域,这个作用域是在函数定义是决定的,而不是函数调用是决定的。
    • 闭包所引起的问题
      A. 变量污染:
      如:
var funB,
funC;
(function() {
  var a = 1;
  funB = function () {
    a = a + 1;
    console.log(a);
  }
  funC = function () {
    a = a + 1;
    console.log(a);
  }
}());
funB();  //2
funC();  //3.

因为funB和funC作用域里面都没有定义a,他们就会在定义funB和分funC的环境中一级一级向上找a的定义,找到的a定义应该是一样的
再举一个最经典的关于闭包的例题:

var array = [
];
for (var i = 0; i < 10; i++) {
  var fun = function () {
    console.log(i);
  }
  array.push(fun);
}
var index = array.length;
while (index > 0) {
  array[--index]();
} //输出结果 全是10;

在讨论这个结果之前我们可以先看两个对比

//代码1:
 var scope='glocal scope';//全局变量
function checkscope(){
    var scope='local scope';
    function f(){
        return scope;
    }
    return f();
}
alert(checkscope());//local scope
//代码2:
 var scope='glocal scope';//全局变量
function checkscope(){
    var scope='local scope';
    function f(){
        return scope;
    }
    return f;
}
alert(checkscope()())//local scope

结果是一样的。为什么呢?因为前面说过:**函数的执行依赖于变量作用域,这个作用域是在函数定义是决定的,而不是函数调用是决定的。**第一个好理解,第二个我们可以看到返回的不是一个调用返回值,而是一个函数对象,在全局环境下调用。但是实际上,作用域还是在函数的定义上,所以最后的alert出来还是local scope
回到我们开始讨论的输出结果,为什么全是10呢?
我试着在全局环境下再在控制台输出(话说到这里,为什么我的谷歌控制台看不到输出啊。。。麻蛋,又逼着我去用火狐)i的值,i发现i在后面调用的时候,就已经是10了,i是用var定义在全局作用域下的。
这种类似问题产生的根源就在于,没有注意到外部作用域链上的所有变量均是静态的。
B.内存泄露
就是内存溢出。为什么会容易溢出呢?

var fun = undefined;
function A() {
  var a = 1;
  fun = function () {
  }
}

这里面的变量a将会在函数fun存在的域中一直存在。如果牵扯到DOM操作,遇到一个贼你妈大的DOM ,那就凉凉了。

所以闭包会引起这么多问题。如何解决呢?
大概就是用C的函数参数的思路:

var funB,funC;
(function () {
  var a = 1;
  (function () {
    funB = function () {
      a = a + 1;
      console.log(a);
    }
  }(a));
  (function (a) {
    funC = function () {
      a = a + 1;
      console.log(a);
    }
  }(a));
}());
funB()||funC();  //输出结果全是2 另外也没有改变作用域链上a的值。

再举一个应用层的例子:

 onPullDownRefresh: function () {
    wx.showNavigationBarLoading();
    var that = this
    const db = wx.cloud.database()
    db.collection('user').get({
      success: function (res) {
        console.log(res.data.reverse());
        //将获取到的json数据,存在名字叫list的这个数组中
        that.setData({
          liuyanlist: res.data

          //res代表success函数的事件对,data是固定的,liuyanlist是数组
        })
        console.log(that.data.liuyanlist)
      }
    })
        // 隐藏导航栏加载框
        wx.hideNavigationBarLoading();
        // 停止下拉动作
        wx.stopPullDownRefresh();
    
  },

这告诉了我们一点:作用域链是沿着执行环境走的
更加深入的理解可以参照我在其他博客里面找到 一段话:

为了解决掉我们从上面学习中会出现的各种困惑,“执行环境(context)”这个词中的“环境(context)”指的是作用域而并非上下文。这是一个怪异的命名约定,但由于 JavaScript 的文档如此,我们只好也这样约定。

JavaScript 是一种单线程语言,所以它同一时间只能执行单个任务。其他任务排列在执行环境中。当 JavaScript 解析器开始执行你的代码,环境(作用域)默认设为全局。全局环境添加到你的执行环境中,事实上这是执行环境里的第一个环境。

之后,每个函数调用都会添加它的环境到执行环境中。无论是函数内部还是其他地方调用函数,都会是相同的过程。

每个函数都会创建它自己的执行环境。

当浏览器执行完环境中的代码,这个环境会从执行环境中弹出,执行环境中当前环境的状态会转移到父级环境。浏览器总是先执行在执行栈顶的执行环境(事实上就是你代码最里层的作用域)。

全局环境只能有一个,函数环境可以有任意多个。
执行环境有两个阶段:创建和执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值