ECMAScript6介绍及环境搭建

发布于:2024-06-28 ⋅ 阅读:(163) ⋅ 点赞:(0)

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

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
    • `);

      上面代码中,所有模板字符串的空格和换行,都是被保留的,比如 <ul> 标签前面会有一个换行。如果你不想要这个换行,可以使用 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)]


          网站公告

          今日签到

          点亮在社区的每一天
          去签到