关于 const     let      var 

总结:

建议使用 let  ,而不使用var,如果要声明常量,则用const。

ES6(ES2015)出现之前,JavaScript中声明变量只有通过 var 关键字,函数声明通过 function 关键字,

ES6之后,声明的方式有 var 、 let 、 const 、 function 、 class 。

const 用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。

let      let是相对var的,优点是let 声明的变量不存在变量提升。

var     存在变量提升,变量提升是指无论 var 出现在一个作用域的哪个位置,这个声明都属于当前的整个作用域,在其中到处都可以访问到。

参考链接: JavaScript中var、let和const的区别   js中 let var const 的差异和使用场景  JS中的块级作用域,var、let、const三者的区别

关于 局部变量 和 全局变量

js的变量有两种作用域:全局变量和局部变量。

没有使用 var 声明的变量和在function之外声明的变量都是全局变量,是window属性之一;

使用 var 声明的变量属于所在函数,不管在函数的哪个位置出现,等价于在函数一开始声明。

局部变量比同名全局变量的优先级高,所以局部变量会隐藏同名的全局变量。要想访问被隐藏的全局变量就加上 window. 前缀。

js(ES6之前)没有块作用域,在语句块之后,在函数结束之前,语句块定义的变量都是可以访问的。

比如:for(var idx =0; idx<2;idx++){} alert(idx); 结果显示为2。ES6出现后,有了let ,这里代码如果为for(let idx =0; idx<2;idx++){} alert(idx);  则会运行时报错:idx is not difined;

关于局部变量和全局变量的示例:

<script type="text/javascript">
var a = 1;
function hehe()
{
window.alert(a);//undifined
var a = 2; //声明了局部变量
window.alert(a);//
}
hehe();
</script>
如果上面这个例子中把 var 换成 let ,则会报错,因为 let 在同一个范围内不能重复定义。 
<script type="text/javascript">
var a = 1;
function hehe()
{
window.alert(a);//
a = 2; // 修改的是全局变量 a
window.alert(a);//
}
hehe();
</script>
<script type="text/javascript">
var a = 1;
function hehe()
{
window.alert(this.a);//1 //访问全局变量 this.a 换成 window.a 也可以 关于 this 的指向,就一句话:谁最终调用函数,this指向谁!!! 参考 this指向
var a = 2;
window.alert(a);//
}
hehe();
</script> 

参考链接:JavaScript局部变量和全局变量的理解

关于 js 闭包

JavaScript 变量属于本地或全局作用域。全局变量能够通过闭包实现局部(私有)。

Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内。

这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。

当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

详见下文。

参考:W3school JS 闭包

原文链接:https://www.cnblogs.com/jesse131/p/9079290.html

在前端开发中闭包是一个很重要的知识点,是面试中一定会被问到的内容。之前我对闭包的理解主要是"通过闭包可以在函数外部能访问到函数内部的变量",对闭包运用的也很少,甚至自己写过闭包自己都不太清楚,只知道这样写可以解决问题。最近在梳理自己的js知识点,发现自己对js闭包理解的很不透彻,于是想全面的分析一下闭包,特别是闭包的形成原因和闭包的使用场景。

闭包的定义

闭包是指有权访问另一个函数作用域中的变量的函数 --《JavaScript高级程序设计》

函数对象可以通过作用域关联起来,函数体内的变量都可以保存在函数作用域内,这在计算机科学文献中称为“闭包”,所有的javascirpt函数都是闭包 --《Javascript权威指南》

看完这些专业的定义是不是感觉一头雾水,没关系,我和一样也没明白这些定义说的是啥,咱接着往下分析。

在认识闭包原理之前我们先必须对作用域、执行上下文、执行上下文堆栈、变量对象、活动对象、作用域链有着全面的认识

作用域 Scope

作用域是一套规则,用于确定在何处以及如何查找变量(标识符)
作用域共有两种主要的工作模型:

  • 词法作用域:作用域是在编写代码的时候确定的
  • 动态作用域:作用域是在代码运行的时候确定的

我们知道javascript使用的是词法作用域

执行上下文 Execution Contexts

Javascript中代码的执行上下文分为以下三种:

  • 全局级别的代码 – 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境。
  • 函数级别的代码 – 当执行一个函数时,运行函数体中的代码。
  • Eval的代码 – 在Eval函数内运行的代码。

一个执行的上下文可以抽象的理解为一个对象。每一个执行的上下文都有一系列的属性(变量对象(variable object),this指针(this value),作用域链(scope chain) )

Execution Contexts = {
variable object:变量对象;
this value: this指针;
scope chain:作用域链;
}

执行上下文堆栈 Execution Contexts Stack

活动的执行上下文在逻辑上组成一个堆栈。堆栈底部永远都是全局上下文(globalContext),而顶部就是当前(活动的)执行上下文。

<script>
function add(num){
var sum = 5;
return sum + num;
}
var sum = add(4);
</script>

当add函数被调用时,add函数执行上下文被压入执行上下文堆栈的顶端,此时执行上下文堆栈可表示为:

EC Stack = [
<add> functionContext
globalContext
];

add函数执行完毕后,其执行上下文将会从执行上下文堆栈顶端弹出并被销毁。全局执行上下文只有在浏览器关闭时才会从执行上下文堆栈中销毁

变量对象 Variable Object

如果变量与执行上下文相关,那变量自己应该知道它的数据存储在哪里,并且知道如何访问。这种机制称为变量对象(variable object)。
可以说变量对象是与执行上下文相关的数据作用域(scope of data) 。它是与执行上下文关联的特殊对象,用于存储被定义在执行上下文中的变量(variables)、函数声明(function declarations) 。
当进入全局上下文时,全局上下文的变量对象可表示为:

VO = {
add: <reference to function>,
sum: undefined,
Math: <...>,
String: <...>
...
window: global //引用自身
}

活动对象 Activation Object

当函数被调用者激活时,这个特殊的活动对象(activation object) 就被创建了。它包含普通参数(formal parameters) 与特殊参数(arguments)对象(具有索引属性的参数映射表)。活动对象在函数上下文中作为变量对象使用。
当add函数被调用时,add函数执行上下文被压入执行上下文堆栈的顶端,add函数执行上下文中活动对象可表示为

AO = {
num: 4,
sum :5,
arguments:{0:4}
}

作用域链 Scope Chain

函数上下文的作用域链在函数调用时创建的,包含活动对象AO和这个函数内部的[[scope]]属性。

var x = 10;
function foo() {
var y = 20;
function bar() {
var z = 30;
alert(x + y + z);
}
bar();
}
foo();

在这段代码中我们看到变量"y"在函数"foo"中定义(意味着它在foo上下文的AO中)"z"在函数"bar"中定义,但是变量"x"并未在"bar"上下文中定义,相应地,它也不会添加到"bar"的AO中。乍一看,变量"x"相对于函数"bar"根本就不存在;
函数"bar"如何访问到变量"x"?理论上函数应该能访问一个更高一层上下文的变量对象。实际上它正是这样,这种机制是通过函数内部的[[scope]]属性来实现的。
[[scope]]是所有父级变量对象的层级链,处于当前函数上下文之上,在函数创建时存于其中。

注意: [[scope]]在函数创建时被存储是静态的(不变的),直至函数销毁。即:函数可以永不调用,但[[scope]]属性已经写入,并存储在函数对象中。
在这里我们逐步分析下
全局上下文的变量对象是:

globalContext.VO === Global = {
x: 10
foo: <reference to function>
};

在"foo"创建时,"foo"的[[scope]]属性是:

foo.[[Scope]] = [
globalContext.VO
];

在"foo"激活时(进入上下文),"foo"上下文的活动对象是:

fooContext.AO = {
y: 20,
bar: <reference to function>
};

"foo"上下文的作用域链为:

fooContext.Scope = [
fooContext.AO,
globalContext.VO
];

内部函数"bar"创建时,其[[scope]]为:

bar.[[Scope]] = [
fooContext.AO,
globalContext.VO
];

在"bar"激活时,"bar"上下文的活动对象为:

barContext.AO = {
z: 30
};

"bar"上下文的作用域链为:

bar.Scope= [
barContext.AO,
fooContext.AO,
globalContext.VO
];

介绍了一大堆内容,是不是有点晕忽忽的?坚持一下,下面是重点

闭包的原理

我们通过一个闭包的例子来分析一下闭包的形成原理

function add(){
var sum =5;
var func = function () {
console.log(sum);
}
return func;
}
var addFunc = add();
addFunc(); //5

js执行流进入全局执行上下文环境时,全局执行上下文可表示为:

globalContext = {
VO: {
add: <reference to function>,
addFunc: undefined
},
this: window,
scope chain: window
}

当add函数被调用时,add函数执行上下文可表示为:

addContext = {
AO: {
sum: undefined //代码进入执行阶段时此处被赋值为5
func: undefined //代码进入执行阶段时此处被赋值为function (){console.log(sum);}
},
this: window,
scope chain: addContext.AO + globalContext.VO
}

add函数执行完毕后,js执行流回到全局上下文环境中,将add函数的返回值赋值给addFunc。

由于addFunc仍保存着func函数的引用,所以add函数执行上下文从执行上下文堆栈顶端弹出后并未被销毁而是保存在内存中。

当addFunc()执行时,func函数被调用,此时func函数执行上下文可表示为:

funcContext = {
this: window,
scope chain: addContext.AO + globalContext.VO
}

当要访问变量sum时,func的活动对象中未能找到,则会沿着作用域链查找,由于js遵循词法作用域,作用域在函数创建阶段就被确定,在add函数的活动对象中找到sum = 5;

介绍到这里你明白形成闭包的原因了吗?
Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

1. 保护变量的安全实现JS私有属性和私有方法

利用闭包可以读取函数内部的变量,变量在函数外部不能直接读取到,从而达到保护变量安全的作用。因为私有方法在函数内部都能被访问到,从而实现了私有属性和方法的共享。
常见的模块模式就是利用闭包的这种特性建立的

var Counter = (function() {
//私有属性
var privateCounter = 0;
//私有方法
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
})();
console.log(privateCounter); //privateCounter is not defined
console.log(Counter.value()); // 0
Counter.increment();
Counter.increment();
console.log(Counter.value()); // 2
Counter.decrement();
console.log(Counter.value()); // 1

在jQuery框架的私有方法和变量也是这么设计的

var $ = jQuery = function(){
return jQuery.fn.init();
}
jQuery.fn = jQuery.prototype = {
init:function(){
return this; //this指向jQuery.prototype
},
length: 1,
size: function(){
return this.length;
}
}
console.log($().size()); // 1

2. 将处理结果缓存

var mult = (function(){
var cache = {};
var calculate = function(){
var a = 1;
for(vari=0,l=arguments.length;i<l;i++){
a = a*arguments[i];
}
return a;
};
return function(){
var args = Array.prototype.join.call(arguments,',');
if(args in cache){
return cache[args];
}
return cache[args] = calculate.apply(null,arguments);
}
})();

这样我们在第二次调用的时候,就会从缓存中读取到该对象。

理解了闭包的原理我们发现闭包的这些用途都是利用了闭包保存了当前函数的活动对象的特点,这样闭包函数在作用域之外被调用时依然能够访问其创建时的作用域

闭包的缺点

  • 闭包将函数的活动对象维持在内存中,过度使用闭包会导致内存占用过多,所以在使用完后需要将保存在内存中的活动对象解除引用;
  • 闭包只能取得外部函数中任何变量的最后一个值,在使用循环且返回的函数中带有循环变量时会得到错误结果;
  • 当返回的函数为匿名函数时,注意匿名函数中的this指的是window对象。

这里仅仅是我对闭包的一些见解,若有错误的地方,还望大家提出,一起交流共同进步!
参考文献

如果您对本文有什么疑问,欢迎提出个人见解,若您觉得本文对你有用,不妨帮忙点个赞,或者在评论里给我一句赞美,小小成就都是今后继续为大家编写优质文章的动力, 欢迎您持续关注我的博客:)

作者:Jesse131

出处:http://www.cnblogs.com/jesse131/

关于作者:专注前端开发。如有问题或建议,请多多赐教!

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

转:对JavaScript中闭包的理解的更多相关文章

  1. 关于javascript中闭包的理解

    闭包就是能够读取其他函数内部变量的函数. 在javascript中,只有函数内部的子函数可以读取局部变量,因此,我理解闭包就是定义在一个函数内部的函数. 例子: var f1 = function() ...

  2. 对JavaScript中闭包的理解

    在前端开发中闭包是一个很重要的知识点,是面试中一定会被问到的内容.之前我对闭包的理解主要是"通过闭包可以在函数外部能访问到函数内部的变量",对闭包运用的也很少,甚至自己写过闭包自己 ...

  3. 第二话:javascript中闭包的理解

    闭包是什么? 通过闭包,子函数得以访问父函数的上下文环境,即使父函数已经结束执行. OK,我来简单叙述下,先上图. 都知道函数是javascript整个世界,对象是函数,方法是函数,并且js中实质性的 ...

  4. JavaScript中闭包的理解

    1.什么是闭包 我个人理解闭包就是函数中嵌套函数,但是嵌套的那个函数必须是返回值,才构成闭包: <!DOCTYPE html> <html> <head> < ...

  5. 在Javascript中闭包(Closure)

    在Javascript中闭包(Closure) 什么是闭包 “官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. ...

  6. javascript中 (function(){})();如何理解?

    javascript中 (function(){})();如何理解? javascript中: (function(){})()是匿名函数,主要利用函数内的变量作用域,避免产生全局变量,影响整体页面环 ...

  7. javascript中闭包最简单的简绍

    javascript中闭包是什么 JavaScript 变量可以是局部变量或全局变量.私有变量可以用到闭包.闭包就是将函数内部和函数外部连接起来的一座桥梁. 函数的闭包使用场景:比如我们想要一个函数来 ...

  8. 在JavaScript中闭包的作用和简单的用法

    在JavaScript中闭包的作用和简单的用法 一.闭包的简介 作用域链:在js中只有函数有作用域的概念,由于函数内能访问函数外部的数据,而函数外部不能访问函数内部的数据,由上述形成一种作用域访问的链 ...

  9. Javascript中闭包的个人理解

       Javascript的一个特殊点就在于它的闭包和回调特性,这两个特性让初学Javascript的我是云里雾里,至今仍在苦苦摸索与理解.在一番苦思之后,整理了一下资料,将自己的理解思路记录下来,以 ...

随机推荐

  1. 模板 - 数据结构 - 栈/Stack

    普通的栈大家都会写,STL的栈据说默认实现方式是deque,没关系反正deque跑得飞快. 这里收录的是一些奇怪的栈,当然双栈实现的队列收录在队列里面. 对顶栈 众所周知,栈可以维护一系列前缀和,包括 ...

  2. git图形化统计工具 - windows下gitstats的安装和使用

    gitstats 是一款git历史统计工具,可以生成定量的统计数据,并以html图表的形式展示.统计文件包括文件数量.代码量.提交量.作者信息.每天活跃度.每周活跃度.每月活跃度以及提交数排名等等,信 ...

  3. 消息队列Rabbit MQ 学习第一篇

    1 介绍  1.1RabbitMQ MQ全称为Message Queue,即消息队列, RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队 ...

  4. Ubuntu安装邮件服务器

    Ubuntu搭建邮件服务器 此文我们使用Postfix来搭建邮箱服务器,Postifx是一个SMTP服务器.SMTP服务器也被称为MTA(message transfer agent) 一.安装pos ...

  5. HugeGraph入门

    一.HugeGraph简介 最近在搞好友推荐方便的工作,选择了图数据的方法,使用并学习了HugeGraph,再次记录一下. HugeGraph是百度在2018年中旬开源的一款图数据库(Graph Da ...

  6. 【转】反编译微信小程序错误: $gwx is not defined和__vd_version_info__ is not defined 已解决

    修改wxappUnpacker文件中的 wuWxss.js function runVM(name, code) { // let wxAppCode = {}, handle = {cssFile: ...

  7. Java TreeMap使用

    场景: 随机生成50个10到50的数字.然后顺序输出每个数字出现的次数 实现原理: 使用TreeMap,默认带了顺序排序的功能 public static void main(String[] arg ...

  8. 关于如何重写Controller和Service技术攻关文档

    版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39784756/articl ...

  9. 一、postman简介

    一.场景 1.开发接口的时候需要快速的调用接口,以便调试 2.测试的时候需要非常方便的调用接口,通过不同的参数去测试接口的输出 3.这些接口调用是需要保存下来的反复运行的 4.在运行过程中如果有断言( ...

  10. pytorch 不使用转置卷积来实现上采样

    上采样(upsampling)一般包括2种方式: Resize,如双线性插值直接缩放,类似于图像缩放,概念可见最邻近插值算法和双线性插值算法——图像缩放 Deconvolution,也叫Transpo ...