JS新语法规范ECMAScript6(ES6)基础知识及核心原理

Posted 珠峰培训

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS新语法规范ECMAScript6(ES6)基础知识及核心原理相关的知识,希望对你有一定的参考价值。

使用Babel编译ES6

一、下载安装Babel
环境:需要电脑上安装node(node中一般都会自带npm包管理器)

npm install babel-cli -g 把模块安装在全局环境下(在任何的项目中,都可以使用命令来编译我们的代码了)
npm uninstall babel-cli -g 把全局下安装的babel模块卸载掉

观看安装目录发现一些细节需要了解的知识点:
1、我们后期之所以可以使用babel的命令,是因为安装在全局环境下之后,会生成一些 xxx.cmd 的文件,而这里的xxx就是可以在DOC窗口中执行的命令
babel.cmd 以后可以使用babel命令了
babel-node.cmd

2、执行babel命令后我们可以完成一些编译或者其它的任务,主要原因是执行babel命令后,会自动加载一些处理任务的文件

二、配置.babelrc文件,安装一些语言解析包
1、我们需要把.babelrc文件配置在当前项目的根目录下(这个文件没有文件名,后缀名是babelrc)
a:在电脑上不能直接创建没有文件名的文件,我们需要使用WS中的 new -> file 来创建,或者使用命令创建
b:babelrc这个后缀名在某些ws中是不识别的,它其实是一个json文件,我们需要在ws中配置一下(让他隶属于json文件)

2、在文件中编写一些内容

{
  "presets": [], //=>存放的是,我们编译代码时候需要依赖的语言解析包
  "plugins": [] //=>存放的是,我们编译代码时候需要依赖的插件信息
}

3、安装依赖的语言解析包
在当前项目的根目录下安装(不是安装在全局),需要特殊注意的是:要在当前项目根目录中打开DOC命令才可以
npm install babel-preset-latest 安装最新已经发布的语言标准解析模块
npm install babel-preset-stage-2 安装当前还没有发布但是已经进入草案的语言解析模块(如果你的代码中用到了发布非标准的语法,我们需要安装他)

安装成功后在自己的当前项目根目录下,会存在一个 node_modules文件夹,在这个文件夹中有我们安装的模块

4、完成最后.babelrc文件的配置

{
  "presets": [    "latest",    "stage-2"  ],
  "plugins": []}

三、使用命令编译JS代码
基本上所有支持命令操作的模块都有一个命令
babel --help / babel -h 查看帮助

babel --version / babel -V 查看版本号

babel --out-file / babel -o 把某一个JS文件中的ES6代码进行编译

babel --out-dir / babel -d 把某一个文件夹中所有的JS文件中的ES6代码进行编译

babel --watch / babel -w 监听文件中代码的改变,当代码改变后,会自动进行编译

结束监听的程序:ctrl+c 按两遍

ES6中的let和const

let基础语法

let 变量名 = 变量值

使用let创建变量和使用var创建变量的区别

1、let不存在变量提升机制

console.log(str);//=>undefined

console.log(fn);//=>FN本身

console.log(avg);//=>undefined

console.log(sum);//=>Uncaught ReferenceError: sum is not defined

console.log(num);//=>Uncaught ReferenceError: num is not defined

var str = '珠峰培训';
let num = 12;
function fn() {}
var avg = function () {};
let sum = function () {};
//=>ES6中只提供了创建变量的新语法标准(let),创建函数还是沿用ES5中的function(还会存在变量提升),如果想让函数也不存在变量提升,都使用函数表达式赋值的方式操作:let fn=function(){}//=>创建变量
let xxx=xxx;//=>创建函数
let xxx=function(){}//=>自执行函数;
(function(){ })();
//=>好处:此时代码中就不要在考虑变量提升了,只要这样处理,没有所谓的变量提升


> 2、使用let定义的变量不允许在`同一个作用域中`重复声明
```javascript
var num2 = 12;
var num2 = 13;
console.log(num2);//=>13

let str = '珠峰';
let str = '培训';
console.log(str);//=>Uncaught SyntaxError: Identifier 'str' has already been declared  当前报错,上面代码也不会执行(在JS代码执行之前就已经知道有重复声明的了,也就是浏览器依然存在类似于变量提升的机制:在JS代码之前先把所有LET声明的变量过一遍,发现有重复的直接报错)

let num = 12;
num = 13;
console.log(num);//=>13 LET不允许重复被声明,但是允许重新赋值

var att=200;
let att=100;//=>Uncaught SyntaxError: Identifier 'att' has already been declared 不管你之前使用什么方式在当前作用域中声明的变量,再使用let声明的时候都会报错
let num = 12,
    fn = function () {        
        let num = 13;    };
    console.log(num);//=>12 当前作用域下别重复声明即可(不同作用域中的变量是自己私有的,名字重复没有关系)
let att = 13,    sum = function () {        att = 14;    }; sum();
console.log(att);
//=>let也存在私有变量和作用域链的概念,和ES5中基本上差不多  =>14

3、关于暂时性死区:使用typeof检测一个未被声明过的变量
ES5中返回的结果是undefined但是不报错
ES6中直接报错

"use strict";
console.log(typeof num);
//=>undefined 当前变量不存在,但是使用typeof检测的时候,不会提示错误,而是返回undefined
console.log(typeof num);//=>Uncaught ReferenceError: num is not defined  ES6中检测一个没有被声明过的变量直接报错,不像之前ES5中的值是UNDEFINED一样了
let num;
let num;
console.log(typeof num);
//=>undefined 只声明没有定义(赋值),默认值是UNDEFINED
 
   
   
 

4、ES6语法创建的变量(let)存在块级作用域,ES5语法创建变量(var/function)没有块级作用域

[ES5]
window全局作用域
函数执行形成的私有作用域

[ES6]
除了有ES5中的两个作用域,ES6中新增加块级作用域(我们可以把块级作用域理解为之前学习的私有作用域:存在私有变量和作用域链的一些机制) ES6语法中把大部分用大括号包起来都称之为块级作用域

let num = 12,
    str = '';
   let fn = function (str) {    str = 'HELLO';    
   //console.log(arguments[0]);
   //=>"HELLO" 当前JS并没有开启严格模式,所以形参变量和ARG存在映射机制(但是我们以后尽量不要这样处理:因为把ES6编译为ES5之后,会默认的开启严格模式,映射机制会中断,此处的值依然是'珠峰',这样导致我们的ES6结果和ES5结果不一致)
   // console.log(num);
   //=>Uncaught ReferenceError: num is not defined
   let num = 13;    
   console.log(num, str);//=>13 "HELLO"
}; fn('珠峰');
console.log(num, str);//=>12 ''

大部分我们遇到的大括号操作都是块级作用域

/* if (10 >= 10) {    let total = 100; } console.log(total);//=>Uncaught ReferenceError: total is not defined 判断体也是一个块级私有作用域,在这个作用域中声明的变量是私有变量,在块级作用域之外是无法使用的 *//* for (let i = 0; i < 5; i++) {    console.log(i); } console.log(i);//=>Uncaught ReferenceError: i is not defined 循环体也是一个块级作用域(每一次循环都会形成一个新的块级作用域:当前案例形成五个块级作用域,每一个块级作用域中都有一个私有变量i,分别存储的是0~4) *//*{    let i=12; } console.log(i);//=>Uncaught ReferenceError: i is not defined 这是一个ES6中标准的块级作用域*//* let i=10; {    let i=20;    {        {            console.log(i);//=>Uncaught ReferenceError: i is not defined 虽然ES6没有变量提升,但是每一次进入当前作用域都会把LET定义的变量找一遍(不提升但是找了,找到了说明当前作用域中是有这个变量的,提前用都会报错)        }        let i=30;    } } *//* try{    let i=100; }catch (e){    let k=200; } console.log(k);//=>Uncaught ReferenceError: k is not defined console.log(i);//=>Uncaught ReferenceError: i is not defined TRY CATCH中的任何大括号都是块级作用域 *//* switch (10){    case 10:        let i=20;        break; } console.log(i);//=>Uncaught ReferenceError: i is not defined *//* let obj={name:'珠峰'}; for (let key in obj) { } console.log(key);//=>Uncaught ReferenceError: key is not defined *
 
   
   
 

块级作用域的增加有什么用?

let tempList = document.getElementsByName('TEMP');
// for (var i = 0; i < tempList.length; i++) {

// tempList[i].onclick = function () {
   //console.log(i);//=>5 怎么点击都是5 异步操作以及作用域链的查找,找到的都是全局下最后一次循环的结束值//     }// }//=>自定义属性解决// for (var i = 0; i < tempList.length; i++) {//     tempList[i].index = i;//     tempList[i].onclick = function () {//         console.log(this.index);//     }// }//=>闭包解决// for (var i = 0; i < tempList.length; i++) {//     ~function (i) {//         tempList[i].onclick = function () {//             console.log(i);//         }//     }(i);// }//=>使用ES6的块级作用域
for (let i = 0; i < tempList.length; i++) {    tempList[i].onclick = function () {        
       console.log(i);    } }

const的基础语法

const的细节知识点和let一样,和let的主要区别在于:let是创建变量,const是创建常量

变量:值是可以修改的
常量:值不能被修改

let num = 12;
num = 13;console.log(num);//=>13const str = '珠峰';
str = '培训';
//=>而且使用BABEL如果遇到了CONST设置的常量在进行修改,就无法进行编译了
console.log(str);
//=>Uncaught TypeError: Assignment to constant variable.

JS中创建变量方式汇总

var :ES5中创建变量
function:ES5中创建函数
ES5中创建变量或者函数存在:变量提升、重复声明等特征,但是没有块级作用域的机制

let:ES6中创建变量
const:ES6创建常量
ES6中创建的变量或者常量都不可以变量提升,也不可以重复声明,而且还存在块级作用域

class:ES6中创建类的方式
import:ES6中模块导入的方式

class Parent{
    constructor(){        
       //=>this.xxx=xxx    }    
   //=>Parent.prototype    aa(){}    
   //=>Parent own property    static bb(){} }

ES6中的解构赋值

按照原有值的结构,把原有值中的某一部分内容快速获取到(快速赋值给一个变量)

数组的解构赋值

结构赋值本身是ES6的语法规范,使用什么关键字来声明这些变量是无所谓的

let [a,b,c] = [12,23,34];
//=>a:12 b:23 c:34
var [d,e,f] = [12,23,34];
//=>d:12 e:23 f:34
[g,h,i] = [12,23,34];
//=>此处相当于给window增加的全局属性
//g:12 h:23 i:34
//=>但是这个操作在JS的严格模式下是不允许的,因为严格模式下不允许出现非使用var/let等声明的变量

多维数组的结构赋值,可以让我们快速获取到需要的结果

let [,[,A],[,B,[,C]]] = [12, [23, 34], [45, 56, [67, 78]]];
console.log(A, B, C);
//=>34 56 78

如果只想获取数组中前面的某几项内容,后面的结构不需要不全

let [D]=[12, 23, 34];
console.log(D);//=>12
let [,E]=[12, 23, 34];
console.log(E);//=>23
 
   
   
 

在解构赋值中,我们可以给某一项设置默认值

let [,,,A]=[12, 23, 34];
console.log(A);//=>undefined
let [,,,B = 0]=[12, 23, 34];
console.log(B);//=>0

在解构赋值中,支持...xxx的拓展运算符

let [A,...B]=[12, 23, 34, 45, 56];
console.log(A, B);//=>12 [23,34...]
let [...C]=[12, 23, 34, 45, 56];
console.log(C);//=>[12,23...] 数组克隆
let [D,...E,F]=[12, 23, 34, 45, 56];
console.log(D, E, F);
//=>Uncaught SyntaxError: Rest element must be last element 拓展运算符只能出现在解构赋值中的结构末尾的位置
let [G,,,...H]=[12, 23, 34, 45, 56];
console.log(G, H);//=>12 [45,56]

对象的解构赋值

let {name, age}={name: '珠峰培训', age: 9};console.log(name, age);//=>'珠峰培训' 9let {A, B}={name: '珠峰培训', age: 9};console.log(A, B);//=>在对象的解构赋值中需要注意的是:赋值的变量需要和对象中的属性名吻合,否则无法获取对应的属性值 undefined*2let {C = 0}={name: '珠峰培训', age: 9};console.log(C);//=>0 可以给当前的变量设置默认值
let {name}={name: '珠峰培训', age: 9};
console.log(name);//=>'珠峰培训'  和数组的解构赋值一样,我们可以把后面不需要获取的结构省略掉

let {,age}={name: '珠峰培训', age: 9};//=>Uncaught SyntaxError: Unexpected token , 和数组的解构赋值不一样的地方在于,对象前面不允许出现空来占位(因为对象获取需要通过具体的属性名获取,写成空的话,浏览器不知道怎么识别)

let {age}={name:'xxx',age:'xxx'};//=>但是我们可以把逗号去掉,这样就是只获取其中一个
let {name, ...arg}={name: '珠峰培训', age: 9, teacher: '周啸天'};
console.log(name, arg);
//=>'珠峰培训' {age:9...}  支持拓展运算符的
//=>把对象进行浅克隆(只把第一级克隆了)
let obj = {name: 'xxx', age: 10, score: [100, 90, 80]};
let {...arg}=obj;console.log(arg, obj);
console.log(arg === obj);//=>false
console.log(arg.score === obj.score);//=>true
let {name:A, age:B}={name: '珠峰培训', age: 9};
console.log(A, B);
//=>'珠峰培训' 9  在对象的结构赋值中,我们可以把对象的属性名起一个小名(A和B相当于小名或者叫做别名)
let data = [
    {        "name": "张三",        "age": 25,        "score": {            "english": [100, 90, 95],            "math": [100, 100, 100],            "chinese": [98, 99, 100]
        }
    },
    {        "name": "李四",        "age": 24,        "score": {            "english": [8, 9, 3],            "math": [149, 150, 148],            "chinese": [138, 140, 145]
        }
    }
];
let [{score:{english:[A], math:[,B], chinese:[,,C]}}]=data;
console.log(A, B, C);//=>100 100 100

解构赋值的作用

快速交换两个变量的值

let a = 12;let b = 13;
[a, b] = [b, a];
console.log(a, b);//=>13 12

接收函数返回的多个值

let fn = function () {    
       let a = 12,        b = 13,        c = 14;    
   return [a, b, c]; };
let [a,b,c] = fn();
console.log(a, b, c);//=>12 13 14

可以快速接收到传递的多个值(我传递的是一个数组或者对象)

let fn = function ([A,B,C,D = 0]) {    
   console.log(A, B, C, D);//=>12 23 34 0}; fn([12, 23, 34]);
//console.log(A);//=>Uncaught ReferenceError: A is not defined 函数中的ABC是私有的变量//=>快速处理options参数的初始化操作

let animate = function ({
   curEle = null, target = {},
   duration = 1000, callBack = null}={})
{    
   console.log(curEle, target, duration, callBack);
   //=>body {opacity: 1} 1000 null
}; animate({    curEle: document.body,    target: {opacity: 1} });
 
   
   
 

在ES6中支持给函数设置默认值

let fn = function (x) {    
   console.log(x);//=>undefined    x = x || 0;    
   console.log(x);//=>0}; fn();
let fn2 = function (x = 0) {    
   console.log(x);//=>0
}; fn2();

ES6中的箭头函数

箭头函数的基础语法

let fn = function (x, y) {    
   return x + y; };
console.log(fn(10, 20));
//=>30//=>改写成箭头函数
let fn = (x, y)=> x + y;
let fn = function (n = 0) {    
   let x = 10,        y = 20;    
   return x + y + n; };
//=>改写成箭头函数
let arrowFn = (n = 0)=> {    
   let x = 10,        y = 20;  
     return x + y + n; };

箭头函数中不支持arguments

//=>传统函数支持ARGUMENTS// let fn = function () {
//     let arg = Array.prototype.slice.call(arguments);
//     return eval(arg.join('+'));// };
let fn = (...arg)=> {    
   //console.log(arguments);
   //=>Uncaught ReferenceError: arguments is not defined
   //=>不支持ARGUMENTS没事,我们使用ES6中的剩余运算符...来获取传递的进来的所有参数值(优势:使用剩余运算符接收到的结果本身就是一个数组,不需要再转换了)    //console.log(arg instanceof Array);//=>true    return eval(arg.join('+')); };
//=>也可以把FN简写成以下方式
//let fn = (...arg)=> eval(arg.join('+'));
console.log(fn(10, 20, 30, 40));

箭头函数中的this问题

复习普通函数中this指向的问题

let obj = {
    name: 'obj',
    fn(){        
   //=>这样处理和下面SUM的处理是一样的        console.log(this);    },    sum: function () {    } }; obj.fn();
//=>this:obj 普通函数执行THIS的指向:看函数执行前面是否有点,有点,点前面是谁THIS就是谁,没有点THIS指向WINDOW或者UNDEFINED(严格模式下)
document.body.onclick = obj.fn;
//=>this:body
setTimeout(obj.fn, 1000);
//=>this:window
obj.fn.call(12);//=>this:12


 
   
   
 

箭头函数中没有自己的THIS指向,用到的THIS都是所在宿主环境(它的上级作用域)中的THIS

let obj = {
    name: 'obj',
    fn: ()=> {        
   console.log(this);        
       //=>不管我们怎么去操作,最后THIS都指向WINDOW:箭头函数中没有自己的THIS指向,用到的THIS都是所在宿主环境(它的上级作用域)中的THIS    } }; obj.fn();
document.body.onclick = obj.fn; setTimeout(obj.fn, 1000); obj.fn.call(12);

以后实战项目中,不是要把所有的函数都改为箭头函数,根据自身需要来修改即可(例如:我们需要让函数中的this是宿主环境中的this,我们才来使用箭头函数;或者不涉及this问题,我们想让代码写起来简单一些也可以使用箭头函数;)

   
let obj = {
    name: 'obj',
    fn(){        //=>this:obj

        // setTimeout(function () {
        //     //=>this:window
        // }, 1000);

        // setTimeout(function () {
        //     //=>this:obj
        // }.bind(this), 1000);

        // var _this = this;
        // setTimeout(function () {
        //     //=>_this:obj
        // }, 1000);

        setTimeout(()=> {            
           //=>this:obj        }, 1000);    } }; obj.fn();


箭头函数的一点扩充

宿主环境:不是执行的环境而是定义的环境

let fn = ()=> {    
   console.log(this); };
let obj = {    name: 'obj',    sum: function () {        
       //=>this:obj        fn();
       //=>this:window        //宿主环境:不是执行的环境而是定义的环境,FN虽然是在这执行的,但是它是在WINDOW下定义的,所以它的宿主环境还是WINDOW    } }; obj.sum();

层级嵌套的箭头函数

// let fn = function (i) {
//     return function (n) {
//         return n + (++i);
//     }
// };

let fn = (i)=> (n)=> n + (++i);

ES6中的类和继承

ES5中创建类和实例,以及如何禁止用户把类当做普通函数执行:new.target

function Person(name, age) {    
//console.log(new.target);//=>ES6新增加的语法,如果是通过NEW执行的,返回的结果是当前创建的类,如果是当做普通函数执行的,返回的是UNDEFINED    if (typeof new.target === 'undefined') {        
       throw new SyntaxError(`当前Person不能作为一个普通函数执行,请使用new Person来执行~~`);    }    //=>NEW执行的时候,THIS是当前类的实例,THIS.XXX=XXX是给当前实例增加的私有属性    this.name = name;    
   this.age = age; }//=>原型上存放的是公有的属性和方法:给创建的实例使用

Person.prototype = {    constructor: Person,    say: function () {        
   console.log(`my name is ${this.name},i am ${this.age} years old~`);    } };
//=>把PERSON当做一个普通的对象,给对象设置的私有属性
Person.study = function () {    
   console.log(`good good study,day day up~`); };
var p1 = new Person('王雪超', '80'); Person('王雪超', '80');

ES6中创建类

//console.log(Person);//=>Uncaught ReferenceError: Person is not defined 不存在变量提升class Person {
    constructor(name = '珠峰培训', age = 9) {        
   //=>给实例设置的私有属性        this.name = name;        
       this.age = age;    }    
   //=>直接在大括号中编写的方法都设置在类的原型上:ES6默认把CONSTRUCTOR的问题解决了,此时原型上的CONSTRUCTOR指向的就是PERSON    say() {        
       console.log(`my name is ${this.name},i am ${this.age} years old~`);    }    
   //=>把PERSON当做普通对象设置属性和方法,只需要在设置的方法前面加STATIC即可    static study() {        
       console.log(`good good study,day day up~`);    } }
let p1 = new Person('王雪超');
//Person();//=>Uncaught TypeError: Class constructor Person cannot be invoked without 'new' =>ES6中使用CLASS创建的类,天生自带NEW.TARGET的验证,不允许把创建的类当做普通函数执行

ES6中的继承

class Person {
    constructor(...ARG) {        
       let [x = 0,y = 0]=ARG;        
       this.x = x;        
       this.y = y;    }    sum() {        
       return this.x + this.y;    } }
class Child extends Person {  
   //=>创建CHILD类,并且让CHILD类继承了PERSON类:    //1、把PERSON中的私有属性继承过来设置给了子类实例的私有属性    //2、让子类实例的原型链上能够找到PERSON父类的原型(这样子类的实例就可以调用父类原型上的方法了)    //-------------    //=>我们可以不写CONSTRUCTOR,浏览器默认会创建它,而且默认就把父类私有的属性继承过来了(而且把传给子类的参数值也传递给父类了)    // constructor(...arg) {    //     //=>ARG:传递给子类的参数(数组) [剩余运算符]    //     super(...arg);//=>[展开运算符] 把ARG中每一项值展开,分别传递给父类方法 SUPPER(10,20,30)    // }    //------------    //=>很多时候我们不仅要继承父类私有的,还需要给子类增加一些而外私有的,此时就必须写CONSTRUCTOR,但是一定要在CONSTRUCTOR中的第一行写上SUPPER,否则会报错    // constructor(...arg) {    //     super(...arg);    //    //     let [,,z]=arg;    //     this.z = z;    // }    constructor(x, y, z) {        super();//<=>Person.prototype.constructor.call(this)        this.z = z;    }    fn() {    } }
let c = new Child(10, 20, 30);


以上是关于JS新语法规范ECMAScript6(ES6)基础知识及核心原理的主要内容,如果未能解决你的问题,请参考以下文章

ES6新语法学习

解构赋值 —— ES6新语法学习心得

第165天学习打卡(项目谷粒商城 7 ES6 ECMAScript6.0 )

let 和 coust —— ES6新语法学习心得

ES6 快速入门

es6简单小复习