自学笔记,参考阮一峰教程和B站小马哥ES6
一、ES6简介
ES6是什么?
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
ES6和javascript的关系
要讲清楚这个问题,需要回顾历史。1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准化组织 ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0 版。
该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。
因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 JScript 和 ActionScript)。日常场合,这两个词是可以互换的。
二、let和const
与过去在ES5中常用的var
关键字相同,这两个关键字在ES6中的作用就是声明变量。
- let声明变量,不存在变量提升
但不同的是,var
声明变量存在变量提升的问题。
console.log(a); // undefined
var a = 10; //这就相当于在打印之前已经声明了a这个变量,只是在这一行进行赋值,执行代码并不会报错
这部分代码相当于:
var a;
console.log(a); // undefined
a = 10;
但是用let
和const
来声明变量就不存在变量提升的问题,例如:
console.log(a) //报错:Uncaught ReferenceError: can't access lexical declaration 'a' before initialization
let a = 10;
- 是一个块级作用域
在这里,用var
声明的变量a
存在变量提升,其作用域就扩大到了if语句外,而let
声明的变量b
的作用域只在if语句内
console.log(a) //undefined
if(1 === 1){
var a = 10;
}
console.log(b) // 报错:Uncaught ReferenceError: b is not defined
if(1 === 1){
let b = 10;
}
- 不能重复声明
var a = 10;
var a = 20;
console.log(a) //20
let b = 10;
let b = 20;
console.log(b) // 报错:Uncaught SyntaxError: redeclaration of let b
- 以上
let
具有的特点const
都有,此外const
声明的变量不能被改变
const a = 10;
a = 20; // 报错:Uncaught TypeError: invalid assignment to const 'a'
console.log(a)
但是要注意,当用const
来声明对象时,对象的属性是可以修改的,但是注意不能修改整个对象。
const person = {
name:'Mike',
year: 20,
Nation: 'China'
}
person.name = 'Jack'
console.log(person.name) //Jack
person.sex = 'male'
console.log(person.sex) //male
person = {
intrest:'sing'
} // 报错:Uncaught TypeError: invalid assignment to const 'person'
console.log(person.intrest)
三、模板字符串
ES6中引入的模板字符串能够很好地简化代码。过去需要输出一段可变的文本时需要通过拼接字符串的方式:
<body>
<div id="Box">
</div>
</body>
<script>
let sentence = '你好,这里是模板字符串'
const box = document.getElementById('Box')
box.innerHTML = '拼接字符串写法:<ul><li><p>'+sentence+'</p></ul>'
</script>
模板字符串即可以用``(反引号,即Tab上面那个符号)的方式将字符串写在里面,如果有需要写入的变量,则用${}将变量包裹起来,这样就无需用+号来拼接字符串了:
box.innerHTML = `模板字符串写法:<ul><li><p>${sentence}</p></ul>`
四、函数之默认值、剩余参数、扩展运算符、箭头函数
1. 函数默认值
ES6之前,当需要为函数的参数定义一个默认值时,需要在函数内部通过判断的方式为形参赋值:
function add(a, b){
a = a || 10;
b = b || 20;
return a + b;
}
console.log(add()); // 30
console.log(add(15)); // 35
console.log(add(15, 30)); // 45
ES6中函数的默认值可以直接写在括号中:
function add(a = 10, b = 20){
return a + b;
}
console.log(add()); // 30
console.log(add(15)); // 35
console.log(add(15, 30)); // 45
默认值也可以是一个表达式:
function add(a, b = getVal()){
return a + b;
}
function getVal(val = 35){
return val + 10;
}
console.log(add(15)); // 60
2. 剩余参数
当无法确定需要输入函数的实参的数量时,过去可以用arguments的方式获取实参:
function search(obj){
let person = new Object();
for(let i = 1; i < arguments.length; i++){
person[arguments[i]] = obj[arguments[i]]
}
return person;
}
let Person = {
name:'Jack',
age:23,
job:'fireman',
country:'China'
}
console.log(search(Person,'name','country')) // Object { name: "Jack", country: "China" }
剩余参数即可以将定义函数时未知数量的实参都存入一个数组中,需要用到实参时可以遍历数组取参,写法为...具名参数
,例如...keys
:
function search(obj, ...keys){
let person = new Object();
for(let i = 0; i < keys.length; i++){
person[keys[i]] = obj[keys[i]]
}
return person;
}
// Object { name: "Jack", country: "China" }
3. 扩展运算符
与剩余参数相同,扩展运算符也是通过...arr
这种在变量名前面加三个点实现的,不同的是,扩展运算符是将数组里面的元素提取出来作为参数输入函数的,也就是输入函数的不再是一个数组,而是一个个参数。
let min = Math.min(10,20,54,42)
console.log(min) // 10
let arr = [10,23,45,29,100,23,56]
let max = Math.max(arr)
console.log(max) // NaN
let arr = [10,23,45,29,100,23,56]
let max = Math.max(...arr)
console.log(max) // 100
4. 箭头函数
在ES6中,可以采用=>
的方式定义函数,让代码更加简洁。
过去定义函数需要用function来定义,例如:
let add = function(a, b){
return a + b;
}
console.log(add(5,7)) // 12
换成箭头函数的方式定义函数就可以写成:
let add = (a, b)=>{
return a + b;
}
console.log(add(5,7)) // 12
当声明函数时只传入一个参数的时候,可以省略括号,让写法更简洁:
let add = a => a + 10;
console.log(add(5)) // 15
箭头函数支持剩余参数、默认参数和解构赋值,但是不能省略括号,会报错。
在用箭头函数返回一个对象时,不能省略括号,省略()
的话会直接报错:
let person =(id,name)=>({
id:id,
name:name
})
let person1 = person(123,'Jack')
console.log(person1) // Object { id: 123, name: "Jack" }
利用箭头函数也可以实现闭包
let hello =(function(){
return function(){
console.log('hello world')
}
})()
hello() // hello world
// 可以改写成:
let hello =(()=>{
return ()=>console.log('hello world')
})()
hello() // hello world
5. 箭头函数this指向和注意事项
(1)箭头函数没有独立的 this,箭头函数内部的this只能通过查找作用域链来实现,一旦使用箭头函数,当前不存在作用域。
在ES6之前的写法中,函数内部的this
会指向调用函数的对象:
let handle = {
id: 123,
init: function(){
document.addEventListener('click', function(event){
this.doSomeThing(event) //此时这里的this是指向document的,报错:Uncaught TypeError: this.doSomeThing is not a function
}, false)
},
doSomeThing: function(event){
console.log(event.type)
console.log(this.id)
}
}
handle.init()
改写成箭头函数后,this
就指向了init
的对象handle
,:
let handle = {
id: 123,
init: function(){
document.addEventListener('click', function(event){
this.doSomeThing(event)
}, false)
},
doSomeThing: function(event){
console.log(event.type) // click
console.log(this.id) // 123
}
}
handle.init()
如果将init
也改写成箭头函数,this
就会顺着作用域链去找init
的对象,也就是window
let handle = {
id: 123,
init: ()=>{
document.addEventListener('click', (event)=>{
this.doSomeThing(event) //此时this指向的是window,报错:Uncaught TypeError: this.doSomeThing is not a function
}, false)
},
doSomeThing: function(event){
console.log(event.type)
console.log(this.id)
}
}
handle.init()
(2)箭头函数没有arguments
let add = function(a, b){
return arguments[0] + arguments[1]
}
console.log(add(10,20)) //30
let add = (a, b) =>{
return arguments[0] + arguments[1] //报错:Uncaught ReferenceError: arguments is not defined
}
console.log(add(10,20))
(3)箭头函数不能用作构造函数,使用 new 调用它们会引发 TypeError。
let person = function(){
this.id = 123,
this.name = 'Jack'
}
let p = new person()
console.log(p) //Object { id: 123, name: "Jack" }
// 改写为箭头函数后:
let person = ()=>{
this.id = 123,
this.name = 'Jack'
}
let p = new person() //报错:Uncaught TypeError: person is not a constructor
console.log(p)