声明变量关键字汇总
- 在 JavaScript 中,一共存在 3 种声明变量的方式:var、let、const;
- 之所以有 3 种方式,这是由于历史原因造成的,最初声明变量的关键字就是 var ,但是为了解决作用域的问题,所以后面新增了 let 和 const 的方式;
var 关键字
变量提升
在栈内存(作用域)形成,JS 代码自上而下执行之前,浏览器会把所有带 「VAR」 和 「FUNCTION」 关键字进行提前声明或者定义;
- 声明(declare):
var a
(默认值是 undefined)、function sum
(值是 16 进制堆内存地址);- 定义(defined):
a = 12
(定义其实就是赋值);变量提升只发生在当前作用域
- 加载页面的时候只对全局作用域下的变量进行变量提升,因为此时的函数中存储的都是 代码字符串 而已;
- 在全局作用域下、私有作用域下声明的函数或变量(带 VAR 或 FUNCTION 的才是声明);
- 浏览器很懒,做过的事情不会重复执行第二遍 (当代码遇到创建函数代码,直接跳过,因为在变量提升阶段已经 声明并定义 了);
- ES3/ES5 规范: 只有全局作用域和函数执行的私有作用域(栈内存),其他花括号不会形成栈内存;
带不带 VAR 的区别
全局作用域
带 var
(本质是变量):在全局作用域下声明一个变量也相当于给 window 设置了一个属性,属性值为变量值,在变量提升阶段,就已经把变量设置成 window 的属性了(let 声明的变量不会给 window 设置该属性),全局变量修改 window 属性也修改,反之同样修改;不带 var
:本质是 window 下的属性,a = 12
相当于window.a = 12
;私有作用域
带 var
(本质是变量):私有作用域带 var ,变量提升阶段,都声明为私有变量,和外界没有任何的关系;不带 var
:会向上级作用域查找,查看是否是上级的变量,若不是则一直查找到 window 为止,如果 window 也没有,相当于给 window 设置了一个属性(作用域链);
变量提升的几种情况
-
函数表达式
:只对等号左边进行变量提升// 普通函数由于关键字是 "function",所以在变量提升时,进行了声明和定义(赋值) // 函数表达式由于关键字是 "var",所以在变量提升时,只进行了声明,默认值为 undefined sum();// 2 fn();// fn is not a function //匿名函数(函数表达式) var fn = function () { console.log(1); } //普通函数 function sum() { console.log(2); }
-
条件判断下的变量提升
- 在当前作用域下,不管条件是否成立都要进行变量提升;
- 带 var 的还只是声明;
- 带 function 的新版本浏览器中 (谷歌 45 之后),在条件判断中的函数,不管条件是否成立,都只先声明,类似 var;
//变量提升: fn (此处不考虑老版本浏览器) console.log(fn);//输出 undefined if (1 === 1) { // 输出 fn 函数本身 // 当条件成立,进入到判断体中(es6中它是一个块级作用域),第一件事并不是执行代码 // 而是类似于变量提升,先把 fn 声明和定义了,也就是判断体中代码执行之前,fn 就已经赋值了 console.log(fn); function fn() { console.log('ok'); } } //上面判断成立,fn 定义成功,则输出 fn 函数本身,判断不成立则输出 undefined console.log(fn);
-
变量提升下的重名问题
- 带 var 和 function 关键字声明相同的名字,这种也是重名了;
- 关于重名的处理: 如果名字重复了,不会重新声明,但是会重名定义(赋值),不管是变量提升阶段还是代码执行阶段皆是如此;
fn();//输出 4 function fn() { console.log(1); } fn();//输出 4 function fn() { console.log(2); } fn();//输出 4 var fn = 100;//fn在此处开始定义(赋值),fn=100; fn();//100()报错: TypeError: fn is not a function function fn() { console.log(3); } fn(); function fn() { console.log(4); } fn();
let 关键字
const 关键字
特点总结
var 关键字
没有块级作用域的概念
有全局作用域、函数作用域的概念
不初始化值默认为 undefined
存在变量提升
全局作用域用 var 声明的变量会挂载到 window 对象下
同一作用域中允许重复声明
let 关键字
有块级作用域的概念
不存在变量提升
暂时性死区
同一块作用域中不允许重复声明
const 关键字
与 let 特性一样,仅有 2 个差别
区别 1:必须立即初始化,不能留到以后赋值
区别 2:常量的值不能改变
面试题
第 1 题:输出 0-9
// 第一种
for (var i = 0; i < 10; i++) {
// 每一轮都生成一个自执行函数,形成全新的执行上下文 EC
// 并且把每一轮循环的 i 当做实参传给私有 上下文中的私有变量 i(形参变量)
// 定时器触发执行用到的 i 都是私有 EC 中的保留下来的 i
// 充分利用闭包的保存机制(闭包有保护和保存 2 个机制)来完成的,这样处理不太好,
//循环 多次就会产生多个不销毁的 EC
~function (i) {
setTimeout(function () {
console.log(i);
}, 10);
}(i);
}
// 第二种
// let 存在块级作用域,var 没有
for (let i = 0; i < 10; i++) {
// 形成了 10 个块级作用域,每个块级作用域中都有一个私有变量 i
setTimeout(function () {
console.log(i);
}, 10);
}
let const var 的区别?什么是块级作用域?如何用?
let const var 的区别?
- var 定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问,有变量提升;
- let 定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问,无变量提升,不可以重复声明;
- const 用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改,无变量提升,不可以重复声明;
什么是块级作用域?
- 最初在 JS 中作用域有:全局作用域、函数作用域,没有块作用域的概念;
- ES6 中新增了块级作用域,块作用域由 { } 包括,if 语句和 for 语句里面的 { } 也属于块作用域;
如何用?
- 在以前没有块作用域的时候,在 if 或者 for 循环中声明的变量会泄露成全局变量;其次就是 { } 中的内层变量可能会覆盖外层变量;
- 块级作用域的出现解决了这些问题;
第 1️⃣ 座大山:作用域和作用域链(闭包的形成)
上一篇