主要内容:
变量:变量本来是先声明,再赋值。但如果一次性以var a = 2这种方式,尤其是放在调用后面的话。ES6会默认为自动声明了,但无法调用后面的赋值,以undefined方式显示出来。
函数提升:函数的调用可以在函数声明定义前,也可以在后。
- 函数表达式不支持提升,调用必须在声明后面
- let变量导致的暂时性死亡(TDZ)
- 块作用域与变量外泄,及解决方案-立即调用函数IEEF
- 三种作用域(全局、函数、块)
- 常量大写全靠约定
1. ES6简介
ECMAScript 6.0(简称 ES6), 是 JavaScript 下一代标准
1-1. ECMAScript 与 JavaScript 的关系
- ECMAScript 是浏览器脚本语言的标准
- JavaScript 是 ECMAScript 标准的实现
- 考虑到 JavaScript(简称 JS)是前端默认脚本语言,所以二者几乎同义
1-2. ES6 与 ECMAScript 2015 的关系
- ECMAScript 2015 (简称 ES2015)
- 2011 年, ECMAScript 5.1 发布后,即开始制定 JS 下一代标准,即 ES6
- 2015 年 6 月,ES6 第一个版本发布,使用年份标记,正式名称为 ECMA2015
- 所以, ES6 是泛指,包括 ES2015/ES2016/ES2017/ES2018…
1-3. Babel 转码器
- 较古老的浏览器不能直接运行 ES6 代码,需要使用转码器将 ES6 语法转为 ES5
- Babel 是使用广泛的转码器,安装指令:
npm install --save-dev @babel/core
- Babel 可以实现命令转码,浏览器转码, 在线转码等方式供选择
2. 变量提升
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>变量提升</title>
</head>
<body>
<script>
// 变量提升: 变量声明的提升
// 在变量声明之前访问该变量,理论上应该报一个引用错误
// 1. 变量声明被引擎自动提前到当前(全局)作用域的顶部
var username;
console.log(username);
username = "Peter Zhu";
console.log(username);
// 1. 变量声明
// var username;
// console.log(username);
// 2. 变量赋值/初始化
// username = "Peter Zhu";
// console.log(username);
// 最常用是将变量的声明与初始化/赋值写到一起
// 声明 + 初始化赋值
var username = "Admin";
function getValue() {
// 2. 变量声明被引擎自动提前到当前(函数)作用域的顶部
// 声明:被提前到当前作用域顶部
var username;
// 如果声明变量时未初始化,那么会自动用undefined赋值
// var username = undefined;
// 函数内私有变量的赋值
console.log(username);
username = "Tony老师";
console.log(username);
}
getValue();
// 变量提升让变量的生命周期(从声明到回收)变得不可控
// 使用es6: 块级作用域 , 同义词: 词法作用域, 静态作用域
</script>
</body>
</html>
3. 函数提升
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>函数提升</title>
</head>
<body>
<script>
// 函数声明: 会自动提前到当前作用域的顶部
function demo1() {
console.log("function demo1()");
}
// 函数调用
demo1();
// 函数允许重复声明,后面声明会覆盖到前面的
function demo1() {
console.log("重复声明了demo()");
}
// js中的函数不能重载:函数名称相同,通过不同的参数具有不同的行为
// 变量也允许重复声明
// var email = "a@php.cn";
// console.log(email);
// var email = "b@php.cn";
// console.log(email);
// 作用域: 全局作用域, 函数作用域
// 在函数作用域中,仍然支持函数声明提前
function outer() {
// 子函数调用
inner();
// 子函数声明: 也会声明提前
function inner() {
console.log("我是outer()中的子函数inner()");
}
}
// 函数表示式(变量函数的声明不会被提升)
// console.log(myemail());
// 全局函数
var myemail = function () {
return "function email()";
};
// 全局变量
var myemail = "admin888@php.cn";
console.log(myemail);
outer();
</script>
</body>
</html>
4. 函数表达式
- 函数表达式不能进行提升,也就是说必须调用在声明的后面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>函数表达式</title>
</head>
<body>
<script>
// 1. 全局不存在函数表达式声明提升
// demo1(); // 调用失败
var demo1 = function () {
console.log("Hello demo1()");
};
demo1(); // 调用成功
// 2. 函数声明与函数表达式: 同名共存,哪个优先,声明优先(声明提升)
demo2();
// 函数声明
function demo2() {
console.log("hello 声明demo2()");
}
// 函数表达式
var demo2 = function () {
console.log("hello 表达式demo()");
};
// demo2();
// 3. 函数内部
var demo3 = function () {
// demo4();
// 函数内部也不存在函数表达式声明提升
var demo4 = function () {
console.log("demo3内部的demo4()");
};
demo4();
};
demo3();
</script>
</body>
</html>
5. 暂时性死亡(TDZ)
- let 支持块作用域, if(){…}
- let 不支持声明提升
- 所有在let声明之前会产生一暂时性的使用上的死区,直接到遇到let声明
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>暂时性死区(TDZ)</title>
</head>
<body>
<script>
var num;
var num = 10;
if (true) {
// num = 10,为什么?因为当前的代码块if()没有num声明,所以就引用了外部
console.log("num =" + num);
}
if (true) {
// let 支持块作用域,if(){...}
// let 不支持声明提升
// 所有在let声明之前会产生一暂时性的使用上的死区,直接到遇到let声明
console.log("num = " + num);
// 不用var , 用let
let num = 88;
}
</script>
</body>
</html>
6. 块作用域与变量泄露
- 解决方案: 立即调用函数IFFE
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>无块作用域会产生什么样的问题</title>
</head>
<body>
<script>
// 块作用域
// 1. 变量泄露
function show1(count) {
for (var i = 0; i < count; i++) {
console.log(i);
}
// for()代码块,js不支持块作用域,所以块中的变量 i 泄露到外部
console.log("i = ", i);
}
// show1()等价语法
function show2(count) {
// 变量提升
var i;
for (i = 0; i < count; i++) {
console.log(i);
}
// for()代码块,js不支持块作用域,所以块中的变量 i 泄露到外部
var i = 20;
console.log("i = ", i);
}
show2(5);
// 解决方案: 立即调用函数IFFE
// (function (a, b) {
// console.log(a + b);
// })(10, 20);
// (函数声明)(函数调用参数);
function show3(count) {
// 使用立即执行函数来解决块中变量泄露的问题
(function () {
for (var i = 0; i < count; i++) {
console.log(i);
}
})(5);
// 使用IIFE创建了一个临时的函数作用域,所以外部已经访问不到这个i
console.log("i = ", i);
}
show3();
</script>
</body>
</html>
7. 作用域
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>作用域</title>
</head>
<body>
<script>
// 作用域: 1.全局 2. 函数 3. 块
// 1. 全局作用域:函数和代码块之外的代码
// 全局变量
var username = "peter zhu";
console.log(username);
// 2. 函数作用域
function demo1(username) {
return username;
}
console.log(demo1("admin"));
// 3. 块作用域
if (true) {
var email = "admin@php.cn";
// 块内部可以访问
console.log(email);
}
// 块外部也可以访问
console.log(email);
// 使用立即执行函数创建一个块作用域
// IIFE来模拟一个块作用域
(function () {
if (true) {
var color = "red";
console.log("innner ", color);
}
})();
// 块外部不能再访问
console.log(color);
</script>
</body>
</html>
8. 几点注意
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>var声明的问题</title>
</head>
<body>
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
</ul>
<script>
// 1. 允许重复声明
var price = 100;
var price = 200;
console.log(price);
// 2. 存在变量提升
var color;
console.log(color);
// var color = "blue";
color = "blue";
console.log(color);
// 变量声明也会发生在函数作用域内
// 3. 不支持块作用域
if (true) {
var email = "peter@qq.com";
}
console.log(email);
var lis = document.querySelectorAll("li");
for (var i = 0; i < lis.length; i++) {
// 通过立即执行函数将块包裹起来,来模拟一个块作用域
(function (i) {
lis[i].addEventListener("click", function () {
console.log("点击了第", i + 1, " 个");
});
})(i);
}
// 4. 常量全靠约定
var APP_NAME = "客户管理系统";
// 相信大家都是好人,都是君子,看到全大写的变量名, 就主动不去更新它
var APP_NAME = "学生管理系统";
console.log(APP_NAME);
</script>
</body>
</html>