ECMAScript 6 (ES6) 入门笔记

目录

一、ECMAScript 6 简介

二、let 与 var与const

三、 解构赋值

四、模板字符串

五、ES6 函数

六. Class类

七、Set结构

八、Map()

九、Promise



一、ECMAScript 6 简介

ECMAScript 6.0(简称ES6)是JavaScript语言(现在是遵循ES5标准)的下一代标准,已经在2015年6月正式发布了。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言

和JavaScript的关系

由于JavaScript的创造者Netscae公司的版权问题,ECMAScript不能叫Javascript。ECMAScript和JavaScript的关系是,前者是后者的规格(语言规范),后者是前者的一种实现。

ES6的新特性

  • ES6中的let命令,声明变量,用法和var差不多,但是let是为JavaScript新增了块级作用域,ES5中是没有块级作用域的,并且var有变量提升的概念,但是在let中,使用的变量一定要进行声明。
  • ES6中变量的解构赋值,比如:var [a,b,c] = [0,1,2];
  • ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念,听起来和Java中的面向对象编程的语法有些像,但是二者是不一样的。
  • ES6中的函数定义也不再使用关键字function,而是利用了=>来进行定义;
  • ES6中可以设置默认函数参数,如function A(x,y=9){}

二、let 与 var与const

  • var:ES5中用于声明变量的关键字,它没有块级作用域,很容易造成变量污染,存在各种问题
  • let:ES6新增,用于声明变量,有块级作用域,在同一个代码块中不允许重复申明,也不存在变量提升问题
  • const:定义的常量,无法被重新赋值,而且在定义常量的时候就必须赋值,否则会报错

块级作用域:即 花括号{},这也是 ES6 新增特性之一,在花括号之中的内容独立作为一个作用域,外部无法访问。在块级作用域搭配 let 和 const 可以有效地避免变量名污染的问题。

var存在的问题:

// 1.声明提升
// 虽然会正常打印,但这使用方式是错误的
 console.log(name); 
 var name = "咔卡熊";
 // 2. 变量覆盖
 var demo = "小明";
 var demo = "小红";
 // 此处会打印小红,这也是错误的
 // 同一个项目中,发生变量覆盖可能会导致数据丢失以及各种不可预知的bug,原则上来说:变量不能重名
 console.log(demo)
// 3. 没有块级作用域
  function fn2(){
      for(var i = 0; i < 5; i++){
          // do something
      }
      console.log(i);
  }
  fn2();
// 此处会正常打印出 i 的值,这是错误的
// i是定义在循环体之内的,只能在循环体内打印,当前现象叫做红杏出墙!!!

let不会存在上述问题:

// 1. 不会存在声明提前
 // 此处会报错(这里必须报错,原则上来说不能先上车后买票)
 console.log(name); 
 let name = "咔卡熊";
 // 2. 不会有变量覆盖
 let demo = "小明";
 let demo = "小红";
 // 此处会报错(不能使用套牌车!)告诉你已经定义了此变量。避免了项目中存在变量覆盖的问题
 console.log(demo)
// 3. 有块级作用域
  function fn2(){
      for(let i = 0; i < 5; i++){
          // do something
      }
      console.log(i);
  }
  fn2();
 // 此处会报错,无法打印,防止红杏出墙!!!
      // i是定义在循环体之内的,循环体外当然无法打印 

const常量

  • const 声明一个只读的常量,一旦声明,常量的值就不能改变

  • 一般用于全局变量,通常变量名全部大写,如 const PI = “3.1415926”;

三、 解构赋值

JavaScript 中最常用的两种数据结构是对象Object 和数组Array。当我们把它们传递给函数时,函数可能不需要整个对象/数组。它可能只需要对象/数组的一部分。解构赋值 是一种特殊的语法,它使我们可以将数组或对象“拆包”至一系列变量中,因为有时这样更方便。解构操作对那些具有很多参数和默认值等的函数也很奏效。本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。

用在数组上

let [a, b, c] = [1, 2, 3];
// a = 1,b = 2,c = 3 相当于重新定义了变量a,b,c,取值也更加方便
let [a, b, c] = [2, 3];
// 输出:2 3 undefined
let [a, [b, c], [[d, e]]] = [0, [1, 2], [[3, 4]]];
console.log(a,b,c,d,e);
//输出:0 1 2 3 4

// , = 占位符
let arr = ["小明", "小花", "小鱼", "小猪"];
let [,,one] = arr; // 这里会取到小鱼

// 解构整个数组
let strArr = [...arr];
// 得到整个数组
console.log(strArr);

//以下会报错,因为左右两边模式不相等
let [a] = 1;
let [b] = false;
let [c] = NaN;
let [d] = undefined;
let [e] = null;
let [f] = {};

//解构赋值允许指定默认值:
let [a = 0, b = 1] = [];
console.log(a, b);//输出:0 1

let [x, y = 'b'] = ['a'];
console.log(x,y);//输出:a b

对比

// 用数组解构
function swap(arr, i, j) {
  [arr[i], arr[j]] = [arr[j], arr[i]];
}

// 不用数组解构
function swap2(arr, i, j) {
  const temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}
//在拆解固定值也挺方便的,例如说经常会出现有些字符串使用 - 进行分割:
const [base, str2] = someStr.split('-');

// 不用数组解构
const splittedStr = someStr.split('-');
const base = splittedStr[0],
  str2 = splittedStr[1];

用在对象上

let obj = {
   className : "卡西诺",
   age: 18
}
let {className} = obj; // 得到卡西诺
let {age} = obj;	// 得到18

// 剩余运算符
let {a, b, ...demo} = {a: 1, b: 2, c: 3, d: 4};
// a = 1
// b = 2
// demo = {c: 3, d: 4}

四、模板字符串

  • 模板字符串相当于加强版的字符串,用反引号 ``
  • 除了作为普通字符串,还可以用来定义多行字符串,可以在字符串中加入变量和表达式

普通字符串

// 普通字符串
let string = "hello"+"小兄弟"; // hello小兄弟
// 如果想要换行
let string = "hello'
'小兄弟"
// hello
// 小兄弟

模板字符串

减少拼接,代码看起来更加的美观

let str1  = "大帅";
let str2 = "B";
// 模板字符串
let newStr = `我是${str1}${str2}`;
console.log(newStr)// 我是大帅B

// 字符串中调用方法
function fn3(){
  return "帅的不行!";
}
let string2= `我真是${fn3 ()}`;
console.log(string2);  // 我真是帅的不行!

五、ES6 函数

箭头函数

  • 箭头函数是一种更加简洁的函数书写方式,可以用来取代以前使用匿名函数的地方
  • 箭头函数本身没有作用域(无this)
  • 箭头函数的this指向上一层,上下文决定其this
  • 基本语法: const/let/var 函数名 = (参数) => {}

a.基本用法
let fn = v => v;
//等价于
let fn = function(num){
 return num;
}
fn(100);  // 输出100

b. 带参数的写法
let fn2 = (num1,num2) => {
 let result = num1 + num2;
 return result;
}
fn2(3,2);  // 输出5

c. 箭头函数中的this指向问题
//箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。在函数定义的时候就已经决定了
function fn3(){
setTimeout(()=>{
// 定义时,this 绑定的是 fn3 中的 this 对象
console.log(this.a);
},0)
}
var a = 10;
// fn3 的 this 对象为 {a: 10},因为它指向全局: window.a
fn3.call({a: 18}); // 改变this指向,此时 a = 18

d. 箭头函数适用的场景
//当我们代码里存在这样的代码:let self = this;
//需要新建变量去保存this的时候,案例如下:
let Person1 = {
    age: 18,
    sayHello: function () {
setTimeout(()=>{
console.log(this.age);
});
}
};
var age = 20;
Person1.sayHello(); // 18

函数参数的扩展

1. 默认参数
// num为默认参数,如果不传,则默认为10
function fn(type, num=10){
 console.log(type, num);
}
fn(1);	// 打印 1,10
fn(1,2); // 打印 1,2 (此值会覆盖默认参数10)
//需要注意的是:只有在未传递参数,或者参数为 undefined 时,才会使用默认参数,null 值被认为是有效的值传递

2. 不定参数
// 此处的values是不定的,且无论你传多少个
function f(...values){
    console.log(values.length);
}
f(1,2);      // 2
f(1,2,3,4);  // 4

六. Class类

  • class (类)作为对象的模板被引入,可以通过 class 关键字定义类,类的命名有两种 ,匿名类和命名类
  • class 的本质是 function,同样可以看成一个块
  • 可以看作一个语法糖,让对象原型的写法更加清晰
  • 更加标准的面向对象编程语法
以前是采用 function 的原型链继承法去实现的继承
function Person(name) {
  this.name = name;
}
// 添加新的原型链继承方法
Person.prototype.work = function () {
  console.log('996是福报……?');
};

而使用 class 的结构会更加的清晰
class Person {
  constructor(name) {
    this.name = name;
  }
  work() {
    console.log('996是福报……?');
  }
}

类的定义

// 匿名类
let Demo = class {
    constructor(a) {
        this.a = a;
    }
}
// 命名类
let Demo = class Demo {
    constructor(a) {
        this.a = a;
    }
}

类的声明

class Demo {
    constructor(a) {
        this.a = a;
    }
}
  • 请注意,类不能重复声明
  • 类定义不会被提升,必须在访问前对类进行定义,否则就会报错。
  • 类中方法不需要 function 关键字。
  • 方法间不能加分号

属性的定义方式

//公共属性
class Demo{}
Demo.prototype.a = 2;

//实例属性
class Demo {
    a = 2;
    constructor () {
        console.log(this.a);
    }
}

new Demo(); // 2

关于constructor

对象的constructor属性用于返回创建该对象的函数,也就是我们常说的构造函数,在JavaScript中,每个具有原型的对象都会自动获得constructor属性

实例化对象

class Demo {
    constructor(a, b) {
        this.a = a;
        this.b = b;
        console.log('Demo');
    }
    sum() {
        return this.a + this.b;
    }
}
let demo1 = new Demo(2, 1);
let demo2 = new Demo(3, 1);
// 两者原型链是相等的
console.log(demo1._proto_ == demo2._proto_); // true
 
demo1._proto_.sub = function() {
    return this.a - this.b;
}
console.log(demo1.sub()); // 1
console.log(demo2.sub()); // 2

七、Set结构

它类似于数组,但是成员的值都是唯一的,没有重复的值,作用可以用于数组去重

var set = new Set([1, 2, 3]);
console.log(set); //Set {1, 2, 3}

//获取元素个数
console.log(set.size); //3

//添加元素
set.add(4);
console.log(set); //Set {1, 2, 3, 4}

//不会添加重复的值
set.add(4);
console.log(set); //Set {1, 2, 3, 4}

//删除元素
set.delete(4);
console.log(set); //Set {1, 2, 3}

//查找元素
console.log(set.has(1)); //true

//遍历元素
set.forEach(E => {
console.log(E); //1,2,3
});

//数组去重
var arr = […new Set([1, 2, 3, 3, 4, 5])]
console.log(arr); //[1, 2, 3, 4, 5]

八、Map()

  • ES6提供了Map数据结构。它类似于对象,也是键值对的集合,不同点在于`键`的范围不限于字符串,可以是各种类型的值(包括对象)

  • Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是

  • Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算

var map = new Map();
console.log(map); //Map?{}

//添加元素
map.set(‘name’, ‘’);
map.set(‘age’, 26);
console.log(map); //Map?{“name” => “”, “age” => 26}

//元素个数
console.log(map.size); //2

//获取元素
console.log(map.get(‘name’)); //

//删除元素
map.delete(‘age’)
console.log(map);// Map?{“name” => “”}

//查找元素
console.log(map.has(‘name’)); //true

//遍历元素
map.forEach((E, K) => {
console.log(E + ‘=’ + K); //name=
});

Map中的key

// 1. key是字符串
let myMap = new Map();
let keyString = "string"; 
 
myMap.set(keyString, "和键'string'关联的值");

// keyString === 'string'
myMap.get(keyString);    // "和键'string'关联的值"
myMap.get("string");   // "和键'string'关联的值"

// 2.key是对象
let myMap = new Map();
let keyObj = {}, 
 
myMap.set(keyObj, "和键 keyObj 关联的值");
myMap.get(keyObj); // "和键 keyObj 关联的值"
myMap.get({}); // undefined, 因为 keyObj !== {}

// 3. key也可以是函数或者NaN   

Map 的迭代

// 1.使用 forEach
let myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
 
// 0 = zero , 1 = one
myMap.forEach(function(value, key) {
  console.log(key + " = " + value);
}, myMap)

// 2. 也可以使用 for...of

Map 与 Array的转换

letkvArray = [["key1", "value1"], ["key2", "value2"]];
 
// Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
let myMap = new Map(kvArray);
 
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
let outArray = Array.from(myMap);

关于遍历forEach()和map()的区别和理解

var array = [10,34,57,43,76];  
var res = array.forEach(function (item,index,input) {  
       input[index] = item*10;  
})  
console.log(res);//--> undefined;  
console.log(array);//--> 通过数组索引改变了原数组;[100,340,570,430,760] 
//只是遍历数组中的每一项,不对原来数组进行修改,但是可以自己通过数组的索引来修改原来的数组 

var array = [10,34,57,43,76];  
var res = array.map(function (item,index,input) {  
       return item*10;   
})  
//map的回调函数中支持return返回值,相当于把数组中的这一项变为啥(并不影响原来的数组,只是相当于把原数组克隆了一份,把克隆这一份的数组中的对应项改变了 )
console.log(res);
console.log(array);不变

详细解析:

相同点:

  • 都是循环遍历数组中的每一项

  • 每一次执行匿名函数都支持三个参数,数组中的当前项item,当前项的索引index,原始数组input

  • 匿名函数中的this都是指window

  • 只能遍历数组

区别:

  •     forEach()方法不会返回执行结果,而是undefined
  •     map()方法会得到一个新的数组并返回
  •     同样的一组数组,map()的执行速度优于 forEach()(map() 底层做了深度优化)

性质决定了两者应用场景的不同

    1、forEach() 适合于你并不打算改变数据的时候,而只是想用数据做一些事情(比如存入数据库)

    let arr = [‘a’, ‘b’, ‘c’, ‘d’];
    arr.forEach((val) => {
    console.log(val); // 依次打印出 a,b,c,d
    });

    2、map() 适用于你要改变数据值的时候,它更快,而且返回一个新的数组

    let arr = [1, 2, 3, 4, 5];
    let arr2 = arr.map(num => num * 2).filter(num => num > 5);
    // arr2 = [6, 8, 10]

九、Promise

Promise是异步编程一种解决方案,解决回调地狱的噩梦

//es5
function fetchData(callback) {
  fetchData2((err, data) => {
    if (err) return handleRejected2;
    fetchData3((err, data) => {
      if (err) return handleRejected3;
      fetchData4((err, data) => {
        // ...
      });
    });
  });
}


//es6
//但是使用了 Promiise 之后,就可以使用 then 去链式调用:
const promise = new Promise(resolve, reject);
promise
  .then(fetchData, handleRejected1)
  .then(fetchData2, handleRejected2)
  .then(fetchData3, handleRejected3)
  .then(fetchData4, handleRejected4);

猜你喜欢

转载自blog.csdn.net/qq_41045128/article/details/125363612