# 有JavaScript以来声明变量的四种方式
a=1
var a=1
上面两种是ES3的写法,下面两种是ES6
- let a=1
- const a=1;
# 但a=1会含义不明,在顶层作用域作为全局变量,但是在函数中又是局部变量。
var a;
function fn(){
var a;
function fn2(){
a=1;
//此时的a就是局部变量的a,在函数fn的第一句中声明
//去掉第二个var a,那么a就是全局变量
//两个var a 都没有的情况下,a=1即会隐式声明为全局变量a
}
}
2
3
4
5
6
7
8
9
10
# 一种不建议的写法
function fn(){
if(true){
console.log(a)
}else{
var a;
console.log(2)
}
}
fn()
//由于var的变量提升,fn函数执行后并不会报错。
//因此建议在函数fn开始时声明需要用到的变量。
2
3
4
5
6
7
8
9
10
11
# let
(function (){
var a=1;
window.sam=function(){
console.log(a)
}
}())
//目的,只暴露sam一个全局变量
//如果不使用函数包裹对a的声明,那么就会不小心暴露window.a全局变量
//而如果是使用具名函数的话,仍然会暴露window.fn全局变量
//因此在没有let声明的时候,需要麻烦的立即理解执行函数来形成无暴露的局部变量
let
//let存在的意义就是为了解决这个问题,为了方便的声明和使用全局变量
//1.let的作用域只看花括号,只在最近的两个花括号之间
//2.在用let声明变量之前使用变量会报错;这也是临时性死区的概念来源
//3.let不能重复赋值,重复赋值也会报错
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# const
//1.const的作用域也是局部,只在最近的两个花括号之间
//2.在用const声明的变量之前使用该变量会报错
//3.const不能重复赋值,重复赋值会报错,且const只能赋值一次
2
3
# 面试题
# 开胃小菜
var a=1
function fn(){
console.log(a)//1
}
//在下方有一行代码隐藏的代码,问在这个情况下log的值是否是确定的?
//隐藏的代码在这里
fn()
//不能确定,因为a是全局声明的变量,且可以重复赋值为其他值
2
3
4
5
6
7
8
# 一个经典的题目
var i
for(i=0;i<6;i++){
function fn(){
//do something to i,
//like log..
console.log(i)
}
fn()
btn.onclick=fn
}
//请问现在btn按钮的一个click事件会打印出什么?
//毫无疑问是6,因为i是全局声明的变量,在for循环执行完毕后,i的已经变为6了
//js并不会在for循环执行时替你保管每一个i的值到fn中
//另外,i为6的原因是
2
3
4
5
6
7
8
9
10
11
12
13
14
当然,我们见的更多的应该是长下面的样子
var liTags=document.querySelectorAll('li')
var i
for(i=0;i<liTags.length;i++){
liTags[i].onclick=function(){
console.log(i);
}
}
//每个li标签的点击事件都是输出当前循环的i
//但其实本质上和上面没差,由于var的存在,这都是刻舟求剑的行为
//因为点击事件必然后于for循环,那再点击li的时候,因为i是全局变量,早已变成length+1
//解决的两种方法
//同上使用立即执行函数得到局部变量
for(i=0;i<liTags.length;i++){
(function(){
var j=argumengs[0]
liTags[j].onclick=function(){
console.log(j)
}
})(i)
}
//利用执行函数创建局部变量j,这里也还涉及到js的另一个概念-闭包
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
a=1 var a=1 //上面两种是ES3,下面两种是ES 6
let a=1; const a=1;
a=1 含义不明,在顶层作用域是作为全局变量, 但是在函数中又是局部变量 var a function fn(){} var a ; function fn2(){} a=1 ;//此时的a就是局部变量的a //去掉第二个var a才是全局变量 //两个var 都没有的情况下,a=1即会隐式声明全局变量a}}
function fn() {
if (true) {
console.log(a)
} else {
var a
console.log(2)
}
}
2
3
4
5
6
7
8
# // 既然会这样,那建议var 放在函数最开始的地方
(function (){
var a = 1
window.frank = function() {
console.log(a)
}
}())
// 目的,只暴露frank一个全局变量
// var a = 1 就会不小心暴露了window.a全局变量
2
3
4
5
6
7
8
为什么立即执行函数不加括号会报错 let 的存在就是为了解决这个问题 为了方便的使用局部变量 let作用域只看花括号,只在最近的两个花括号之间 在let a之前使用a,报错 let不能重复赋值,重复也报错
{ //这就是临时性死区console.log(a); let a=1; }
let作用域只看花括号,只在最近的两个花括号之间 在let a之前使用a,报错 let不能重复赋值,重复也报错 对于以上,const也是 const 只能赋值一次而且必须在声明的时候立马赋值
既然会这样,那建议var 放在函数最开始的地方
阮一峰
Es6新增了let命令,用来声明变量,用法类似于var,但是所声明的变量,只在let命令所在的代码快内有效 Var 是全局变量
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a6;//10
来自 http://es6.ruanyifeng.com/#docs/let (opens new window) 变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10
来自 http://es6.ruanyifeng.com/#docs/let (opens new window)
如图示,a[6]传的6只是数组中的位置,而数组中的所有成员都是函数,函数跟数组的位置是分离的,函数执行完之后i=10
变量提升,意为,函数及变量的声明总是会被解释器提升到方法体的最顶部, 函数提升的优先级大于变量提升的优先级 需要注意的是,初始化并不会被提升。 JavaScrip严格模式中不允许使用未声明的变量 function f(a){ console.log(a);//? var a = 2; console.log(a);//? function a(){}; console.log(a);//? } f(1);
function f(a){ var a = function(){}; console.log(a);// function var a = 2; console.log(a);//2 console.log(a);//2 } f(1);
来自 https://zhuanlan.zhihu.com/p/23873265 (opens new window)
暂时性死区 只要块级作用域内存在let 命令,它所声明的变量就"绑定"这个区域,不再受到外部的影响。 ES6 明确规定,如果区块中存在let命令和const命令,这个区块对这些命令声明的变量,从 一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。错误是reference error。 Let x=x; //会报错,x is not defined 而undefined是一个数据类型
暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
let不允许在相同作用域内,重复声明同一个变量,因此不能在函数内部重新声明参数。
块级作用域 ES5只有全局作用域和函数作用域,没有块级作用域。
Var s='hello; for (var i = 0; i < s.length; i++) { console.log(s[i]);//可以对字符串类型这样操作 }
来自 http://es6.ruanyifeng.com/#docs/let (opens new window)
ES6的块级作用域 let实际上为JavaScript新增了块级作用域, 所以内层作用域就可以定义外层作用域的同名变量。
块级作用域与函数声明 ES6引入了块级作用域,明确允许在块级作用域之中声明函数。 ES6规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。
ES6附录b又规定 允许在块级作用域内声明函数 函数声明类似于var,即会提升到全局作用域或函数作用域的头部 同时,函数声明还会提升到所在的块级作用域的头部
ES6的块级作用域允许声明函数的规则,只在使用大括号的的情况下成立,如果没有使用大括号,就会报错
const命令 Const 声明一个只读的常量。一旦声明,常量的值就不能改变,这也意味着,const一旦声明 变量,就必须立即初始化,不能留到以后赋值。
Const 的作用域与let命令相同:只在声明所在的块级作用域内有效。
const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
也不可重复声明。
const命令的本质
const实际上保证的是变量指向的那个内存地址不得改动,而不是变量的值不得改动。
对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量;
但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的知识一个指针,const只能保证这个指针是固定的,并不能控制其所指向的数据结构是不是可变的。
ES6声明变量的六种方法 ES5只有两种声明变量的方法,var命令和function命令,ES6除了添加let和const命令 还有两种两种,import和class命令
顶层对象的属性 顶层对象,在浏览器环境指的是window对象,在node指的是global对象。 Es5中,顶层对象的属性与全局变量是等价的。 ES6的新规定,var命令和function命令声明的全局变量,依旧是顶层对象的属性 let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性 即从ES6开始,全局对象将逐步与顶层对象的属性脱钩
global对象//没看怎么明白
-浏览器里面,顶层对象是window,但是node和web worker没有window
-浏览器和web worker里面,self也指向顶层对象,但是node没有self
-node里面,顶层对象是global,但其他环节都不支持
同一段代码为了能够在各种环境,都能取到顶层对象,现在一般是使用this
变量,但是有其局限性。
var 只会提前声明 function即声明又定义 let 重复声明会报错
let 没有变量提升 不可一次重复声明 不会给window增加属性
const 一旦声明必须赋值 定义的是一个常量,不可以重新赋值
在块级作用域中 var和function声明的变量依然是全局的 在块级作用域下 let 和const 声明的变量是私有的
函数的变量提升是提升整体吗 是的 变量的声明被提前到作用域顶部,赋值保留在原地 函数声明会整个被提前 但是只有函数声明式会被提前
{} 一个{}就是一个块级作用域 在块级作用域下 var 和function声明的变量依然是全局的 在块级作用域下 let 和const 声明的变量是私有的 {}如果想表示一个对象不可以放在行首
← JS | 新版字符串 属性修饰符 →