一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情。
一、什么是ES6
ES6即:ECMAScript6的缩写
- ECMA:指的是欧洲计算机制造商协会,是一个标准化组织
- ECMAScript = 由ECMA这个标准化组织指定的一个语言标准
那什么是语言标准呢?
- 语言标准指的是语法和API
- 也就是说ECMAScript规定了语法和API(语法:即如何声明变量、如何定义函数、使用哪些数据类型等等;API:可简单理解为方法/函数)
二、ES与JavaScript的关系
可理解为:JavaScript = ECMAScript(语法 + API) + DOM + BOM
三、let、const
3-1、let 和 const 是什么
let 和 const 就是用来声明变量/常量的
- let用来代替var声明变量的
- const是专门用来声明常量的
- 变量在声明后可以重新赋值,而常量声明后不能重新赋值
3-2、const
3-2-1、为什么需要const
往往我们有时定义的变量不想后续被修改掉,然而在经过一些代码书写后会修改掉此变量,然而编译时不会有任何错误提示的,为了避免这种错误,引入了const
- const就是为了那些一旦初始化就不希望重新赋值的设计
3-2-2、const注意事项
1、一旦声明就必须初始化
const a = 1;
2、const声明的常量允许在不重新赋值的情况下修改
const person = {name: 'zs'}
person.name = 'lisi'
- 这种情况下出现在该值的类型是引用类型的时候
3-3、let、const与var的区别
3-3-1、重复声明
重复声明:即已经存在的变量或常量又重新声明了一遍
- var定义的变量允许重复声明
- let、const定义的变量或常量不允许重复声明
var a = 1;
//.....代码
var a = 2;
let a = 1;
// let a = 2; 不允许重复声明
同样需要注意的是,以下情况也属于重复声明
function fun(a) {
// let a = 1; // 会报错,这种情况也属于重复声明
}
3-3-2、变量提升
- var会提升变量的声明到当前作用域的顶部(注意,是变量的声明而不提升初始化)
console.log(a); // undefined
var a = 1;
我们会发现不但没有报错,还会输出undefined,就是因为var将变量提升了,以上代码可以看成:
var a;
console.log(a);
a = 1;
- 而let 、const不存在变量提升,如果像上面代码一样编写,将会报错
// console.log(a); // 此处会报错
let a = 1;
console.log(a); // 1
3-3-3、暂时性死区
暂时性死区:即只要作用域内存在let、const,它们所声明的变量或常量就自动“绑定”在这个区域中,不再受外部作用域的影响。
- 使用let/const声明的变量存在暂时性死区
- 使用var声明的变量不存在暂时性死区
let a = 1;
function fun() {
console.log(a);
let a = 2;
}
fun();
当我们运行以上代码时,会发现报错了,这是因为:函数运行会产生作用域,然而在函数作用域内存在了let声明的变量a,此时a便与该作用域进行“绑定”,不再会去寻找上层作用域的变量a,这样的话由于先打印的a,随后才声明了导致报错。
3-3-4、window对象的属性或方法
在全局作用域中:
- 通过var声明的变量,或通过function声明的函数,会变成window对象的属性或方法
- 而通过let、const声明的变量/方法不会变成window对象的属性/方法
var age = 18;
function add () {}
console.log(window.age); // 18
console.log(window.add === add); // true
let age = 18;
const add = function() {}
console.log(window.age);
console.log(window.add === add); // false
3-3-5、块级作用域
- var没有块级作用域
for (var i = 0; i < 3; i++) {
// console.log(i);
}
console.log(i); //3
- let/const有块级作用域
for (let i = 0; i < 3; i++) {
// console.log(i);
}
console.log(i); // 报错
由于for循环的{}构成了块级作用域,let定义的i是存在在此块级作用域中的,在for循环执行完毕后,块级作用域便销毁了,所以console.log(i)时找不到i了,会报错“未定义i”
具有块级作用域的结构除了for(){}外,还有: {} 、while(){}、do{}while()、if(){}、switch(){}
- 注意,对象的{}是不构成块级作用域的!如:const person = {...} 此处的{}是不构成块级作用域的
四、模板字符串
4-1、ES5中的字符串
ES5中定义一个字符串会把字符串放入引号中,使用单引号''或双引号""来包裹字符串,而对于多个字符串的拼接则需要使用‘+’连接:
var a = 1;
var b = 2;
console.log('a的值是:' + a + 'b的值是:' + b);
// a的值是:1b的值是:2
而且,在多行拼接时需要使用换行符\n和+组合实现拼接:
var str = '111\n' +
'222\n' +
'333\n';
console.log(str);
/**
111
222
333
*/
此外,在拼接时还会遇到传递变量的问题:
<div id="box"></div>
<script type="text/javascript">
let lang = 'ES6';
document.getElementById('box').innerHTML = '<h1>今天我们来学习' + lang + '</h1>' ;
</script>
可见,我们稍微不注意就特别容易出现拼接错误,很不方便,而ES6模版字符串的出现就是为了解决这些问题的。
4-2、ES6中的模版字符串
模版字符串通过反引号`` 来包裹,在反引号中我们可以直接定义多行字符串和变量的拼接,如果是变量就可以包裹在{}中我们还可以使用表达式以及调取函数的方式来实现更加复杂的字符串拼接。
4-2-1、字符串拼接
const name = 'zhangsan';
const age = 18;
console.log(`hi,my name is ${name}, i am${age} years old!`);
4-2-2、多行字符串
const str = `123
456
789`;
- 在模版字符串中,所有的空格、换行或缩进都会被保留在输出之中
4-2-3、逻辑运算
const age = 20;
console.log(`张三是${age > 18 ? '成年人' : '未成年人'}`)
4-2-4、使用函数
const name = 'zhangsan';
const person = { age: 18, sex: 'male'};
const getSex = function(sex) {
return sex === 'male' ? '男' : '女';
}
const info = `${name}, ${person.age}, ${getSex(person.sex)}`;
console.log(info);
五、箭头函数
在ES5中是使用function关键字来定义函数的,但是function定义的函数会存在一些问题:如this指向、函数参数arguments等;在ES6中可以使用箭头“=>”来定义一个函数,它本身没有自己的this、arguments、super或new.target,箭头函数表达式更适用于那些本来需要匿名函数的地方,但它不能用作构造函数。
5-1、什么是箭头函数
箭头函数也有参数部分与函数体部分,与普通函数不同的是,其省略了关键字function,在参数部分与函数体部分之间使用“=>”连接,箭头函数比传统函数定义方式更加简洁。
为了使箭头函数在别处可以被调用,我们往往在声明一个箭头函数的时候会使用一个变量去接收:
const sum = (a, b) => {
return a + b;
};
console.log(sum(1,2));
5-2、如何将一般函数改写成箭头函数
在ES5中我们定义函数一般有两种形式:一种是声明的形式、另一种是函数表达式的形式:
function add() {}
const sum = function() {}
5-2-1、将声明形式的函数改写成箭头函数
- 我们先将声明形式改写成函数表达式形式:
如:funcion add() {} => const add = function() {};
- 再从函数表达式的形式转为箭头函数
const add = function() {}; => const add = () => {}; (即:去掉function关键字将参数部分与函数体部分用“=>”连接)
5-3、箭头函数的注意事项
我们在箭头函数是单个参数或者是单行函数体的情况下,可以进一步化简箭头函数;此外我们还要注意单行对象的情况:
5-3-1、单个参数
const add = (x) => {
return x + 1;
};
// 简写形式
const add = x => {
return x + 1;
};
单个参数可以省略()
- 要注意的是多个参数或0个参数不能省略()
5-3-2、单行函数体
const add = (a, b) => {
return a + b;
};
// 简写
const add = (a, b) => a + b;
单行函数体可以同时省略{}和return
- 要注意的是一定要同时省略{}和return,不能只省略其中一个
5-3-3、单行对象
const add = (a, b) => {
return {
value: a + b;
};
};
// 错误写法
// 这种写法浏览器会把外面的{}当成函数体的{}
// const add = (a, b) => {value: a + b};
// 此时我们用()在外侧包一层就可以啦
const add = (a, b) => ({value: a + b});
当箭头函数返回一个单行对象时,省略后要在外侧包一层(),不然浏览器会将对象的{}当成函数体的{}
5-4、箭头函数中的this指向
5-4-1、普通函数的this指向
只有在函数被调用的时候this指向才能确定,不调用时不能知道this指向谁;this指向和函数在哪调用没有关系,只和谁在调用this有关系!
(具体上下文绑定规则请参考:this指向)
function add() {
console.log(this); // window
}
add();
当调取函数时,打印出window,注意这是在非严格模式下,如果使用严格模式,将会打印出undefined:
'use strict';
function add() {
console.log(this); // undefined
}
add();
可见,在严格模式下,直接调取add(),将会打印出undefined
但如果我们写成window.add(),不管是否在严格模式下,都会输出window,这是因为此时能明确知道是window对象调用的add函数
'use strict';
function add() {
console.log(this); // window
}
window.add();
我们再来看两个例子:
function add() {
console.log(this);
}
const sum = {
add: add
};
sum.add();
由于是sum发起的调用,所以此时this指向sum对象
function add() {
console.log(this);
}
const sum = {
add: add
};
const adder = sum.add;
adder();
这里虽然将sum的add赋值给了adder,但adder()是独立调用的,所以还是指向window(非严格模式下)
(this指向的情况有多种多样,此处不再一一讲述,具体参考:this指向)
5-4-2、箭头函数的this指向
箭头函数中没有自己的this
const a = {
add: () => {
console.log(this);
}
};
a.add(); // window
这里之所以会打印window,是因为箭头函数没有自己的this,当箭头函数被调用时它会顺着作用域链去寻找this的,注意,对象是没有作用域的,所以它上层的作用域是全局作用域,由于全局作用域的this指向window,所以这里会打印出window。
我们再来看一个例子:
const calc = {
add: function() {
const sum = () => {
console.log(this);
};
sum();
}
};
calc.add();
执行完毕会发现,this指向calc对象:
这里当add被执行时,sum函数也会被执行,sum函数执行时输出this,由于sum是箭头函数没有自己的this会顺着作用域链去找this,它的上层作用域是add函数的作用域,所以sum函数的this就是add函数作用域的this,由于add函数是被calc发起调用的,add函数作用域的this指向calc对象,所以sum函数的this也会指向calc对象。
5-5、不适用箭头函数的场景
不适用箭头函数场景有如下几个:
- 作为构造函数(是由于箭头函数没有this,构造函数最重要的就是创建出一个新对象,使this指向该对象,箭头函数都没有this,所以自然做不了构造函数~)
- 需要this指向调用对象的时候
// 比如我们想使用document.onclick 或document.addEventListener('click', ...)为dom添加监听事件时:
document.onclick = function() {}
document.addEventListener('click', function() {})
// 此时我们是希望this指向document的,如果我们使用了箭头函数,那么会去上层作用域(全局作用域)去找this,这样的话就不回指向document了
- 需要使用arguments的时候(箭头函数没有arguments)
function add() {
console.log(arguments);
}
add(1,2,3);
// 当我们使用箭头函数时会发现报错,因为箭头函数没有arguments
// const add = () => {
// console.log(arguments);
// }
// add();
- 需要通过call、apply等方法来改变this指向时(这也是因为箭头函数没有this,自然也无法通过这些方法改变this)