前端进阶学习——js高级之es6

ES6

1.ES6 介绍

  • ECMAScript 6,简称ES6 ,目标是使JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
  • ES6与javascript的关系:ECMAScript是一种语言标准,Javascript实现了这个标准。
  • ES6 与 ECMAScript2015的关系
    • ECMAScript2015 是具体到2015年6月发布的那一版。
    • ES6有两层含义
      • 特指ECMAScript2015
      • 泛指ES2015及之后的新增特性,虽然之后的版本应当称为ES2018,ES2019…但可统称为ES6。

学习内容

ES6新增了很多强大的语法,如下是比较常使用的:

  1. let 和 const
  2. 解构赋值
  3. 函数扩展
  4. 字符串扩展
  5. 数组扩展
  6. 对象的简写

学习参考

  1. 各种环境中对es6的支持程度: http://kangax.github.io/compat-table/es6/
  2. ES6电子书: http://es6.ruanyifeng.com/#docs/intro

2.let 变量

let作用:定义变量。(var也是用来定义变量)

我们之前定义变量的关键字是var,它定义的变量有很多奇怪的特点,如下有两点比较突出:

  • 变量先使用再定义------ 变量提升。

    console.log(a);  //undefined
    var a = 1;
    console.log(a)   //1
    
  • 缺少块级作用域。

    // 此处为了使用循环,定义一个循环变量i。
    for(var i=1; i<10;i++){}
    // i在循环体之外也能使用。
    console.info(i);   //10
    

这两点都容易让初学者混淆,也容易让从其它语言转过来的程序员混淆。为了解决这些问题,ES6中新增了let。

2.1.let基本使用

格式: let 变量名 = 变量值;

它用来定义变量,基本使用格式与var关键字一样。在可以在使用var 的地方改成let。

2.2.let与var的区别

  1. 不能重复定义
// let不能进行重复定义
let num = 100;
console.log(num);   //100

let num = 300;   // 报错Uncaught SyntaxError: Identifier 'num' has already been declared
console.log(num); 
  1. 没有变量提升(var定义的变量是有变量提升的),必须先定义再使用
// let不会进行变量提升
console.log(num); // 报错 Uncaught ReferenceError: Cannot access 'num' before initialization
let num = 100; 
  1. 全局变量不会附加到window对象的属性中
//let声明的变量不会成为window的成员
let num = 100;
console.log(num,window.num);  //100 undefined
  1. 具有块级作用域
// let声明的变量具有块作用域
// 所有的代码块都具有作用域
// 注意:对象字面量{}不是代码块!

if (true) {
    let num = 100;
}
console.log(num);      // 报错,num is not defined

for (let i = 0; i < 10; i++) {
    console.log(i);
}
console.log(i, 'for执行之后,外面书写的i的访问');   // 报错: i is not defined

// ES6提供了一种单独的块作用域书写形式
{
    let num1 = 100, num2 = 200;
    console.log(num1 + num2);
}
console.log(num1, num2); // num1 is not defined

(function () {
    var num = 100;
})();

案例应用

点击li,输出索引值

<ul>
    <li>这是li</li>
	<li>这是li</li>
	<li>这是li</li>
	<li>这是li</li>
</ul>

<script>
    // 需求:点击li,输出索引值
    var lis = document.getElementsByTagName('li');
	for (let i = 0; i < lis.length; i++) {
    // lis[i].setAttribute('data-index', i);
    // lis[i].dataset.index = i;
    // lis[i].index = i;
    lis[i].onclick = function () {
        // 输出索引值
        console.log(i);
        // console.log(this.getAttribute('data-index'));
        // console.log(this.dataset.index);
        // console.log(this.index);
    };
}

console.log(i);
</script>

3.const 常量

ES6引入const的原因是,因为ES5中并没有提供常量的功能。

使用场景

程序员在协同开发项目时,会遇到一种场景:有一些数据大家都需要用到,但是都不能修改,即数据是只读的。

举个例子:在开发公司的网站时,公司的基本信息:地址,公司名,电话等等信息可以在多个地方都需要使用,但不允许去修改。 显然,使用变量来保存这些信息是不适合的,因为变量不是只读的。这种情况下, 我们就可以把这些数据保存在常量中。

语法格式及命名规范

作用:定义一个只读的常量。

格式: const 常量名 = 常量值;

示例:var COMPANY_NAME = “XXX公司”

注意:区别于变量名,常量名推荐采用全大写的方式,多个单词之间使用_下划线分隔

3.1.const特点

所谓常量,就是不能修改,即:

  1. 一旦定义就不能修改,不能进行重新赋值 ;
const num = 100;
console.log(num);  //100

num = 300;         // Uncaught TypeError: Assignment to constant variable.
  1. 一旦声明,就必须立即初始化;
// 2 声明常量时必须设置初始值
const num;        // Uncaught SyntaxError: Missing initializer in const declaration

const与let相似的特点

  • 具有块级作用域
  • 不能重复声明
  • 没有变量提升,必须先定义再使用,声明之前不能访问
  • 常量也是独立的,定义后不会添加为window对象的属性

3.2.const本质

基本数据类型在内存单元中保存的是具体值(值本身)

复杂数据类型在内存单元中保存的是地址

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存单元中保存的数据不得改动

// 1 保存了基本类型的常量完全不能修改
const NUM = 100;
console.log(NUM);
NUM = 2;      //Uncaught TypeError: Assignment to constant variable.

// 2 保存了复杂类型的常量不能重新赋值为其他值,但是内部属性可以修改
const obj = {
    name: 'jack',
    age: 18
};
obj = 3;               // 不能直接修改obj中保存的数据

obj.name = 'rose';     //内部属性可以修改
console.log(obj);  
  1. 对于简单类型数据(数值、字符串、布尔值),值就保存在变量指向的那个内存单元,因此等同于常量值。

  2. 对于复杂类型数据(如对象和数组),变量指向的内存单元,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就不能控制了。因此,将一个对象声明为常量必须非常小心,它的属性是可以修改的。

定义一个不能修改的对象(属性不能添加,修改,删除),可以使用Object.freeze()。

// 冻结操作:可以让某个对象的属性无法修改
// 如果希望某个对象完全无法修改,可以使用const+冻结操作
// 注意:冻结只是对象的属性无法修改,属性的属性还是可以修改
// 需要通过递归来对某个对象进行冻结操作即可
// 封装用于深冻结的函数:
function deepFreeze(obj) {
    // 1 先通过Object.freeze()对对象的属性进行冻结
    Object.freeze(obj);
    // 2 遍历obj获取所有属性
    for (var k in obj) {
        // 3 检测当前属性值是否为复杂类型
        if (typeof obj[k] === 'object' && obj[k] !== null) {
            // console.log(k + '是复杂类型值');
            // 4 进行递归调用
            deepFreeze(obj[k]);
        }
    }
}

3.3.var、let和const

关键字变量提升块级作用域是否必设初始值是否可更改是否是顶级对象属性
let×-YesNo
const×YesNoNo
var×-YesYes

4.解构赋值

// 为什么需要解构赋值呢?
var arr = [98, 87, 86, 78];
// 如果希望多次使用数组中的某个元素,可以声明变量保存,但是书写较为繁琐。
var a = arr[1];
var b = arr[2]
console.log(a,b); 

解构赋值可以让数组的取值和变量的声明操作简化

ES6提供了一个更加方便的方式从对象中取出属性值来,这就是解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

它有两个动作:

//解构:意思是把有结构的数据分解开成为一个一个的值
//赋值:把解构之后的值保存到变量

4.1.解构赋值的基本使用

let [变量1=默认值1, 变量2=默认值2, 变量n=默认值n] = [数组元素1, 数组元素2, 数组元素n];

注:默认值是可选的,可以设置,也可以不设置;

规则

  1. 赋值符号左右两边都是数组,把右边的数组按下标从小到大取出来,放入左边的数组中。

  2. 如果左边的变量并没有分配到某一个值:

​ 有默认值,使用其默认值。

​ 无默认值,其值是undefined

  1. 最开始的let可选,也可写成var或省略,与解构赋值功能无关,仅代表变量的声明方式。

4.2数组解构赋值

它能够快速从数组中取出值保存到变量中。它的本质是给变量赋值。

// 1.左右的结构需要对应,左侧表示要声明的变量,赋值给右侧对应的元素值
// 常规使用:变量个数等于数组长值
var arr = [98, 87, 86, 78];
var [a, b, c, d] = arr;
console.log(a, b, c, d);          //98, 87, 86, 78

// 2 左右数据个数不对应
var arr = [98, 87, 86, 78];
var [a, b] = arr;                 //变量个数小于数组长度
console.log(a, b);                //98, 87

var arr3 = [98, 87, 86, 78];
var [a, b, c, d, e, f] = arr3;    //变量个数大于数组长度
console.log(a, b, c, d, e, f);    //98 87 86 78 undefined undefined

// 3 通过跳项,跳过某个元素取值
var arr = [98, 87, 86, 78];
var [a, , , c] = arr;             //用空跳过不需要的值
console.log(a, c);                // 98 78 

// 4 解构赋值的默认值
// 设置默认值后,如果可以取到值,就使用取到的值,
// 如果没有取到值,使用默认值
var arr = [98, 87, 86, 78];
var [a, b = 1000, c, d, e, f = 200, g] = arr;
console.log(a, b, c, d, e, f, g);    //98 87 86 78 undefined 200 undefined

// 5 剩余值
// 在解构左侧的最后一个名称前设置...,表示获取剩余值
// 指的是将剩下的数据保存在一个新数组中
// 如果剩余值功能不在最后一个名称使用,会报错  
//Uncaught SyntaxError: Rest element must be last element
//rest element 剩余值元素
var arr = [1, 5, 4, 6, 8, 2, 3];
// let [a, b, ...c, d] = arr;     // 报错写法
var [a, b, ...c] = arr;    //... 只能用在最后一个变量上。这个变量一定是一个数组
console.log(a, b, c);     //1 5 (5) [4, 6, 8, 2, 3]

// 6 复杂嵌套场景
var arr = [1, 2, [3, 4, [5, 6, 7]]];
let [, a, [, b, [, c]]] = arr;      
console.log(a, b, c);    //2   4   6

解构赋值的面试题

如何交换两个变量的值?

let a = 1, b = 2;       // 写代码,实现互换a,b的值
 
[a, b] = [b, a];

console.log(a,b);      // 要求输出 2, 1

4.3.对象解构赋值

作用:快速从对象中获取属性值保存到变量中。它的本质是给变量赋值。

let {"属性名1":变量名1=默认值1, "属性名2":变量名2=默认值2,... } = {"属性名1":属性值1,"属性名2":属性值2,...}

解构规则

  1. 默认值是可选的。你可以指定默认值,也可以不指定。

  2. 右边的"属性名"与左边的“属性名” 一致,则把右边的属性值赋值给左边的变量名。

  3. 如果右边的匹配不成立,看看是否有使用默认值,有默认值就使用默认值,没有就是undefined。

如果左侧对象中属性名与变量名相同,则可左侧合并:

let {变量名1=默认值1,变量名2=默认值2} = {"属性名1":属性值1,"属性名2":属性值2,...}

解析规则:右边的"属性名"与左边的变量名 一致,则把右边的属性值赋值给左边的变量名。

基本使用

// 1.基本使用
// 对象是无序的存储方式,进行解构时,默认按照属性名进行取值
// 注意:进行解构时,只要属性名称对应即可,顺序无所谓
var obj = {
    username: 'jack',
    age: 18,
    password: '123456'
};
var { username, password, age } = obj;
console.log(username, age, password);     //jack 18 123456

// 2 属性个数不对应
// 无需使用逗号进行分割,直接书写要获取的属性名即可
var obj = {
    username: 'jack',
    age: 18,
    password: '123456',
    gender: '男'
};
var { gender, age } = obj;
console.log(gender, age);    //男 18
var { school, username, gf } = obj;
console.log(school, username, gf);  //undefined "jack" undefined

// 3 属性别名
// 一定注意,冒号后面的名称才是变量名称
var obj = {
    username: 'jack',
    age: 18,
    password: '123456',
    gender: '男'
};

let { username: uName, age, password: uPsw } = obj;
// 此时username与password只是属性查找方式,不是变量名
console.log(username, password);    //username is not defined
// 需要使用:后的值进行变量访问
console.log(uName, uPsw, age);    //jack 123456 18

// 4 默认值
var obj = {
    username: 'jack',
    age: 18,
    password: '123456',
    gender: '男'
};
let { gf = 'rose', age = 200, school, username } = obj;
console.log(gf, age, school, username);    //rose 18 undefined jack

// 4.1 属性名设置别名和默认值的写法
// 注意顺序,先写别名,再写默认值
var obj = {
    age: 18,
    password: '123456',
    gender: '男'
};
let { username: uName = 'admin' } = obj;
console.log(uName);    //admin

// 5 剩余值
//  对象解构赋值的剩余值结果为对象结构,同样必须设置在最后位置,否则报错
var obj = {
    username: 'jack',
    age: 18,
    password: '123456',
    gender: '男'
};
let { username: uName, ...other } = obj;
console.log(uName, other);    //jack {age: 18, password: "123456", gender: "男"}   
// 复杂的嵌套,只要符合模式,即可解构
var obj = {
    username: 'jack',
    age: 18,
    gf: {
        school: '北大',
        class: '1班'
    },
    gender: '男'
};

// 写法1:只获取gf内的属性,gf没有获取
let { age, gf: { school } } = obj;

console.log(age, school);   //18 "北大"
console.log(gf);            // 报错  gf is not defined

// 写法2:获取gf属性,同时也获取gf本身
let { age, gf: { school }, gf } = obj;
console.log(age, school);  18 "北大"

console.log(gf); // 可以取值    {school: "北大", class: "1班"}
// 假设从服务器上获取的数据如下
let response = {
    data: ['a', 'b', 'c'],
    meta: {
        code: 200,
        msg: '获取数据成功'
    }
}
// 如何获取到 code 和 msg
let { meta: { code, msg } } = response;
console.log(code, msg);     // 200 '获取数据成功'

5.对象简写

ES6将对象的写法进行了简化,分为属性和方法两种简化形式

5.1. 对象属性简写

// 前提条件,已经与属性同名的变量,保存了属性值
var name = 'jack';
var age = 18;

var obj = {
    name, // 相当于 name:name,
    age
};
console.log(obj);  //age:18,name:"jack"

案例应用

var username = $('#ipt').val().trim();
var password = $('#ipt2').val().trim();
$.ajax({
    url: '...',
    data: {
        username,
        password
    },
    success: function (res) {
    }
}) 

5.2.对象方法简写

var obj = {
      name: 'rose',
      age: 18,
      // sayHi是传统写法
      sayHi: function () {
        console.log('你好,我叫' + this.name);
      },
      // sayHehe是ES6的简写形式,功能没区别
      sayHehe() {
        console.log('呵呵,我叫' + this.name)
      }
    };

    obj.sayHi();
    obj.sayHehe();

6.逻辑运算符回顾

逻辑运算符的短路运算(逻辑中断操作)
&& || !

// 逻辑运算符使用的两种场景
// 1.操作数均为布尔值(基本规则)
// 2.某个操作数为非布尔值(&& || 规则相同)
// 从左往右查看操作数
// 当操作数为非布尔值时,隐式转换
// 哪个操作数可以决定式子结果,就返回这个操作数(原值)

console.log(3 && 'abc');        // 'abc'
console.log(0 && true);         // 0
console.log(undefined || 200);  // 200
console.log('xyz' || 123);      // 'xyz' 

7.函数拓展

7.1.参数默认值

在定义一个函数时,我们可以给形参设置默认值:当用户不传入对应实参时,我们有一个保底的值可以使用。

这个特性在ES6之前,是不支持的,我们以前会通过一些变通的方式来实现。

传统的默认值设置方式

参数的默认值在之前的课程内容已经涉及,例如在xhr.open(请求类型,请求地址,是否异步)方法中,第3个参数默认是true,即表示异步ajax。

  • 默认值的意思是:
    • 如果传入对应实参,就用你传的值。
    • 如果不传,就使用某个特殊的、事先定义好的值。这个值也就是默认值。
  • 示例代码:(以ajax课程中的ajax函数封装为示例)
// ES5 中给参数设置默认值的变通做法,以ajax函数部分封装为示例:
function ajax (type, url, isAsync) {
    // 基本写法:  
    if(isAsync === undefined){
        isAsync = true;
    }
    // 简化写法:isAsync = isAsync || true;
    console.log(type, url, isAsync);
}
// 下面这两句是等价的
open("get","common/get"); // 参数3使用默认值true
open("get","common/get",true);

open("get","common/get",false);

以上代码是利用了形参的一个特点:没有传入对应实参的话,其默认值是undefined。

观察后发现,代码是可以工作的,但是显得很累赘,es6提供了更简单的实现。

7.2. ES6实现参数默认值

格式:

function 函数名 (参数名1=默认值1,参数名2=默认值2,参数名3=默认值3...){
    // 函数体
}

示例:

function open(type, url, isAsync = true) {
    console.log(type, url, isAsync);
}
// 下面这两句是等价的
open("get","common/get")// // 参数3使用默认值true
open("get","common/get",true);

open("get","common/get",false); 

注意:

  1. 带默认值的形参放在形参列表的最右边。

案例应用:

function f(a = 1,b = 2){
	console.log(a, b);
}
f(10);           // 10 2
f(10,20);        // 10 20
f();             // 1  2

function f2(a = 1,b){
	console.log(a, b);
}
f2(10);          // 10 undefined
f2(10,20);       // 10  20
f2(,3);          // 报错:Uncaught SyntaxError: Unexpected token
f2();            // 1 undefined

// 与解析赋值一起使用:
// 注意:如果形参位置不设置 = {},会出现报错
function f1({a = 1, b = 2} = {}){
   console.log(a, b);
}	

f1({a:10,b:20});   // 10 20
f1({a:20});        // 20 2
f1({c:1});         // 1  2
f1();              // 1  2

7.3.rest 参数

rest (其它的,剩余的)参数 用于获取函数多余参数,把它们放在一个数组中。

语法格式:

rest参数不能设置默认值,且必须设置在参数列表最后位置

在定义函数时,在最后一个参数前面加上..., 则这个参数就是剩余参数;

let fn = function(参数1,参数2...rest参数){}   // 普通函数

let fn = (参数1,参数2...rest参数) => { };     // 箭头函数

只是在定义函数时,在形参列表中区别一下,而在调用函数时并无区别。

示例:

function f2 (x,...y){
    console.log(x,y)
}
f2(1,2);         // 1 [2]
f2(2,3,4);       // 2 [3, 4]

function f1 (x,y){
    console.log(x,y)
}  
f1(1,2);        // 1 2
f1(2,3,4);      // 2 3

rest应用--代替arguments:

问题:编写一个函数,求所有参数之和;

方法一:arguments

function getSum (){
    var sum = 0 ; 
    for(var i = 0; i < arguments.length; i++){
        sum += arguments[i];
    }
}

方法二:rest参数

如果以箭头函数 的方式去定义这个函数,则内部不可以使用arguments这个对象了。此时,我们就可以使用

rest 参数,它可以替代 arguments 的使用。 代码如下:

// 参数很多,不确定多少个,可以使用剩余参数
const  getSum = (...values) => {
    var sum = 0 ; 
    for(var i = 0; i < values.length; i++){
        sum += values[i];
    }
}

rest参数与arguments相比,它是一个真正的数组,可以使用数组的全部方法。arguments是一个伪数组。

8.箭头函数

箭头函数能让代码更简洁,效率更高;

let fn3 = x => x * 2;    //let fn3=(x)=>{x*2}

箭头函数本质上是 匿名函数的简化写法

let 函数名 = (形参1...形参n) => {
    // 函数体
};

定义函数有两种方式:

  1. 函数声明式

    // 1. 函数声明式
    function fu1(x){
        return x * 2;
    }
    
  2. 函数表达式

    // 2. 函数表达式
    let fn2 = function (x) {
        return x * 2;
    };
    

ES6 中允许使用箭头函数的方式来定义一个函数:

  1. 箭头函数
// 3. 箭头函数 (赋值给变量,作为函数表达式使用)
let fn3 = (x) => {
    return x * 2;
};

对比前两种写法会发现,箭头函数的写法要比传统匿名函数简洁。

8.1.箭头函数总结

  1. 去除function关键字
  2. 在形参和函数体之间设置 =>

注意:

  1. =>是一个整体,不要加空格。
  2. 箭头函数只在书写格式上有些区别,但在函数调用方式上,是没有区别的。
  3. 箭头函数内部的this指向和外部一致。

8.2.箭头函数简化写法

  • 当形参有且只有一个,可以省略小括号

    let f = (x) => {console.log(x)}
    // 可以简化成:
    let f = x => {console.log(x)}
    
  • 当函数体只有一条语句,可以省略大括号;

    let f = x => {console.log(x);}
    // 可以简化成:
    let f = x => console.log(x);
    
  • 当函数体只有一条语句,并且就是return语句,则可以省略return和大括号。

    如果省略了大括号,则必须省略return,否则报错

    let f = x => {return x*2; }
    // 可以简化成:
    let f = x => x*2;
    
  • 注意如果返回值是一个对象,要加()

    let f = x => { return {a:1};  }
    // 可以简化成:
    let f = x => {a:1};    // 如果不加,{}会被认为是函数的代码块,不会被解析器当成对象处理
    let f = x => ({a:1});
    

8.3.箭头函数与普通函数的区别

8.3.1.箭头函数内部没有this

箭头函数内部的this对象,指向定义时所在的对象,而不是使用时所在的对象。

let obj = {
    name: 'jack',
    age: 18,
    sayHi() {
        // 方法中直接访问this:
        console.log(this);     // 对象obj

        // 普通调用的函数中的this:
        var f1 = () => {
            console.log(this);  // 对象obj
        };
        f1();

        setTimeout(() => {
            console.log(this); // 对象obj
        }, 0);

        // 另一个对象的方法中的this
        let obj2 = { name: 'rose' };
        obj2.sayHehe = () => {
            console.log(this); // 对象obj
        };
        obj2.sayHaha = function () {
            console.log(this); // 对象obj2,因为没有使用箭头函数
        };
        obj2.sayHehe();
        obj2.sayHaha();
    }
};
obj.sayHi();
8.3.2.函数内部没有 arguments

因为es6中提供了函数的rest剩余值功能,可以替代arguments

let fn = (a,b) => {
    console.log(arguments);    // 报错,arguments is not defined
};
fn(1, 2);

###箭头函数不能作为构造函数

let Person = () => {};
let obj = new Person();     // 报错,Person is not a constructor
// 换个角度理解,箭头函数中都没有自己的this,无法处理成员,所以不能当构造函数

在javascript中,函数的功能太多了,除了起到最基本的封装代码之外,还可以当做构造器来使用。

ES6中提出的箭头函数让函数减负了,只适合代码的封装操作。

9.数组的扩展

数组对象是js中非常重要的对象,它本身提供了非常多的方法,例如:push,pop,shift,unshift,splice,concat,sort,indexOf,join,map,forEach,filter,every,some,reduce… 。由于前端的主要工作内容之一是与后端进行数据交互,而数据交互的载体大多是数组和对象,所以我们对数组的操作要求也非常高。

9.1.扩展运算符

功能:把数组中的元素一项项地展开,把一个整体的数组拆开成单个的元素。

格式:…数组

基本用法:

console.log(...[1,2,3]);

应用1-数组拷贝:

//数组拷贝
let arr1 = [1, 2, 3, 4];
let arr2 = arr1; 
console.log(arr2);   //[1,2,3,4]
arr2.push(5)
console.log(arr1);   //[1,2,3,4,5]
console.log(arr2);   //[1,2,3,4,5]
// arr1和arr2指向的内存空间是一致的,只要一个修改,另一个也会同时改变

let arr1 = [1, 2, 3, 4];
let arr2 = [...arr1];     //赋值给arr2的是arr1的值,而不是数组
console.log(arr2);        //[1,2,3,4]
arr2.push(5);
console.log(arr1);        //[1,2,3,4]
console.log(arr2);        //[1,2,3,4,5]

应用2-数组合并:

从把一个数组中的值全取出来,放在另一个数组中的

var arr0 = ['a', 'b'];
var arr1 = [1, 2, 3];
var arr2 = [...arr1];
var arr3 = [...arr0, ...arr1];

应用3-Math.max():

Math.max(1,3,4,6);
var arr = [1,3,4,6];
Math.max(...arr);
// 或者 Math.max.apply(this, [1, 2, 3, 566]);

9.2.Array.from()

功能:把其它伪数组的对象转成数组。

格式: 数组 = Array.from(伪数组对象)

它的实参有三种情况:

  1. 自定义的,类似数组格式的对象。
let fakeArr = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};

就是为了演示,并无实际应用价值。

  1. arguments对象

  2. DOM 操作返回的 NodeList 集合

9.3.find()

在实际开发中,经常会遇到一种场景:从一个数组中找出符合条件的元素。重点是如何从数组中找出符合条件的元素,当然,我们可以通过手写循环的方式来实现这个功能,但是现在es6中提供了现成的功能。find/findIndex

find和findIndex的格式和执行流程是一致的。

作用

从数组中找出符合条件的第一个元素。

let result = [].find(function(item,index,self){ 
    // 如果满足查找的条件
    return true;
});
  • 回调函数有三个参数,分别表示:数组元素的值、索引、整个数组
  • 如果某次循环返回的是true,find方法的返回值就是满足这个条件的第一个元素。

执行流程:

  • find()方法,会遍历传递进来的数组

  • 如果在回调函数体内,某个时刻return true,则表示查找过程结果,返回值就是本轮循环中的元素;

    如果全部的循环结束,也没有return true,则表示没有找到,没有找到会返回undefined。

let arr = [1, 2, 4, 0, -4, 3, -2, 9];
arr.find(function (item, index, self) {
    console.log(item);   // 数组中的每个值
    console.log(index);  // 数组中的每个索引/下标
    console.log(self);   // 当前的数组
});
// 用法:找数组中第一个小于0的数字
let arr = [1, 2, 4, 0, -4, 3, -2, 9];
let result = arr.find(function (item) {
    return item < 0;    //遍历过程中,根据这个条件去查找
});
let result =arr.find(item=>item<0)   //箭头函数简化代码
console.log(result);    // -4

从一个复杂的对象数组中找出符合条件的对象。

let data = [
    {id:2,name:'严高',age:15},
    {id:3,name:'徐阶',age:17},
    {id:1,name:'张居正',age:12},
];

 const result = data.find(item => item.age < 14)
 console.log(result)  //{id: 1, name: "张居正", age: 12}

9.4.findIndex()

作用

从数组中找出符合条件的第一个元素的下标。

let result = [].findIndex(function(item,index,self){ 
    // 如果满足查找的条件
    return true;
});
  • 回调函数有三个参数,分别表示:数组元素的值、索引、整个数组
  • 如果某次循环返回的是true,findIndex方法的返回值就是满足这个条件的第一个元素的索引(下标)

执行流程:

  • findIndex()方法,会遍历传递进来的数组

  • 如果在回调函数体内,某个时刻return true,则表示查找过程结果,返回值就是本轮循环中的元素下标;

    如果全部的循环结束,也没有return true,则表示没有找到,没有找到会返回undefined。

  • findIndex 找到数组中第一个满足条件的成员并返回该成员的索引,如果找不到返回 -1

9.5.includes()

作用:

判断数组是否包含某个值,返回 true / false

格式

数组.includes(参数1,参数2)

//参数1,必须,表示查找的内容
参数2,可选,表示开始查找的位置,0表示从第一个元素开始找。默认值是0
let arr = [1, 4, 3, 9];
console.log(arr.includes(4));    // true
console.log(arr.includes(4, 2)); // false, 从2的位置开始查,所以没有找到4
console.log(arr.includes(5));    // false

字符串也有这个方法,功能也是相似的。

10.字符串的扩展

es6中对字符串提供了新的特性,我们介绍其中几个方法:

模板字符串 includes() startsWith() endsWith() repeat()

10.1.模板字符串

在做字符串拼接时,使用+来拼接复杂内容是很麻烦的,而模板字符串可以解决这个问题。

格式

`${变量} ${表达式}`

语法

  1. 模板字符串使用反引号 ` 把内容括起来,类似于普通字符串的""。

  2. ${}充当界定符的作用,其中可以写变量名,表达式等。

  3. 允许字符串内部进行换行,代码更好读更好的体验

let name = 'zs';
let age = 18;
// 拼接多个变量,在模板字符串中使用占位的方式,更易懂
let str = `我是${name},今年${age}`;

// 内容过多可以直接换行
let obj = [{name: 'flex', age: 20},{name: 'james', age: 21}];
let arr = ['175cm', '60kg'];
let html = `
	<div>
		<ul>
			<li>${obj.name}</li>
			<li>${obj.age}</li>
			<li>${arr[0]}</li>
			<li>${arr[1]}</li>
		</ul>
	</div>`;

10.2.includes()

格式

str.includes(searchString, [position])

功能

返回布尔值,表示是否找到了参数字符串

  • position: 从当前字符串的哪个索引位置开始搜寻子字符串,默认值为0。

10.3.startsWith()

格式

str.startsWidth(searchString, [position])

功能

返回布尔值,表示参数字符串是否在原字符串的头部或指定位置

  • position: 在 str中搜索 searchString的开始位置,默认值为 0,也就是真正的字符串开头处。

10.4.endsWith()

格式:

str.endsWith(searchString, [len])

功能

返回布尔值,表示参数字符串是否在原字符串的尾部或指定位置.

  • len:可选。作为 str 的长度。默认值为 str.length

10.5.repeat()

格式:

str.repeat(n)

功能

repeat方法返回一个新字符串,表示将原字符串重复n次。

应用:

let html = '<li>itheima</li>';
html = html.repeat(10);

11.Set对象

Set是集合的意思。是ES6 中新增的内置对象,它类似于数组,但是成员的值都是唯一的,即没有重复的值。

使用set对象可以方便地实现数组去重的操作。

11.1.创建set对象

  1. 创建空set;
// 1. 基本使用
let set = new Set();
// 得到一个空的Set对象
  1. 根据已有数组创建set
let set = new Set([1,2,3])

11.2.Set 的成员方法

size: 属性,获取 set 中成员的个数,相当于数组中的 length

add(value): 添加某个值,返回 Set 结构本身。

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

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

clear(): 清除所有成员,没有返回值。

forEach: 遍历

应用-数组去重:

let arr = [1,1,2,3,3];
console.log([...new Set(arr)])

12.ES6 降级处理

12.1.ES6 的兼容性问题

  1. ES6 (2015年10月)虽好,但是有兼容性问题,IE7-IE11 基本不支持 ES6

​ ES6 兼容性列表:(http://kangax.github.io/compat-table/es6/)

  1. 在最新的现代浏览器、移动端、Node.js 中都支持 ES6

12.2.ES6 降级处理

因为 ES 6有浏览器兼容性问题,可以使用一些工具进行降级(把ES6的代码转成ES5的代码),例如:babel

babel官网: (https://www.babeljs.cn/)

实时转码:https://babeljs.io/repl

13. ES6 模块化

模块化

项目中一些程序代码经常被其他业务场景使用,为了避免重复开发,就把这些代码设置为共享模式,共享模式就是模块化。像jquery、axios、vue等等都是模块化的体现,需要的时候直接拿过来用即可

模块化技术有哪些:

CommonJS(nodejs)、ES6模块化、AMD、CMD等

CommonJS模块化

CommonJS模块化 是2009年发布的,是民间出品的,相对不正规,可以在nodejs中应用

// 导出
module.exports = 对象
// 导入
var obj = require(模块文件)

ES6模块化

ES6模块化 是2015官方正式出品的,已经被纳入到JavaScript标准里边,也是js未来的标准,由于各种原因 nodejs和浏览器中现在还不能直接使用ES6模块化,要相信未来可以

AMD模块化

Asynchronous Module Definition 异步模块定义

2009年诞生,可以实现浏览器中应用模块化技术

CMD模块化

Common Module Definition 通用模块定义

2011年诞生,阿里公司出品,可以实现浏览器中应用模块化技术

各个模块化应用的场合

  1. commonjs模块化可以应用在nodejs中,浏览器中不可以
  2. es6模块化暂时还不能在nodejs 和 浏览器 中使用,但是要相信,未来可以
  3. 浏览器中可以运行的模块化名称为 AMD 和 CMD

AMD、CMD、Commonjs、ES6模块化介绍(https://segmentfault.com/a/1190000015302578)

13.1.默认导出和导入

默认导出和导入

数据提供者称为 导出

数据使用者称为 导入

在一个js文件中,通过一个对象把全部的数据导出出去,就是默认导出

对默认导出的成员进行接收就是默认导入

模块

一个js文件就是一个模块,前提是该文件要有做导出动作

导出语法

// 导出:
export default  对象

导入语法

// 导入:
import 名称  from  模块文件名字

注意

  1. 一个模块中 默认导出 只能进行一次
  2. es6模块化现在只可以在VueCli项目中使用
  3. 默认必须 导出一个对象

示意案例

01-默认导出.js

// 当前文件对外部导出内容,以便被使用
// 01. 默认导出:通过一个对象把所有的信息都导出出去
// CommonJS: module.exports = {}
// ES6: export default 对象
export default {
  name:'kitty',
  age:4,
  walk:function(){
    console.log('走直线')
  }
}

main.js导入

// import 名称 from '模块js文件路径名'
import cat from './modules/01-默认导出.js'
console.log(cat)

13.2.按需导出和导入

按需导出和导入

一个js模块中,定义了N多的成员信息,根据需要,用哪个就导出哪个,这就是按需

哪些成员可以做按需导出导入处理

常量对象函数 三种信息可以做模块化应用 (var、let等变量用于模块化没有意义)

导出语法

export const  a = 10		   // 常量
export function ab(){}         // 函数
export const cat = {name:'kitty',age:5} // 对象

注意:一般按需只做常量、函数、对象导出,var/let变量不导出,本身没有意义

导入语法

import {xx,yy,zz} from 模块文件
import {xx as XX,yy as YY,zz as ZZ} from 模块文件     // 根据需要可以设置别名

注意

  1. xx,yy,zz代表被导入的成员名称,与导出的要求一致
  2. 成员不用全部都导入,根据需要,导入 1个或多个或全部 都可以
  3. 如果导入进来的成员名称 与 当前环境名称 有冲突,可以使用 as 设置别名

案例

按需导出:

// 语法: export 常量声明/函数声明/对象声明
// CommonJs :  (module.exports.xx = yy)

export const city = 'beijing'
const people = 2000

export function getApple(){
  return '国光苹果'  
}
function getPear(){
  return '雪花梨'
}

export const tiger = {color:'yellow and black'}
const dog = {hobby:'看家'}

按需导入:

// import {名称,名称...} from '模块文件'
import {city as ct,getApple,tiger} from './modules/02-按需导出.js'
console.log(ct)
console.log(getApple)
console.log(tiger)

13.3.默认和按需同时导出和导入

有时,在一个模块中 默认 和 按需 导出会同时存在

导出语法

export const  a = 10        // 按需导出
export function ab(){}      // 按需导出

export default  对象/{}     // 默认导出
  1. 一个模块只能 默认导出一次,按需导出可以设置多次
  2. 默认导出 的语句没有摆放位置要求

导入语法

// 1) 分别导入
import  名称  from  模块          //默认导入
import  {xx,yy}  from  模块     //按需导入

// 2) 一并导入
import 名称,{xx,yy} from 模块

一并导入必须是 默认在"前",按需在"后"

13.4.没有导出应用

非模块文件:

项目开发时有的文件没有做导出动作(例如 css、less),这些文件可以称为 非模块文件,那么可以通过如下方式做导入:

import  文件路径名

示意案例

导出:

// 4. 没有导出
for(var i=0; i<5; i++){
  console.log(i)
}

导入:

// 4. 导入空的模块
// import  xx模块名字
import './modules/04-没有导出.js'

13.5 ES6模块化总结

  1. 导出,就是共享内容提供方
  2. 导入,就是共享内容使用者
  3. 默认导出就是通过一个对象把全部的内容都提供出来 export default 对象{}
  4. 按需导出就是需要哪个信息就导哪个出来,不需要的不操作
  5. 按需导出导入的相关元素:常量、函数、对象
  6. 一个模块只能做一次默认导出,但是可以做多次按需导出
  7. 默认导出必须导出 对象
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值