JS中的作用域及闭包

1、JS中的作用域

在 es6 出现之前JS中只有全局作用域函数作用域没有块级作用域。即在函数体内就可以有自己的作用域,但是如果不是在函数体的话就全部都是全局作用域。比如在 if、for 等有 {} 的结构体,就不会具备自己的作用域,在这些结构体内声明的变量将会是全局变量。由此可能导致一些问题,下面代码示例:

var tmp = new Date();
function f() {
  console.log(tmp);
  if (false) {  //即使没有运行到下面的代码,该变量声明也被提升了。因为变量声明是在编译阶段就被运行的,而不是代码运行阶段
    var tmp = 'hello world';
  }
}
f(); // undefined  由于变量提升,导致内层的tmp变量覆盖了外层的tmp变量
var s = 'hello';
for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}
console.log(i); // 5  用来计数的循环变量泄露为全局变量。

在 es6 中引入的块级作用域就解决了这些问题。通过 let、const 引入块级作用域后:

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5   无法访问到块级作用域里的变量
}

1.1、调用函数时,函数内部的变量的作用域

编程语言的作用域规则有动态作用域和词法作用域,词法作用域的指的是函数和变量的作用域由声明时所处的位置决定,JS使用的就是词法作用域。

在JS中,调用函数时,函数内部的变量的作用域由函数声明时所处的位置决定,而不是调用的位置。

var a = 'window'
var f = function (){
  console.log(a);
}
var b = function (){
  var a = 'else'
  f();
}
b();    //输出 'window'

 但是 JS 中的 this 指针并不遵守词法作用域,而是取决于函数的调用方式。

2、全局变量和局部变量

JS中的变量只有两种:全局变量和局部变量。函数体外声明的变量,称为全局变量。 函数内部使用 var 声明的变量,称为局部变量。

(网上都说在函数体内声明的变量称为局部变量,那块级作用域中的变量是什么变量?我觉得应该是所有在 {} 结构体内声明的变量都称之为局部变量)

//函数内部可以直接读取全局变量。    
var n=999;
 function f1(){
  alert(n);
 }
f1(); // 999

//但是在函数外部无法读取函数内的局部变量,因为函数内部的变量在函数执行完毕以后就会被释放掉
function f1(){
 var n=999;
}
alert(n); // error

注意:在函数内部声明变量的时候,一定要使用 var、let、const 命令。否则的话实际上是声明了一个全局变量!

function f1(){
 n=999;
}
f1();
alert(n); // 999

而要想访问到局部变量,那么就可以使用闭包。

3、闭包的概念

闭包就是能够读取其他函数内部变量的函数。

在 es6之前,JavaScript中的变量的作用域只有两种:全局作用域和函数作用域。函数内部可以直接读取全局变量,但是在函数外部就无法读取函数内的局部变量。要想从外部读取函数内的局部变量就要用到闭包。

function f1() {
  n = 999;
  function f2() {
    alert(n);
  }
  return f2;
}
var result = f1();
result(); // 999

 

4、闭包的作用

闭包可以用在许多地方。它的最大用处有两个:可以读取函数内部的变量、让这些变量的值始终保持在内存中。

4.1、让这些变量的值始终保持在内存中。

function f1() {
  var n = 999;
  nAdd = function () {
    n += 1
  }
  function f2() {
    alert(n);
  }
  return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000

在上面的代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制回收。(如果n是一个需要很费时的操作才能得到的值的话就会作用明显,而且可以把局部变量驻留在内存中,避免使用全局变量)

这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,nAdd是一个全局变量,这个函数本身也是一个闭包,因为该函数的存在我们可以在函数外部对函数内部的局部变量进行操作。

 

4.2、减少全局变量的污染(匿名自执行函数)

所有的变量声明时如果不加上var等关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误用这些变量、造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。除了每次使用变量都是用var关键字外,我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,比如UI的初始化等,此时我们可以使用闭包。

(function(){
  var foo = function(){
      alert("执行完这个闭包后,立即销毁");
  };
  foo();
})();

上面创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在函数执行完后会立刻释放资源,关键是不污染全局对象。

 

4.3、实现封装

var person = function () {
  //变量作用域为函数内部,外部无法访问    
  var name = "default";
  return {
    getName: function () {
      return name;
    },
    setName: function (newName) {
      name = newName;
    }
  }
}();
console.log(person.name); //直接访问,结果为undefined    
console.log(person.getName());
person.setName("abruzzi");
console.log(person.getName());

 

4.4、实现继承

下面定义了Person,它就像一个类,我们new一个 Person 对象,访问它的方法。下面我们定义了Jack,继承自 Person,并添加自己的方法,Jack 继承了 Person。

function Person2() {
  var name = "default";
  this.age = 12;
  return {
    getName: function () {
      return name;
    },
    setName: function (newName) {
      name = newName;
    }
  }
};

var p = new Person2();
console.log(p.age);
p.setName("Tom");
console.log(p.getName());

var Jack = function () {};
//继承自Person
Jack.prototype = new Person2();
//添加私有方法
Jack.prototype.Say = function () {
  console.log("Hello,my name is Jack");
};
var j = new Jack();
j.setName("Jack");
j.Say();
console.log(j.getName());

 

5、闭包的危害

5.1、可能导致内存泄露

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露(即己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,将导致程序运行速度减慢)。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

5.2、可能不小心修改掉私有属性

闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,闭包可能会修改掉父函数的私有属性。

 

6、垃圾回收机制

不再使用的变量也就是生命周期结束的变量,是局部变量,局部变量只在函数的执行过程中存在,当函数运行结束,没有其他引用(闭包),那么该变量会被标记回收。全局变量的生命周期直至浏览器卸载页面才会结束,也就是说全局变量不会被当成垃圾回收。

(局部变量应该是只有在函数执行时才会被创建然后占用内存,当函数执行结束之后如果没有闭包引用它的话就会被回收。当函数再次执行时又再次创建然后占用内存)

可以参考:https://blog.youkuaiyun.com/zxd10001/article/details/81038533

6.1、JavaScript的内存生命周期

  • 分配所需要的内存
  • 使用分配到的内存(读、写)
  • 不需要时将其释放

垃圾回收机制的原理其实很简单:确定变量中哪些还在继续使用的,哪些已经不用的,然后垃圾收集器每隔固定的时间就会清理一下,释放内存。

局部变量在程序执行过程中,会为局部变量分配相应的空间,然后在函数中使用这些变量,如果函数运行结束了,而且在函数之外没有再引用这个变量了,局部变量就没有存在的价值了,因此会被垃圾回收机制回收。在这种情况下,浏览器很容易辨别哪些变量该回收,但是并非所有情况下都这么容易。比如说全局变量。在现代浏览器中,通常使用标记清除策略来辨别及实现垃圾回收。

  • 标记清除

标记清除会给内存中所有的变量都加上标记,然后去掉环境中的变量以及不在环境中但是被环境中变量引用的变量(闭包)的标记。剩下的被标记的就是等待被删除的变量,原因是环境中的变量已经不会再访问到这些变量了。最后垃圾回收器会完成内存清理,销毁那些被标记的值释放内存空间。

参考:https://www.cnblogs.com/yunfeifei/p/4019504.html、  https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

转载于:https://www.cnblogs.com/wenxuehai/p/10317437.html

标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广泛使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模型构建、验证与优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模型 逻辑回归是一种常用于分类任务的线性模型,特别适用于二类问题。在信用评估中,该模型可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模型 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约与非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理与转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模型的适应性。 六、模型构建与验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模型性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模型的召回率与特异性。 七、集成学习方法 为提升模型预测能力,可采用集成策略,如结合多个模型的预测结果。这有助于降低单一模型的偏差与方差,增强整体预测的稳定性与准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理与模型优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模型以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值