深入javascript之作用域与闭包

理解作用域与闭包的问题是理解this指向的基本阶段。

执行上下文
概念定义:当前 JavaScript 代码被解析和执行时所在环境的抽象概念
函数指定参数(若未传入,初始化该参数值为 undefined)
函数声明 (若发生命名冲突,会覆盖之前的函数参数)
变量声明 (初始化变量值为 undefined,若发生命名冲突,会忽略)

作用域
作用域存在着嵌套关系,对某段代码来说,可能存在多个同名的标识符,其作用域都覆盖了这段代码,这时,被适用的将是作用域范围最小(即离代码最近)的那个标识符。

作用域分为词法域与动态域。
javascript采用的是词法作用域,也就是静态作用域。

  • 静态作用域
    标识符的作用范围是由标识符的声明位置和程序的结构决定的,也就是说某段代码所参照的标识符是在哪定义的,通过对程序代码进行词法解析即可确定,标识符的作用域是静态不变的。即静态作用域是在函数定义的时候决定的。

  • 动态作用域
    与静态作用域相对。
    标识符的作用范围是由程序运行时函数的调用情况决定的,也就是说某段代码所参照的标识符是在哪定义的,需在程序运行时根据执行栈才能确定,标识符的作用域可能会是动态变化的。即动态作用域是在函数被调用的时候决定的。

看下例子:

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

bar();

因为js采用的是静态作用域,所以上述打印结果是1。 foo方法内部没有定义value,在作用域的限制下,会向上寻找定义好的变量,这里就是全局定义的value = 1
如果js采用的是动态作用域,上述打印结果就是2。foo方法内部没有定义,但是在调用的时候已经能拿到bar方法内部定义的value = 2
那么,动态作用域的语言一般很少。

作用域的类型有这样几种:

  • 全局作用域 => 变量是全局变量,所有程序都能访问
    在浏览器中,我们的全局作用域就是 window。因此在浏览器中,所有的全局变量和函数都是作为 window 对象的属性和方法创建的。
  • 文件作用域 => 与全局作用域类似,但变量只能是同一个源文件模块里的程序才能访问。比如C/C++。
  • 函数作用域 => 一个局部作用域,变量在函数内声明,只在函数内部可见。大部分语言都支持函数作用域。
    用var声明的变量都具有函数作用域。不管在函数那个位置声明的,只要是函数内部就可以访问到。
  • 闭包作用域 => 一种让函数的代码能够访问函数声明(函数对象被创建)时作用域内(上下文环境)的变量机制。闭包在函数式语言中非常普遍。
    闭包是将函数定义时的局部作用域环境保存起来后生成的一个实体。
    闭包实现了一个作用域,函数始终是运行在它们被定义的闭包作用域里,而不是它们被调用的作用域里。
    闭包可以嵌套,全局作用域→闭包(0…n)作用域→函数作用域→代码块(0…n)作用域就整个的形成了一个代码执行时的作用域链。
  • 代码块作用域 => 一个局部作用域,只在声明变量的代码块内部可见。
    ECMAScript6 之后,函数内用 let 声明的变量和 const 声明的常量都属于代码块作用域。比如C/C++/Java…
  • 静态局部作用域 =>
    局部作用域一般只在某个特定的代码片段内可访问到,JavaScript 中的局部作用域分为函数作用域和代码块作用域两类,其中代码块作用域在 ECMAScript6 之前不被支持。

如果是自由变量,取值应该去哪里取呢? 要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记!

作用域链
举例子说明:当前调用了一个之前定义的函数fn(里面了自由变量x),那么fn中的自由变量x就要到定义fn的作用域中去取,如果没有就跨到上一个上下文作用域中取,一步步跨作用域,直到跨到全局作用域中取值,如果没有找到定义就是真的没有了。这个一步步“跨”的路线,就是作用域链。

闭包
闭包就是函数套函数,即有权访问另一个作用域的函数。
创建闭包的方式就是在一个函数的内部,创建另外一个函数。那么,当外部函数被调用的时候,内部函数也就随着创建,这样就形成了闭包。

闭包容易造成的问题:

  1. 变量污染:
    因为外部作用域链上的变量是内部定义函数的自由变量,是一个静态变量,如果内部函数改变了自由变量的值,就会影响到外部作用域链上的变量值,那么其他函数引用到这个变量就是更改后的变量值。这样就造成了变量污染的问题。

拯救方法:匿名函数传参的形式,形成一个独立的函数作用域,不影响外部作用域链上的自由变量。

  1. 内存泄漏:
    俗称内存空间不足,变量或者方法使用的内存空间过大或得不到释放。 是内存溢出的一种原因。
    借用别人的例子:
    内存泄漏是申请的空间没有及时释放或者干脆丢了指针没法释放.不是泄漏,是漏不出来;只是泄漏的内存远小于可分配的内存时影响不大,多了就玩完.
    内存溢出就是申请的内存超过了可用内存,内存不够用了,比如申请了10m的内存,但是一共只有5m,申请不了,就溢出了.

内存泄漏堆积形成内存溢出。

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值