ES6学习

1 ES6 新特性

1.1 let 关键字

let 关键字用来声明变量,使用 let 声明的变量有几个特点:

  1. 不允许重复声明;
  2. 块级作用域(局部变量);
  3. 不存在变量提升;
  4. 不影响作用域链;

let创建变量代码示例:

// let关键字使用示例:
let a; // 单个声明
let b,c,d; // 批量声明
let e = 100; // 单个声明并赋值
let f = 521, g = 'iloveyou', h = []; // 批量声明并赋值
块级作用域

不仅仅指花括号{},还有if else while for 等语句后面的循环也是

{
let cat = "猫";
}
console.log(cat);
// 报错:Uncaught ReferenceError: cat is not defined
不存在变量提升

变量提升: 就是在变量创建之前使用,let不存在,var存在;

console.log(people1); // 可输出默认值
console.log(people2); // 报错
var people1 = "大哥"; // 存在变量提升
let people2 = "二哥"; // 不存在变量提升
不影响作用域链

作用域链:就是代码块内有代码块,跟常规编程语言一样,上级代码块中的局部变量下级可用

{
	let p = "大哥";
	function fn(){
		console.log(p);   //这里是可以使用的
	}
	fn();
}
let案例

使用 var 下面的声明会将上面的覆盖掉,所以点击事件每次找到的都是3,越界报错

由于 let 声明的是局部变量,每一个保持着原来的值,调用的时候拿到的是对应的i

<body>
	<div class="container">
		<h2 class="page-header">let案例:点击div更改颜色</h2>
		<div class="item"></div>
		<div class="item"></div>
		<div class="item"></div>
	</div>
	<script>
		// 获取div元素对象
		let items = document.getElementsByClassName('item');
		// 遍历并绑定事件
		for (let i = 0; i < items.length; i++) {
			items[i].onclick = function() {
			// 修改当前元素的背景颜色
			// this.style.background = 'pink'; // 写法一:常规写法一般无异常
			items[i].style.background = 'pink'; // 写法二
			
			// 写法二:需要注意的是for循环内的i必须使用let声明
			// 如果使用var就会报错,因为var是全局变量,
			// 经过循环之后i的值会变成3,items[i]就会下标越界
			// let是局部变量
			// 我们要明白的是当我们点击的时候,这个i是哪个值
			// 使用var相当于是:
			// { var i = 0; }
			// { var i = 1; }
			// { var i = 2; }
			// { var i = 3; }
			// 下面的声明会将上面的覆盖掉,所以点击事件每次找到的都是3
			// 而使用let相当于是:
			// { let i = 0; }
			// { let i = 1; }
			// { let i = 2; }
			// { let i = 3; }
			// 由于let声明的是局部变量,每一个保持着原来的值
			// 点击事件调用的时候拿到的是对应的i
			}
	}
	</script>
</body>

1.2 const关键字

const 关键字用来声明常量,const 声明有以下特点:

  1. 声明必须赋初始值;
  2. 标识符一般为大写(习惯);
  3. 不允许重复声明;
  4. 值不允许修改;
  5. 块儿级作用域(局部变量);
声明必须赋初始值
const CAT;     //报错
const CAT = "喵喵";   //正确
值不允许修改

对数组元素的修改和对对象内部的修改是可以的(数组和对象存的是引用地址);

应用场景:声明对象类型使用 const,非对象类型声明选择 let

对于数组:

const TEAM = ['UZI','MXLG','Ming','Letme'];
TEAM.push('Meiko');  //正确
TEAM = 100//直接赋值,报错
console.log(TEAM);

对于对象:

const obj = {
    uname: 'rick',
    age: 30
}
obj.age = 40;
// 只要不改变地址,就不报错

1.3 变量的解构赋值

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

应用场景:频繁使用对象方法、数组元素,就可以使用解构赋值形式

数组的解构赋值
const arr = ['red', 'green', 'blue'];
let [a, b, c] = arr;
对象的解构赋值:同名赋值
const F3 = {
	name : "大哥",
	age : 22,
	xiaopin : function(){ // 常用
		console.log("我会演小品!");
	}
}
//let {name,age,xiaopin} = F3;  //注意解构对象这里用的是{}
//console.log(name + age + xiaopin); // 大哥22
//xiaopin(); // 此方法可以正常调用

let {xiaopin} = F3; 
xiaopin();  // 这样就简化了F3.xiaopin();

1.4 模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识

特点: 字符串中可以出现换行符; 可以使用 ${xxx} 形式引用变量;

应用场景: 当遇到字符串与变量拼接的情况使用模板字符串

在拼接中, ${内容} 是固定格式

//1. 声明
// let str = `我也是一个字符串哦!`;
// console.log(str, typeof str);

//2. 内容中可以直接出现换行符
let str = `<ul>
           <li>沈腾</li>
           <li>玛丽</li>
           <li>魏翔</li>
           <li>艾伦</li>
           </ul>`;
变量拼接
// 变量拼接
let lovest = '魏翔';
let out = `${lovest}是我心目中最搞笑的演员!!`;
console.log(out);
let x = 1;
let y = 2;
`${x} + ${y} = ${x + y}`// "1 + 2 = 3"
`${x} + ${y * 2} = ${x + y * 2}`// "1 + 4 = 5"
let obj = {x: 1, y: 2};
`${obj.x + obj.y}`// "3"

1.5 简化对象和函数写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法,更加简洁+

对象里面的方法也可简写,比如improve: function(){} 写成 improve(){}

let name = '尚硅谷';
let change = function(){
      console.log('我们可以改变你!!');
}
const school = {
     name,  //直接写入变量和函数
     change,
     //improve: function(){}   //之前的写法
     improve(){
         console.log("我们可以提高你的技能");
     }
}
console.log(school);

1.6 箭头函数

ES6允许使用箭头(=>)定义函数,箭头函数提供了一种更加简洁的函数书写方式,箭头函数

多用于匿名函数的定义

箭头函数适合与 this 无关的回调. 定时器, 数组的方法回调

箭头函数不适合与 this 有关的回调. 事件回调, 对象的方法

1.6.1 基本声明调用
// 传统方法
let fn = function(){ }
// 箭头函数
let fn = (a,b) => {
     return a + b;
}
//调用函数
let result = fn(1, 2);
console.log(result);
1.6.2 箭头函数的简写
  1. 省略小括号, 当形参有且只有一个的时候**
let add = n => {     // let add = (n) =>
   return n + n;
}
  1. 省略花括号, 当代码体只有一条语句的时候, 此时 return 必须省略**

而且语句的执行结果就是函数的返回值

let pow = n => n * n;
console.log(pow(8));  //64
1.6.3 this为静态

this 是静态的. this 始终指向函数声明时所在作用域下的 this 的值(外层的作用域)

let getName2 = () => {
    console.log(this.name);
}
//设置 window 对象的 name 属性
window.name = '尚硅谷';
const school = {
    name: "ATGUIGU"
}
//直接调用
getName2();
//call 方法调用
getName2.call(school);  //输出为指向window的 尚硅谷
1.6.4 不能作为构造实例化对象
// 会报错
let Persion = (name,age) => {
		this.name = name;
		this.age = age;
}
let me = new Persion("訾博",24);
console.log(me);
1.6.5 不能使用 arguments 变量
// 会报错
let fn = () =>{
		console.log(arguments); 
} 
fn(1,2,3);
1.6.6 箭头函数的应用前景

需求-1 点击 div 2s 后颜色变成『粉色』

this.style.background = ‘pink’;的 this 用普通函数指向window,

使用箭头函数,指向addEventListener点击的对象

<div id="ad"></div>
<script>
   let ad = document.getElementById("ad");
   ad.addEventListener("click",function(){
   	    // let _this = this;
        setTimeout(()=>{
        		 // 之前的做法
        		 // _this.style.background = 'pink';
             this.style.background = 'pink';
        },2000);
    });
</script>

需求-2 从数组中返回偶数的元素

const arr = [1,6,9,10,100,25];
 // 传统写法
// const result = arr.filter(function(item){
// 		if(item % 2 === 0){
//				 return true;
// 		}else{
//				 return false;
//		 }
// });
 // 箭头函数写法
const result = arr.filter(item => item % 2 === 0);
 // 输出
console.log(result);

1.7 设置函数参数的默认值

ES6 允许给函数参数设置默认值,当调用函数时不给实参,则使用参数默认值。

具有默认值的形参

一般要靠后(潜规则)

let add = (x, y, z=3) => x + y + z;
console.log(add(1, 2)); // 6
与解构赋值结合
function connect({host="127.0.0.1", username,password, port}){
     console.log(host)
     console.log(username)
     console.log(password)
     console.log(port)
}
connect({
     host: 'atguigu.com',
     username: 'root',
     password: 'root',
     port: 3306
})

1.8 rest参数

ES6 引入 rest 参数 …args ,用于获取函数的实参,用来代替 arguments;

用在函数形参中,语法格式:fn(a, b, ...args),写在参数列表最后面

将接收的参数序列转换为一个数组对象

// ES5 获取实参的方式
function date(){
   console.log(arguments);
}
date('白芷','阿娇','思慧');

// rest 参数 
function date(...args){
    console.log(args);  
}
date('阿娇','柏芝','思慧');

// rest 参数必须要放到参数最后
function fn(a,b,...args){
     console.log(a);  //1
     console.log(b);  //2
     console.log(args);  //[3,4,5,6]
}
fn(1,2,3,4,5,6);

1.9 扩展运算符

… 扩展运算符将数组转换为逗号分隔的参数序列

它的作用

扩展运算符(spread)也是三个点(…),它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包;

const tfboys = ['易烊千玺', '王源', '王俊凯'];
// 声明一个函数
function chunwan() {
		console.log(arguments);
}
// 使用扩展运算符
chunwan(...tfboys);  // 相当于chunwan('易烊千玺','王源','王俊凯')
它的应用
//1. 数组的合并 
const kuaizi = ['王太利','肖央'];
const fenghuang = ['曾毅','玲花'];
// 传统的合并方式
// const zuixuanxiaopingguo = kuaizi.concat(fenghuang);
// 使用扩展运算符
const zuixuanxiaopingguo = [...kuaizi, ...fenghuang];
console.log(zuixuanxiaopingguo);

//2. 数组的克隆
const sanzhihua = ['E','G','M'];
const sanyecao = [...sanzhihua]; //['E','G','M']
console.log(sanyecao);

//3. 将伪数组转为真正的数组
const divs = document.querySelectorAll('div');
console.log(divs); // 伪数组  Object
const divArr = [...divs];
console.log(divArr); // 真正的数组 Object

1.10 Symbol

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript 语言的第

七种数据类型,是一种类似于字符串的数据类型

JavaScript 的七种基本数据类型:

  • 值类型(基本类型):string、number、boolean、undefined、null、symbol
  • 引用数据类型:object(包括了array、function)
Symbol 特点
  1. Symbol 的值是唯一的,用来解决命名冲突的问题;
  2. Symbol 值不能与其他数据进行运算
  3. Symbol 定义的对象属性不能使用for…in 循环遍历 ,但是可以使用Reflect.ownKeys 来获取对象的所有键名;
Symbol 的创建

使用 Symbol() 方法创建

let s1 = Symbol();
console.log(s1, typeof s1);  // Symbol() 'symbol'

添加具有标识的 Symbol()

let s2 = Symbol('尚硅谷');
let s2_1 = Symbol('尚硅谷');
console.log(s2 === s2_1);  // false  Symbol 都是独一无二的

使用 Symbol.for() 方法创建,名字相同的 Symbol 实际上是同一个值

let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
console.log(s1 === s2); // true
// s1和s2都是 Symbol 值,但是它们都是由同样参数的Symbol.for方法生成的,所以实际上是同一个值

Symbol.for()Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。

输出 Symbol 变量的描述,使用 description 属性

let s4 = Symbol('测试');
console.log(s4.description); // 测试
对象添加Symbol类型的属性
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

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

案例:安全的向对象中添加属性和方法。
分析:如果直接向对象中添加属性或方法,则原来对象中可能已经存在了同名属性或方法,会覆盖掉原来的。所以使用 Symbol 生成唯一的属性或方法名,可以更加安全的添加

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

在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。

// 这是一个 game 对象,假设我们不知道里面有什么属性和方法
const game = {
    uname: '俄罗斯方块',
    up: function () { },
    down: function () { }
}
// 通过 Symbol 生成唯一的属性名,然后给 game 添加方法
let [up, down] = [Symbol('up'), Symbol('down')];
game[up] = function () {
    console.log('up');
}
game[down] = function () {
    console.log('down');
}
// 调用刚刚创建的方法
game[up]();
game[down]();

此外,还可以在对象中添加

let youxi = {
    name:"狼人杀",
    [Symbol('say')]: function(){
        console.log("我可以发言")
    },
    [Symbol('zibao')]: function(){
        console.log('我可以自爆');
    }
}
Symbol内置值

除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行;

方法描述
Symbol.hasInstance当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法
Symbol.isConcatSpreadable对象的这个属性等于的是一个布尔值,表示该对象用于Array.prototype.concat() 数组合并时,是否可以展开
Symbol.species创建衍生对象时,会使用该属性
Symbol.match当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。
Symbol.replace当该对象被 str.replace(myObject) 方法调用时,会返回该方法的返回值。
Symbol.search当该对象被 str.search(myObject) 方法调用时,会返回该方法的返回值。
Symbol.split当该对象被 str.split(myObject) 方法调用时,会返回该方法的返回值。
Symbol.iterator对象进行for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器
Symbol.toPrimitive该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
Symbol. toStringTag在该对象上面调用 toString() 方法时,返回该方法的返回值
Symbol. unscopables该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除。

案例1:Symbol.hasInstance 方法判断是否属于这个对象时被调用

class A {
    static [Symbol.hasInstance](param) {  //para为传递的 obj
    	  console.log(param);
        console.log('判断是否属于这个对象时被调用');
        return false;  //内部返回什么,结果就是什么
    }
}
let obj = {};
console.log(obj instanceof A)  // false  内部返回什么,结果就是什么

案例2:数组使用 concat 方法时,设置是否可以展开

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr1.concat(arr2));
//结果:[ 1, 2, 3, Array(3) ]  //最后一个元素为结合体

1.11 迭代器 Iterator

迭代器(Iterator)就是一种接口,为各种不同的数据结构提供统一的访问机制。任何

数据结构只要部署 Iterator 接口(对象的一个属性),就可以完成遍历操作;

ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费;

原生具备 iterator 接口的数据(可用 for of 遍历):

Array; Arguments; Set; Map; String; TypedArray; NodeList;

for…of 循环

for … of 中保存的是值,而for … in 中保存的是index

//声明一个数组
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
//使用 for...of 遍历数组
for(let v of xiyou){  
   console.log(v);
}
遍历 iterator 接口的数据
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
let iterator = xiyou[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next()); //{ value: '唐僧', done: false }
console.log(iterator.next()); //{ value: '孙悟空', done: false }
console.log(iterator.next()); //{ value: '猪八戒', done: false }
console.log(iterator.next()); //{ value: '沙僧', done: false }
工作原理
  • 创建一个指针对象,指向当前数据结构的起始位置
  • 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  • 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  • 每调用 next 方法返回一个包含 valuedone 属性的对象

应用场景:需要自定义遍历数据的时候,要想到迭代器

自定义遍历数据

我们可以通过给数据结构添加自定义 [Symbol.iterator]() 方法来使该数据结构能够直接被遍历,从而使 for...of 能够直接遍历指定数据,达到为 for...of 服务的功能

遍历对象中的数组示例:

<script>
    const banji = {
        name: "一班",
        stus: ['xiaoming','xiaoning','xiaotian','knight'],
        [Symbol.iterator]() {
            let index = 0;   //索引变量
            let _this = this;  // 保存this
            return {
                next: function(){
                    if(index < _this.stus.length){
                        const result = { value: _this.stus[index], done: false };
                        index++;  //下标自增
                        //返回结果
                        return result;
                    }else{
                        return {value: undefined, done: true};
                    }
                }
            }     
        }
    }
    // 遍历这个对象
    for (let v of banji){
        console.log(v);
    }
</script>

1.12 生成器 Generator

生成器函数是 ES6 提供的一种 异步编程解决方案,语法行为与传统函数完全不同

生成器其实就是一个特殊的函数

异步编程 纯回调函数 node fs ajax mongodb

声明和调用
  • * 的位置没有限制

  • 使用 * 和 yield 可以声明一个生成器函数。生成器函数返回的结果是迭代器对象调用迭代器对象的 next 方法可以得到 yield 语句后的值。

  • 每一个 yield 相当于函数的暂停标记,也可以认为是一个分隔符,每调用一次 next(),生成器函数就往下执行一段。

  • next 方法可以传递实参,作为上一个 yield 语句的返回值

    Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。

// 以下生成器函数中,3 个 `yield` 语句将函数内部分成了 4 段
function * gen(){
    console.log(111);
    yield '一只没有耳朵';
    console.log(222);
    yield '一只没有尾部';
    console.log(333);
    yield '真奇怪';
    console.log(444);
}
let iterator = gen();
console.log(iterator.next());  //迭代器对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

输出结果:

在这里插入图片描述

函数参数

next方法里的参数表示上一个yield表达式的返回值

function* generator(arg) {
    console.log(arg); // 生成器第 1 段
    let one = yield 111;
    console.log(one); // 生成器第 2 段
    let two = yield 222;
    console.log(two); // 生成器第 3 段
    let three = yield 333; 
    console.log(three); // 生成器第 4 段
}

let iter = generator('AAA'); // 传给生成器第 1 段
console.log(iter.next());
console.log(iter.next('BBB')); // 传给生成器第 2 段,作为这一段yield语句返回值
console.log(iter.next('CCC')); // 传给生成器第 3 段,作为这一段yield语句返回值
console.log(iter.next('DDD')); // 传给生成器第 4 段,作为这一段yield语句返回值

输出结果:

在这里插入图片描述

生成器函数案例1

案例1:1s后输出111,2s后输出222,3s后输出333

传统方式:嵌套太多,代码复杂,产生 回调地狱

setTimeout(() => {
    console.log(111);
    setTimeout(() => {
        console.log(222);
        setTimeout(() => {
            console.log(333);
        }, 3000);
    }, 2000);
}, 1000);

生成器实现:结构简洁明了

function one() {
    setTimeout(() => {
        console.log(111);
        iterator.next(); //执行第二个yield语句
    }, 1000);
}
function two() {
    setTimeout(() => {
        console.log(222);
        iterator.next(); //执行第三个yield语句
    }, 2000);
}
function three() {
    setTimeout(() => {
        console.log(333);
    }, 3000);
}
// 生成器函数
function* gen() {
    yield one();
    yield two();
    yield three();
}
let iterator = gen();
iterator.next();  //执行第一个yield语句
// 或者也可以在最下面多次调用iterator.next(); 
生成器函数案例2

案例2:生成器函数模拟每隔1s获取商品数据

function getUsers(){
    setTimeout(()=>{
        let data = '用户数据';
        //调用 next 方法, 并且将数据传入
        iterator.next(data);
    }, 1000);
}
function getOrders(){
    setTimeout(()=>{
        let data = '订单数据';
        iterator.next(data);
    }, 1000)
}
function getGoods(){
    setTimeout(()=>{
        let data = '商品数据';
        iterator.next(data);
    }, 1000)
}
function * gen(){
    let users = yield getUsers();
    // console.log(users);  //用户数据
    let orders = yield getOrders();
    // console.log(orders);  //订单数据
    let goods = yield getGoods();
    // console.log(goods);  //商品数据
}
//调用生成器函数
let iterator = gen();
iterator.next();

1.13 Promise

Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果

一个 Promise 必然处于以下几种状态之一:

  • 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(fulfilled/resolved):意味着操作成功完成。
  • 已拒绝(rejected):意味着操作失败。
Promise 的定义与使用
  • Promise 构造函数:new Promise((resolve, reject)=>{})
  • Promise.prototype.then 方法
  • Promise.prototype.catch 方法
let p = new Promise(function (resolve, reject) {
    // 使用 setTimeout 模拟请求数据库数据操作
    setTimeout(function () {
        // 这个异步请求数据库数据操作是否正确返回数据
        let isRight = true;
        if (isRight) {
            let data = '数据库中的数据';
            // 设置 Promise 对象的状态为操作成功
            resolve(data); //可以传值
        } else {
            let err = '数据读取失败!'
            // 设置 Promise 对象的状态为操作失败
            reject(err);  //可以传值
        }
    }, 1000);
});
//调用 promise 对象的 then 方法
// 不一定非要用value和reason,但是这样更直观
p.then(function (value) { //成功
    console.log(value);
}, function (reason) {  //失败
    console.error(reason);
})
Promise 封装来读取文件

可以解决多个异步任务,过于嵌套的问题

// 使用 nodejs 的 fs 读取文件模块
const fs = require('fs');

const p = new Promise(function (resolve, reject) {
    fs.readFile('./resources/为学.txt', (err, data) => {
        // err 是一个异常对象
        if (err) reject(err); //状态为操作失败
        resolve(data);  //状态为操作成功
    })
})

p.then(function (value) {  //成功
    // 转为字符串输出
    console.log(value.toString());
}, function (reason) {   //失败
    console.log('读取失败!!');
})
Promise 封装Ajax请求
const p = new Promise((resolve, reject) => {
	  // 创建对象
    const xhr = new XMLHttpRequest();
    // 初始化
    xhr.open('get', 'https://api.apiopen.top/getJoke');
    xhr.send();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
                // 成功
                resolve(xhr.response);
            } else {
                // 失败
                reject(xhr.status);
            }
        }
    }
});

// 指定成功和失败的回调
p.then(function (value) {
    console.log(value);
}, function (reason) {
    console.error(reason);
})
Promise的then 方法

Promise.prototype.then 方法返回的结果依然是 Promise 对象,对象状态由回调函数的执行结果决定

(1) 若 then 方法没有返回值,则 then 方法返回的对象的状态值为成功 ,返回结果值为 undefined

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        // resolve('用户数据')
        reject('出错了');
    }, 1000);
})
// 未设定返回值
const res = p.then((value) => {
    console.log(value);
}, (reason) => {
    console.warn(reason);
})
// 打印 then 方法的返回值
console.log(res);

在这里插入图片描述

(2) 回调函数中返回的结果是非 Promise 类型的属性,则 then 方法返回的对象,其状态为成功,返回结果值取决于 then 方法所执行的是那个函数(resolvereject)。

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        // resolve('用户数据')
        reject('出错了');
    }, 1000);
})
 // 返回的非 Promise 对象
const res = p.then((value) => {
    console.log(value);
    return '成功了!!';
}, (reason) => {
    console.warn(reason);
    return '出错啦!!'
})
// 打印 then 方法的返回值
console.log(res);

在这里插入图片描述

(3) 回调函数中返回的结果是 Promise 类型return new Promise()),则 then 方法返回的 Promise 对象状态与该返回结果的状态相同,返回值也相同

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('用户数据')
        // reject('出错了');
    }, 1000);
})
const res = p.then((value) => {
    console.log(value);
    // 返回 Promise 对象
    return new Promise((resolve, reject) => {
        resolve('(1)成功了!!!');
        // reject('(1)出错了!!!')
    })
}, (reason) => {
    console.warn(reason);
    return new Promise((resolve, reject) => {
        // resolve('(2)成功了!!!');
        reject('(2)出错了!!!')
    })
})
// 打印 then 方法的返回值
console.log(res);

在这里插入图片描述

(4) 回调函数中返回的结果是 throw 语句抛出异常,则 then 方法的对象的状态值为 rejected,返回结果值为 throw 抛出的字面量或者 Error 对象。

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('用户数据');
    }, 1000);
});
const res = p.then((value) => {
    console.log(value);
    return new Promise((resolve, reject) => {
        throw new Error('错误了!!');
    })
});
// 打印结果
console.log(res);

在这里插入图片描述

链式调用

Promise.prototype.then 方法返回的结果还是 Promise象,这意味着我们可以继续在该结果上使用 then 方法,也就是链式调用。

const p = new Promise(resolve=>{}, reject=>{});
p.then(value=>{}, reason=>{})
.then(value=>{}, reason=>{})
.then(value=>{}, reason=>{})
...
链式调用练习-多个文件读取

传统方式,存在回调地狱

fs.readFile('./resources/为学.md', (err, data1)=>{
    fs.readFile('./resources/插秧诗.md', (err, data2)=>{
        fs.readFile('./resources/观书有感.md', (err, data3)=>{
            let result = data1 + '\r\n' +data2  +'\r\n'+ data3;
            console.log(result);
        });
    });
}); 

Promise的链式调用

const fs = require('fs');

let p = new Promise((resolve, reject) => {
    fs.readFile('./resources/users.md', (err, data) => {
        // 传给下一轮文件读取操作
        resolve(data);
    })
});

p.then(value => {
    return new Promise((resolve, reject) => {
        // value 为第一次读取的文件数据,data 为第二次(当前)读取的数据
        fs.readFile('./resources/orders.md', (err, data) => {
            // 将上轮读取结果和本轮合并传到下一轮轮读取操作
            resolve([value, data]);
        });
    });
}).then(value => {
    return new Promise((resolve, reject) => {
        fs.readFile('./resources/goods.md', (err, data) => {
            // value 为上一轮传递过来的文件数据数组
           // data 为第三次(当前)读取的数据
            value.push(data); //压入
            // 传给下一轮操作
            resolve(value);
        });
    });
}).then(value => {
    // 合并数组元素,输出
    console.log(value.join('\r\n'));
});
Promise的catch方法

catch() 方法返回一个 Promise,并且处理拒绝的情况。它的行为与调用 Promise.prototype.then(undefined, onRejected) 相同

const p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        //设置 p 对象的状态为失败, 并设置失败的值
        reject("出错啦!");
    }, 1000)
});
// p.then(function(value){}, function(reason){
//     console.error(reason);
// });
p.catch(function(reason){
    console.warn(reason);
});

1.14 集合 Set

ES6 提供了新的数据结构 Set(集合),它类似于数组,但 成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符…』和『for...of』进行遍历

定义和使用
let st1 = new Set();  //类型为Object
// 可以传入可迭代数据
// 可以自动去重
let s2 = new Set(['大事儿','小事儿','好事儿','坏事儿','小事儿']);

集合(这里假设有一个集合 st)的属性和方法:

  • st.size:返回集合个数
  • st.add(item):往集合中添加一个新元素 item,返回当前集合
  • st.delete(item):删除集合中的元素,返回 boolean
  • st.has(item):检测集合中是否包含某个元素,返回 boolean
  • st.clear():清空集合
  • 集合转为数组:[...st]
  • 合并两个集合:[...st1, ...st2]
集合案例

案例1: 数组去重

let arr1 = [1, 2, 2, 3, 3, 3, 4, 1, 2];
let res1 = [...new Set(arr1)];
console.log(res1); // [ 1, 2, 3, 4 ]

案例2:数组求交集

let arr2_1 = [1, 2, 2, 3, 4, 5];
let arr2_2 = [3, 6, 6, 7, 1, 4];
let res2 = arr2_1.filter(v => new Set(arr2_2).has(v))
console.log(res2); // [ 1, 3, 4 ]

案例3:数组求并集

let arr3_1 = [1, 2, 2, 3, 4, 5];
let arr3_2 = [3, 6, 6, 7, 1, 4];
let res3 = [...new Set([...arr3_1, ...arr3_2])];
console.log(res3); // 数组[ 1, 2, 3, 4, 5, 6, 7 ] 

案例4:数组求差集

let arr4_1 = [1, 2, 2, 3, 4, 5];
let arr4_2 = [3, 6, 6, 7, 1, 4];
let res4 = [...new Set(arr4_1)].filter(v => !(new Set(arr4_2).has(v)))
console.log(res4); // [ 2, 5 ]

1.15 Map

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合但是 “键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用『扩展运算符…』和『for…of』进行遍历

Map的定义
let mp1 = new Map();
mp1.set('aaa', 111); //键和值
mp1.set('bbb', 222);
mp1.set('ccc', 333);

let key = {  //对象
            school : 'ATGUIGU'
};
mp1.set(key, ['北京','上海','深圳']);  //添加键为对象 和值为数组

let mp2 = new Map([
    ['aaa', 111],
    ['bbb', 222],
    ['ccc', 333]
]);
console.log(mp1['aaa']); // 111
console.log(mp2.get('bbb')); // 222
console.log(mp1.get(key)); // ['北京','上海','深圳']
属性和方法

Map 的属性和方法:(k 为键,v为值)

  • size:返回 Map 的元素(键值对)个数
  • set(k, v):增加一个键值对,返回当前 Map
  • get(k):返回键值对的键值
  • has():检测 Map 中是否包含某个元素
  • clear():清空集合,返回 undefined
//for...of 遍历
for(let v of m){
    console.log(v);
}

1.16 Class 类

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已

Class 的定义

ES5对象的写法

//手机  构造函数
function Phone(brand, price){
    this.brand = brand;
    this.price = price;
}
//添加方法
Phone.prototype.call = function(){
    console.log("我可以打电话!!");
}
//实例化对象
let Huawei = new Phone('华为', 5999);
Huawei.call();  //调用原型里的方法
console.log(Huawei);

ES6的class的写法

class Shouji{
    //constructor 构造方法 名字不能修改
    constructor(brand, price){
        this.brand = brand;
        this.price = price;
    }
  	// 类中添加方法
    //方法不需要添加 function 关键字
    call(){
        console.log("我可以打电话!!");
    }
}
//实例化对象
let onePlus = new Shouji("1+", 1999);
onePlus. call();
console.log(onePlus);
Class 静态成员

属于类的,不属于实例对象的,叫做静态成员

在object中,实例对象和构造函数对象的属性和方法是不通的

function Phone(){  //构造函数对象
    name = "手机";
    change = function(){
        console.log("我可以改变世界!");
    }
}
let nokia = new Phone();  //实例对象
console.log(nokia.name);  //undefined
nokia.change();  //报错

class给成员属性或成员方法添加 static,该成员就成为静态成员,只能由该类调用

class Phone{
    //静态属性
    static name = '手机';
    static change(){
        console.log("我可以改变世界");
    }
}
let nokia = new Phone();
console.log(nokia.name);  //undefined
console.log(Phone.name);  //手机
ES5构造函数继承
 // 父级  手机
function Phone(brand, price){
    this.brand = brand;
    this.price = price;
}
Phone.prototype.callPhone = function(){
    console.log("我可以打电话");
}
// 子级  智能手机
function SmartPhone(brand, price, color, size){
    Phone.call(this, brand, price);
    this.color = color;
    this.size = size;
}
//设置子级 构造函数的原型
SmartPhone.prototype = new Phone;
// 做一个校正
SmartPhone.prototype.constructor = SmartPhone;
//声明 子类的方法
SmartPhone.prototype.photo = function(){
    console.log("我可以拍照")
}
SmartPhone.prototype.playGame = function(){
    console.log("我可以玩游戏");
}
// 实例化
const chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch');
console.log(chuizi);

输出chuizi对象的结果:

在这里插入图片描述

ES6的类继承

ES6 中直接使用 extends 语法糖(更简洁高级的实现方式)来实现继承,同时可以重写父类的方法,直接在子类中重新写一次要重写的方法即可覆盖父类方法

  • 使用 extends 关键字
  • 使用 super 关键字

super 关键字,用于访问和调用对象父类上的函数。可以调用父类的构造函数也可以调用父类的普通函数。子类在构造函数中使用 super, 必须放到 this 前面(必须先调用父类的构造方法,再使用子类构造方法)

继承中的属性或者方法查找原则:就近原则

继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的。继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)

// 声明父类
class Phone{  
    // 构造方法
    constructor(brand, price){
        this.brand = brand;
        this.price = price;
    }
    // 父类的方法
    callPhone(){
        console.log("我可以打电话!!");
    }
}
// 声明子类
class SmartPhone extends Phone {
    //构造方法
    constructor(brand, price, color, size){
        super(brand, price); //类似 Phone.call(this, brand, price)
        this.color = color;
        this.size = size;
    }
    // 子类的方法
    photo(){
        console.log("拍照");
    }
    playGame(){
        console.log("玩游戏");
    }
    // 方法的重写
    callPhone(){
        console.log('我可以进行视频通话');
    }
}

const xiaomi = new SmartPhone('小米',799,'黑色','4.7inch');
console.log(xiaomi);
// xiaomi.callPhone(); //我可以进行视频通话
// xiaomi.photo();  //拍照
// xiaomi.playGame();  //玩游戏

输出xiaomi对象的结果:

在这里插入图片描述

getter和setter

当属性拥有 get/set 特性时,属性就是访问器属性。代表着在访问属性或者写入属性值时,对返回值做附加的操作。而这个操作就是 getter/setter 函数。

使用场景: getter 是一种语法,这种 get 将对象属性绑定到 查询该属性时将被调用的函数。适用于某个需要动态计算的成员属性值的获取。setter 则是在修改某一属性时所给出的相关提示

class Test {
    constructor(log) {
        this.log = log;
    }
    get latest() {
        console.log('latest 被调用了');
        return this.log;
    }
    set latest(e) {
        console.log('latest 被修改了');
        this.log.push(e);
    }
}
let test = new Test(['a', 'b', 'c']);
// 每次 log 被修改都会给出提示
test.latest = 'd';
// 每次获取 log 的最后一个元素 latest,都能得到最新数据。
console.log(test.latest);

以上输出:

latest 被修改了
latest 被调用了
[ 'a', 'b', 'c', 'd' ]

1.17 数值扩展

(1)Number.EPSILON 是 JavaScript 表示的最小精度,一般用来处理浮点数运算。例如可以用于两个浮点数的比较。

function equal(a, b){
    if(Math.abs(a-b) < Number.EPSILON){
        return true;
    }else{
        return false;
    }
}
// 也可以写成箭头函数
let equal = (x, y) => Math.abs(x - y) < Number.EPSILON;

console.log(0.1 + 0.2 === 0.3); // false
console.log(equal(0.1 + 0.2, 0.3)); // true

(2)二进制和八进制:二进制以 0b 开头,八进制以 0o 开头,十六进制以0x开头

let b = 0b1010;
let o = 0o777;
let d = 100;
let x = 0xff;
console.log(x);  

(3)Number.isFinite 检测一个数值是否为有限数。

console.log(Number.isFinite(100)); // true
console.log(Number.isFinite(100 / 0)); // false
console.log(Number.isFinite(Infinity)); // false

(4)Number.isNaN 检测一个数值是否为 NaN

console.log(Number.isNaN(123)); //false

(5)Number.parseIntNumber.parseFloat
ES6 给 Number 添加了 parseInt 方法,Number.parseInt 完全等同于 parseInt。将字符串转为整数,或者进行进制转换。Number.parseFloat 则等同于 parseFloat()

Number.parseInt === parseInt; // true
Number.parseFloat === parseFloat; // true

// s:待转换的字符串
// base: 进位制的基数
// Number.parseInt(s, base);

console.log(Number.parseInt('5211314love'));  //5211314
console.log(Number.parseInt('5211314love',16));  //86053652
console.log(Number.parseFloat('3.1415926神奇')); // 3.1415926

(6)Number.isInteger() 判断一个数是否为整数。

console.log(Number.isInteger(5)); // true
console.log(Number.isInteger(2.5)); // false

(7)Math.trunc() 将数字的小数部分抹掉。

console.log(Math.trunc(3.5)); // 3

(8)Math.sign 判断一个数到底为正数 负数 还是零

console.log(Math.sign(100));  // 1
console.log(Math.sign(0));   // 0
console.log(Math.sign(-20000));  // -1

1.18 对象方法扩展

ES6 新增了一些 Object 对象的方法。

  • Object.is 比较两个值是否严格相等,与『===』行为 基本一致
  • Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
  • __proto__setPrototypeOfsetPrototypeOf 可以直接设置对象的原型
Object.is 两个值完全相等

Object.is() 方法判断两个值是否完全相同。Object.is 比较两个值是否严格相等,与 === 行为 基本一致。返回一个 Boolean 类型。

Object.is() 方法判断两个值是否为同一个值。如果满足以下条件则两个值相等:

  • 都是 undefined
  • 都是 null
  • 都是 truefalse
  • 都是相同长度的字符串且相同字符按相同顺序排列
  • 都是相同对象(意味着每个对象有同一个引用)
  • 都是数字且
    • 都是 +0
    • 都是 -0
    • 都是 NaN
    • 或都是非零而且非 NaN 且为同一个值

== 运算不同。 == 运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换 (这种行为的结果会将 "" == false 判断为 true),而 Object.is 不会强制转换两边的值。

===算也不相同。 === 运算符 (也包括 == 运算符) 将数字 -0+0 视为相等,而将 Number.NaNNaN 视为不相等。

console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN);// false
console.log(Object.is(+0, -0)); // false
console.log(+0 === -0);  // true
Object.assign 对象的合并

Object.assign 对象的合并,相当于浅拷贝,同样的 后面的会把前面的覆盖

const config1 = {
    host: 'localhost',
    port: 3306,
    name: 'root',
    pass: 'root',
    test: 'test'
};
const config2 = {
    host: 'http://atguigu.com',
    port: 33060,
    name: 'atguigu.com',
    pass: 'iloveyou',
    test2: 'test2'
}
console.log(Object.assign(config1, config2));

结果如下:

在这里插入图片描述

设置原型对象

Object.setPrototypeOf 用于设置对象的原型对象有多个参数

Object.getPrototypeof 用于获取对象的原型对象,相当于 __proto__

const school = {
    name: '尚硅谷'
}
const cities = {
    xiaoqu: ['北京','上海','深圳']
}
// 把 cities 设置为 school 的原型对象
Object.setPrototypeOf(school, cities);
console.log(school);

// 获取 school 对象的原型对象
console.log(Object.getPrototypeOf(school));

1.19 ES6 模块化

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来

模块化的好处:防止命名冲突,代码复用,高维护性

模块化规范产品,ES6 之前的模块化规范有:

  • CommonJS => NodeJS、Browserify
  • AMD => requireJS
  • CMD => seaJS

从ES6 开始,加入了模块化

ES6 模块化语法

模块功能主要由两个命令构成:exportimport

  • export 命令用于规定模块的对外接口, 暴露
  • import 命令用于输入其他模块提供的功能, 导入
ES6 的模块暴露语法

(1)单个(分别)暴露

// 单个导出
export let uname = 'Rick';
export let sayHello = function () {
    console.log('Hi, bro!');
}

(2)合并(统一)暴露

let uname = 'Rick';
let sayHello = function () {
    console.log('Hi, bro!');
}
// 合并导出
export { uname, sayHello };

(3)默认暴露

// 默认导出  
// export default{} 把要暴露的数据 括起来,里面可以是任意类型
export default {
    uname: 'Rick',
    sayHello: function () {
        console.log('Hi, bro!');
    }
}
ES6 的模块导入语法

html 的 script 标签必须要加入 type="module"

(1)通用导入方式

<script type="module">
		// 注意:路径最前面的 ./ 不要省略,会报错
		import * as m1 from './js/m1.js';  
		import * as m2 from './js/m2.js';
		import * as m3 from './js/m3.js';
</script>

(2)解构赋值导入方式

<script type="module">
		import { uname, sayHello } from './js/m1.js';

		// 有重复名可以设置别名
		import { uname as uname2, sayHello as sayHello2 } from './js/m2.js';

		// 配合默认暴露
		import {default as m3} from "./src/js/m3.js";
</script>

(3)简便方式导入,只针对默认暴露

<script type="module">
		import m3 from "./src/js/m3.js";
</script>
ES6模块化方式二

之前都是在script中写导入的模块,这样不方便,可以将文件导入都写进一个 app.js 文件中,然后在里面写入要导入的模块,想当于模块的一个入口

比如app.js 中的内容如下:

import * as m1 from './js/m1.js';
import * as m2 from './js/m2.js';
import * as m3 from './js/m3.js';

在 html 中引入 app.js 文件内容:

注意路径,还有必须加上 type="module"

<script src="./app.js" type="module"></script>
对模块化代码转换和打包

有的浏览器不支持 ES6 语法,这时候就需要使用 Babel 来将其转换成 ES5 等价语法,然后使用 browserify 进行打包

(1)安装工具,这里选择安装 browserify 来打包,以后会用到webpack

npm init --yes  //初始化
npm i babel-cli babel-preset-env browserify -D  //安装

(2)编译

对源文件 src/js 里面的代码进行转换,存在 dist/js 下面,–presets=babel-preset-env为参数

npx babel src/js -d dist/js --presets=babel-preset-env

(3)打包

dist/js/app.js为入口文件,dist/bundle.js为输出文件

npx browserify dist/js/app.js -o dist/bundle.js
ES6 模块化引入 npm 包

安装 jquery 包

npm i jquery
npm install jquery  //或者

再通过 import 导入

import $ from 'jquery'; //const $ = require("jquery");
// 修改背景颜色为粉色
$('body').css('background','pink');
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值