深刻浅出ES6知识大合集

1.ES6的声明方式

ES6一共有三种声明方式:javascript

  1. var:是variable的缩写,全局变量;
  2. let:局部变量;
  3. const:常量

var声明
在ES6中,var被定义为全局变量,咱们作个测试:在区块中定义一个var的变量a,而后在区块外看看可否打印出来。java

{
    var a = 1;
}
console.log(a);
复制代码

你会发现,这个时候,a是能够打印出来的,这就说明var定义的是全局变量。node

let声明
let是ES6新引入的语法,是一个局部变量,咱们也作一个测试:在区块中定义一个let变量a,看看在区块外可否打印出来。es6

{
    let a = 1;
}
console.log(a);
复制代码

你会发现,这个时候,浏览器会报 a is not defined的错误,说明了let确实定义的是一个局部变量,只能函数内或者区块内访问。正则表达式

const声明算法

咱们在写代码的时候,可能有时候须要定义一个变量,这个变量在被定义之后,业务层就不会在对这个变量进行修改,简单的说,咱们须要定义一个只声明一次的常量。ES6很好的为咱们作到了这一点,这就是const。json

注意一点,const定义的常量最好使用大写字母,若是是两个字母,用_分开,好比const PRODUCT_ID。咱们一样作一个测试,看看const定义的常量是否能够被更改。数组

const FORM_ID = 1;
FORM_ID =2;
复制代码

这个时候你会发现,在编译的过程当中就出错了,会提示你"a" is read-only,意思很简单,当你定义了const的常量之后,就不容许在去修改了。promise

2.变量的解构赋值

这是ES6的一个特别实用的功能,ES6容许按照必定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。解构赋值在实际开发中能够大量减小咱们的代码量,而且让咱们的程序结构更清晰。浏览器

数组的解构赋值

之前,对变量赋值只能这样写:

var a = 1;
var b = 2;
var c = 3;
复制代码

那么如今,经过数组解构赋值的方式咱们能够这样简化了:

let [a,b,c] = [1,2,3];
复制代码

学过函数映射的同窗应该都能看懂,就是按照对应的位置关系进行赋值。注意,左边和右边必定要统一,否则会解构失败。

let [[a,b],c,[d,e]] = [[1,2],3,[4,5]];
复制代码

解构赋值可不止这么点功能,它仍是能够进行默认赋值的,那么咱们继续往下看:

解构的默认赋值

在看例子以前,咱们来解释一下解构的默认赋值:当对变量解构赋值的时候,若是没有赋值,或者赋值为undefined,那么变量就会读取默认赋值。

let [a=1] = [];
复制代码

这个时候,打印出来的a就是1.

let [a=1,b] = [undefined,2];
复制代码

这个时候打印出来的a的值一样是1.

注意:

let [a=1,b] = [null,2];
复制代码

此时打印出来的a的值是null,而不是1,那么这是为何呢?缘由很简单:ES6内部是使用严格相等运算符(===)判断一个位置是否有值的。因此,若是一个数组成员不严格等于undefined,默认值是不会生效的。

对象的解构赋值

对象的解构赋值与数组有一个不一样,数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。下面看个例子:

let {a,b,c} = {
    c:"3",
    a:"1",
    b:"2"
}
复制代码

这个时候,输出的a,b,c的值分别是1,2,3

其实,上面的写法是对象解构赋值的简写,真正的写法应该是:

let {
    a:a,
    b:b,
    c:c
} = {
    c:"3",
    a:"1",
    b:"2"
}
复制代码

其实对象的解构赋值的内部机制,是先找到同名属性,而后再赋给对应的变量。真正被赋值的是后者,而不是前者。若是以为不太懂,能够看下面的列子:

let {
    yuan:will
} = {
    yuan:1
}
复制代码

这个时候,当你console.log(yuan)的时候,会报错,说 yuan is not defined,而console.log(will),会发现will的值是1,如今明白了么,是否是很简单。

接下来,咱们来看看二次赋值的一个坑,上代码:

let a;
{a} = {a:1};
复制代码

这样在编译的时候就报错了,缘由就是解析器会将起首的大括号,理解成一个代码块,而不是赋值语句,解决方法很简单,用()包起来就能够了。

let a;
({a} = {a:1});
复制代码

固然啦,对象解构赋值也是能够设置默认值的,下面列一个很简单的默认赋值的demo:

let {a=1,b,c} ={
    a:undefined,
    c:3
}
复制代码

这个时候,打印出来的a,b,c分别是1,undefined,3,因此看出来了么,当你定义了变量,可是并无赋值的时候,是不报错的,而是赋值为undefined(b)

字符串的解构赋值

这个用的很少,因此我这个小菜鸡也没怎么去特别研究,写个例子吧:

const [A,B,C,D] = 'will';
复制代码

这个时候打印出来的A,B,C,D的值分别为w,i,l,l

用途

解构赋值除了对对象赋值之外,仍是有蛮多其余用途的,下面列一些大神总结的吧。

  • 交换变量的值

    [x,y] = [y,x];
    复制代码

  • 从函数返回多个值

    函数只能返回一个值,若是要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就很是方便。

    // 返回一个数组
    function example() { 
      return [1, 2, 3];
    }
    var [a,b,c] = example();
    
    // 返回一个对象
    function example() { 
      return { a: 1, b: 2 };
    }
    var {a,b} = example();
    复制代码

    这个函数方式用的仍是老的,后面的我都会用es6的语法,若是你们看不懂不要紧,能够直接跳转先看箭头函数那一块知识,再回头看前面内容。


  • 函数参数的定义

    解构赋值能够方便地将一组参数与变量名对应起来。

    //参数是一组有序的值
    let example =([a,b,c]) =>{
        ...
    }
    example([1,2,3]);
    
    //参数是一组无序的值
    let example =({a,c,b}) =>{
        ...
    }
    example({a:1,b:2,c:3});
    复制代码

  • 提取JSON数据

    let jsonData = {
        a:1,
        b:"2",
        c:[1,2]
    }
    let {a,b,c} = jsonData;
    复制代码

还有一些用途我就不一一列举了,感兴趣的同窗能够上网找。

3.扩展运算符和reset运算符

扩展运算符

如今有个问题问你们,如今要编写一个方法,它的参数是不肯定的,若是用es5的来写,是否是很复杂,可是es6轻松就能解决这个问题,那就是扩展运算符!

let example =(...arg) {
    console.log(arg[0]);
    console.log(arg[1]);
    console.log(arg[2]);
    console.log(arg[3]);
}
example(1,2,3);
复制代码

这个时候打印出来的会是1,2,3,undefined。这样就太好了,即便传值少了,也不会报错,只是打印undefined.

那么扩展运算符又有什么用处呢,下面咱们来看看:

1.数组的深度拷贝

首先来看一个es5的demo:

let arrOne = [1,2,3];
let arrTwo = arrOne;
arrTwo.push(4);
console.log(arrOne);
复制代码

这个时候你会发现,打印出来的arrOne是[1,2,3,4],这不是咱们想要的,因此呢,咱们就能够用扩展运算符解决,往下看:

let arrOne = [1,2,3];
let arrTwo = [...arrOne];
arrTwo.push(4);
console.log(arrOne);
复制代码

那么如今打印出来的arrOne就是[1,2,3],完美解决!

2.字符串转换数组

let str = 'will';
let arr = [...str];
console.log(arr);
复制代码

那么打印出来的数组就是['w','i','l','l'];

reset运算符

reset运算符也是经过...来表示,它和扩展运算符很是类似,你不须要去特别的区分。

let example =(a,...arg) => {
    for(var item of arg) {
        console.log(item);
    }
}
example(1,2,3,4,5,6);
复制代码

这个时候打印台会打印出2,3,4,5,6,for..of循环后面会有介绍,暂时你们能够理解成一个循环。

4.字符串模板和标签模板

字符串模板

首先,咱们先看一下之前咱们是怎么实现字符串拼接的:

let will = 'yuan';
let str = '欢迎您,' + yuan + ',咱们的好朋友';
复制代码

很显然,这样的方式既麻烦又容易出错,特别是在进行复杂的字符串拼接的时候,ES6的字符串模板就很好的解决了这个问题,咱们如今来看看ES6的字符串模板是怎么作的:

let will = 'yuan';
let str = `欢迎您,${will},咱们的好朋友`;
复制代码

除了支持变量插值,字符串模板仍是支持简单的计算的:

let str =`1+2的值是:${1+2};`
复制代码

这样打印出来的就是:1+2的值是:3

其实这只是很简单的两个案例,对于${}中的数据结构,不只仅能够是变量,还能够放入任意的JavaScript表达式,能够进行运算,以及引入对象属性。咱们看下面一个例子。

let example =(arr) => `
    <ul>
    ${
        arr.map((data) =>{
            return`<li>${data}</li>`
        }).join('')
    }
    </ul>
`
let node = example([1,2,3]);

document.write(node);
复制代码

这样打印出来的是什么呢,会是

那么提个小问题,我为何在后面加上join()方法呢,你们能够想一想。

标签模板

标签模板其实并非一个模板,和字符串模板是彻底不同的,你们千万不要按照字符串模板的思惟去理解。标签模板是函数的一种特殊的调用形式,紧跟在后面的字符串模板是这个函数的参数,你们不理解不要紧,咱们先看一个实例:

let a = 1;
let b = 2;
example`我是${a+b},你是${b-a*5}`
function example(arr,a,b){
    console.log(arr);
    console.log(a);
    console.log(b);
}
复制代码

先解释一下:这个example函数的第一个参数是一个数组,数组则是被变量分隔开来的每一个字符串,后面的参数则对应每一个字符串模板当中的变量,那么打印出来的结果就显而易见了:

须要注意的是,第一个打印出来的arr是三个数据,后面会有一个空字串,理由很简单,我就不解释了。

下面,咱们来看一个复杂的例子,也是标签模板比较有用的一个用途,就是防止用户的恶意输入。

function SaferHTML(templateData){
    let s = templateData[0];//templateData取的是第一个参数,那么就是arr数组,第一个那就是<p>
    let i;
    for(i = 1;i<arguments.length;i++){ //arguments数组包含的是所有的参数,在这个例子中,也就是[arr数组,变量];
        var arg = String(arguments[i]);
        s += arg.replace(/&/g,'&amp;')
                .replace(/</g,'&lt;')
                .replace(/>/g,'&gt;');
        s += templateData[i];
    }
    return s;
}
let sender = '<script>alert("abc")</script>';
let message = SaferHTML`<p>${sender} has sent you a message.</p>`;
console.log(message);
复制代码

这个例子上面的标注都已经说明了,你们能看得懂么?看不懂仔细钻研哦

字符串查找

在ES6以前,咱们都是使用indexOf()来查找字符串是否存在,indexOf()的强大并不纯粹只是字符串的查找,因此咱们缺乏了纯粹的字符串查找的方法,很幸运,ES6一共提供了三种方式:

  1. includes():该方法在给定文本存在于字符串中的任意位置时会返回 true ,不然返回false;
  2. startsWith():该方法在给定文本出如今字符串起始处时返回 true ,不然返回 false;
  3. endsWith():该方法在给定文本出如今字符串结尾处时返回 true ,不然返回 false;

这三个方法都提供了两个参数:

第一个参数:须要搜索的文本;

第二个参数:includes() 与 startsWith() 方法会从该索引位置(参数)开始尝试匹配;而endsWith() 方法则从字符串长度减去这个索引值的位置开始尝试匹配;

下面,咱们看看例子:

var a = "Hello world!";
console.log(a.includes("o")); // true
console.log(a.startsWith("Hello")); // true
console.log(a.endsWith("!")); // true

console.log(a.includes("o", 8)); // false
console.log(a.startsWith("o", 4)); // true
console.log(a.endsWith("o", 5)); // true
复制代码

这个很是简单,相信你们也都能看得懂,可是有两点须要注意:

  1. 虽然这三个方法使得判断子字符串是否存在变得更容易,也更加纯粹,但它们只返回了一个布尔值。若须要找到它们在字符串中的确切位置,则须要使用 indexOf() 和 lastIndexOf() ;
  2. 若是向 startsWith() 、 endsWith() 或 includes() 方法传入了正则表达式而不是字符串,会抛出错误。而对于indexOf()和lastIndexOf()这两个方法,它们会将正则表达式转换为字符串并搜索它

字符串复制

有的时候,一个字符串不止使用一次,须要重复使用,那么就须要用到字符串复制了,咱们来看:

let a = '*';
console.log(a.repeat(3));
console.log(a);
复制代码

注意一点:repeat()方法是不改变变量的,也就是并不会改变变量a的值

5.ES6增长的数字操做

在ES5中,有几个数字操做的全局函数,相信你们都比较熟悉,分别是isNaN(),isFinite(),parseInt(),parseFloat()。这些方法在ES5中都是window对象下的方法,在ES6中已经属于Number对象下了,如今咱们分别看一下区别:

isNaN()

首先,在ES5中,有两种写法:

isNaN(2) //false
window.isNaN(2) //false
复制代码

以上两种写法均可以,由于isNaN自己就是window对象下的一个方法,大部分人都会用第一种。如今来对比一下ES5和ES6下的isNaN()的区别:

ES5:会首先把非数值的参数转换成数值类型,再进行判断。

ES6:不会强制将参数转换成数字,只有在参数是真正的数字类型,且值为 NaN 的时候才会返回 true。

这个若是你们不理解不要紧,咱们先看例子:

isNaN('a')                //true 由于a没法装换成数字,因此返回true
Number.isNaN('a')         //false a是一个字符串,直接返回false

Number.isNaN(NaN);        // true
Number.isNaN(Number.NaN); // true
Number.isNaN(0 / 0)       // true


// 下面这几个若是使用全局的 isNaN() 时,会返回 true。
Number.isNaN("NaN");      // false,字符串 "NaN" 不会被隐式转换成数字 NaN。
Number.isNaN(undefined);  // false
Number.isNaN({});         // false
Number.isNaN("blabla");   // false

// 下面的都返回 false
Number.isNaN(true);
Number.isNaN(null);
Number.isNaN(37);
Number.isNaN("37");
Number.isNaN("37.37");
Number.isNaN("");
Number.isNaN(" ");
复制代码

相信经过上面那么多例子,你们差很少能明白了,那么为何ES6要这样干呢?由于在 JavaScript 中,NaN 最特殊的地方就是,咱们不能使用相等运算符(== 和 ===)来判断一个值是不是 NaN,由于 NaN == NaN 和 NaN === NaN 都会返回 false。所以,必需要有一个判断值是不是 NaN 的方法,那么Number.isNaN()方法就很幸运的被选中了!

isFinite()

这个方法和isNaN()在ES5和ES6中的区别特别相似:

ES5:会首先把非数值的参数转换成数值类型,再进行判断。

ES6:不会强制将一个非数值的参数转换成数值,这就意味着,只有数值类型的值,且是有穷的(finite),才返回 true。

一样咱们看个例子:

isFinite('0')                    //true,会先转换成数字0,0是有穷的,因此返回false
Number.isFinite('0')             //false,'0'是字符串,直接返回false
Number.isFinite(Infinity);       //false,Infinity是window对象下的一个常量,表示一个无穷数
复制代码

parseInt()

这个方法是函数解析一个字符串参数,并返回一个指定基数的整数,parseInt函数一样是从window对象下移植到Number对象下,可是它的做用没有任何变化。

parseInt()方法一共有两个参数:

第一个参数:要被解析的值。若是参数不是一个字符串,则将其转换为字符串(使用ToString抽象操做)。字符串开头的空白符将会被忽略。

第二个参数:一个介于2和36之间的整数(数学系统的基础),表示上述字符串的基数。好比参数"10"表示使用咱们一般使用的十进制数值系统。始终指定此参数能够消除阅读该代码时的困惑而且保证转换结果可预测。当未指定基数时,不一样的实现会产生不一样的结果,一般将值默认为10。

parseInt('12.3abc');         //返回12
Number.parseInt('12.3abc');  //返回12
复制代码

parseFloat()

和parseInt()同样,被移植到Number对象下,做用保持不变。

parseFloat()只有一个参数,就是要被解析的字符串。

parseFloat('12.3abc');            //返回12.3
Number.parseFloat('12.3abc');     //返回12.3
复制代码

上述那些方法都是es5自己就有,从全局函数转到了Number身上,下面,咱们看看ES6新出的一些方法和新定义的一些常量,首先,咱们来看ES6对Number对象的扩展。

Number.isInteger()

这个函数很简单,用来判断一个数是否为整数,可是有两点须要注意:

1.只有传入的自己就是数值,才会判断是不是整数,若是传入的是字符串或者数组等其余类型,是不会强制转换的,是直接返回false。

2.在javascript内部对整数和浮点数采用同样的存储方式,所以小数点后若是都是0的浮点数,都会被认为是整数

Number.isInteger(0)          //true
Number.isInteger(-1)         //true
Number.isInteger(2.3)        //false
Number.isInteger(2.0)        //true
Number.isInteger("10");      // false
Number.isInteger(true);      // false
Number.isInteger(false);     // false
Number.isInteger([1]);       // false
复制代码

除了上述两点之外,还有一点,不是这个方法产生的问题,可是输出的结果会让你很奇怪,咱们来看案例:

Number.isInteger(Number.MAX_SAFE_INTEGER - 1.2)   //true
Number.isInteger(0.09+0.01)                       //false
复制代码

其实输出这样的结果,并非这个方法出现了问题,而是Number.MAX_SAFE_INTEGER - 1.2最后获得的结果是9007199254740990,而0.09+0.01 获得的结果是0.09999999999999999。为何会这样呢?由于计算机是用二进制来存储和处理数字,不能精确表示浮点数,而JavaScript中没有相应的封装类来处理浮点数运算,直接计算会致使运算精度丢失


Number.MAX_SAFE_INTEGER Number.MIN_SAFE_INTEGER

这两个因为是一个系列的,因此一块儿介绍,在介绍这两个常量以前,得先解释一下什么是安全整数和非安全整数。

因为JavaScript可以精确表示的整数范围在-2^53到2^53之间(不包含2^53和-2^53),超过这个范围,JavaScript就没法精确的表示,因此这个范围内的整数被称为安全整数,不在这个范围内的则称为非安全整数。

对此,ES6对这个范围的边界定义了两个常量,最大值是:Number.MAX_SAFE_INTEGER,最小值是:Number.MIN_SAFE_INTEGER。 也就是:

Number.MAX_SAFE_INTEGER === Math.pow(2,53)-1      //true
Number.MIN_SAFE_INTEGER === -Math.pow(2,53)+1     //true
复制代码

Number.isSafeInteger()

Number.isSafeInteger()函数就是用来判断数值是不是安全整数,只有当传入的数值是数值而且是安全整数,才会返回false

Number.isSafeInteger(Number.MAX_SAFE_INTEGER);      //true
Number.isSafeInteger(Number.MIN_SAFE_INTEGER);      //true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1);  //false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1);  //false
Number.isSafeInteger('1');                          //false
复制代码

Number.EPSILON

这是一个常量,表示极小极小的数,它的值为:2.220446049250313e-16,这个值的出现是为了用来判断浮点数的计算偏差,若是浮点数计算获得的偏差不超过Number.EPSILON的值,就表示能够接受这样的偏差。

Number.EPSILON    //2.220446049250313e-16
复制代码

ES6不只仅是对Number对象作了扩展,对Math对象也作了扩展,下面咱们来看看:

Math.trunc()

这个方法首先会把传入的参数自动转化成数字类型,删除掉数字的小数部分和小数点,无论它是正数仍是负数。

Math.trunc(13.37)    // 13
Math.trunc(42.84)    // 42
Math.trunc(0.123)    //  0
Math.trunc(-0.123)   // -0
Math.trunc("-1.123") // -1
Math.trunc(NaN)      // NaN
Math.trunc("foo")    // NaN
Math.trunc()         // NaN
复制代码

Math.sign()

这个方法首先会把传入的参数自动转化成数字类型,而后判断这个数是整数仍是负数仍是零,这个方法一共有五个返回值,分别是:1, -1, 0, -0, NaN

Math.sign(3);     //  1
Math.sign(-3);    // -1
Math.sign("-3");  // -1
Math.sign(0);     //  0
Math.sign(-0);    // -0
Math.sign(NaN);   // NaN
Math.sign("foo"); // NaN
Math.sign();      // NaN
复制代码

Math.cbrt()

这个方法首先会把传入的参数自动转化成数字类型,而后算出这个数的立方根

Math.cbrt(NaN); // NaN
Math.cbrt(-1); // -1
Math.cbrt(-0); // -0
Math.cbrt(-Infinity); // -Infinity
Math.cbrt(0); // 0
Math.cbrt(1); // 1
Math.cbrt(Infinity); // Infinity
Math.cbrt(null); // 0
Math.cbrt(2);  // 1.25992104989487
复制代码

ES6除了给Math扩展了这三个函数外,还有不少,基本是高中数学课本的一些方法,你们有兴趣能够本身去研究,这里就不一一列举了。

对于数字运算,ES6还增长了一种运算符,即指数运算符**

3 ** 2  //9
3 ** 3  //27

let a = 1.5;
a **= 2; //2.25
复制代码

6.ES6新增的数组知识

ES5的数组已经很是强大了,可是ES6依然增长了一些颇有用的数组方法,咱们挨个看一下。

Array.from()

Array.from()方法能够经过两种方式来建立数组:

1.伪数组对象(拥有一个length属性和若干个索引属性的对象,例如arguments)

2.可迭代对象(能够获取对象中的元素,例如 Map和 Set )

该方法一共有三个参数:

1.想要转换成数组的伪数组对象或可迭代对象

2.若是指定了该参数,新数组中的每一个元素会执行该回调函数

3.可选参数,执行回调函数 (第二个参数) 时 this 对象

其实,Array.from()方法只是后面有一个可选的回调函数mapFn,让你能够在最后生成的数组上再执行一次 map 方法后再返回。也就是说 Array.from(obj, mapFn, thisArg) 就至关于 Array.from(obj).map(mapFn, thisArg)。 咱们来看几个例子:

1.经过伪数组对象建立新数组

function example() {
    console.log(Array.from(arguments))
}
example('s','d','e')                //["s", "d", "e"]
复制代码

2.经过可迭代对象建立新数组

let s = new Set(['foo', window]); 
Array.from(s);                      // ["foo", window]

Array.from('1234');                 // ["1", "2", "3", "4"] 
Array.form(1234);                   // []
复制代码

3.给数组传入第二个参数

function example() {
    console.log(Array.from(arguments,value => value + 2))
}
example(1,2,3)                      // [3, 4, 5]
复制代码

Array.of()

Array.of() 方法建立一个具备可变数量参数的新数组实例,而不考虑参数的数量或类型。

Array.of()和Array的构造函数很是类似,而Array.of()的出现也是为了解决Array的一个困惑(或者说是特性) => 在使用Array的构造函数的时候,当咱们new一个数字的时候,生成的是这个数字长度的数组,每一个位置的值都是undefined,而咱们new一个字符串的时候,又会生成一个该字符串为元素的数组。

const a = new Array('2')   // ['2']
const b = new Array(2)     // [undefined,undefined]
复制代码

而对于Array.of(),不管传入的是字符串仍是数字,都会生成一个该参数为元素的数组。

const a = Array.of('2')   // ['2']
const b = Array.of(2)     // [2]
const b = Array.of(2,3,4) // [2,3,4]
复制代码

find()实例方法

数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,全部数组成员依次执行该回调函数。

这个回调函数有三个参数,依次为当前的值、当前的位置和原数组。咱们来看实例:

注意,我看不少书里都说这个方法会返回符合条件的第一个值,若是找不到符合条件的则返回undefined,可是我写了几个demo发现并不会返回,除非本身去手动return,你们能够一块儿探讨,咱们来看写的案例。

[1,2,3,4,5].find(val => val > 3)     //4
复制代码

这个会返回4,缘由是由于箭头函数的特性,若是只写一行而且没有使用{},则默认增长return,咱们再看下面例子就明白了:

let a = [1,2,3,4,5].find(val => {val > 3})   //undefined
复制代码

这个时候我增长了{},并无去return,那么打印出来的就是undefined,说明没有去自动返回,而是须要主动返回,我猜想find的机制只是将你的数组循环出来,而后回调函数根据循环出来的元素本身去处理需求,当遇到return的时候,数组循环终止,返回这时候循环进去的value值(注意:并不会返回你本身return的值),咱们来看一个例子:

let a = [1,2,3,4,5].find((val,index) => {
    if(val < 3) {
        console.log('val:' + val)
    }else {
        return {
            'val' : val,
            'index' : index
        }
    }
})

console.log(a) //分别打印val:1  val:2  3
复制代码

从这个例子能够看出来,find()方法是在程序终止的时候,返回终止的那一刻的value值。

findIndex()实例方法

findIndex()方法和find()方法几乎同样,只是返回值变成了元素在数组的下标,就不详细介绍了,看个例子:

let a = [1,2,3,4,5].findIndex((val,index) => {
    if(val < 3) {
        console.log('val:' + val)
    }else {
        return {
            'val' : val,
            'index' : index
        }
    }
})

console.log(a) //分别打印val:1  val:2  2
复制代码

fill()实例方法

fill()方法是使用给定值去填充数组,该方法一共有三个参数,依次是:填充的值,起始填充位置,结束填充位置。

[1,2,3,4].fill('a')        // ["a", "a", "a", "a"]
[1,2,3,4].fill('a',1,3)    // [1, "a", "a", 4]
复制代码

includes()

这个方法与字符串的includes方法特别类似,只是把对字符串的匹配改为了对数组的匹配,因此就不详细介绍了,给你们一个例子吧。

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true
复制代码

7.遍历

因为遍历是一个很是重要的知识点,因此单独抽出来说一讲,并不局限于ES6新增的遍历方法。

1.for循环

这个是最基础也是ES5最经常使用的循环,这个太简单太经常使用了,就不解释了。

2.for..in 循环

for..in循环是专门为普通对象定义的循环,虽然能够用来循环数组,可是强烈建议必定不要用for..in循环去循环数组,必定不要,缘由你们能够去百度,我这里就不一一阐述了。

3.forEach 循环

forEach循环是ES5中循环数组的方法,该方法有两个参数:

第一个参数:必需,数组中每一个元素须要调用的函数,这个函数有三个参数,依次是:遍历的数组内容,数组的索引,原数组。

第二个参数:可选,传递给函数的值通常用 "this" 值。若是这个参数为空, "undefined" 会传递给 "this" 值。

let arr = [1,2,3,4];
arr.forEach((value,index,arr)=>{
    arr[index] = value * 2;
});
console.log(arr); // [2, 4, 6, 8]
复制代码

注意:使用forEach遍历数组的话,使用break不能中断循环,使用return也不能返回到外层函数。

let arr = [1,2,3,4];
arr.forEach((value,index,arr)=>{
    if(value === 3) {
        return;
    }
    arr[index] = value * 2;
});
console.log(arr); // [2, 4, 3, 8] 继续执行了value = 4的状况
复制代码

4.map循环

map循环和forEach循环很是类似,都是用来遍历数组中的每一项值的,不一样之处在于:

map的回调函数中支持return返回值;return的是啥,至关于把数组中的这一项变为啥(并不影响原来的数组,只是至关于把原数组克隆一份,把克隆的这一份的数组中的对应项改变了)

let arr = [1,2,3,4];
let newArr = arr.map((value,index,arr) => {
    return value * 2;
})
console.log(newArr); // [2, 4, 6, 8]
复制代码

5.filter循环

filter()函数实际上是循环原数组里的每一个元素,而后筛选出符合条件的元素,造成一个新的数组,参数和forEach()以及map()如出一辙。

let arr = [1,2,3,4];
let newArr = arr.filter((value,index,arr) => {
    return value % 2 == 0;
})
console.log(newArr); // [2,4]
复制代码

注意:必须return 不然获得的是空数组。

6.for..of 循环

上面的5个循环都是ES5具备的,而for..of循环是ES6新增的,也是最强大的一个循环,你们只须要明白几点:

  1. 这是最简洁、最直接的遍历数组元素的语法
  2. 这个方法避开了for-in循环的全部缺陷
  3. 与forEach()不一样的是,它能够正确响应break、continue和return语句

总的来讲,遍历数组,用for..of就对了!

首先,咱们具体来看看for..of如何遍历数组的:

let arr = ['yuan','will','js','es6'];
for(let value of arr) {
    console.log(value); // yuan will js es6
}
复制代码

这个是最简单的方式,增长break试试:

let arr = ['yuan','will','js','es6'];
for(let value of arr) {
    if(value === 'js') {
        break;
    }
    console.log(value); // yuan will
}
复制代码

这个结果说明是能够经过break跳出循环的,不只如此,咱们还能够经过数组的keys()方法来获取索引,经过数组的entrie()方法来获取键值对:

循环keys()获取索引

let arr = ['yuan','will','js','es6'];
for(let key of arr.keys()) {
    console.log(key); // 0 1 2 3
}
复制代码

循环entries()获取键值对

let arr = ['yuan','will','js','es6'];
for(let [key,value] of arr.entries()) {
    console.log('key值:' + key + '; value值:' + value);
}
复制代码

打印出来的结果以下:

for-of循环不只支持数组,还支持大多数类数组对象,例如DOM NodeList对象。同时也支持Map对象,Set对象(ES6新增对象,后面会讲到),固然也支持字符串的遍历,这些就不一一列举了。只须要记住:将来的JS可使用一些新型的集合类,甚至会有更多的类型陆续诞生,而for-of就是为遍历全部这些集合特别设计的循环语句。

有一点必定要注意:for-of循环不支持普通对象,但若是你想迭代一个对象的属性,你能够用for-in循环(这也是它的本职工做)或内建的Object.keys()方法:

let person = {
    id : 1,
    age : 18,
    name : 'will',
    address : 'shenzhen',
}

for (let key of Object.keys(person)) {
    console.log(key + "是:" + person[key]);
}
复制代码

打印出来的结果:

8.函数及其扩展

函数参数的默认值

ES5中,咱们并不能给函数的参数设置默认值,这让咱们一直很痛苦,咱们来看咱们之前是怎么作的:

function example(name) {
    name = name || 'yuan';
    console.log(name);
}

example('will');      // will
example();            // yuan 
example('');          // yuan =>这个结果并非咱们想要的
example(undefined);   // yuan
复制代码

这样写的缺点相信你们都能看出来也经历过,就是参数若是咱们赋值了,只是对应的布尔值是false,那么咱们对应的赋值并不起做用,因此咱们又从新修改了代码:

function example(name) {
    if (typeof name === 'undefined') {
        name = 'yuan';
    }
    console.log(name);
}

example('will');        // will
example();              // yuan
example('');            // ''
example(undefined);     // yuan
复制代码

这样就达到了目的,可是显得很麻烦,由于咱们的要求很简单,只是参数默认赋值而已,ES6为咱们作到了:

function example(name = 'yuan') {
    console.log(name);
}
example('will');        // will
example();              // yuan
example('');            // ''
example(undefined);     // yuan
复制代码

注意:函数的参数默认赋值并非传值方式的,而是每次都从新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。

let x = 1;
function example(p = x + 1) {
    console.log(p);
}

example();    // 2

x = 100;
example();    // 101
复制代码

函数的length属性

若是一个函数的某个参数具备默认值,那么函数将计入的length的数量为这个参数以前的没有设置默认属性的参数个数。看起来很难读懂,其实很简单,咱们看个例子:

(function(a){}).length         // 1
(function(a=1){}).length       // 0
(function(a,b=1,c){}).length   // 1
复制代码

第一个函数,并无设置参数默认值,有一个参数,因此返回1;第二个函数,设置了参数默认值且没有不设置默认值的参数,返回0;第三个,b设置了默认参数,b前面有一个没有设置默认参数的参数a,返回1(注意,并不会去计算b后面的c)。

函数的name属性

函数的name属性,返回该函数的函数名。

function example() {}
console.log(example.name); // example
复制代码

这个属性早就被浏览器普遍支持,可是直到 ES6,才将其写入了标准。

须要注意的是,ES6 对这个属性的行为作出了一些修改。若是将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。

var example = function () {}

//ES5
console.log(example.name);  // ''

//ES6
console.log(example.name);  // example
复制代码

若是将一个具名函数赋值给一个变量,则 ES5 和 ES6 的name属性都返回这个具名函数本来的名字。

var exa = function example () {}

//ES5
console.log(exa.name);   // example

//ES6
console.log(exa.name);   // example
复制代码

箭头函数

箭头函数是ES6更新的很是重要的一个功能,咱们来看一下基本用法:

var example = value => value;
复制代码

上面的箭头函数等同于:

var example = function(value) {
  return value;
};
复制代码

若是箭头函数不须要参数或须要多个参数,就使用一个圆括号表明参数部分。

var example = () => 5;
// 等同于
var example = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};
复制代码

若是箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,而且使用return语句返回。

var sum = (num1, num2) => { return num1 + num2; }
复制代码

因为大括号被解释为代码块,因此若是箭头函数直接返回一个对象,必须在对象外面加上括号,不然会报错。

// 报错
let getTempItem = id => { id: id, name: "Temp" };

// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });
复制代码

若是箭头函数只有一行语句,且不须要返回值,能够采用下面的写法,就不用写大括号了。

let example = () => 5;
复制代码

箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不能够看成构造函数,也就是说,不可使用new命令,不然会抛出一个错误。

(3)不可使用arguments对象,该对象在函数体内不存在。若是要用,能够用 rest 参数代替。

(4)不可使用yield命令,所以箭头函数不能用做 Generator 函数。

Object.is()方法

这个方法是用来进行对象比较的方法,固然啦,也能够进行其余值的比较,咱们来对比一下它和==以及===的区别:

==: 等同,比较运算符,两边值类型不一样的时候,先进行类型转换,再比较;

===: 恒等,严格比较运算符,不作类型转换,类型不一样就是不等,也就是同值相等;

Object.is(): 比较两个值是否严格相等的方法,只有严格同样才返回true;

let obj1 = {name:'yuan'};
let obj2 = {name:'yuan'};
obj1.name === obj2.name;            // true
Object.is(obj1.name,obj2.name);     // true
-0 === +0;                          // true
NaN === NaN;                        // false
Object.is(-0,+0);                   // false
Object.is(NaN,NaN);                 // true
复制代码

9.Symbol对象

Symbol类型的基本写法

ES5 的对象属性名都是字符串,这容易形成属性名的冲突。好比,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。若是有一种机制,保证每一个属性的名字都是独一无二的就行了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的缘由。(注:这部份内容不少都是摘抄于阮大神的书)

ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

Symbol 值经过Symbol函数生成。这就是说,对象的属性名如今能够有两种类型,一种是原来就有的字符串,另外一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,能够保证不会与其余属性名产生冲突。

首先,咱们来看一下最简单的Symbol类型的声明:

let s = Symbol();
typeof s;          //symbol
复制代码

Symbol函数能够接受一个字符串做为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。可是注意,Symbol函数的参数只是表示对当前 Symbol 值的描述,所以相同参数的Symbol函数的返回值是不相等的。

let s1 = Symbol('will');
let s2 = Symbol('yuan');
let s3 = Symbol('will');

console.log(s1);
console.log(s2);

console.log(s1.toString());
console.log(s2.toString());

console.log(s1 === s2);
复制代码

打印出来的结果是:

Symbol做为属性名

因为每个 Symbol 值都是不相等的,这意味着 Symbol 值能够做为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的状况很是有用,能防止某一个键被不当心改写或覆盖。

let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都获得一样结果
a[mySymbol] // "Hello!"
复制代码

注意,Symbol 值做为对象属性名时,不能用点运算符。

const mySymbol = Symbol();
const a = {};

a.mySymbol = 'Hello!';
a[mySymbol] // undefined
a['mySymbol'] // "Hello!"
复制代码

上面代码中,由于点运算符后面老是字符串,因此不会读取mySymbol做为标识名所指代的那个值,致使a的属性名其实是一个字符串,而不是一个 Symbol 值。

属性名的遍历

Symbol 做为属性名,该属性不会出如今for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。这样就很好的保护了Symoble类型的属性,可是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,能够获取指定对象的全部 Symbol 属性名。

const obj = {};

let s1 = Symbol('will');
let s2 = Symbol('yuan');

obj[s1] = 'hello';
Object.defineProperty(obj,s2,{
    value: 'world'
});

obj.s3 = 'shen';

for(let i in obj) {
    console.log(i);                                 // s3
}

console.log(Object.getOwnPropertyNames(obj));       // ['s3']

console.log(Object.getOwnPropertySymbols(obj));     // [Symbol(will), Symbol(yuan)]
复制代码

上面这个例子,能够发现经过for循环并不能循环出来Symbol类型的属性名,可是Object.getOwnPropertySymbols()方法又不能拿到字符串类型的属性名,那么咱们若是要拿到一个对象所有的属性名怎么办呢,能够用Reflect.ownKeys()方法:

let obj = {
    'age' : 20,
    'address' : 'shenzhen',
    [Symbol('name')] : 'will',
    [Symbol('id')] : 5
}

console.log(Reflect.ownKeys(obj));

for(let i of Reflect.ownKeys(obj)) {
    console.log(obj[i]);
}
复制代码

打印出来的结果就是:

Symbol.for(),Symbol.keyFor()

有时,咱们但愿从新使用同一个 Symbol 值,Symbol.for方法能够作到这一点。它接受一个字符串做为参数,而后搜索有没有以该参数做为名称的 Symbol 值。若是有,就返回这个 Symbol 值,不然就新建并返回一个以该字符串为名称的 Symbol 值。

let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');

s1 === s2 // true
复制代码

注意:Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,若是不存在才会新建一个值。也就是说,经过Symbol()生成的Symbol并不会被检索到,咱们看个例子:

let s1 = Symbol('will');
let s2 = Symbol.for('will');
let s3 = Symbol.for('will');

console.log(s1 === s2);      //false
console.log(s2 === s3);      //true
复制代码

Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key

let s1 = Symbol('will');
let s2 = Symbol.for('will');

console.log(Symbol.keyFor(s1));   // undefined
console.log(Symbol.keyFor(s2));   // 'will'
复制代码

Symbol类型主要用于函数的属性,Symbol还有不少的方法,可是用的不是不少,因此就不一一列举了,你们想了解更加详细的,能够去看阮一峰大神的书。

10.Set数据结构

Set是ES6提供的一种新的数据结构,它很是相似于数组,可是它的成员值都是惟一的,就算添加剧复的值,也会自动删除掉,而且计算Set的size的时候不会计算重复的值。

Set接受一个数组来进行初始化(也能够不接受任何参数)

const set1 = new Set();

const set2 = new Set([1,2,3,4,5]);

const set3 = new Set([1,2,3,4,5,4,3,2,1]);

console.log (set1);
console.log (set2);
console.log (set3);
复制代码

打印结果:

因为Set是没有重复值的,因此咱们能够利用这个特性进行数组去重:

let arr = [1,2,3,4,5,6,4,3,4,5,2];

let newArr = [...new Set(arr)];          // 方法一

let newArr1 = Array.from(new Set(arr));  // 方法二
复制代码

注意:Set内部判断值是否重复的算法是“Same-value equality”,相似于===,不一样之处在于NaN对于这种算法来言是相等的。

let set1 = new Set(['1',1,2,NaN,NaN]);

console.log(set1);
复制代码

打印结果是:

Set和数组同样,也是能够增删查改的,不一样的是方法不同,如下说明:

1.add(value) : 添加某个值,返回 Set 结构自己

2.delete(value) : 删除某个值,返回一个布尔值,表示删除是否成功

3.has(value) : 返回一个布尔值,表示该值是否为Set的成员

4.clear() : 清除全部成员,没有返回值

如下是demo:

let set = new Set();

set.add(1).add(2).add(3);

console.log(set);
console.log(set.has(3));

set.delete(3);

console.log(set.has(3));

set.clear();

console.log(set);
复制代码

打印结果:

和数组同样,set数据结构也是能够进行遍历的,可是注意一点,set是没有键名的(也能够认为键名和键值同样),因此在遍历keys和values的时候,返回值同样。

let set = new Set([1, 2, 3, 4, 5]);

for (let i of set) {
    console.log(i);                         // 1 2 3 4 5
}

for (let i of set.keys()) {
    console.log(i);                         // 1 2 3 4 5
}

for (let i of set.values()) {   
    console.log(i);                         // 1 2 3 4 5
}

for (let [key,value] of set.entries()) {
    console.log(key + ':' + value);         // 1:1 2:2 3:3 4:4 5:5
}
复制代码

咱们在数组中,常常会存放对象,而WeakSet为咱们作到了,不过用的不多,能够稍做了解便可。

WeakSet 是一个构造函数,可使用new命令,建立 WeakSet 数据结构,可是须要注意两点,一是在new的时候不容许放入值,不然会报错,二是WeakSet里面的值也是不容许重复的,这里的不容许重复指的是不能指向同一内存块。

let objSet = new WeakSet();

let obj1 = {name:'will',age:18};

let obj2 = {name:'will',age:18}; //因为obj和obj2是指向不一样内存块,因此在WeakSet里面是都会添加进去的

let obj3 = obj1;                  //因为obj和obj3是指向不一样同一内存块,因此在WeakSet里面是只会添加以此

objSet.add(obj1);
objSet.add(obj2);
objSet.add(obj3);

console.log(objSet);  //WeakSet {{…}, {…}}
复制代码

11.Map数据结构

Map是ES6提供的一种新的数据结构,相似于对象,也是键值对的集合,可是“键”的范围不限于字符串,各类类型的值(包括对象)均可以看成键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。若是你须要“键值对”的数据结构,Map 比 Object 更合适。

Map构造函数接受一个数组做为参数(固然也能够不传参数)

let map1 = new Map();

let map2 = new Map([['name','yuan'],['age',18]]);

console.log(map1);
console.log(map2);
复制代码

打印结果:

Map能够将对象做为键值:

let map = new Map();

let obj = {name:'yuan','age':18};

map.set(obj,'will');

console.log(map);
复制代码

打印结果:

Map一样有增删查改方法,如下说明:

  1. set(key,value) : 设置键名key对应的键值为value,而后返回整个 Map 结构。若是key已经有值,则键值会被更新,不然就新生成该键
  2. get(key) : 取key对应的键值,若是找不到key,返回undefined
  3. has(key) : 返回一个布尔值,表示某个键是否在当前 Map 对象之中
  4. delete(key) : 删除某个键,返回true。若是删除失败,返回false
  5. clear() : 清除全部成员,没有返回值
  6. size : 这个是属性,不是方法,返回 Map 结构的成员总数

如下是demo:

let map1 = new Map();

map1.set('string', true);           // 键是字符串

map1.set({name:'yuan'},false);      // 键是对象

map1.set(1,true);                   // 键是数字

map1.set(undefined,false);          // 键是undefined

console.log(map1);

console.log(map1.get(1));           // true
console.log(map1.get(undefined));   // false

console.log(map1.has(undefined));   // true
console.log(map1.has('string'));    // true
console.log(map1.has('1'));         // false

map1.delete(1);

console.log(map1.size);             // 3

map1.clear();

console.log(map1)
复制代码

打印结果:

有一点须要注意:Map 的键其实是跟内存地址绑定的,只要内存地址不同,就视为两个键。若是 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 将其视为一个键,好比0和-0就是一个键,布尔值true和字符串true则是两个不一样的键。另外,undefined和null也是两个不一样的键。虽然NaN不严格相等于自身,但 Map 将其视为同一个键。

let map = new Map();

let obj1 = {name:'yuan'};
let obj2 = {name:'yuan'};

map.set(obj1,1);
map.set(obj2,2);

console.log(map.get(obj1));     // 1
console.log(map.get(obj2));     // 2

map.set(-0,true);
console.log(map.get(+0));       // true

map.set(true,5);
console.log(map.get('true'));   // undefined

map.set(undefined,6);
map.set(null,7);
console.log(map.get(undefined));// 6

map.set(NaN,8);
console.log(map.get(NaN));      // 8
复制代码

ES6的基本知识差不都就这些了,相似promise,class等没有作讲述,只是讲述了基础的知识,上述内容有部分摘抄于阮一峰大神的ES6书籍,也有部分借鉴了网上各位大神的资料,我只是作了部分的概括和总结,有不少不足之处或者不完善的地方,但愿多多包容!

相关文章
相关标签/搜索