• ECMAScript6介绍及环境搭建


    这实际上说明,对象的解构赋值是下面形式的简写。

    let { foo: foo, bar: bar } = { foo: ‘aaa’, bar: ‘bbb’ };

    也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

    let { foo: baz } = { foo: ‘aaa’, bar: ‘bbb’ };

    baz // “aaa”

    foo // error: foo is not defined

    上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。

    (2)嵌套对象的解构赋值

    与数组一样,解构也可以用于嵌套结构的对象。

    let obj = {

    p: [

    ‘Hello’,

    { y: ‘World’ }

    ]

    };

    let { p: [x, { y }] } = obj;

    x // “Hello”

    y // “World”

    注意,这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以写成下面这样。

    let obj = {

    p: [

    ‘Hello’,

    { y: ‘World’ }

    ]

    };

    let { p, p: [x, { y }] } = obj;

    x // “Hello”

    y // “World”

    p // [“Hello”, {y: “World”}]

    下面是另一个例子。

    4.5、解构赋值注意事项

    (1)如果要将一个已经声明的变量用于解构赋值,必须非常小心。

    // 错误的写法

    let x;

    {x} = {x: 1};

    // SyntaxError: syntax error

    上面代码的写法会报错,因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。

    // 正确的写法

    let x;

    ({x} = {x: 1});

    上面代码将整个解构赋值语句,放在一个圆括号里面,就可以正确执行。关于圆括号与解构赋值的关系,参见下文。

    (2)解构赋值允许等号左边的模式之中,不放置任何变量名。因此,可以写出非常古怪的赋值表达式。

    ({} = [true, false]);

    ({} = ‘abc’);

    ({} = []);

    上面的表达式虽然毫无意义,但是语法是合法的,可以执行。

    (3)由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构。

    let arr = [1, 2, 3];

    let {0 : first, [arr.length - 1] : last} = arr;

    first // 1

    last // 3

    上面代码对数组进行对象解构。数组arr的0键对应的值是1,[arr.length - 1]就是2键,对应的值是3。方括号这种写法,属于“属性名表达式”。

    4.6、解构赋值的用途

    变量的解构赋值用途很多。

    (1)交换变量的值

    let x = 1;

    let y = 2;

    [x, y] = [y, x];

    上面代码交换变量x和y的值,这样的写法不仅简洁,而且易读,语义非常清晰。

    (2)从函数返回多个值

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

    // 返回一个数组

    function example() {

    return [1, 2, 3];

    }

    let [a, b, c] = example();

    // 返回一个对象

    function example() {

    return {

    foo: 1,

    bar: 2

    };

    }

    let { foo, bar } = example();

    (3)函数参数的定义

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

    // 参数是一组有次序的值

    function f([x, y, z]) { … }

    f([1, 2, 3]);

    // 参数是一组无次序的值

    function f({x, y, z}) { … }

    f({z: 3, y: 2, x: 1});

    (4)提取JSON数据

    解构赋值对提取 JSON 对象中的数据,尤其有用。

    let jsonData = {

    id: 42,

    status: “OK”,

    data: [867, 5309]

    };

    let { id, status, data: number } = jsonData;

    console.log(id, status, number);

    // 42, “OK”, [867, 5309]

    上面代码可以快速提取 JSON 数据的值。

    (5)函数参数的默认值

    jQuery.ajax = function (url, {

    async = true,

    beforeSend = function () {},

    cache = true,

    complete = function () {},

    crossDomain = false,

    global = true,

    // … more config

    } = {}) {

    // … do stuff

    };

    指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || ‘default foo’;这样的语句。

    (6)遍历Map结构

    任何部署了 Iterator 接口的对象,都可以用for…of循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。

    const map = new Map();

    map.set(‘first’, ‘hello’);

    map.set(‘second’, ‘world’);

    for (let [key, value] of map) {

    console.log(key + " is " + value);

    }

    // first is hello

    // second is world

    如果只想获取键名,或者只想获取键值,可以写成下面这样。

    // 获取键名

    for (let [key] of map) {

    // …

    }

    // 获取键值

    for (let [,value] of map) {

    // …

    }

    (7)输入模块的指定方法

    加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。

    const { SourceMapConsumer, SourceNode } = require(“source-map”);

    5、字符串、函数、数组、对象的扩展


    5.1、模板字符串

    传统的 JavaScript 语言,输出模板通常是这样写的(下面使用了 jQuery 的方法)。

    $(‘#result’).append(

    ‘There are ’ + basket.count + ' ’ +

    'items in your basket, ’ +

    ’ + basket.onSale +

    ‘ are on sale!’

    );

    上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。

    $(‘#result’).append(`

    There are ${basket.count} items

    in your basket, ${basket.onSale}

    are on sale!

    `);

    模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

    // 普通字符串

    In JavaScript '\n' is a line-feed.

    // 多行字符串

    `In JavaScript this is

    not legal.`

    console.log(`string text line 1

    string text line 2`);

    // 字符串中嵌入变量

    let name = “Bob”, time = “today”;

    Hello ${name}, how are you ${time}?

    上面代码中的模板字符串,都是用反引号表示。

    转义符号

    如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。

    let greeting = \Yo` World!`;

    多行字符串

    如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。

    $(‘#list’).html(`

      • first
      • second
      • `);

        上面代码中,所有模板字符串的空格和换行,都是被保留的,比如

          标签前面会有一个换行。如果你不想要这个换行,可以使用 trim 方法消除它。

          $(‘#list’).html(`

          • first
          • second
          • `.trim());

            插入变量

            模板字符串中嵌入变量,需要将变量名写在${}之中。

            function authorize(user, action) {

            if (!user.hasPrivilege(action)) {

            throw new Error(

            // 传统写法为

            // 'User ’

            // + user.name

            // + ’ is not authorized to do ’

            // + action

            // + ‘.’

            User ${user.name} is not authorized to do ${action}.);

            }

            }

            插入表达式

            大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。

            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”

            调用函数

            模板字符串之中还能调用函数。

            function fn() {

            return “Hello World”;

            }

            foo ${fn()} bar

            // foo Hello World bar

            如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的 toString 方法。

            如果模板字符串中的变量没有声明,将报错。

            // 变量place没有声明

            let msg = Hello, ${place};

            // 报错

            由于模板字符串的大括号内部,就是执行 JavaScript 代码,因此如果大括号内部是一个字符串,将会原样输出。

            Hello ${'World'}

            // “Hello World”

            注意要点

            模板字符串中的换行和空格都是会被保留的

            innerHtml = `

            • menu
            • mine
            • `;

              console.log(innerHtml);

              // 输出

              • menu
              • mine
              • 5.2、字符串扩展方法

                (1)子串的识别

                ES6 之前判断字符串是否包含子串,用 indexOf 方法,ES6 新增了子串的识别方法。

                • includes():返回布尔值,判断是否找到参数字符串。

                • startsWith():返回布尔值,判断参数字符串是否在原字符串的头部。

                • endsWith():返回布尔值,判断参数字符串是否在原字符串的尾部。

                以上三个方法都可以接受两个参数,需要搜索的字符串,和可选的搜索起始位置索引。

                let s = ‘Hello world!’;

                s.startsWith(‘Hello’) // true

                s.endsWith(‘!’) // true

                s.includes(‘o’) // true

                这三个方法都支持第二个参数,表示开始搜索的位置。

                let s = ‘Hello world!’;

                s.startsWith(‘world’, 6) // true

                s.endsWith(‘Hello’, 5) // true

                s.includes(‘Hello’, 6) // false

                上面代码表示,使用第二个参数 n 时, endsWith 的行为与其他两个方法有所不同。它针对前 n 个字符,而其他两个方法针对从第 n 个位置直到字符串结束。

                注意点:

                • 这三个方法只返回布尔值,如果需要知道子串的位置,还是得用 indexOf 和 lastIndexOf 。

                • 这三个方法如果传入了正则表达式而不是字符串,会抛出错误。而 indexOf 和 lastIndexOf 这两个方法,它们会将正则表达式转换为字符串并搜索它。

                (2)字符串重复

                repeat():返回新的字符串,表示将字符串重复指定次数返回。

                ‘x’.repeat(3) // “xxx”

                ‘hello’.repeat(2) // “hellohello”

                ‘na’.repeat(0) // “”

                参数如果是小数,会被向下取整。

                ‘na’.repeat(2.9) // “nana”

                如果 repeat 的参数是负数或者 Infinity ,会报错。

                ‘na’.repeat(Infinity)

                // RangeError

                ‘na’.repeat(-1)

                // RangeError

                但是,如果参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。0 到-1 之间的小数,取整以后等于 -0repeat 视同为 0。

                ‘na’.repeat(-0.9) // “”

                参数 NaN 等同于 0。

                ‘na’.repeat(NaN) // “”

                如果 repeat 的参数是字符串,则会先转换成数字。

                ‘na’.repeat(‘na’) // “”

                ‘na’.repeat(‘3’) // “nanana”

                (3)字符串补全

                ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。

                • padStart:返回新的字符串,表示用参数字符串从头部(左侧)补全原字符串。

                • padEnd:返回新的字符串,表示用参数字符串从尾部(右侧)补全原字符串。

                以上两个方法接受两个参数,第一个参数是指定生成的字符串的最小长度,第二个参数是用来补全的字符串。如果没有指定第二个参数,默认用空格填充。

                console.log(“h”.padStart(5,“o”)); // “ooooh”

                console.log(“h”.padEnd(5,“o”)); // “hoooo”

                console.log(“h”.padStart(5)); // " h"

                console.log(‘x’.padStart(5, ‘ab’)); // ‘ababx’

                console.log(‘x’.padStart(4, ‘ab’)); // ‘abax’

                console.log(‘x’.padEnd(5, ‘ab’)); // ‘xabab’

                console.log(‘x’.padEnd(4, ‘ab’)); // ‘xaba’

                上面代码中, padStart()padEnd() 一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。

                如果指定的长度小于或者等于原字符串的长度,则返回原字符串:

                console.log(“hello”.padStart(5,“A”)); // “hello”

                如果原字符串加上补全字符串长度大于指定长度,则截去超出位数的补全字符串:

                console.log(“hello”.padEnd(10,“,world!”)); // “hello,worl”

                如果省略第二个参数,默认使用空格补全长度。

                console.log(‘x’.padStart(4)); // ’ x’

                console.log(‘x’.padEnd(4)); // 'x ’

                padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。

                console.log(‘1’.padStart(10, ‘0’)); // “0000000001”

                console.log(‘12’.padStart(10, ‘0’)); // “0000000012”

                console.log(‘123456’.padStart(10, ‘0’)); // “0000123456”

                另一个用途是提示字符串格式。

                console.log(‘12’.padStart(10, ‘YYYY-MM-DD’)); // “YYYY-MM-12”

                console.log(‘09-12’.padStart(10, ‘YYYY-MM-DD’)); // “YYYY-09-12”

                (4)消除空格

                ES6对字符串实例新增了 trimStart()trimEnd() 这两个方法。它们的行为与 trim() 一致,trimStart() 消除字符串头部的空格,trimEnd() 消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

                const s = ’ abc ';

                s.trim() // “abc”

                s.trimStart() // "abc "

                s.trimEnd() // " abc"

                上面代码中,trimStart() 只消除头部的空格,保留尾部的空格。trimEnd() 也是类似行为。

                除了空格键,这两个方法对字符串头部(或尾部)的 tab 键、换行符等不可见的空白符号也有效。

                浏览器还部署了额外的两个方法,trimLeft()trimStart() 的别名,trimRight()trimEnd() 的别名。

                5.3、函数的扩展

                (1)默认值

                ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。

                function log(x, y) {

                y = y || ‘World’;

                console.log(x, y);

                }

                log(‘Hello’) // Hello World

                log(‘Hello’, ‘China’) // Hello China

                log(‘Hello’, ‘’) // Hello World

                上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。

                为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。

                if (typeof y === ‘undefined’) {

                y = ‘World’;

                }

                ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

                function log(x, y = ‘World’) {

                console.log(x, y);

                }

                log(‘Hello’) // Hello World

                log(‘Hello’, ‘China’) // Hello China

                log(‘Hello’, ‘’) // Hello

                可以看到,ES6 的写法比 ES5 简洁许多,而且非常自然。下面是另一个例子。

                function Point(x = 0, y = 0) {

                this.x = x;

                this.y = y;

                }

                const p = new Point();

                p // { x: 0, y: 0 }

                除了简洁,ES6 的写法还有两个好处:首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码无法运行。

                参数变量是默认声明的,所以不能用letconst再次声明。

                function foo(x = 5) {

                let x = 1; // error

                const x = 2; // error

                }

                上面代码中,参数变量x是默认声明的,在函数体中,不能用letconst再次声明,否则会报错。

                使用参数默认值时,函数不能有同名参数。

                // 不报错

                function foo(x, x, y) {

                // …

                }

                // 报错

                function foo(x, x, y = 1) {

                // …

                }

                // SyntaxError: Duplicate parameter name not allowed in this context

                另外,一个容易忽略的地方是,参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。

                let x = 99;

                function foo(p = x + 1) {

                console.log§;

                }

                foo() // 100

                x = 100;

                foo() // 101

                上面代码中,参数p的默认值是x + 1。这时,每次调用函数foo,都会重新计算x + 1,而不是默认p等于 100。

                (2)不定参数

                不定参数用来表示不确定参数个数,形如,…变量名,由…加上一个具名参数标识符组成。具名参数只能放在参数组的最后,并且有且只有一个不定参数。

                基本用法

                function f(…values){

                console.log(values.length);

                }

                f(1,2); //2

                f(1,2,3,4); //4

                (3)箭头函数

                箭头函数提供了一种更加简洁的函数书写方式。基本语法是:

                参数 => 函数体

                基本用法:

                var f = v => v;

                //等价于

                var f = function(a){

                return a;

                }

                f(1); //1

                当箭头函数没有参数或者有多个参数,要用 () 括起来。

                var f = (a,b) => a+b;

                f(6,2); //8

                当箭头函数函数体有多行语句,用 {} 包裹起来,表示代码块,当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。

                var f = (a,b) => {

                let result = a+b;

                return result;

                }

                f(6,2); // 8

                当箭头函数要返回对象的时候,为了区分于代码块,要用 () 将对象包裹起来

                // 报错

                var f = (id,name) => {id: id, name: name};

                f(6,2); // SyntaxError: Unexpected token :

                // 不报错

                var f = (id,name) => ({id: id, name: name});

                f(6,2); // {id: 6, name: 2}

                注意点:没有 this、super、arguments 和 new.target 绑定。

                var func = () => {

                // 箭头函数里面没有 this 对象,

                // 此时的 this 是外层的 this 对象,即 Window

                console.log(this)

                }

                func(55) // Window

                var func = () => {

                console.log(arguments)

                }

                func(55); // ReferenceError: arguments is not defined

                箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。

                function fn(){

                setTimeout(()=>{

                // 定义时,this 绑定的是 fn 中的 this 对象

                console.log(this.a);

                },0)

                }

                var a = 20;

                // fn 的 this 对象为 {a: 19}

                fn.call({a: 18}); // 18

                不可以作为构造函数,也就是不能使用 new 命令,否则会报错

                5.4、数组的扩展

                (1)扩展运算符

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

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

                // 1 2 3

                console.log(1, …[2, 3, 4], 5)

                // 1 2 3 4 5

                […document.querySelectorAll(‘div’)]

                // [

                ,
                ,
                ]

                该运算符主要用于函数调用。

                function push(array, …items) {

                array.push(…items);

                }

                function add(x, y) {

                return x + y;

                }

                const numbers = [4, 38];

                add(…numbers) // 42

                上面代码中,array.push(...items)add(...numbers)这两行,都是函数的调用,它们都使用了扩展运算符。该运算符将一个数组,变为参数序列。

                (2)扩展运算符的应用

                复制数组

                数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。

                const a1 = [1, 2];

                const a2 = a1;

                a2[0] = 2;

                a1 // [2, 2]

                上面代码中,a2并不是a1的克隆,而是指向同一份数据的另一个指针。修改a2,会直接导致a1的变化。

                ES5 只能用变通方法来复制数组。

                const a1 = [1, 2];

                const a2 = a1.concat();

                a2[0] = 2;

                a1 // [1, 2]

                上面代码中,a1会返回原数组的克隆,再修改a2就不会对a1产生影响。

                扩展运算符提供了复制数组的简便写法。

                const a1 = [1, 2];

                // 写法一

                const a2 = […a1];

                // 写法二

                const […a2] = a1;

                上面的两种写法,a2都是a1的克隆。

                合并数组

                扩展运算符提供了数组合并的新写法。

                const arr1 = [‘a’, ‘b’];

                const arr2 = [‘c’];

                const arr3 = [‘d’, ‘e’];

                // ES5 的合并数组

                arr1.concat(arr2, arr3);

                // [ ‘a’, ‘b’, ‘c’, ‘d’, ‘e’ ]

                // ES6 的合并数组

                […arr1, …arr2, …arr3]

                // [ ‘a’, ‘b’, ‘c’, ‘d’, ‘e’ ]

                不过,这两种方法都是浅拷贝,使用的时候需要注意。

                const a1 = [{ foo: 1 }];

                const a2 = [{ bar: 2 }];

                const a3 = a1.concat(a2);

                const a4 = […a1, …a2];

                a3[0] === a1[0] // true

                a4[0] === a1[0] // true

                上面代码中,a3a4是用两种不同方法合并而成的新数组,但是它们的成员都是对原数组成员的引用,这就是浅拷贝。如果修改了原数组的成员,会同步反映到新数组。

                (3)数组实例的find()和findIndex()

                数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined

                [1, 4, -5, 10].find((n) => n < 0)

                // -5

                上面代码找出数组中第一个小于 0 的成员。

                [1, 5, 10, 15].find(function(value, index, arr) {

                return value > 9;

                }) // 10

                上面代码中,find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。

                数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1

                [1, 5, 10, 15].findIndex(function(value, index, arr) {

                return value > 9;

                }) // 2

                5.5、对象的扩展

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

                const foo = ‘bar’;

                const baz = {foo};

                baz // {foo: “bar”}

                // 等同于const baz = {foo: foo};

                除了属性简写,方法也可以简写。

                const o = {

                method() {

                return “Hello!”;

                }

                };

                // 等同于

                const o = {

                method: function() {

                return “Hello!”;

                }

                };

                对象的新方法

                Object.assign(target, source_1, ···)

                用于将源对象的所有可枚举属性复制到目标对象中。

                基本用法

                let target = {a: 1};

                let object2 = {b: 2};

                let object3 = {c: 3};

                Object.assign(target,object2,object3); // 第一个参数是目标对象,后面的参数是源对象

                target; // {a: 1, b: 2, c: 3}

                6、Class基本使用和继承


                6.1、类的由来

                JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。

                function Point(x, y) {

                this.x = x;

                this.y = y;

                }

                Point.prototype.toString = function () {

                return ‘(’ + this.x + ', ’ + this.y + ‘)’;

                };

                var p = new Point(1, 2);

                上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。

                ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

                基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。

                class Point {

                constructor(x, y) {

                this.x = x;

                this.y = y;

                }

                toString() {

                return ‘(’ + this.x + ', ’ + this.y + ‘)’;

                }

                }

                上面代码定义了一个“类”,可以看到里面有一个constructor方法,这就是构造方法,而this关键字则代表实例对象。也就是说,ES5 的构造函数Point,对应 ES6 的Point类的构造方法。

                Point类除了构造方法,还定义了一个toString方法。注意,定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。

                6.2、constructor方法

                constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

                class Point {

                }

                // 等同于

                class Point {

                constructor() {}

                }

                上面代码中,定义了一个空的类Point,JavaScript 引擎会自动为它添加一个空的constructor方法。

                6.3、类的实例

                生成类的实例的写法,与 ES5 完全一样,也是使用new命令。前面说过,如果忘记加上new,像函数那样调用Class,将会报错。

                class Point {

                // …

                }

                // 报错

                var point = Point(2, 3);

                // 正确

                var point = new Point(2, 3);

                6.4、类的继承

                Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

                class Point {

                }

                class ColorPoint extends Point {

                }

                super关键字

                super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。

                第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。

                class A {}

                class B extends A {

                constructor() {

                super();

                }

                }

                上面代码中,子类B的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。

                第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

                class A {

                p() {

                return 2;

                }

                }

                class B extends A {

                constructor() {

                super();

                console.log(super.p()); // 2

                }

                }

                let b = new B();

                上面代码中,子类B当中的super.p(),就是将super当作一个对象使用。这时,super在普通方法之中,指向A.prototype,所以super.p()就相当于A.prototype.p()

                6.5、静态方法

                类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

                class Foo {

                static classMethod() {

                return ‘hello’;

                }

                }

                Foo.classMethod() // ‘hello’

                var foo = new Foo();

                foo.classMethod()

                // TypeError: foo.classMethod is not a function

                上面代码中,Foo类的classMethod方法前有static关键字,表明该方法是一个静态方法,可以直接在Foo类上调用(Foo.classMethod()),而不是在Foo类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。

                6.6、静态属性

                静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。

                class Foo {

                }

                Foo.prop = 1;

                Foo.prop // 1

                上面的写法为Foo类定义了一个静态属性prop

                目前,只有这种写法可行,因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。现在有一个提案提供了类的静态属性,写法是在实例属性的前面,加上static关键字。

                class MyClass {

                static myStaticProp = 42;

                constructor() {

                console.log(MyClass.myStaticProp); // 42

                }

                }

                这个新写法大大方便了静态属性的表达。

                // 老写法

                class Foo {

                // …

                }

                Foo.prop = 1;

                // 新写法

                class Foo {

                static prop = 1;

                }

                上面代码中,老写法的静态属性定义在类的外部。整个类生成以后,再生成静态属性。这样让人很容易忽略这个静态属性,也不符合相关代码应该放在一起的代码组织原则。另外,新写法是显式声明(declarative),而不是赋值处理,语义更好。

                7、Set和Map数据结构


                7.1、Set

                ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

                基础用法:

                let mySet = new Set();

                mySet.add(1); // Set(1) {1}

                mySet.add(5); // Set(2) {1, 5}

                mySet.add(5); // Set(2) {1, 5} 这里体现了值的唯一性

                mySet.add(“some text”);

                // Set(3) {1, 5, “some text”} 这里体现了类型的多样性

                var o = {a: 1, b: 2};

                mySet.add(o);

                mySet.add({a: 1, b: 2});

                // Set(5) {1, 5, “some text”, {…}, {…}}

                // 这里体现了对象之间引用不同不恒等,即使值相同,Set 也能存储

                上面代码通过add()方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。

                Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。

                // 例一

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

                […set]

                // [1, 2, 3, 4]

                // 例二

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

                items.size // 5

                数据类型转换

                Array与Set类型转换

                // Array 转 Set

                var mySet = new Set([“value1”, “value2”, “value3”]);

                // 用…操作符,将 Set 转 Array

                var myArray = […mySet];

                //Array.from方法可以将 Set 结构转为数组。

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

                const array = Array.from(items);

                String与Set类型转换

                // String 转 Set

                var mySet = new Set(‘hello’); // Set(4) {“h”, “e”, “l”, “o”}

                // 注:Set 中 toString 方法是不能将 Set 转换成 String

                Set实例的属性

                • Set.prototype.constructor:构造函数,默认就是Set函数。

                • Set.prototype.size:返回Set实例的成员总数。

                Set实例的操作方法

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

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

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

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

                代码示例:

                s.add(1).add(2).add(2);

                // 注意2被加入了两次

                s.size // 2

                s.has(1) // true

                s.has(2) // true

                s.has(3) // false

                s.delete(2);

                s.has(2) // false

                Set实例的遍历方法

                • Set.prototype.keys():返回键名的遍历器

                • Set.prototype.values():返回键值的遍历器

                • Set.prototype.entries():返回键值对的遍历器

                • Set.prototype.forEach():使用回调函数遍历每个成员

                需要特别指出的是,Set的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。

                代码示例:

                keys方法、values方法、entries方法返回的都是遍历器对象(详见《Iterator 对象》一章)。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。

                let set = new Set([‘red’, ‘green’, ‘blue’]);

                for (let item of set.keys()) {

                console.log(item);

                }

                // red

                // green

                // blue

                for (let item of set.values()) {

                console.log(item);

                }

                // red

                // green

                // blue

                for (let item of set.entries()) {

                console.log(item);

                }

                // [“red”, “red”]

                // [“green”, “green”]

                // [“blue”, “blue”]

                forEach()代码示例:

                let set = new Set([1, 4, 9]);

                set.forEach((value, key) => console.log(key + ’ : ’ + value))

                // 1 : 1

                // 4 : 4

                // 9 : 9

                遍历的应用

                (1)数组去重

                var mySet = new Set([1, 2, 3, 4, 4]);

                […mySet]; // [1, 2, 3, 4]

                (2)并集

                var a = new Set([1, 2, 3]);

                var b = new Set([4, 3, 2]);

                var union = new Set([…a, …b]); // {1, 2, 3, 4}

                (3)交集

                var a = new Set([1, 2, 3]);

                var b = new Set([4, 3, 2]);

                var intersect = new Set([…a].filter(x => b.has(x))); // {2, 3}

                (4)差集

                var a = new Set([1, 2, 3]);

                var b = new Set([4, 3, 2]);

                var difference = new Set([…a].filter(x => !b.has(x))); // {1}

                7.2、Map

                Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。

                基本用法:

                const m = new Map();

                const o = {p: ‘Hello World’};

                m.set(o, ‘content’)

                m.get(o) // “content”

                m.has(o) // true

                m.delete(o) // true

                m.has(o) // false

                Map中的key

                key是字符串

                var myMap = new Map();

                var keyString = “a string”;

                myMap.set(keyString, “和键’a string’关联的值”);

                myMap.get(keyString); // “和键’a string’关联的值”

                myMap.get(“a string”); // “和键’a string’关联的值”

                // 因为 keyString === ‘a string’

                key是对象

                ajax

                1)ajax请求的原理/ 手写一个ajax请求?
                2)readyState?
                3)ajax异步与同步的区别?
                4)ajax传递中文用什么方法?

                ajax.PNG

                前12.PNG

                , “e”, “l”, “o”}

                // 注:Set 中 toString 方法是不能将 Set 转换成 String

                Set实例的属性

                • Set.prototype.constructor:构造函数,默认就是Set函数。

                • Set.prototype.size:返回Set实例的成员总数。

                Set实例的操作方法

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

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

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

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

                代码示例:

                s.add(1).add(2).add(2);

                // 注意2被加入了两次

                s.size // 2

                s.has(1) // true

                s.has(2) // true

                s.has(3) // false

                s.delete(2);

                s.has(2) // false

                Set实例的遍历方法

                • Set.prototype.keys():返回键名的遍历器

                • Set.prototype.values():返回键值的遍历器

                • Set.prototype.entries():返回键值对的遍历器

                • Set.prototype.forEach():使用回调函数遍历每个成员

                需要特别指出的是,Set的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。

                代码示例:

                keys方法、values方法、entries方法返回的都是遍历器对象(详见《Iterator 对象》一章)。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。

                let set = new Set([‘red’, ‘green’, ‘blue’]);

                for (let item of set.keys()) {

                console.log(item);

                }

                // red

                // green

                // blue

                for (let item of set.values()) {

                console.log(item);

                }

                // red

                // green

                // blue

                for (let item of set.entries()) {

                console.log(item);

                }

                // [“red”, “red”]

                // [“green”, “green”]

                // [“blue”, “blue”]

                forEach()代码示例:

                let set = new Set([1, 4, 9]);

                set.forEach((value, key) => console.log(key + ’ : ’ + value))

                // 1 : 1

                // 4 : 4

                // 9 : 9

                遍历的应用

                (1)数组去重

                var mySet = new Set([1, 2, 3, 4, 4]);

                […mySet]; // [1, 2, 3, 4]

                (2)并集

                var a = new Set([1, 2, 3]);

                var b = new Set([4, 3, 2]);

                var union = new Set([…a, …b]); // {1, 2, 3, 4}

                (3)交集

                var a = new Set([1, 2, 3]);

                var b = new Set([4, 3, 2]);

                var intersect = new Set([…a].filter(x => b.has(x))); // {2, 3}

                (4)差集

                var a = new Set([1, 2, 3]);

                var b = new Set([4, 3, 2]);

                var difference = new Set([…a].filter(x => !b.has(x))); // {1}

                7.2、Map

                Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。

                基本用法:

                const m = new Map();

                const o = {p: ‘Hello World’};

                m.set(o, ‘content’)

                m.get(o) // “content”

                m.has(o) // true

                m.delete(o) // true

                m.has(o) // false

                Map中的key

                key是字符串

                var myMap = new Map();

                var keyString = “a string”;

                myMap.set(keyString, “和键’a string’关联的值”);

                myMap.get(keyString); // “和键’a string’关联的值”

                myMap.get(“a string”); // “和键’a string’关联的值”

                // 因为 keyString === ‘a string’

                key是对象

                ajax

                1)ajax请求的原理/ 手写一个ajax请求?
                2)readyState?
                3)ajax异步与同步的区别?
                4)ajax传递中文用什么方法?

                [外链图片转存中…(img-r9nfexn9-1719211722806)]

                [外链图片转存中…(img-xzvp885N-1719211722807)]

          • 相关阅读:
            苯硫酚荧光探针 激发波长465 nm
            双非二本找实习前的准备day3
            艾美捷AAT别藻蓝蛋白普通储备溶液制备方案
            云积天赫AI营销:重塑品牌营销新生态
            计算机毕业设计Python+Django的学生作业管理系统
            GAN网络系列博客(一):用于人脸生成的StyleGAN结构
            1软件管理
            云小课|云小课带你玩转可视化分析ELB日志
            mysql 问题解决 4
            Webpack 和 Vite 的区别
          • 原文地址:https://blog.csdn.net/2401_85124347/article/details/139926172