ES学习笔记

本 笔记来源于视频 尚硅谷Web前端ES6教程,涵盖ES6-ES11

概述

ES

ES全称是EcmaScript,是脚本语言的规范,而平常编写的JavaScript是EcmaScript的一种实现,而ES新特性指的就是JavaScript的新特性。

为什么要学习新特性

  • 语法简洁,功能丰富,编程实现更简单高效
  • 框架开发应用
  • 前端开发职位要求,是就业必备功能

前置知识

  • JavaScript基本语法
  • Ajax与Node.js

ES6

let变量声明及其特性

let关键字是用来声明变量的,而var也可以声明变量。使用let声明变量有如下几个特点:

  • 不允许重复声明。即在同一作用域中不能存在使用let声明的同名变量,否则报错SyntaxError: Identifier 'xxx' has already been declared
  • 作用范围是块级作用域。即使用let声明的变量作用在大括号{}内,而在大括号之外是访问不到这个变量的。
  • 不存在变量提升。在声明变量之前是访问不到变量的,也不会输出undefined,反而会报错ReferenceError: Cannot access 'a' before initialization
  • 不影响作用域链。即在函数外声明的变量,在函数内是可以使用的,不会因为函数的{}无法调用。
// 0.使用let关键字声明变量
let a;
let b, c, d;
let e = 100;
let f = 111, g = 'hello world', h = [], i = {}, j = true;

// 1.变量不能重复声明,但var声明的变量是可以重复声明的
let msg = 'ES6';
let msg = 'JavaScript';
console.log(msg);// 报错SyntaxError: Identifier 'msg' has already been declared

// 2.块级作用域,在if、else、while、for等{}块中声明的变量,只会在大括号块内有效
if (2 > 1) {
    let result = true;
}
console.log(result);// 报错ReferenceError: result is not defined

// 3.不存在变量提升,在变量声明之前是不能访问的
console.log(info);// 报错ReferenceError: Cannot access 'info' before initialization
let info = 'INFO';

// 4.不影响作用域链,即在函数外定义的变量,在函数内是可以访问的
{
    let test = 'T E S T';
    function print() {
        console.log(test);
    }
    print();
}

建议以后在JavaScript中声明变量使用let关键字。

const关键字声明常量及其特点

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

  • 声明的同时必须赋初值。即const a;是错误的,必须是const a='xxx';赋予初值的。
  • 一般常量名大写,潜规则。
  • 也不允许重复声明。
  • 值不允许修改。
  • 块级作用域。
// 0.声明常量
const DATE = '2022-01-01';

// 1.声明必须赋初值
const A;
console.log(A);// 报错SyntaxError: Missing initializer in const declaration

// 2.一般常量名大写,潜规则
const MSG = 'hello world';

// 3.不允许重复声明
const NUM = 12;
const NUM = 15;// 报错SyntaxError: Identifier 'NUM' has already been declared

// 4.常量的值不允许修改
const NAME = '张三';
NAME = '李四';// 报错TypeError: Assignment to constant variable.

// 5.块级作用域
{
    const BOOL = true;
}
console.log(BOOL);// 报错ReferenceError: BOOL is not defined

// 6.对于数组和对象元组的修改,不算对常量的修改,因此不会报错
const ARRAY = [123, 456, 789];
ARRAY.push(963);
console.log(ARRAY);// [ 123, 456, 789, 963 ]

const OBJECT = {
    name: '张三',
    age: 18
};
OBJECT.name = '李四';
console.log(OBJECT);// { name: '李四', age: 18 }

建议声明对象类型使用const,非对象类型声明则使用let

变量的解构赋值

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

// 1.数组的解构
// 用[]中声明的变量一一对应数组中的值,如果[]中变量的个数与数组中元素个数不同,则以[]中的变量为准解构值
const ARRAY = ['唐僧', '孙悟空', '猪八戒', '沙僧'];
let [ts, swk, zbj, ss] = ARRAY;
console.log(ts, swk, zbj, ss);// 唐僧 孙悟空 猪八戒 沙僧
let [a, b, c] = ARRAY;
console.log(a, b, c);// 唐僧 孙悟空 猪八戒

// 2.对象的解构
// 用{}来提取对象中的指定键名对应的值,{}中的变量名必须同对象中的键名相同才能解构到对应的值
const OBJECT = {
    name: '张三',
    age: 18,
    hello: function () {
        console.log('hello world');
    },
    hobbies: ['吃', '喝', '玩', '乐']
};
let {name, hello, hobbies} = OBJECT;
console.log(name);// 张三
hello();// hello world
console.log(hobbies);// [ '吃', '喝', '玩', '乐' ]

模板字符串

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

  • 字符串可以进行换行。
  • 可以使用${xxx}方式输出变量。
// 1.`可以声明字符串,''和""也可以声明变量
let str = `hello world`;
console.log(str, typeof str);// hello world string

// 2.`可以直接换行
let str1 = '
    ' + '
  • 张三
  • '
    + '
  • 李四
  • '
    + '
  • 王五
  • '
    + '
'
;// 是字符串拼接 let str2 = `
  • 张三
  • 李四
  • 王五
`
;// 可读性更好,更简洁,不需要拼接字符串 // 3.可以在`中使用${xxx}形式引用变量的值 let name = '张三'; const hobbies = ['吃', '喝', '玩', '乐']; const info = { age: 19, gender: 'm' }; let msg = `${name}${hobbies.length}个爱好:${hobbies[0]}${hobbies[1]}等,今年${info.age}岁!`; console.log(msg);

简化对象写法

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

// 变量和函数
let name = '张三';
let hello = function () {
    console.log('hello world');
};

// 未简化的写法
const info1 = {
    name: name,
    hello: hello,
    print: function () {
        console.log('hello world');
    }
};
console.log(info1);

// 简化的写法
const info2 = {
    // 直接简化变量,只需要输入变量名即可
    name,
    hello,
    // 函数也直接写函数名和函数体即可,不需要写function关键字了
    print() {
        console.log('hello world');
    }
};
console.log(info2);

箭头函数

ES6允许使用箭头(=>)来定义函数,语法如下:

let 函数名 = ([参数列表]) => {
	函数体
}

箭头函数注意事项如下:

  • 如果形参只有一个,则小括号可以省略。即arg => {}
  • 如果函数体只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果。即arg => conso.log(arg);
  • 箭头函数this指向声明时所在作用域下this的值。
  • 箭头函数不能作为构造函数实例化。
  • 不能使用arguments
<script type="text/javascript">
    // 1.声明函数
    // 声明普通函数
    let fn1 = function (a, b) {
        return a + b;
    };
    // 声明箭头函数
    let fn2 = (a, b) => {
        return a + b;
    };

    // 2.this 是静态的. this 始终指向函数声明时所在作用域下的 this 的值。箭头函数不会更改 this 指向,用来指定回调函数会非常合适
    function getName1() {
        console.log(this.name);
    }

    let getName2 = () => {
        console.log(this.name);
    };
    window.name = '唐僧';
    getName1();// 唐僧
    getName2();// 唐僧

    const obj = {
        name: 'tangseng'
    };
    getName1.call(obj);// tangseng
    getName2.call(obj);// 唐僧

    // 3.不能作为构造实例化对象
    let Person = (name, age) => {
        this.name = name;
        this.age = age;
    };
    // let person = new Person('张三', 15);
    // console.log(person);// 报错Uncaught TypeError: Person is not a constructor

    // 4.不能使用arguments变量
    let fn = () => {
        // console.log(arguments);// 报错Uncaught ReferenceError: arguments is not defined
    };
    fn(1, 2, 3);

    // 5.箭头函数的省略
    // 5.1 当只有一个形参时,可以省略小括号
    let add = n => {
        return n * 2;
    };
    // 5.2 当函数体只有一条语句时,可以省略花括号,此时return必须省略
    let pow = n => n * n;
script>

函数参数默认值

ES6允许给函数参数赋予默认值,语法如下:

// 定义带有参数默认值的函数,例如:
function 函数名(参数名, 参数名=默认值, 参数名[,...]) {
	// 函数体
}
// 调用函数,可以选择传入值覆盖默认值,也可以选择不传入而使用默认值
fn(1, 2, 3);
fn(1, 3);

例如:

// 1.设置带有默认值的参数,一般默认值参数放到最后
function add(a, b, c = 5) {
    return a + b + c;
}

let result1 = add(1, 2);// 使用默认值参数
console.log(result1);// 8
let result2 = add(1, 2, 3);// 覆盖默认值参数
console.log(result2);// 6

// 2.与解构赋值相结合
function connect({host = '127.0.0.1', username, password, port}) {
    console.log(host, username, password, port);// www.baidu.com root root 8888
}

connect({
    host: 'www.baidu.com',
    username: 'root',
    password: 'root',
    port: 8888
});// 传入一个对象,该函数的参数体会利用解构赋值

rest参数

ES6引入了rest来获取函数传入的实参,用来代替arguments,语法如下:

// 单个rest参数
function 函数名(...rest参数) {
	函数体
}

// 普通参数与rest参数结合使用,则rest参数必须放到参数列表的最后,一个函数中只能有一个rest参数
function 函数名(参数, 参数, ...rest参数) {
	函数体
}

例如:

// 1.arguments参数
function add() {
    console.log(arguments);// [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
}
add(1, 2, 3, 4, 5);// 即使函数没有形参,但只要传入了实参,那么就可以通过 arguments 来进行获取所有的实参,是一个对象

// 2.rest参数
function print(...args) {
    console.log(args);// [ '唐僧', '孙悟空', '猪八戒', '沙僧' ]
}
print('唐僧', '孙悟空', '猪八戒', '沙僧');

function hello(a, b, ...args) {
    console.log(a);// 1
    console.log(b);// 2
    console.log(args);// [ '唐僧', '孙悟空', '猪八戒', '沙僧' ]
}
hello(1, 2, '唐僧', '孙悟空', '猪八戒', '沙僧');

扩展运算符

扩展运算符也是三个点(...),可以讲一个数组转换为逗号分隔的参数序列,可以用来对数组进行解包。

// 0.扩展运算符(...)可以将数组转换为逗号分隔的序列,如 [1,2,3] => 1,2,3
const array = [1, 2, 3, 4];

function add() {
    console.log(arguments);// [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 }
}

add(...array);// 等价于 add(1, 2, 3, 4);

// 1.扩展运算符的应用
// 1.1 合并数组
const arrayA = [1, 2, 3, 4];
const arrayB = [9, 8, 7, 6];
const arrayC = [...arrayA, ...arrayB];// 合并两个数组为一个新的数组
console.log(arrayC);
// 1.2 克隆数组
const oldArray = ['A', 'B', 'C', 'D'];
const newArray = [...oldArray];// 将一个数组的所有元素克隆到另外一个数组中
console.log(newArray);
// 1.3 将伪数组转换成真正的数组
function fun() {
    console.log(arguments);// 伪数组,[Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
    console.log([...arguments]);// 真正的数组,[ 1, 2, 3, 4, 5 ]
}
fun(1, 2, 3, 4, 5);

Symbol

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。Symbol 函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。可以接受一个字符串作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分。Symbol特点如下:

  • Symbol 的值是唯一的,用来解决命名冲突的问题。
  • Symbol 值不能与其他数据进行运算。
  • Symbol 定 义 的 对 象 属 性 不 能 使 用 for...in 循 环 遍 历 , 但 是 可 以 使 用Reflect.ownKeys来获取对象的所有键名
// 1.创建Symbol
// 1.1 查看Symbol类型
let s = Symbol();
console.log(s, typeof s);// Symbol() symbol
// 1.2 创建带值的Symbol
let s1 = Symbol('张三');
let s2 = Symbol('张三');
console.log(s1, s2);// Symbol(张三) Symbol(张三)
// 1.3 使用Symbol.for创建
let s3 = Symbol.for('张三');
let s4 = Symbol.for('张三');
console.log(s3, s4);// Symbol(张三) Symbol(张三)

// 2.不能与其他数据进学校运算
console.log(s + 100);// 报错TypeError: Cannot convert a Symbol value to a number
console.log(s > 100);
console.log(s + s);

由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名。Symbol 作为对象属性名时不能用.运算符,要用方括号[]

// 让Symbol作为对象的属性名
let sym = Symbol('key');
// 写法一:利用中括号为对象添加属性并且赋值
let obj = {};
obj[sym] = 'value';// 为对象添加属性并且赋值
console.log(obj);
// 写法二:直接在花括号中设置属性名
let obj = {
    [sym]: 'value'
};
console.log(obj);

Symbol 值作为属性名时,该属性是公有属性不是私有属性,可以在类的外部访问。但是不会出现在 for...infor...of 的循环中,也不会被 Object.keys()Object.getOwnPropertyNames() 返回。如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols()Reflect.ownKeys() 取到。

let sym = Symbol('key');
let obj = {};
obj[sym] = 'Symbol';
obj['name'] = '张三';

for (let i in obj) {
    console.log(i);// name
}
console.log(Object.keys(obj));// Object.keys(obj)
console.log(Object.getOwnPropertySymbols(obj));// [ Symbol(key) ]
console.log(Reflect.ownKeys(obj));// Reflect.ownKeys(obj)

迭代器

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

实现了Iterator接口的数据可以通过for...of命令来进行遍历。而原生就具备Iterator接口并且可用for...of遍历的数据有:

  • Array
  • Arguments
  • Set
  • Map
  • String
  • TypedArray
  • NodeList

Iterator接口的原理:

  1. 创建一个指针对象,指向当前数据结构的起始位
  2. 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
  3. 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
  4. 每调用 next 方法返回一个包含 value 和 done 属性的对象
// 使用for...of遍历数组
const array = ['唐僧', '孙悟空', '猪八戒', '沙僧'];
for (let item of array) {
    console.log(item);
}

// 使用迭代器来遍历数组
let iterator = array[Symbol.iterator]();// 获取数组的迭代器对象
console.log(iterator.next());// 调用对象的next方法
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());// 当遍历完之后再调用next方法则值为undefined,{ value: undefined, done: true }

js中对象分为可迭代和不可迭代 如果是可迭代哪它就会有一个Symbol.iterator函数,如果想要让对象拥有属于自己的迭代器,只需要为该对象添加一个Symbol.iterator函数即可。例如:

const obj = {
    ts: "唐僧",
    swk: "孙悟空",
    zbj: "猪八戒",
    ss: "沙僧"
};
// 为对象添加一个Symbol.iterator方法
obj[Symbol.iterator] = function () {
    let keys = Object.keys(obj);// 取到对象的每个key值返回一个数组
    let len = keys.length;// 取到key值组成数组的长度
    let index = 0;// 循环变量
    // 返回对象,每次迭代都会自动调用next方法
    return {
        next: function () {
            // 返回值是一个对象,对象中有value和done,当done为true时跳出循环
            if (index < len) {
                const result = {value: obj[keys[index]], done: false};
                index++;
                return result;
            } else {
                return {value: undefined, done: true};
            }
        }
    }
};
// 使用for...of循环遍历迭代器对象
for (let k of obj) {
    console.log(k);
}

生成器

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

    <script>    
        //生成器其实就是一个特殊的函数
        //异步编程  纯回调函数  node fs  ajax mongodb
        //函数代码的分隔符
        function * gen(){
            // console.log(111);
            yield '一只没有耳朵';
            // console.log(222);
            yield '一只没有尾部';
            // console.log(333);
            yield '真奇怪';
            // console.log(444);
        }

        let iterator = gen();
        console.log(iterator.next());// 第一次调用返回第一个yield后面的值
        console.log(iterator.next());// 第二次调用返回第二个yield后面的值
        console.log(iterator.next());
        console.log(iterator.next());

        //遍历
        // for(let v of gen()){
        //     console.log(v);
        // }

    script>

代码说明:

  • * 的位置没有限制
  • 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到
    yield 语句后的值
  • yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next
    方法,执行一段代码
  • next 方法可以传递实参,作为 yield 语句的返回值
    <script>
        function * gen(arg){
            console.log(arg);
            let one = yield 111;
            console.log(one);
            let two = yield 222;
            console.log(two);
            let three = yield 333;
            console.log(three);
        }

        //执行获取迭代器对象
        let iterator = gen('AAA');// 整体传参
        console.log(iterator.next());
        //next方法可以传入实参
        console.log(iterator.next('BBB'));// 第二次调用next方法传入的参数将作为第一个yield的返回结果
        console.log(iterator.next('CCC'));
        console.log(iterator.next('DDD'));
        
    script>

实例:

    <script>
        // 异步编程  文件操作 网络操作(ajax, request) 数据库操作
        // 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();

    script>

实例:

   <script>
        //模拟获取  用户数据  订单数据  商品数据 
        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();
    script>

Promise

参考:Promise学习笔记

Set

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

  • size:属性,返回集合中元素个数。
  • add(ele):添加一个新元素,返回当前集合。
  • delete(ele):删除一个元素,返回布尔值。
  • has(ele):检测集合中是否包含某个函数,返回布尔值。
  • clear():清空集合,返回undefined。
// 1.创建集合
// 1.1 创建空集合
let s1 = new Set();
// 1.2 创建带值集合
let s2 = new Set(['唐僧', '孙悟空', '猪八戒', '沙僧']);

// 2.常用属性和方法
// 2.1 size属性,获取元素个数
console.log(s2.size);
// 2.2 add方法,向集合中添加元素
s2.add('唐三藏');
console.log(s2);
// 2.3 delete方法,删除集合中的元素
s2.delete('沙僧');
console.log(s2);
// 2.4 has方法,检测集合中是否包含某元素
let has = s2.has('唐僧');
console.log(has);
// 2.5 遍历集合
for (let v of s2) {
    console.log(v);
}
// 2.6 清空集合
s2.clear();
console.log(s2);

Map

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

  • size:属性,返回Map中的元素个数。
  • set(key, value):方法,增加一个新元素,返回添加后的Map。
  • get(key):方法,返回键名对象的键值。
  • has(key):判断Map中是否包含某个元素,返回布尔值。
  • clear():方法,清空Map集合,返回undefined。
// 1.创建Map
let m = new Map();

// 2.set方法,添加元素,键可以是任意类信息
m.set('name', '张三');
m.set('hello', function () {
    console.log('hello world');
});
m.set({
    name: 'hobby'
}, ['吃', '喝', '玩', '乐']);

// 3.size属性,获取Map的长度
console.log(m.size);

// 4.delete方法,删除元素
m.delete('name');

// 5.get方法,获取元素
m.get('hello')();

// 6.遍历Map
for (let v of m) {
    console.log(v);
}

// 7.清空
m.clear();

class类

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

class声明类与constructor定义构造函数

基本语法如下:

class 类名 {
    // 构造方法,方法名必须是constructor
    constructor([参数列表]) {
        // 初始化参数
    }

    // 类中的普通方法,必须使用下面的语法,不能使用ES5的对象完整形式,即不能使用name:function(){}这样的形式
    函数名([参数列表]) {
        函数体
    }
}

例如:

// 1.ES5定义类和初始化构造函数
// 创建构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 添加方法
Person.prototype.hello = function () {
    console.log('Hello, I am ' + this.name);
};

// 实例化对象
let person = new Person('张三', 18);
person.hello();
console.log(person);// Person { name: '张三', age: 18 }


// 2.使用class关键字创建类
class Phone {
    constructor(brand, price) {
        this.brand = brand;
        this.price = brand;
    }

    call() {
        console.log('呼叫...');
    }
}

let phone = new Person('华为', 3999);
console.log(phone);// Person { name: '华为', age: 3999 }

静态成员

在类中声明静态属性和静态方法的语法如下:

class 类名 {
	// 静态属性
	static 变量名 = 变量值;
	// 静态方法
	static 方法名([参数列表]) {
		函数体
	}
}

// 调用静态属性
类名.静态属性名
// 调用静态方法
类名.静态方法名()

示例如下:

// 1.ES5语法中函数对象的属性和实例对象的属性是不共通的
function Person() {

}

Person.name = '张三';
Person.hello = function () {
    console.log('hello world');
};
Person.prototype.age = 22;

let person = new Person();
console.log(person.name);// 事实上,实例对象无法访问函数对象上的属性
person.hello();
console.log(person.age);


// 2.类中声明静态属性
class Phone {
    // 静态属性
    static brand = '华为';

    // 静态方法
    static call() {
        console.log('呼叫...');
    }
}

let phone = new Phone();
console.log(phone.brand);
phone.call();
console.log(Phone.brand);// 类中的静态成员只能通过类名来进行访问
Phone.call();

对象继承

在ES5中继承的示例如下:

// 定义父类构造函数初始化参数
function Phone(brand, price) {
    this.brand = brand;
    this.price = price;
}

// 声明父类的方法
Phone.prototype.call = 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('拍照...');
};

// 测试
const huawei = new SmartPhone('华为', 3999, '黑色', '5.5');
huawei.call();
huawei.photo();

而ES6继承的语法如下:

class 父类 {
    constructor([参数列表]) {
        初始化父类参数
    }

    方法名() {
        方法体
    }
}

class 子类 extends 父类 {
    constructor([参数列表]) {
        super([参数列表]);// 给父类初始化参数
        初始化子类参数
    }

    方法名() {
        方法体
    }
}

示例如下:

// 定义父类
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);
        this.color = color;
        this.size = size;
    }

    photo() {
        console.log('拍照...');
    }

    // 可以重写父类方法
    call() {
        console.log('视频呼叫...');
    }
}

// 测试
let phone = new SmartPhone('华为', 3999, '黑色', '5.5');
phone.call();
phone.photo();

set&get

可以通过setget关键字为属性赋值和得到属性的值。因为方法可以对属性值进行一些处理。

class Phone {

    get price() {
        console.log('price属性被读取了...');
        return 3999;
    }

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

// 实例化对象
let phone = new Phone();
console.log(phone.price);
phone.price = 1999;

数值扩展

ES6为Number类型的数据提供了一些方法,扩展了其功能:

  • 0b前缀:二进制数值的新写法。
  • 0o前缀:八进制数值的新写法。
  • Number.isFinite():检查一个数值是否是有限的。
  • Number.isNaN():检查一个值是否为NaN。
  • Number.parseInt():转换成整数。
  • Number.parseFloat():转换成浮点数。
  • Math.trunc():用于去除一个数的小数部分,返回整数部分。
  • Number.isInteger():用来判断一个数值是否为整数。
  • Math.sign():判断一个数到底为正数、负数还是零。
// 二进制数值
let b = 0b1010;
console.log(b);

// 八进制数值
let o = 0o11;
console.log(o);

// 检查一个数值是否是有限的
console.log(Number.isFinite(100));// true
console.log(Number.isFinite(1 / 0));// false

// 检查一个值是否为NaN
console.log(Number.isNaN(123));// false

// 转换成整数
console.log(Number.parseInt("123.12"));// 123

// 转换成浮点数
console.log(Number.parseFloat("12.12"));// 12.12

// 用于去除一个数的小数部分,返回整数部分
console.log(Math.trunc(12.34));// 12

// 用来判断一个数值是否为整数
console.log(Number.isInteger(12.34));// false

// 判断一个数到底为正数、负数还是零
console.log(Math.sign(-12), Math.sign(0), Math.sign(123));// -1 0 1

对象方法扩展

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

  • Object.is():判断两个值是否完全相等。
  • Object.assign():对象的合并,将源对象的所有可枚举属性,复制到目标对象。
  • Object.setPrototypeOf():设置原型对象。
  • Object.getPrototypeof():获取原型对象。
// 判断两个值是否完全相等
console.log(Object.is(12, 12));// true
console.log(Object.is('abc', "abc"));// true
console.log(Object.is(NaN, NaN));// true
console.log(NaN === NaN);// false

// 对象的合并,将源对象的所有可枚举属性,复制到目标对象
const obj = {
    name: '张三',
    age: 19
};
const dest = {
    gender: 'm',
    hello: function () {
        console.log('hello world');
    }
};
console.log(Object.assign(dest, obj));// { gender: 'm', hello: [Function: hello], name: '张三', age: 19 }
console.log(obj);// { name: '张三', age: 19 }
console.log(dest);// { gender: 'm', hello: [Function: hello], name: '张三', age: 19 }

// 设置原型对象和获取原型对象
const person = {
    name: '张三'
};
let say = function () {
    console.log('hello world');
};
Object.setPrototypeOf(person, say);// 设置原型对象
console.log(Object.getPrototypeOf(person));// [Function: say]

模块化

概述

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。模块化的优势有以下几点:

  • 防止命名冲突
  • 代码复用
  • 高维护性

ES6的模块功能主要由两个命令组成:exportimport

  • export:用于规定模块对外接口。
  • import:用于导入其他模块提供的功能。

基本使用

创建一个js文件,该js文件是一个模块,里面使用export关键字向外暴露变量和方法:

// 暴露属性
export let msg = 'hello world';

// 暴露方法
export function hello() {
    console.log('hello world!');
}

然后在html中引入使用:


<html lang="en">
<head>
    <meta charset="UTF-8">
head>
<body>

<script type="module">
    // 使用import引入模块内容
    import * as test from './test.js';
    // 调用模块内的属性和方法
    console.log(test.msg);
    test.hello();
script>
body>
html>

暴露的几种情况

  • 分别暴露。每个待暴露的变量和方法都使用export关键字声明。
// 暴露属性
export let msg = 'hello world';

// 暴露方法
export function hello() {
    console.log('hello world!');
}
  • 统一暴露。我们可以将需要暴露出去的变量和方法都放到对象中,统一暴露出去。
// 暴露属性
let msg = 'hello world';

// 暴露方法
function hello() {
    console.log('hello world!');
}

// 统一暴露一个对象,对象中是待暴露的属性和方法
export {msg, hello}
  • 默认暴露。使用export default暴露对象,对象中要默认暴露出去的属性和方法,但注意使用方式有所不同。
// 默认暴露
export default {
    msg: 'hello world',
    hello: function () {
        console.log('hello world!');
    }
}

使用方式如下,需要default


<html lang="en">
<head>
    <meta charset="UTF-8">
head>
<body>

<script type="module">
    // 使用import引入模块内容
    import * as test from './test.js';
    // 调用模块内的属性和方法
    console.log(test.default.msg);
    test.default.hello();
script>
body>
html>

导入的几种情况

  • 通用导入方式。语法是import * as 模块别名 from "js模块路径"
<body>

<script type="module">
    // 使用import引入模块内容
    import * as test from './test.js';
    // 调用模块内的属性和方法
    console.log(test.msg);
    test.hello();
script>
body>
  • 解构赋值导入方式。语法如下:import {变量名, 方法名, ...} from "js模块路径".
<body>

<script type="module">
    // 解构赋值方式导入
    // 引入普通暴露中的变量和函数,{}中是暴露出的变量名和函数名
    import {msg, hello} from './test.js';
    // 引入普通暴露中的变量和函数,可以使用as为暴露出的变量和函数起别名
    import {msg as m, hello} from "./test.js";
    // 引入默认暴露对象,default就是默认暴露的对象,as是起别名
    import {default as test} from './test.js';

    console.log(test.print());
script>
body>
  • 简便形式,针对默认暴露。语法格式是import 模块名 from "模块路径"
<body>
<!--导入test.js模块-->
<script type="module">
    // 简便形式,针对默认暴露
    import test from './test.js';

    test.hello()
</script>
</body>

注意还可以专门将引入模块的代码写在一个js文件中,然后使用如下代码来专门引入这个js文件:

<body>

<script src="./app.js" type="module">
    
script>
body>

ES7新特性

Array.prototype.includes

includes 方法用来检测数组中是否包含某个元素,返回布尔类型值。例如:

const array = [1, 2, 3, 4, 5, 6];
let result = array.includes(3);
console.log("数组是否包含 3:", result);// 数组是否包含 3: true

指数操作符

在 ES7 中引入指数运算符 **,用来实现幂运算,功能与 Math.pow(x, n) 结果相同。例如:

// ** 操作符等价于 Math.pow(x, y)
console.log(2 ** 3);// 8
console.log(Math.pow(2, 3));// 8

ES8新特性

asyncawait

请阅读Promise学习笔记 - async和await。

对象方法扩展

为对象增加了一些新的方法:

  • Object.values() 方法返回一个给定对象的所有可枚举属性值的数组。
  • Object.entries() 方法返回一个给定对象自身可遍历属性 [key, value] 的数组。
  • Object.getOwnPropertyDescriptors()该方法返回指定对象所有自身属性的描述对象。

例如:

// 先声明一个对象
const person = {
    name: '张三',
    age: 18,
    hobbies: ['阅读', '音乐', '学习']
};

// 获取对象的所有键
console.log(Object.keys(person));// [ 'name', 'age', 'hobbies' ]

// 获取对象的所有值
console.log(Object.values(person));// [ '张三', 18, [ '阅读', '音乐', '学习' ] ]

// 获取对象的所有键值对,以 entry 的形式
console.log(Object.entries(person));
/*
[
  [ 'name', '张三' ],
  [ 'age', 18 ],
  [ 'hobbies', [ '阅读', '音乐', '学习' ] ]
]
 */

// 可以利用Map集合将entry转换成键值对的形式,然后使用get方法进行访问
const map = new Map(Object.entries(person));
console.log(map.get('name'));// 张三

// 对象属性的描述对象
console.log(Object.getOwnPropertyDescriptors(person));
/*
{
  name: { value: '张三', writable: true, enumerable: true, configurable: true },
  age: { value: 18, writable: true, enumerable: true, configurable: true },
  hobbies: {
    value: [ '阅读', '音乐', '学习' ],
    writable: true,
    enumerable: true,
    configurable: true
  }
}
 */

ES9新特性

Rest/Spread 属性

rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,在 ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符。

例1 - rest参数:

// rest 参数
function connect({host, port, ...user}) {
    console.log(host);// 127.0.0.1
    console.log(port);// 3306
    console.log(user);// { username: 'root', password: 'root' }
}

connect({
    // 注意,调用的时候传的对象的属性名必须跟函数的参数中的属性名一致,而剩下的所有属性都被传给rest参数
    host: "127.0.0.1",
    port: 3306,
    username: 'root',
    password: 'root'
});

例2 - 对象合并:

// 对象合并
const obj1 = {
    name: '张三',
    age: 18
};
const obj2 = {
    gender: 'm'
};
const obj3 = {
    score: 18.8
};
const obj4 = {
    hobbies: ['阅读', '音乐', '学习']
};
const newObj = {...obj1, ...obj2, ...obj3, ...obj4};// 合并多个对象
console.log(newObj);
/*
{
  name: '张三',
  age: 18,
  gender: 'm',
  score: 18.8,
  hobbies: [ '阅读', '音乐', '学习' ]
}
 */

正则表达式命名捕获组

ES9 允许命名捕获组使用符号 ?,这样获取捕获结果可读性更强。例如:

let str = '百度';// 待匹配的字符串

// 不使用分组命名
const reg1 = /(.*?)<\/a>/g;// 正则表达式
const result1 = reg1.exec(str);// 用正则表达式匹配字符串
console.log(result1);
console.log(result1[1]);// http://www.baidu.com
console.log(result1[2]);// 百度

// 使用分组命名
const reg2 = /(?.*?)<\/a>/g;// 为分组进行了命名的正则表达式,其中 url 为第一个分组的名字,而 text 为第二个分组的名字
const result2 = reg2.exec(str);// 用正则表达式匹配字符串
console.log(result2);
console.log(result2.groups.url);// http://www.baidu.com
console.log(result2.groups.text);// 百度

正则表达式反向断言

ES9 支持反向断言,通过对匹配结果前面的内容进行判断,对匹配进行筛选。

//声明字符串
let str = 'JS5211314你知道么555啦啦啦';

// 正向断言
const reg1 = /\d+(?=啦)/;
const result1 = reg1.exec(str);
console.log(result1);// [ '555', index: 13, input: 'JS5211314你知道么555啦啦啦', groups: undefined ]

// 反向断言
const reg2 = /(?<=么)\d+/;
const result2 = reg2.exec(str);
console.log(result2);// [ '555', index: 13, input: 'JS5211314你知道么555啦啦啦', groups: undefined ]

正则表达式 dotAll 模式

正则表达式中点 . 匹配除回车外的任何单字符,标记 s 改变这种行为,允许行终止符出现。

//dot  .  元字符  除换行符以外的任意单个字符
let str = `
        `;
//声明正则
// const reg = /
  • \s+(.*?)<\/a>\s+

    (.*?)<\/p>/; const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs; //执行匹配 // const result = reg.exec(str); let result; let data = []; while (result = reg.exec(str)) { data.push({title: result[1], time: result[2]}); } //输出结果 console.log(data); /* [ { title: '肖生克的救赎', time: '上映日期: 1994-09-10' }, { title: '阿甘正传', time: '上映日期: 1994-07-06' } ] */

  • ES10

    Object.fromEntries

    // 会将二维数组转换成对象,二维数组中的每一行都是一个键值对,其中第一列是键名,第二列是键值
    const result = Object.fromEntries([
        ['name', '张三'],
        ['hobbies', '阅读, 学习, 音乐']
    ]);
    console.log(result);// { name: '张三', hobbies: '阅读, 学习, 音乐' }
    
    // Map,其中Map的键是键名,Map的值是键值
    const m = new Map();
    m.set('age', 18);
    const result2 = Object.fromEntries(m);
    console.log(result2);// { age: 18 }
    
    // 对象,会将对象转换成二维数组
    const arr = Object.entries({
        name: "张三",
        age: 18,
        gender: 's'
    });
    console.log(arr);// [ [ 'name', '张三' ], [ 'age', 18 ], [ 'gender', 's' ] ]
    

    trimStart 和 trimEnd

    清除字符串左侧或右侧的空白。

    // 清除字符串左右两侧的空白
    let str = '   i love you   ';
    
    console.log(str);//   i love you   
    console.log(str.trimStart());//i love you   
    console.log(str.trimEnd());//   i love you
    

    Array.prototype.flat 与 flatMap

    //flat 平 将多维数组转化为低位数组
    // const arr = [1,2,3,4,[5,6]];
    const arr1 = [1, 2, 3, 4, [5, 6, [7, 8, 9]]];
    console.log(arr1.flat(2));// 参数为深度 是一个数字,默认值是1
    /*
    [
      1, 2, 3, 4, 5,
      6, 7, 8, 9
    ]
     */
    
    // flatMap 对迭代的每一项进行处理
    const arr2 = [1, 2, 3, 4];
    const result = arr2.flatMap(item => [item * 10]);
    console.log(result);// [ 10, 20, 30, 40 ]
    

    Symbol.prototype.description

    获取 Symbol 的描述信息。

    //创建 Symbol
    let s = Symbol('xxx');
    
    console.log(s.description);// xxx
    

    ES11

    类的私有属性

    在类中在属性名的前面加上 # 可以将该属性声明为私有属性,注意私有属性只能在本类中使用,通过实例对象是无法访问私有属性的。

    class Person {
        //公有属性
        name;
        //私有属性
        #age;
        #weight;
    
        //构造方法
        constructor(name, age, weight) {
            this.name = name;
            this.#age = age;
            this.#weight = weight;
        }
    
        intro() {
            console.log(this.name);
            console.log(this.#age);
            console.log(this.#weight);
        }
    }
    
    //实例化
    const girl = new Person('晓红', 18, '45kg');
    
    // console.log(girl.name);
    // console.log(girl.#age);
    // console.log(girl.#weight);
    
    girl.intro();
    

    Promise.allSettled

    Promise.allSettled() 方法返回一个在所有给定的promise都已经 fulfilledrejected 后的promise,并带有一个对象数组,每个对象表示对应的promise结果。

    //声明两个promise对象
    const p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('商品数据 - 1');
        }, 1000)
    });
    
    const p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('商品数据 - 2');
            // reject('出错啦!');
        }, 1000)
    });
    
    //调用 allSettled 方法
    const result = Promise.allSettled([p1, p2]);
    console.log(result);
    
    const res = Promise.all([p1, p2]);
    console.log(res);
    

    String.prototype.matchAll

    matchAll() 方法返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器。

    let str = `百度`;
    
    //声明正则
    const reg = /<a href="(.*?)">(.*?)<\/a>/sg;
    
    //调用方法
    const result = str.matchAll(reg);
    
    // for(let v of result){
    //     console.log(v);
    // }
    
    const arr = [...result];
    
    console.log(arr);
    /*
    [
      [
        '百度',
        'http://www.baidu.com',
        '百度',
        index: 0,
        input: '百度',
        groups: undefined
      ]
    ]
     */
    

    可选链操作符

    对象类型参数时使用。而不需要去判断这个对象是否存在。

    // ?. 操作符
    function main(config) {
        // const dbHost = config && config.db && config.db.host;
        const dbHost = config?.db?.host;
    
        console.log(dbHost);
    }
    
    main({
        db: {
            host: '192.168.1.100',
            username: 'root'
        },
        cache: {
            host: '192.168.1.200',
            username: 'admin'
        }
    });
    

    动态import

    可以按需加载。

    // import * as m1 from './hello.js';
    
    const btn = document.getElementById('btn');// 获取元素
    btn.onclick = function () {// 当点击按钮时才加载
        // 按需加载
        import('./hello.js').then(module => {
            console.log(module);
        })
    };
    

    Bigint

    大数值运算。

    //大整形
    // let n = 521n;
    // console.log(n, typeof(n));
    
    //函数
    // let n = 123;
    // console.log(BigInt(n));
    // console.log(BigInt(1.2));
    
    //大数值运算
    let max = Number.MAX_SAFE_INTEGER;
    console.log(max);
    console.log(max + 1);
    console.log(max + 2);
    
    console.log(BigInt(max))
    console.log(BigInt(max) + BigInt(1))
    console.log(BigInt(max) + BigInt(2))
    

    globalThis 对象

    都有一个 globalThis 对象,无论是浏览器还是Node.js。

    console.log(globalThis);
    

    你可能感兴趣的:(学习笔记,学习,javascript)