ECMAScript 6+
介绍:
ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。也就是说,ES6就是ES2015。
ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言。这种语言在万维网上应用广泛,它往往被称为JavaScript或JScript,所以它可以理解为是JavaScript的一个标准,但实际上后两者是ECMA-262标准的实现和扩展。
ES6+提供了许多新的语法和编程特性以提高ECMAScript的开发效率并优化ECMAScript的开发体验。ECMAScript的发展速度在不断加快,影响范围越来越大,除了Web前端开发以外,借助着Node.js的力量在服务器、桌面端甚至硬件设备等领域中也发光发热着。
let
概念: ES6新增声明变量的指令 let,它的用法类似与var,他是为了解决var在作用域方面出现的异常情况而新增使用词法作用域或块级作用域的变量
语法:
let name = '小明'
let 与 var 的区别
块级作用域变量获取,在循环中块级作用域会针对每一次迭代创建一个新的作用域
例:
// var
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
},1000)
}
// 输出结果 5 5 5 5 5
var 的声明等价于,在循环的外部创建一个变量,循环内部计时器中的调用都是这这个全局变量
因为 js 先同步执行再异步执行,当setTimeout 被执行时 for 循环已经结束了
这时每个setTimeout引用都是循环结束后的全局变量i,所有打印都是5
// 上面的代码等价于
var i = 0
for ( i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
},1000)
}
// let
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
},1000)
}
// 输出结果 0 1 2 3 4
let 只在自身的作用域内有效
上面的for循环 其实等价于每次循环都在循环内创建了一个新的let i(i只属于代码块的内部)
内部setTimeout引入其对应作用域的i值 输出的值就是 0 1 2 3 4
for(var j = 0;j < 5; j++) {
let i = j
setTimeout(function() {
console.log(i)
},1000)
}
块级作用域
概念:拥有块级作用域的变量只能在当前当前代码块中使用。不能在外部访问。
- 块级作用域还有一个特点他们不能在声明之前读写,直到声明let变量的代码之前的区域都被称为暂时性死区。
console.log(age) // 正确, var 允许变量提前
var age = "小明"
console.log(name) // 错误, let 不允许变量提前
let name = "小明"
- 重复声明,块级作用域变量不允许在同一个作用域下被重复声明
let name = "小明"
let name = "小华" // 错误,同一个作用域下let变量不可以重复声明
var name = "小刚" // 错误,同一个作用域下let变量也不可以使用var重复声明
const
概念: const是声明变量的另一种方式,它与let相似。但是const声明的变量被赋值后不能再改变。(我们可以理解为他是一个常量),并且const声明的变量必须在声明时赋值。
const name = "小明"
name = "小华" // 错误,const只允许赋值一次
console.log(name)
const age; // 错误 const必须在初始化时赋值
注意:
- 基本数据类型存放在栈中,引用数据类型存放在堆中
- 栈中的数据大小已知
- 栈中的数据由系统自动开辟自动释放
- 栈的赋值都是深拷贝(创建了新的内存空间)
- 堆中的数据大小未知
- 堆中的数据由开发人员手动开辟手动释放
- 堆的赋值都是浅拷贝(引用同一段内存地址)
"use strict";
const person = {
name: 18,
age: 19
}
person.name = 1 // 正确,修改引用数据类型属性不会报错
console.log(person)
const s = [5, 6, 7];
s = [1, 2, 3]; // 试图给 const 变量赋值,报错
s[2] = 45; // 与用 var 或 let 声明的数组一样,这个操作也会成功
console.log(s); // 返回 [5, 6, 45]
注意:因为引用数据类型声明的const 变量只是一段地址值,内部属性发生变化不会影响地址。所以即使是const声明的对象、数组依然可以修改他们的属性方法。本质上来说const并不能保证其值是不能改变,只能保证变量指向的那个内存地址不能改动。
备注:const声明并不会真的保护你的数据不被改变。为了确保数据不被改变,JavaScript 提供了一个函数Object.freeze来防止数据改变。
当一个对象被冻结的时候,你不能再对它的属性再进行增、删、改的操作。任何试图改变对象的操作都会被阻止,却不会报错。
let obj = {
name:"FreeCodeCamp",
review:"Awesome"
};
Object.freeze(obj);
obj.review = "bad"; // obj 对象被冻结了,这个操作会被忽略
obj.newProp = "Test"; // 也会被忽略,不允许数据改变
console.log(obj);
// { name: "FreeCodeCamp", review:"Awesome"}
顶层对象属性
概念:全局变量一般与window对象的属性(顶层对象属性)挂钩。ES6中使用let、const可以声明作用在全局的变量。但是这些变量不是顶层对象的属性
const person = {
name: 18,
age: 19
}
console.log(window.person) // undefinded
var a = 6
console.log(window.a) // 6
globalThis (ES2020)
概念: 不同js环境下的全局属性 globalThis 包含全局的 this 值,类似于全局对象(global object)。
function canMakeHTTPRequest() {
return typeof globalThis.XMLHttpRequest === 'function';
}
console.log(canMakeHTTPRequest());
// expected output (in a browser): true
在以前,从不同的 JavaScript 环境中获取全局对象需要不同的语句。在 Web 中,可以通过 window、self 或者 frames 取到全局对象,但是在 Web Workers 中,只有 self 可以。在 Node.js 中,它们都无法获取,必须使用 global。
在松散模式下,可以在函数中返回 this 来获取全局对象,但是在严格模式和模块环境下,this 会返回 undefined。
globalThis 提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身)。不像 window 或者 self 这些属性,它确保可以在有无窗口的各种环境下正常工作。所以,你可以安心的使用 globalThis,不必担心它的运行环境。为便于记忆,你只需要记住,全局作用域中的 this 就是 globalThis。
函数
默认值
概念:在ES6中函数的参数支持使用默认值,当在调用函数时如果没有给函数传递实参则形参会使用默认值。
语法:
function test(x = 1, y = 2) {
console.log(x, y)
}
test() // 1,2
test(null, 1) // null 1
test(15, 16) // 15, 16
test(undefined, 20) // 1, 20 可以使用但不推荐
注意:
- 函数中的任意形参都可以包含默认值
- 传递null并不会使得形参使用默认值, 参数的值将会变成null
- 传递undefined会使得形参使用默认值
- 推荐传递了实参的参数前的参数不要使用默认值
- 设置默认值形参后面的所有形参会被函数的length属性忽略
var a = function (a,b,c,d ) {}
a.length // 4
var a1 = function (a,b,c,d = 2 ) {}
a1.length // 3 忽略 d 只有abc
var a1 = function (a,b = 0,c,d ) {}
a1.length // 1 忽略b之后所有形参 a
- 函数的任何一个形参一旦被设置默认值,函数在声明初始化时。所有的参数都将变为独立作用域的参数
function a(x, x, y ) { } // 正确,形参可以重复的定义
function a(x, x, y = 2 ) { } // 错误,所有形参变成了拥有独立作用域的变量。不可以有同名参数
- 函数的默认值是惰性求值的,默认值只会在函数调用的时候才会执行,观察下面代码:
let x = 5
function a(y = x) {
// 正确, 设置了默认值后 y 作用域当前函数作用域内变量。但是赋值给y的x变量作用域函数外部
let x = 1
console.log(y,x)
}
// 函数的默认值是惰性求值,并不是在声明函数时就已经传递了默认值。而是在每次调用时才会调用默认值
a() //等价于 a(x) 5 1
x = 200
a() //等价于 a(x) 200 1,函数每次调用时才会将默认值赋值给形参
在开发中,函数默认值惰性赋值特性可以实现函数的特定参数不可以为空
function throwErrorOfUndefined(funcName, funcArg) {
throw new Error('函数:'+ funcName+"缺少必传参数'"+funcArg+"'")
}
// 因为是惰性,所以函数在声明时throwErrorOfUndefined不会立即执行
function test (a = throwErrorOfUndefined('test','a')) {
}
test(123) // 因为没有使用默认值,所以默认值也不会被调用
test() // 当你不传参时,函数才会调用默认值,浏览器根据默认值抛出异常
作用域
概念:作用域就是一个变量的有效范围,在ES6之前js只有函数作用域和全局作用域。ES6引入了块级作用域,在ES6中创建块级作用域最简单方法时使用大括号"{}" (在ES6中代表块)配合拥有块级作用域的变量let const使用
{
// 大括号代表一个代码块,有自己独立的块级作用域
let a = 7
console.log(a) // 7
var b = 1
console.log(b) // 1
}
console.log(b) // 1
console.log(a) // a is not defined
使用ES5实现方法实现块
- 方法一 利用函数作用域
(function(a) { console.log(a) })(9)
console.log(a)
- 方法二 使用try catch实现块级作用域
try {
throw 7
} catch (a) {
console.log(a)
}
console.log(a)
- 方法三 通过严格模式 eval()方法实现内部代码独立作用域(后面严格模式会讲到该知识点)
"use strict"
eval('var a = 10086')
console.log(a)
name属性
概念:ES6函数拥有name属性,属性值为当前函数的函数名
function aaa () {
console.log(this)
}
console.log(aaa.name) // "aaa"
let demo = function () {
}
console.log(demo.name) // ES5 "" ES6 "demo"
严格模式(use strict)
概念:让js代码在严格的条件下执行。非严格模式下的代码很多是不符合逻辑的,严格模式可以让我们的代码更规范(提高编译效率,消除语法不严谨不合理)。也为js的升级做好铺垫。
语法:严格模式声明是将 "use strict"
字面量声明在文档的头部、函数的外部或函数的内部(ES6不允许)
"use strict" //正确 写在外部
function a(x, y ) {
"use strict" // 正确,ES5允许将 "use strict"写到函数内部
}
function a(x, y = 5) { // 错误 ES6 语法中不允许将 "use strict"写到函数内部
"use strict"
}
为什么在ES6中 "use strict" 不能写在函数内部,我们看下面的代码:
// 因为在ES6中函数的默认值是惰性赋值的
// 在函数的声明时8进制数077 不会立即赋值 "use strict" 没有办法检测到这里的问题
function a( y = 077) {
"use strict"
return y
}
// 当调用该函数时 8进制先赋值再执行 "use strict" 不符合开发需求
a()
ES6为了避免这些问题,规定 "use strict" 不能写在 es6的函数内部。
注意:严格模式规范
- 不能使用未声明的变量
a = 7 // Error
- 不能删除对象的属性,不能删除不允许删除的属性
var x = 7
delete x // Error
delete Object.prototype //Error
- 不允许变量重命名
var a = 1
var a = 2 // Error
function (x,x) {} // Error
- 不允许使用八进制
var num = 070 // Error
- 不允许使用除\u以外的其他转义符
var \u710A = 15
console.log(\u710A)
var \010 = 'ascii' // Error
- 不允许修改只读属性 不允许对只有get方法的属性进行赋值
const person = {};
Object.defineProperty(person, 'name', {
value: "小明",
writable: false // 是否允许修改该属性 false不允许 true允许
});
person.name = "小刚" // Error
const person = {
name: '小王',
get age() {return 18}
}
person.age = 12 // Error
- 不允许声明变量名为关键字的变量
var eval = 1 // Error
var arguments = 2 // Error
var let = 3 // Error
var static = 4 // Error
var public = 5 // Error
var private = 5.1 // Error
var protected = 5.2 // Error
var package = 6 // Error
var interface = 7 // Error
var implements = 8 // Error
var yield = 9 // Error
- eval 作用域独立
eval("var a =10086")
console.log(a)// undefined
- 不允许this 指向全局对象
"use strict"
function aaa () {
console.log(this)
}
aaa() // undefined
箭头函数
概念: 在ES6中允许使用 => 形式声明一个函数
语法:
function a (x,y) {
return x + y + 1
}
// 上面的函数就等等价于下面的箭头函数
let a = (x, y) => x + y + 1
注意:
- 箭头函数不能直接设置函数名,只能将箭头函数以赋值的形式传递给变量
- 若箭头函数 => 符号后没有跟大括号则箭头函数会将 => 后面的代码自动return (注意没有大括号的箭头函数只适用于函数内部只有一行js表达式函数), 如果箭头函数包含大括号, 则默认return功能会被取消。
let a = (x, y) => x + y
let a = (x, y) => {
let result = x + y + 1
// 因为当前箭头函数包含大括号,导致自动return功能被取消
// 这时必须使用return 返回特定的值
return result
}
// 如果箭头函数使用默认return功能返回一个对象,必须用()将对象包含
let a = (name, age) => ({name: name, age: age})
- 若箭头函数有且仅有一个参数,则参数的()可以被省略
let func = (age) => age >= 18
// 等价于
let func = age => age >= 18
4.箭头函数自身没有this。函数内部的this指向声明这段代码的作用域。
例:下面的代码中在setTimeout中使用箭头函数,箭头函数内部的this指向的是声明这段代码的作用域对象,而不是window对象。
function Cat(name) {
this.name = name
this.sayHello = function () {
console.log('Meow !')
// 如果是普通函数 setTimeout中的this应该指向window
// 而箭头函数指向当前代码的作用域
setTimeout(() => console.log('你好,我叫' + this.name), 500)
}
}
let cat = new Cat('旺财')
cat.sayHello()
// 对象的方法不要使用箭头函数
let cat1 = {
name: '阿橘',
sayHello: () => {
// 因为对象是全局声明的,他的作用域就是全局
// 这里的this就是全局对象
console.log(this)
}
}
cat1.sayHello() // window
- 箭头函数不能作为构造函数配合 new 关键字使用。箭头函数本身就没有实例(this)。
- 箭头函数没有arguments对象。
function test (x,y) {
// 伪数组,包含了当前函数的所有实参
console.log(arguments) // Arguments[1,2,8,0,4,5,9,7,callee]
}
test(1,2,8,0,4,5,9,7)
let test = (x,y) => {
console.log(arguments) // Error!箭头函数没有arguments
}
test(1,2,8,0,4,5,9,7)
- 箭头函数不能作为 Generator 函数。
优势:
- 简化js 回调函数
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100)
}
$.ajax({
url: "test.html",
context: document.body,
success: () => $(this).addClass("done")
});
[1, 2, 3, 4, 5].forEach(item => console.log(item));
- 简化柯里化函数 (函数一次只能接受一个参数)
// 普通函数
function getPerson(name, age, address) {
return {
name: name,
age: age,
address: address
}
}
getPerson('小明',18, 'gz')
// 柯里化函数
function getPerson(name) {
return function (age) {
return function (address) {
return {
name: name,
age: age,
address: address
}
}
}
}
// ES6 箭头函数简化柯里化函数
let getPerson = name => age => address => ({
name: name,
age: age,
address: address
})
getPerson('小明')(18)('gz')