1、let&&const 块级作用域 const也是块级作用域
let、const不存在变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
var与let的区别:
(1)var定义的变量,作用域是整个封闭函数,是全域的。通过let定义的变量,作用域是在块内的;
(2)关于变量的提升。 var可以变量提升,但是let不存在变量提升。
(3)let不允许在相同的作用域内,重复声明同一个变量。
// 报错
function func() {
var a = 1;
let a = 10;
}
//如果在当前作用域中嵌套另一个作用域,便可在嵌套作用域中用let声明同名变量
function func() {
var a = 1;
if(condition){
let a = 10;
}
}
为什么需要块级作用域:
ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}
f(); // undefined
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
上面代码中,变量i
只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。
//(1)在循环中使用var声明
//循环中每次迭代同时共享着变量i,循环内部创建的函数全都保留了对相同变量的引用,循环结束时变量i的值为10
var a = [];
for (var i = 0; i < 10; i++) {
a.push(function () {
console.log(i);
});
}
a.forEach(function(item){
item();//输出10次数字10
})
//(2)为解决以上问题,一般使用立即执行函数,以强制生成变量的副本,如
for (let i = 0; i < 10; i++) {
a.push((function (num) {
return function(){
console.log(num);
}
})(i));
}
//(3)在循环中使用let声明
//每次循环时let声明都会创建一个新变量i,并将其初始化为当前值,所以内部函数每次都能得到属于他们自己的i的副本
for (let i = 0; i < 10; i++) {
a.push(function () {
console.log(i);
});
}
a.forEach(function(item){
item();//输出0-9
})
const用于声明一个常量,设定后值不会发生改变,强行对其进行重新赋值会报错。
这个不可变对于基础类型(按值访问)而言是值不可变;而对于引用类型(按引用访问)是引用地址不变,比如用const声明对象后,可以修改该对象的属性值。
const num=123;
num=456;
console.log(num);//Uncaught SyntaxError: Identifier 'num' has already been declared
const obj={
a:2
}
//可以修改对象属性的值
obj.a=4;
console.log(obj.a);//4
//抛出语法错误
obj={
a:4
}
2、变量的解构赋值
ES6允许按照一定的模式,从数组和对象中提取值,对变量进行赋值,称为解构。本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
let[a,b,c]=[1,2,3];
let [bar, foo] = [1];
console.log(bar);//1
console.log(foo);//undefined
解构不仅可以用于数组,还可以用于对象。
let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
3、箭头函数
ES6 允许使用“箭头”(=>
)定义函数。
var f = v => v;等同于
var f = function(v) {
return v;
};
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
箭头函数与传统的js函数的区别:
(1)箭头函数体内的this对象,就是函数定义时所在的对象,而普通函数的this是函数运行时所在的对象。且箭头函数内部的this值不可被改变
(2)不可以当作构造函数,也就是说不可以使用new命令,否则会抛出一个错误。
//普通函数
function fun(){
console.log('普通函数');
}
var p=new fun();//普通函数
//箭头函数
var fun=()=>{
console.log('箭头函数');
}
var p=new fun();//Uncaught TypeError: fun is not a constructor
(3)不可以使用arguments对象,该对象在函数体内不存在。
//普通函数
function fun(x,y){
console.log(arguments[0]);
}
fun('a','b');//a
//箭头函数
var fun=(x,y)=>{
console.log(arguments[0]);
}
fun('a','b');//ƒ (e,t){return new v.fn.init(e,t,n)}
(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。
(5)没有原型,因为不可以通过new关键字调用箭头函数,因而没有构建原型的需求,所以箭头函数不存在prototype这个属性。
箭头函数可以让setTimeout
里面的this
,绑定定义时所在的作用域,而不是指向运行时所在的作用域。下面是另一个例子。
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
this
指向的固定化,并不是因为箭头函数内部有绑定this
的机制,实际原因是箭头函数根本没有自己的this
,导致内部的this
就是外层代码块的this
。正是因为它没有this
,所以也就不能用作构造函数。
所以,箭头函数转成ES5的代码如下。
上面代码中,转换后的ES5版本清楚地说明了,箭头函数里面根本没有自己的this
,而是引用外层的this
。
//原型
function Student(name) {
this.name = name;
}
Student.prototype.hello = function () {
alert('Hello, ' + this.name + '!');
}
//类
class Student {
constructor(name) {
this.name = name;
}
hello() {
alert('Hello, ' + this.name + '!');
}
}
var xiaoming = new Student('小明');
xiaoming.hello();
class继承
用class定义对象的另一个好处就是继承更方便了,直接用extends来实现
class PrimaryStudent extends Student{
constructor(name,grade){
super(name);
this.grade=grade;
}
myGrade(){
console.log('my grade is'+this.grade);
}
}
var stu=new PrimaryStudent('an','32');
stu.hello();//hello,an
stu.myGrade();//my grade is32

6、默认参数值
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
7、for of值遍历
我们都知道for in循环用于遍历数组,类数组或对象,ES6中新引入的for of循环功能相似,不同的是每次循环它提供的不是序号而是值
for in遍历的是数组的索引,而for of遍历的是数组的元素值
for...in
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const index in digits) {
console.log(digits[index]);
}
依然需要使用 index 来访问数组的值
当你需要向数组中添加额外的方法(或另一个对象)时,for...in
循环会带来很大的麻烦。因为 for...in
会遍历数组所有可枚举的属性,意味着如果向数组的原型中添加任何其他属性,这些属性也会出现在循环中。
所以for in更适合遍历对象,不要使用for in遍历数组。
for...of
for...of
循环用于循环访问任何可迭代的数据类型。
for of遍历的只是数组内的元素,不包括数组原型上的属性
8、展开运算符
展开运算符(用三个连续的点 (...
) 表示)是 ES6 中的新概念,使你能够将字面量对象展开为多个元素
const books = ["Don Quixote", "The Hobbit", "Alice in Wonderland", "Tale of Two Cities"];
console.log(...books);
Prints: Don Quixote The Hobbit Alice in Wonderland Tale of Two Cities
9、set数据结构 Map
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。 可用于数组去重
Set
本身是一个构造函数,用来生成 Set 数据结构。
// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set] 获Array.from(set);
// [1, 2, 3, 4]
// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5
map()方法将调用的数组的每个元素传递给指定的函数,并返回一个数组,它包含该函数的返回值。
传递给map()的函数的调用方式和传递给forEach()的函数的调用方式一样。但传递给map()的函数应该有返回值。注意:map()返回的是新数组:它不修改调用的数组。
举个例子:
要求:为数组 arr 中的每个元素求二次方。不要直接修改数组 arr,结果返回新的数组
实现:
function square(arr){
return arr.map(function(item)
{
return item*item;}
);
}
var arr=[1, 2, 3, 4];
console.log(square(arr));
结果:[1,4,9,16]
10、promise
ES5新增对数组的操作map,filter和reduce分别是什么?
map():对数组的每个元素进行一定的操作(映射)后,会返回一个新的数组。
map()传参与forEach()相同
forEach():该方法对数组中的每一项运行给定函数,该方法没有返回值。其实就是遍历循环
forEach()包含两个参数,第一个参数是匿名函数,第二个参数是this。匿名函数中包含三个参数:item(每一项的值),index(每一项的索引),self(代表所遍历函数自己)
filter():该方法对数组中的每一项运行给定函数,返回该函数会返回包含true和false的项组成的数组。
filter()传参与forEach()相同。
归并方法reduce():该方法会迭代数组中的每一项,然后生成一个最终返回值。接收两个参数(回调函数,初始值),将第二个参数作为初始值
数组求和来启动累积。
reduce()接收一个函数作为累加器
回调函数接收最多四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce 的数组。
如数组扁平化处理可以用reduce
例:实现一个flatten方法,使得输入一个数组,该数组里面的元素也可以是数组,该方法会输出一个扁平化的数组。
arr=[[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10];
console.log([1,2,2,3,4,5,5,6,7,8,9,11,12,12,13,14,10]);
法一:递归
function flatten(arr){
var res=[];
for(var i=0;i<arr.length;i++){
if(Array.isArray(arr[i])){
res=res.concat(flatten(arr[i]));
}
else{
res.push(arr[i]);
}
}
return res;
}
法二:reduce
function flatten(arr){
return arr.reduce(function(pre,item){
return pre.concat(Array.isArray(item)?flatten(item):item);
},[]);
}
法三:map
function flatter2(arr){
var res=[];
arr.map(function(item){
if(Array.isArray(item)){
res=res.concat(flatter2(item));
}
else{
res.push(item);
}
})
return res;
}
11、模块
导入、导出
转载于:https://www.cnblogs.com/xiaoan0705/p/8671495.html