第2章 ECMAScript 6 新特性

2.1 let 关键字用来声明变量

使用 let 声明的变量有几个特点:

  1. 不允许重复声明
  2. 块儿级作用域:只能在代码块里面有效
  3. 不存在变量提升:不允许在声明这个变量之前使用这个变量
  4. 不影响作用域链:
{
        let school = '深圳大学';
        function fun() {
        console.log(school);
        }
        fun();
        // 运行结果:正常输出“深圳大学”
        // 虽然函数fun的作用域里面没有school这个变量
        // 但是不影响作用域链,fun()从上一级里面去寻找这个school变量
}

2.1.1 let 实践案例

在这里插入图片描述

<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';
        }
    }
</script>
  1. 如果for循环里面不用let的话,var相当于在全局变量中存储
  2. 即var i,i此时遍历2次后再自增则为3,但是items里面只有三个元素,下标只到2,所以就会报错

2.1.2 应用场景

  • 以后声明变量使用 let 就对了

2.2 const 关键字

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

  1. 声明必须赋初始值
  2. 标识符一般为大写,如:let SCHOOL = '深圳大学'
  3. 不允许重复声明
  4. 值不允许修改
  5. 块儿级作用域

注意: 对象属性修改和数组元素变化不会出发 const 错误,如下:

const TEAM = ['123','456','789'];
TEAM.push = ('666')
// 常量所指向的地址没有发生变化,所以不会报错

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

2.3 变量的解构赋值

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

2.3.1 数组的解构

const F4 = ['小沈','刘无','赵四','宋三'];
let [xiao, liu, zhao, song];
// 相当于
let xiao = '小沈', liu = '刘无', zhao = '赵四', song = '宋三';

2.3.2 对象的解构

  • 在对象的解构赋值中,解构的对象变量名要和被解构的变量名一致
// 1. 对象的解构赋值
const ZHAO = {
    name: '赵三三',
    age: '不详',
    xiaopin: function () {
        console.log("我可以说相声")
    }
}
let {name, age, xiaopin} = ZHAO;
// 相当于
let name = ZHAO.name;
let age = ZHAO.age;
let xiaopin = ZHAO.xiaopin();

// 2. 频繁使用对象方法
const ZHAO = {
    xiaoping: function () {
        console.log("我可以说相声");
    }
}
ZHAO.xiaoping();  // 一般调用
// 解构调用
let {xiaopin} = ZHAO;
xiaopin();

2.4 模板字符串

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

  1. 字符串中可以出现换行符
let str = `<ul>
            <li>赵毅</li>
            <li>赵三</li>
            <li>赵二</li>
            <li>赵五</li>
</ul>`;
  1. 可以使用 ${xxx} 形式输出变量
let star = '王某';
let result = `${star}在前几年离开了开心麻花`;
  • 注意:当遇到字符串与变量拼接的情况使用模板字符串

2.5 简化对象写法

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

let name = '向上';
let slogon = '永远追求行业更高标准';
let improve = function () {
    console.log('可以提高你的技能');
}

//属性和方法简写
let xiang = {
    name,
    slogon,
    improve,
    change () {  // 这里简写了,change = function () {}
        console.log('可以改变你');
    }
};
  • 注意:对象简写形式简化了代码,所以以后用简写就对了

2.6 箭头函数

ES6 允许使用「箭头」(=>)定义函数。

// 通用写法
// 声明一个函数
let fn = (a, b) => {
    return a + b;
}
// 调用函数
let result = fn(1, 2);
console.log(result);

2.6.1 this的指向

this是静态的,this始终指向函数声明时所在作用域下的this的值

// 声明普通函数
function getName () {
    console.log(this.name);
}
// 声明箭头函数
let getName2 = () = > {
    console.log(this.name);
}

// 设置window对象的name属性
window.name = '深圳大学';
const school = {
    name: "SZU"
}

// 直接调用,this指向window
getName();  // 控制台打印“深圳大学”
getName2();  // 控制台打印“深圳大学”

// call方法调用(call方法可以改变函数内部this的值)
getName.call(school);  // 此时this指向school,所以控制台打印“SZU”
getName2.call(school);  // 此时this依旧指向window,所以控制台打印“深圳大学”

2.6.2 不能作为构造实例化对象

let Person = (name, age) => {
    this.name = name;
    this.age = age;
}
let me = new Person('xiao', 30);
console.log(me);
// 报错:Person is not a constructor

2.6.3 不能使用arguments变量

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

2.6.4 箭头函数的简写

// 1. 省略小括号,当形参有且只有一个的时候
let add = n => {
    return n + n;
}

// 2. 省略花括号,当代码体只有一条语句的时候,此时return必须省略
// 而且语句的执行结果就是函数的返回值
let pow = n => n * n;
  • 注意:箭头函数不会更改this指向,用来指定回调函数会非常合适

2.6.5 箭头函数的实践

2.6.5.1 需求1

点击div 2s后颜色变成“粉色”

// 获取元素
let ad = document.getElementById('ad');
// 绑定事件
ad.addEventListener("click", function() {
    // 保存this的值
    // let _this = this;
    // 定时器
    setTimeout(() => {
        // 修改背景颜色 this
        // 此时箭头函数this指向ad元素,因为是在ad下定义的箭头函数
        // _this.style.background = 'pink';
        this.style.background = 'pink';
    }, 2000);
});
2.6.5.2 需求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);

2.6.6 箭头函数总结

  • 箭头函数适合与this无关的回调:定时器,数组的方法回调
  • 箭头函数不适合与this有关的回调:事件回调,对象的方法

2.7 函数参数的默认值设置

  • ES6允许给函数参数赋值初始值

2.7.1 形参初始值 具有默认值的参数,一般位置要靠后(潜规则)

function add(a, b, c = 10) {
    return a + b + c;
}
let result = add(1, 2);
console.log(result);  // 控制台输出13

2.7.2 与解构赋值结合

function connect({host = "127.0.0.1", username, password, port}) {
    console.log(host)
    console.log(username)
    console.log(password)
    console.log(port)
}
connect({
    host: 'baidu.com',
    username: 'root',
    password: 'root',
    port: 3306
})

2.8 rest 参数

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

// ES5 获取实参的方式
function data() {
    console.log(arguments);
}
data('白纸', '阿娇', '思慧');
// 控制台输出为一个对象

// rest参数
function data(...args) {
    console.log(args);
}
data('白纸', '阿娇', '思慧');
// 控制台输出为一个数组,可以用数组的api如:filter some every map等

// rest 参数必须要放到参数最后
function fn(a, b, ...args) {
    console.log(a);
    console.log(b);
    console.log(args);
}

fn(1, 2, 3, 4, 5, 6);
// 控制台输出:1, 2, [3, 4, 5, 6]

2.9 spread 扩展运算符

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

/**
* 展开数组
*/ 
let tfboys = ['盖文','赵信','皇子'];
function fn(){
    console.log(arguments);
}
fn(...tfboys)
// 控制台输出为:'盖文','赵信','皇子'

2.9.1 数组的合并

const kuaizi = ['a', 'b'];
const fengh = ['c', 'd'];
const zuhe = [...kuaizi, ... fengh];
console.log(zuhe);
// 控制台输出:['a', 'b', 'c', 'd']

2.9.2 数组的克隆

const szh = ['E', 'G', 'M'];
const syc = [...szh];
console.log(syc);
// 控制台输出:['E', 'G', 'M']

2.9.3 将伪数组转为真正的数组

const divs = document.querySelectorAll('div');  // 这里拿到的div是object
const divArr = [...divs];  // 这里转为了真正的数组
console.log(divArr);

2.10 Symbol

2.10.1 Symbol 基本使用

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

Symbol 特点:

  1. Symbol 的值是唯一的,用来解决命名冲突的问题
  2. Symbol 值不能与其他数据进行运算
  3. Symbol 定义 的 对象属 性 不能 使 用 for…in 循环遍历,但是可以使用Reflect.ownKeys 来获取对象的所有键名
// 创建Symbol
let s2 = Symbol('水果干');
let s3 = Symbol('水果干');
console.log(s2 === s3);
// 控制台打印:false(因为编码不同)

// Symbol.for 创建(for获取唯一值,所以这里s4等于s5)
let s4 = Symbol.for('水果干');
let s5 = Symbol.for('水果干');
console.log(s4 === s5);
// 控制台打印:true

JavaScript数据类型记忆方法:USONB

  • u: undefined
  • s: string, symbol
  • o: object
  • n: null, number
  • b: boolean

注: 遇到唯一性的场景时要想到 Symbol

2.10.2 对象添加Symbol类型的属性

向对象中添加方法 up down,但是该对象中的属性很多,你不确定有没有已经有up 和 down的方法,这个时候可以用Symbol来添加唯一的方法,就不会重复了

let game = {...

// 声明一个对象
let methods = {
    up: Symbol(),
    down: Symbol()
};

game[methods.up] = function() {
    console.log("我可以改变形状");
}

game[methods.down] = function() {
    console.log("我可以快速下降!!");
}

console.log(game);
  • 另外一个方法
let youxi = {
    name: "狼人杀",
    [Symbol('say')]: function() {
        console.log("我可以发言")
    },
    [Symbol('zibao')]: function() {
        console.log("我可以说话")
    }
}

2.10.3 Symbol的内置属性

指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。

方法描述
Symbol.hasInstance当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法
Symbol.isConcatSpreadable对象的 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环境排除。

2.11 迭代器

遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。

2.11.1 ES6 创造了一种新的遍历命令

for…of 循环,Iterator 接口主要供 for…of 消费

  • for…of遍历键值 for…in遍历键名
// 声明一个数组
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧'];

// 使用 for...in 遍历数组输出键名
for (let v in xiyou) {
    console.log(v);
}
// 输出:0,1,2,3

// 使用 for...of 遍历数组输出键值
for (let v of xiyou) {
    console.log(v);
}
// 输出:唐僧, 孙悟空, 猪八戒, 沙僧

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

  • Array、Arguments、Set、Map、String、TypedArray、NodeList
  • 对象里面有Symbol.iterator属性就可以进行遍历了

2.11.3 工作原理

  1. 创建一个指针对象,指向当前数据结构的起始位置
  2. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  4. 每调用 next 方法返回一个包含 value 和 done 属性的对象
// 原理
let iterator = xiyou[Symbol.iterator]();

// 调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

注: 需要自定义遍历数据的时候,要想到迭代器。

  • 例子:
// 迭代器案例:遍历这个自定义对象
// 声明一个对象
const banji = {
    name: "终极一班",
    stus: [
        'xiaoming',
        'xiaoning',
        'xiaotian',
        'knight'
    ],
    [Symbol.iterator]() {
        // 索引变量
        let index = 0;
        //
        let _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};
                }
            }
        };
    }
}

2.12 生成器

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

在这里插入图片描述

function * gen(){

    yield '一只没有耳朵';

    yield '一只没有尾巴';

    yield '真奇怪';

}

let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

代码说明:

  1. “ * ”的位置没有限制
  2. 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到yield 语句后的值
  3. yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next方法,执行一段代码
  4. next 方法可以传递实参,作为 yield 语句的返回值,默认返回yield后面的值

2.12.1 整体传参与next方法传参

在这里插入图片描述

2.12.2 定时器案例

异步编程: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();  // 往下执行
    }, 1000)
}

function two() {
    setTimeout(() => {
        console.log(222);
        iterator.next();
    }, 2000)
}

function three() {
    setTimeout(() => {
        console.log(333);
        iterator.next();
    }, 3000)
}

function * gen() {
    yield one();
    yield two();
    yield three();
}

// 调用生成器函数
let iterator = gen();
iterator.next();

2.12.3 模拟获取数据

模拟获取用户的数据,然后获取用户对应的订单数据,然后获取订单对应的商品数据

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();
    let orders = yield getOrders();
    let goods = yield getGoods();
}

// 调用生成器函数
let iterator = gen();
iterator.next();

2.13 Promise

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

  1. Promise 构造函数: Promise (excutor) {}
  2. Promise.prototype.then 方法
  3. Promise.prototype.catch 方法

2.13.1 Promise的基本使用

// 实例化 Promise 对象
const p = new Promise(function (resolve, reject) {
    setTimeout(function () {
        //
        // let data = '数据库中的用户数据';
        // resolve
        // resolve(data);

        let err = '数据读取失败';
        reject(err);
    }, 1000);
});

// 调用 promise 对象的 then 方法
p.then(function (value) {
    // 成功调用第一个value
    console.log(value);
}, function (reason) {
    // 失败调用第二个reason
    console.log(reason);
})

2.13.2 Promise 封装读取文件

// 1. 引入 fs 模块
const fs = require('fs');

// 2. 调用方法读取文件
fs.readFile('./resources/为学.md', (err, data) => {
    // 如果失败
    if (err) throw err;
    // 如果没有出错,则输出内容
    console.log(data.toString());
});

// 3. 使用 Promise 封装
const p = new Promise(function(resolve, reject) {
    fs.readFile("./resources/为学.md", (err, data) => {
        // 判断如果失败
        if(err) {
            // 调用reject,修改promise的状态为失败
            reject(err);
        }
        // 如果成功
        // 调用resolve,修改promise的状态为成功
        resolve(data);
    });
});

// 通过then方法来处理失败或成功的结果
p.then(function(value){
    console.log(value.toString());
}, function(reason) {
    console.log("读取失败!!");
});

2.13.3 Promise 封装AJAX请求


const p = new Promise((resolve, reject) => {
    // 1. 创建对象
    const xhr = new XMLHttpRequest();

    // 2. 初始化
    xhr.open("GET", "https://api.apiopen.top/getJoke");

    // 3. 发送
    xhr.send();

    // 4. 绑定事件,处理响应结果
    xhr.onreadystatechange = function () {
        // 判断
        if (xhr.readyState === 4) {
            // 判断响应状态码 200-299(成功)
            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);
});

2.13.4 Promise.prototype.then方法

  • 调用then方法,then方法的返回结果是Promise对象,对象状态由回调函数的执行结果决定
    1. 如果回调函数中返回的结果是非Promise类型的属性,状态为成功,返回值为对象的成功的值
// 创建 promise 对象
const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('用户数据');
        // reject('出错啦');
    }, 1000)
});

const result = p.then(value => {
    console.log(value);
    // 1. 非Promise类型的属性
    // return 'iloveyou';
    // 2. 是Promise对象
    // return new Promise((resolve, reject) => {
    //     // resolve('ok');
    //     reject('error');
    // });
    // 3. 抛出错误
    // throw new Error('error');
    throw '出错啦';
}, reason => {
    console.warn(reason);
});

// 因为then方法返回一个Promise对象的特性,then方法是可以链式调用的
// 链式调用
p.then(value => {

}).then(value => {

});
// 链式调用一形成,杜绝回调地狱

console.log(result);


2.13.5 Promise实践练习 - 多个文件内容获取

获取多个文件内容,将他们合在一起并输出

// 引入fs模块
const fs = require("fs");

// 回调地狱写法
// 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 p = new Promise((resolve, reject) => {
    fs.readFile("./resources/为学.md", (err, data) => {
        resolve(data);
    });
});

p.then(value => {
    return new Promise((resolve, reject) => {
        fs.readFile("./resources/插秧诗.md", (err, data) => {
            resolve([value, data]);
        });
    })
}).then(value => {
    return new Promise((resolve, reject) => {
        fs.readFile("./resources/观书有感.md", (err, data) => {
            // 压入
            value.push(data);
            resolve(value);
        });
    })
}).then(value => {
    console.log(value.join('\r\n'));
});

2.13.6 Promise对象catch方法

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        // 设置p对象的状态为失败,并设置失败的值
        reject("出错啦!");
    }, 1000)
});

// p.then(function(value){}, function(reason){
//     console.error(reason);
// });

// 只需要一个错误的参数即可,相当于then方法的语法糖
p.catch(function(reason) {
    console.warn(reason);
});

2.14 Set

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

  1. size:返回集合的元素个数
  2. add:增加一个新元素,返回当前集合
  3. delete:删除元素,返回 boolean 值
  4. has:检测集合中是否包含某个元素,返回 boolean 值
  5. clear:清空集合,返回 undefined

2.14.1 集合方法实践

//创建一个空集合
let s = new Set();

//创建一个非空集合
let s1 = new Set([1, 2, 3, 1, 2, 3]);

//集合属性与方法
//返回集合的元素个数
console.log(s1.size);
//添加新元素
console.log(s1.add(4));
//删除元素
console.log(s1.delete(1));
//检测是否存在某个值
console.log(s1.has(2));
//清空集合
console.log(s1.clear());

2.14.2 集合案例实践

// 实际案例
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1];
// 1. 数组去重
let result = [...new Set(arr)];
console.log(result);

// 2. 交集
let arr2 = [4, 5, 6, 5, 6];
let result = [...new Set(arr)].filter(item => {
    let s2 = new Set(arr2);  // 4 5 6
    if (s2.has(item)) {
        return true;
    } else {
        return false;
    }
});
// 简写
let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
console.log(result);

// 3. 并集
let union = [...new Set([...arr, ...arr2])];
console.log(union);

// 4. 差集
let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
console.log(diff);

2.15 Map

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

Map 的属性和方法:

  1. size:返回 Map 的元素个数
  2. set:增加一个新元素,返回当前 Map
  3. get:返回键名对象的键值
  4. has:检测 Map 中是否包含某个元素,返回 boolean 值
  5. clear:清空集合,返回 undefined

2.15.1 Map方法实例

//创建一个空 map
let m = new Map();
//创建一个非空 map
let m2 = new Map([
    ['name', '333'],
    ['slogon', '不断提高行业标准']
]);

// 添加元素
m2.set('change', function () {
    console.log("我们可以改变你!")
});
// 声明一个对象
let key = {
    school: 'abc'
};
m2.set(key, ['a', 'b', 'c']);

//属性和方法
//获取映射元素的个数
console.log(m2.size);
//添加映射值
console.log(m2.set('age', 6));
//获取映射值
console.log(m2.get('age'));
//检测是否有该映射
console.log(m2.has('age'));
//清除
console.log(m2.clear());

2.16 Class 类

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

知识点:

  1. class 声明类
  2. constructor 定义构造函数初始化
  3. extends 继承父类
  4. super 调用父级构造方法
  5. static 定义静态方法和属性
  6. 父类方法可以重写

2.16.1 class介绍与constructor实例

// 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 Shouji {
    // 构造方法 名字不能修改
    constructor(brand, price) {
        this.brand = brand;
        this.price = price;
    }

    // 方法必须使用该语法,不能使用ES5的对象完整形式
    call() {
        console.log("我可以打电话!!");
    }
}

let onePlus = new Shouji("1+", 1000);

console.log(onePlus);

2.16.2 class 静态成员

  • 实例对象与函数对象的属性是不相通的
  • 函数对象的属性是属于函数对象的,并不属于实例对象,这样的属性我们称为静态成员
function Shouji() {

}

Shouji.name = '手机';
Shouji.change = function() {
    console.log("222");

}
Phone.prototype.size = '5.5inch';

let mamba = new Shouji();

console.log(mamba.name);  // 打印:undefined
mamba.change();  // 报错:未定义该函数
console.log(mamba.size);  // 打印:5.5inch
  • 对于类来说,静态属性是属于类的,并不属于实例对象
class Phone {
    // 静态属性
    static name = '手机';
    static change() {
        console.log("111");
    }
}

let nokia = new Phone();
console.log(nokia.name);  // 打印:undefined
console.log(Phone.name);  // 打印:手机

2.16.3 ES5构造函数继承

// 手机
function Phone(brand, price) {
    this.brand = brand;
    this.price = price;
}

Phone.prototype.call = function() {
    console.log("我可以");
}

// 智能手机
function SmartPhone(brand, price, color, size) {
    // 改变this指向为SmartPhone
    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('锤子', 2222, '黑色', '5.5inch');

console.log(chuizi);

2.16.4 class的类继承

class Phone {
    // 构造方法
    constructor(brand, price) {
        this.brand = brand;
        this.price = price;
    }

    // 父类的成员属性
    call() {
        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("玩游戏");
    }
}

const xiaomi = new SmartPhone('小米', 3333, '黑色', '4,7inch');

console.log(xiaomi);
xiaomi.photo();
xiaomi.playGame();
xiaomi.call();

2.16.5 子类对父类方法的重写

  • 在js中的class类方法中,子类是不能够直接去调用这个父类的同名方法的
  • 可以通过重写去调用父类的同名方法
class Phone {
    // 构造方法
    constructor(brand, price) {
        this.brand = brand;
        this.price = price;
    }

    // 父类的成员属性
    call() {
        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("玩游戏");
    }

    // 重写父类同名方法
    call() {
        console.log("视频通话");
    }
}

const xiaomi = new SmartPhone('小米', 3333, '黑色', '4,7inch');

console.log(xiaomi);
xiaomi.photo();
xiaomi.playGame();
xiaomi.call();  // 这里调用的是子类的call方法

2.16.6 class中getter和setter设置

  • 当值被获取时,执行get对应的函数
  • 当值被修改时,执行set对应的函数
// get 和 set
class Phone {
    // 类可以没有构造函数
    get price() {
        console.log("价格属性被读取了");
        return 'hello';
    }

    set abb(newVal) {
        console.log('价格属性被修改了');
    }
}

// 实例化对象
let s = new Phone();

// 因为s.price被获取了,所以执行get对应的函数;控制台打印:价格属性被读取了,并且返回'hello'
console.log(s.price);  

// 因为s.price值被修改了,所以执行set对应的函数;控制台打印:价格属性被修改了
s.price = 'free';  

2.17 数值扩展

  • Number.EPSILON 是js表示的最小精度
  • EPSILON 属性的值接近于 2.220446…

应用:

function equal(a, b) {
    if (Math.abs(a - b) < Number.EPSILON) {
            return true;
    } else {
        return false;
    }
}
console.log(0.1 + 0.2 === 0.3);  // false
console.log(equal(0.1 + 0.2, 0.3));  // true

2.17.1 二进制和八进制

  • ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示。
let b = 0b1010;
let o = 0o777;
let d = 100;
let x = 0xff;

2.17.2 Number.isFinite() 与 Number.isNaN()

  • Number.isFinite() 用来检查一个数值是否为有限的:

console.log(Number.isFinite(100));

  • Number.isNaN() 用来检查一个值是否为 NaN:

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

2.17.3 Number.parseInt() 与 Number.parseFloat()

  • ES6 将全局方法 parseInt 和 parseFloat,移植到 Number 对象上面,使用不变。
console.log(Number.parseInt('131234神奇'));  // 打印:131234
console.log(Number.parseFloat('1.31234神奇'));  // 打印:1.31234

2.17.4 Number.isInteger

  • Number.isInteger() 用来判断一个数值是否为整数
console.log(Number.isInteger(5)); // 打印:true
console.log(Number.isInteger(2.5)); // 打印:false

2.17.5 Math.trunc

  • 用于去除一个数的小数部分,返回整数部分。

console.log(Math.trunc(2.3)); // 打印:2

2.17.6 Math.sign

  • 用来判断一个数到底是为正数(1)、负数(-1)还是0(0)
console.log(Math.sign(100));  // 打印 1
console.log(Math.sign(0));  // 打印 0
console.log(Math.sign(-100));  // 打印 -1

2.18 对象扩展

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

2.18.1 Object.is

  • 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
console.log(Object.is(NaN, NaN));  // 打印:true
console.log(NaN === NaN);  // 打印:false

2.18.2 Object.assign

  • 对象的合并,将源对象的所有可枚举属性,复制到目标对象
  • 如果参数名相同,后者对象属性值覆盖前者
const config1 = {
    host: 'localhost',
    port: 3306,
    name: 'root'
};
const config2 = {
    host: '111',
    port: 222
};
console.log(Object.assign(config1, config2));
// 打印:{host: "111", port: 222, name: "root"}

2.18.3 Object.setPrototypeOf 设置原型对象

  • __proto__、setPrototypeOf、 setPrototypeOf 可以直接设置对象的原型
  • Object.getPrototypeOf
const school = {
    name: 'ab大学'
};
const cities = {
    xiaoqu: ['11', '22', '33', '44']
    };
Object.setPrototypeOf(school, cities);
console.log(Object.getPrototypeOf(school));
console.log(school);

2.19 模块化

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

2.19.1 模块化的好处

模块化的优势有以下几点:

  1. 防止命名冲突
  2. 代码复用
  3. 高维护性

2.19.2 模块化规范产品

ES6 之前的模块化规范有:

  1. CommonJS => NodeJS、Browserify
  2. AMD => requireJS
  3. CMD => seaJS

2.19.3 ES6 模块化语法

模块功能主要由两个命令构成:export 和 import。

2.19.3.1 export 命令用于规定模块的对外接口
  • 如果需要被引用,那就export暴露数据
  1. 分别暴露
// m1.js 文件下
// 分别暴露
export let school = '111';

export function teach() {
    console.log(222);
}
  1. 统一暴露
// m2.js 文件下
// 统一暴露
let school = '234';

function findJob() {
    console.log("找工作");
}

// 导出要暴露的数据
export {school, findJob};
  1. 默认暴露
// m3.js 文件下
// 默认暴露
export default {
    school: '555',
    change: function() {
        console.log('changer');
    }
}
2.19.3.2 import 命令用于输入其他模块提供的功能
  1. 通用的导入方式
<script type="module">
    // 1. 通用的导入方式
    // 引入 m1.js 模块内容
    import * as m1 from "./src/js/m1.js";
    console.log(m1);
    // 引入m2.js 模块内容
    import * as m2 from "./src/js/m2.js";
    console.log(m2);
    // 引入m3.js 模块内容
    import * as m2 from "./src/js/m3.js";
    console.log(m3);
    m3.default.change();
</script>
  1. 解构赋值形式
<script type="module">
    // 2. 解构赋值形式
    import {school, teach} from "./src/js/m1.js";
    import {school as guigu, findJob} from "./src/js/m2.js";  // 变量命名冲突,设置别名
    import {default as m3} from "./src/js/m3.js";  // 导入默认对象,必须使用别名
</script>
  1. 简便形式(针对默认暴露)
<script type="module">
    // 3. 简便形式 针对默认暴露
    import m3 from "./src/js/m3.js";
    console.log(m3);
</script>

2.19.4 浏览器使用ES6模块化方式二

  1. 先建一个入口文件
// app.js
// 入口文件

// 模块引入
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";
import * as m3 from "./m3.js";

console.log(m1);
console.log(m2);
console.log(m3);
  1. 在html里面引用这个入口文件
// html
<script src="./src/js/app.js" type="module"></script>

2.19.5 babel对ES6模块化代码转换

  1. html文件引入
<!--
    1. 安装工具 babel-cli babel-preset-env browserify(webpack)
    2. npx babel src/js -d dist/js
    3. 打包 npx browserify dist/js/app.js -o dist/bundle.js
-->
<script src="dist/bundle.js"></script>
  1. 命令行指令
1. 安装工具
npm init --yes
npm i babel-cli babel-preset-env browserify -D

// 局部安装选择npx
// 2. 选择文件路径,命令行传参
npx babel -d dist/js --presets=babel-preset-env

// 3. 打包到bundle.js,进行文件转换
npx browserify dis/js/app.js -o dist/bundle.js  // 文件转换

// 全局安装选择babel
babel

2.19.6 ES6模块化引入NPM包

  • 使用npm包
// app.js 文件
// 修改背景颜色为粉色
import $ from 'jquery';  // const $ = require("jquery");
$('body').css('background', 'pink');
// 安装jquery
npm i jquery

// 打包
npx babel -d dist/js --presets=babel-preset-env
// 重新打包
npx browserify dis/js/app.js -o dist/bundle.js
  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值