let const var 比较说明
现在先来做两道练习题
for(var i=0;i<10;i++){
var a='a'
let b = 'b'
}
console.log(a)
console.log(b)
for(var i=0;i<3;i++){
setTimeout(function(){
console.log(i)
},1000)
}
function(){
console.log(a)
var a =1;
function a(){}
console.log(a)
}
理解js作用域
作用域我自己的理解是变量在某个的范围内可访问,那这个范围就是这个变量的作用域。
在ES5中,js只有两种形式的作用域:全局作用域和函数作用域。
- 全局作用域:变量在程序中任意地方都可以访问到
- 函数作用域:变量在函数内部可以访问到,在函数外部无法访问
for(var i=0;i<10;i++){
var a='a'
let b = 'b'
}
console.log(a) //'a'
console.log(b) //'b' is not defined
上述代码中,变量a为全局变量
function test(){
var a = 'a'
}
test()
console.log(a) // 函数外部无法直接访问函数内部变量,报错
上述代码中,变量a为局部变量,控制台打印报错信息a is not defined
function test(){
a = 'a'
}
test()
console.log(a) //'a'
函数内部未使用var关键字定义变量,此时a为全局变量
小结
- ES5中, js的作用域分为全局作用域和函数作用域
- 函数内部可以访问函数外部的全局变量,函数外部却无法直接访问函数内部的局部变量
- 未使用var关键字定义的变量是全局变量
现在我们知道函数内部可以访问函数外部的全局变量,函数外部却无法直接访问函数内部的局部变量。但有的时候我们需要读取函数内部的局部变量,该怎么办呢?
闭包
举个例子:
function f1(){
var a = 1;
function f2(){
return a;
}
return f2;
}
console.log(f1()()) //1
上述代码就创建了一个闭包,我们可以在f1函数外部访问到f1函数内部的值。
我们看到上述代码有两个特点:
- f1函数嵌套了f2函数
- f1函数返回了f2函数
闭包的用处:
1.很多js流行框架都是使用匿名自执行函数来避免变量污染
;(function(){
//todo
})()
2.缓存:闭包可以让变量的值始终保存在内存中,因此在使用时也要注意不要滥用闭包
function f1(){
var n=999;
nAdd = function(){n+=1}
function f2(){alert(n)}
return f2;
}
var result = f1(); //注意只有f1的返回值被外部引用,才不会被回收
result(); // 999
nAdd();
result(); //1000
3.封装
var person = function(){
var name = ‘default’
return {
getName:function(){ return name},
setName:function(newName){ name=newName}
}
}()
既然ES5中有定义变量的方法,那为什么ES6中又定义了let,const关键字呢?
js中的变量提升
var定义变量存在变量提升:只提升声明语句,不提升赋值语句
var foo = {n:1};
(function(foo){
console.log(foo.n);
foo.n = 3;
var foo = {n:2};
console.log(foo.n);
})(foo)
console.log(foo.n);
执行上述代码,我们可以看到控制台中按顺序依次打印:1,2,3。这是因为Javascript先编译后执行。编译阶段,先声明变量,所以引擎会将上面的代码理解为以下格式
var foo = {n:1};
(function(foo){
var foo;
console.log(foo.n)
foo.n = 3;
foo = {n:2};
console.log(foo.n)
})(foo);
console.log(foo.n)
说明:
- 函数内部定义变量foo时,因为当前作用域中已经存在名为foo的变量,所以编译器忽略当前声明,继续进行编译,因此第一次打印的内容为外部变量foo的属性n值:1
- foo.n=3 改变的是外部变量foo,foo={n:2}将foo指向了内部变量,并重新赋值为{n:2},所以第二次打印的内容为内部重新赋值的变量foo的属性n值:2
- 第三次打印内容是外部变量foo.n,因为函数内容已经更改了外部变量foo,所以打印结果为:3
js中先提升函数,后提升变量。
思考以下代码:
(function(){
console.log(a)
var a =1;
function a(){}
console.log(a)
})()
执行上述代码,我们可以看到控制台中按顺序依次打印:a(){},1。按照刚才的理解,js引擎将上面的代码会理解为下面的格式
(function(){
var a;
console.log(a)
a = 1;
function a(){}
console.log(a)
})()
那打印的结果应该为 undefined , f(){},这是因为我们忽略了一点,js先提升函数,后提升变量。所以正确的格式为
(function(){
function a(){}
var a;
console.log(a)
a = 1;
console.log(a)
})()
说明:
1.定义变量a时,因为已经存在命名为a的函数,所以第一次打印结果为a(){}
2.a=1,将变量a重新赋值,所以第二次打印结果为1
小结
- ES5中,使用var定义变量,变量的作用域有两种:全局作用域、函数作用域
- var定义变量存在变量提升,此外,先提升函数,后提升变量
但是开发过程中,变量提升往往会对开发造成困扰,幸好ES6中引入了let语法。
let
块级作用域
我们刚才提到,ES5中,js只用两种作用域:全局作用域与函数作用域。在ES6中,let关键字会隐式地创建一个块级作用域(通常是{}内部),变量只能在这个作用域中被访问。例如题目一中
for(var i=0;i<10;i++){
var a='a'
let b = ‘b'
}
console.log(a)
console.log(b)
我们在循环的内部,使用let创建了变量b,在循环外部访问时报错,b is not defined.就是这个原因。
块级作用域的引入大大改善了代码中由于全局变量而引发的错误,比如文章开头提出的第二题:
for(var i=0;i<3;i++){
setTimeout(function(){
console.log(i)
},1000)
}
上述代码由于变量i是用var声明的,所以全局范围有效 ,当循环体执行完时,i=2,所以定时器中console.log(i)中的i是指向全局变量i的,所以打印结果为2,2,2
如果我们将代码改为
for(let i=0;i<3;i++){
setTimeout(function(){
console.log(i)
},1000)
}
上述代码中,变量i使用let定义,所以只在本轮for循环中有效,所以打印结果为0,1,2。
let不存在变量提升,其所声明的变量一定要在声明语句之后使用。
例如:
console.log(bar);
let bar = 2;
打印结果报错:bar is not defined
此外,let 声明的变量不能重复声明,例如
let foo = {n:1};
(function(foo){
console.log(foo.n);
foo.n = 3;
let foo = {n:2};
console.log(foo.n);
})(foo)
console.log(foo.n);
函数内部定义变量foo时,因为当前作用域中已经存在命名为foo的变量,所以报错:’foo’ has already been declared.
const
ES6中新增了let关键字的同时,也新增了const关键字。
let与const有很多共同点:
- 都支持块级作用域
- 都不支持变量提升
- 都不支持重复声明
此外,我们知道var声明全局变量时,变量是挂在window上的。而let,const声明变量,却不是。这样子便避免了window对象的变量污染问题。
当然,const与let也有区别。const与let的区别在于:
- let声明变量时无需赋值,const声明变量时必须赋值
- let声明变量,变量可重新赋值,const声明变量,完成初始化后,值不得更改 (基本类型)
刚刚提到const声明变量后,如果值类型是基本类型,则不得更改,如果是引用类型呢?
如图所示,可修改。如果我用const定义变量,值为对象,但是想让对象的属性无法修改应该怎么做呢?
对象属性的保护方法
Object.defineProperty
Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
更多关于Object.defineProperty方法的信息,推荐阅读 MDN:Object.defineProperty
function setConst(obj) {
Object.keys(obj).forEach(function (t,i) {
Object.defineProperty(obj,t,{
value:obj[t],
writable:false, // 是否可重新赋值
enumerable:false, //是否可枚举
configurable:false // 是否可删除,其他设置属性,是否可修改
})
})
}
如图所示,虽然实现了需求,对象属性无法修改,无法删除。可是控制台无提示,不是很人性化。幸好,ES6中提供了Proxy方法
Proxy对象代理
function setConst(obj) {
return new Proxy(obj,{
set:function (obj,prop,value) {
throw new TypeError('Assignment to constant variable')
},
deleteProperty(target, key) {
throw Error(`Error! ${key} cannot be deleted.`);
}
})
}
let const var 比较说明的更多相关文章
- js中const,var,let区别(转载)
js中const,var,let区别 来源:https://www.cnblogs.com/zzsdream/p/6372729.html 今天第一次遇到const定义的变量,查阅了相关资料整理了这篇 ...
- const,var,let区别(转载)
1.const定义的变量不可以修改,而且必须初始化. const b = 2;//正确 // const b;//错误,必须初始化 console.log('函数外const定义b:' + b);// ...
- const,var,let 区别
js中const,var,let区别 1.const定义的变量不可以修改,而且必须初始化. 声明的是常量 1 const b = 2;//正确 2 // const b;//错误,必须初始化 3 co ...
- js中const,var,let区别
今天第一次遇到const定义的变量,查阅了相关资料整理了这篇文章.主要内容是:js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始化. co ...
- let const var的区别与作用
今天第一次遇到const定义的变量,查阅了相关资料整理了这篇文章.主要内容是:js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始化. 1 ...
- javascript---lat const var 的区别
首先,ECMAScript和JavaScript关系: ECMAScript是一个国际通过的标准化脚本语言.JavaScript由ECMAScript和DOM.BOM三者组成.可以简单理解为 ...
- let,const,var
1.const定义的变量不可以修改,而且必须初始化. 1 const b = 2;//正确 2 // const b;//错误,必须初始化 3 console.log('函数外const定义b:' + ...
- ES6 js中const,var,let区别 今天第一次遇到const定义的变量
今天第一次遇到const定义的变量,查阅了相关资料整理了这篇文章.主要内容是:js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始化. 1 ...
- js-变量定义关键字const,var,let
1.var定义的变量可以修改,如果不初始化会输出undefined,不会报错. js中最常用的关键字:基本大多数据学js时都只看到使用过var.从没相关还有其他定义 var a = 1; // var ...
- const,var,let笔记
js中定义变量的方式有三种const.var.let const 作用域:全局作用域或函数作用域 定义的变量不可修改,且必须初始化 eg: const a= 1; a= 2; console.log( ...
随机推荐
- sublime-text3 安装 emmet 插件
下载sublime,http://www.sublimetext.com/ 安装package control :https://packagecontrol.io/ins... 这个地址需要翻墙,访 ...
- aarch64架构下安装tensorflow详细过程
本人使用的是: EAIDK-610开发板,Redhat的Linux系统,arm64架构,python3.6环境. 重要的: 一定要下载符合自己环境架构相同的tensorflow安装包. 三种架构: x ...
- OracleDBConsoleorcl 服务无法启动:Agent process exited abnormally during initialization.
OracleDBConsoleorcl 服务无法启动 在事件查看器里看到 Agent process exited abnormally during initialization.的记录.知道是因为 ...
- SpringBoot 开发的那些小趣事儿
经过这次在公司实习中获取到的经历,我发现确实有时候书本上的知识发挥的作用微乎其微,好像是被问题打了太极拳一样,你明明想去攻克这个地方,他却给你报了其他地方的错误. 平常的一些小项目根本就不能匹配到企业 ...
- 6_6 小球下落(UVa679)<完全二叉树编号>
有K个球从一完整二叉树(fully binary tree,FBT)的树根(root)一个一个往下掉.当这个球遇到非终端节点时,可能往左子树跑,也可能往右子树跑,如此直到这颗球到达终端节点(也就是树叶 ...
- 6_4 破损的键盘(UVa11988)<链表>
你用了一个有点坏掉的键盘打字,该键盘会自动按下”Home”键与“End”键,直到打完整个内容以前,你都没有发现到这个问题.本题给定键盘输出的字串(包含Home与End),请你输出该字串在屏幕显示的内容 ...
- python文件打开模式&time&python第三方库
r:以只读方式打开文件.文件的指针将会放在文件的开头.这是默认模式. w:打开一个文件只用于写入.如果该文件已存在则将其覆盖.如果该文件不存在,创建新文件. a:打开一个文件用于追加.如果该文件已存在 ...
- 记录STM32调试
问题:加入红外初始化后,程序卡在红外初始化处 解决思路: 1.确认时钟是不是好的 2.把定时器分解调试(输入捕获.溢出分开一步一步弄) 已解决:定时器的溢出中断 注意:STM32Cube配置好后,需要 ...
- java8中的常用日期操作
java8有很多时间上的新api,在操作时间的时候很好用,这儿算是个备忘录吧,(补充中...) 定位某个时间:of方法 LocalDateTime dateTime = LocalDateTime.o ...
- 前端框架vue学习笔记:环境搭建
兼容性 不兼容IE8以下 Vue Devtools 能够更好的对界面进行审查和调试 环境搭建 1.nodejs(新版本的集成了npm)[npm是node包管理 node package manager ...