当前位置: 首页 > article >正文

HTML-ES6.0核心技术

1.ES6简介

  1. ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了。
  2. 它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
  3. ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。日常场合,这两个词是可以互换的。
  4. ES6 从开始制定到最后发布,整整用了15年 2009年12月,ECMAScript 5.0版正式发布 2011年6月,ECMAscript5.1版发布,并且成为ISO国际标准(ISO/IEC 16262:2011)。 2013年3月,ECMAScript6草案冻结,不再添加新功能。新的功能设想将被放到ECMAScript 7。 2013年12月,ECMAScript6草案发布。然后是12个月的讨论期,听取各方反馈。 2015年6月,ECMAScript6正式通过,成为国际标准。从2000年算起,这时已经过去了15年。

附录:

ECMA是“European Computer Manufacturers Association”的缩写,中文名称为欧洲计算机制造商协会。是1961年成立的旨在建立统一的电脑操作格式标准(包括程序语言和输入输出)的组织。

2.ES6基础编程

2.1.let命令

ES6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

2.1.1.let命令存在块级作用域

if (true) {
    var a = 10;
    let b = 20;
}
console.log(a); //10
console.log(b); //Uncaught ReferenceError: b is not defined

上面代码在代码块之中,分别用var和let声明了两个变量。然后在代码块之外调用这两个变量,结果let声明的变量报错,var声明的变量返回了正确的值。这表明,let声明的变量只在它所在的代码块有效。

那为什么需要块级作用域呢? ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。 下面场景中,内层变量可能会覆盖外层变量。

var tmp = new Date();
function fn(){
    console.log(tmp);
    if(false){
        var tmp = 'hello world';
    }
}
fn();    //undefined

上面代码中,函数f执行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。

2.1.2.Let不存在变量提升

//var的情况
console.log(foo);
var foo = 2;   //undefined
//let的情况
console.log(bar);
let bar = 2;   //ReferenceError: Cannot access 'bar'

上面代码中,变量foo用var命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。 变量bar用let命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。 因此,使用let声明变量,极大的消除了代码的潜在bug的隐患。

2.1.2.Let暂时性死区

只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

var temp = 123;
if(true){
    temp = 'abc';  //ReferenceError: Cannot access 'temp' 
    let temp;
}

上面代码中,存在全局变量temp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对temp赋值会报错。

2.2.const 命令

const声明一个只读的常量。一旦声明,常量的值就不能改变。

const PI = 3.1415926;
PI = 3;  //TypeError: Assignment to constant variable.

上面代码表明改变常量的值会报错。

2.3.字符串的扩展-模板字符串

传统的JavaScript语言,输出模板通常是这样写的:

<p id="result"></p>
<script>
    let basket = {
        count: 100,
        onSale: 80
    }
    document.getElementById('result').innerHTML =
        'There are <b>' + basket.count + '</b> ' +
        'items in your basket, ' +
        '<em>' + basket.onSale +
        '</em> are on sale!';
</script>

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

<p id="result"></p>
<script>
    let basket = {
        count:100,
        onSale:80
    }
    document.getElementById('result').innerHTML =
        `There are <b>${basket.count}</b> items
         in your basket, <em>${basket.onSale}</em>
        are on sale!`;
</script>

注意:ES6模板字符串中必须要使用反引号。

2.4.函数的扩展rest参数

ES6 引入 rest 参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function add(...values) {
    let sum = 0;
    for(var val of values) {
          sum += val;
    }
    return sum;
}
add(2, 5, 3) // 10

上面代码的add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数。

rest 参数中的变量代表一个数组,所以数组特有的方法都可以用于这个变量。下面是一个利用 rest 参数改写数组push方法的例子

function push(array, ...items) {
    items.forEach(function(item) {
        array.push(item);
        console.log(item);
    });
}
var a = [];
push(a, 1, 2, 3)

2.5. 函数的扩展-箭头函数

ES6允许使用“箭头”(=>)定义函数:

//ES5定义函数
var f = function(v) {
    return v;
};
//ES6定义箭头函数
var f = v => v

2.5.1.箭头函数的语法

  1. 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分
let f = () => 5;
// 等同于
let f = function() { return 5 };
let sum = (num1, num2) => {
    return num1 + num2;
};
// 等同于
let sum = function(num1, num2) {
    return num1 + num2;
};
  1. 如果只有一个参数,那么圆括号就可以省略了
let f = str => {
  console.log(str);
}
f('hello');
  1. 如何函数体中的代码只有一句,那么大括号可以省略
let f = str => console.log(str);
f('hello');
  1. 但如果函数体中的代码只有一个return语句,那么return关键词也必须省略
//这是错误的
let sum = (num1, num2) => return num1 + num2;
//这是正确的
let sum = (num1, num2) => num1 + num2;
  1. 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。
let getTempItem = id => ({ id:id, name:'Temp' })

2.5.2.使用箭头函数的注意点

箭头函数有几个使用注意点。

  1. 函数体内的this对象。
    函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
    或者说:箭头函数当方法使用时没有定义this绑定,而是捕获所在上下文的 this ,作为自己的 this 。
<button id="one">one点击(使用function)</button>
<button id="two">two点击(使用箭头函数)</button>
<script>
    var one = document.getElementById('one');
    var two = document.getElementById('two');
    one.onclick = function (){
        console.log(this);   //<button id="one">one点击(使用function)</button>
    }
    two.onclick = ()=>{
        console.log(this);   //window
    }
</script>

注意:

  • 箭头函数的this永远指向其上下文的 this,任何方法都改变不了其指向,如call(), bind()等。
  • 普通函数的this指向调用它的那个对象。
  1. 不可以当作构造函数。
    也就是说,不可以使用new命令,否则会抛出一个错误。
  2. 不可以使用arguments对象。
    该对象在函数体内不存在。如果要用,可以用Rest参数代替。

2.5.3.this指向问题测试

<button>点击</button>
<script>
    //指向Window,因为处于Window作用域中(或者说console本身就是window对象的函数)
    window.console.log('script块:'+this);
    //指向Window,因为处于Window作用域中(或者说if是声明在window对象中的)
    if(true){
        console.log('if块:'+this);
    }
    //指向Window,因为处于Window作用域中(或者说此函数是在window对象中的)
    //注意:可以这样写:var fn = function(){}
    //     但不能这样写:let fn = function(){}
    var fn = function(){
        console.log('函数中:'+this);
    }
    window.fn();
    //指向Window,因为处于Window作用域中(或者说setTimeout本身就是window对象的函数)
    window.setTimeout(function(){
        console.log('setTimeout函数中:'+this);
    },1);
    //指向Button对象
    document.getElementsByTagName('button')[0].onclick=function(){
        console.log('button事件处理函数中:'+this);
    }
    /**
     * 测试:点击按钮后,结果为:
     * script块:[object Window]
     * if块:[object Window]
     * 函数中:[object Window]
     * setTimeout函数中:[object Window]
     * button事件处理函数中:[object HTMLButtonElement]
     */
</script>

2.5.4.使用箭头函数简化回调函数

箭头函数的一个用处是简化回调函数

实例1:

let arr = [1,2,3];
// function函数写法
arr = arr.map(function(x) {
    return x * x;
});
// 箭头函数写法
arr = arr.map(x => x * x);

实例2:

let values = [5,2,8];
// function函数写法
let result = values.sort(function(a, b) {
    return a - b;
});
// 箭头函数写法
let result = values.sort((a, b) => a - b);

2.5.5.rest参数与箭头函数结合

let numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)     // [1,2,3,4,5]
let headAndTail = (head, ...tail) => [head, tail];
headAndTail(1, 2, 3, 4, 5)  // [1,[2,3,4,5]]

2.6 数组的扩展

本节内容使用普通函数实现,如果已经学习过箭头函数,请同学将代码改写为箭头函数形式!

2.6.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')]
// [<div>, <div>, <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)这两行,都是函数的调用,它们都使用了扩展运算符。该运算符将一个数组,变为参数序列。

扩展运算符与正常的函数参数可以结合使用,非常灵活。

function f(v, w, x, y, z) { }
const args = [0, 1];
f(-1, ...args, 2, ...[3]);

扩展运算符后面还可以放置表达式。

const arr = [
  ...(x > 0 ? ['a'] : []),
  'b',
];

如果扩展运算符后面是一个空数组,则不产生任何效果。

[...[], 1]
// [1]

注意,只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。

(...[1, 2])
// Uncaught SyntaxError: Unexpected number

console.log((...[1, 2]))
// Uncaught SyntaxError: Unexpected number

console.log(...[1, 2])
// 1 2

上面三种情况,扩展运算符都放在圆括号里面,但是前两种情况会报错,因为扩展运算符所在的括号不是函数调用。

扩展运算符的应用
(1)复制数组

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

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的克隆。

(2)合并数组

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

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

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

(3)与解构赋值结合

扩展运算符可以与解构赋值结合起来,用于生成数组。

// ES5
a = list[0], rest = list.slice(1)

// ES6
[a, ...rest] = list

下面是另外一些例子。

const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest  // [2, 3, 4, 5]

const [first, ...rest] = [];
first // undefined
rest  // []

const [first, ...rest] = ["foo"];
first  // "foo"
rest   // []

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

const [...butLast, last] = [1, 2, 3, 4, 5];
// 报错

const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 报错
(4)字符串

扩展运算符还可以将字符串转为真正的数组。

[...'hello']
// [ "h", "e", "l", "l", "o" ]

上面的写法,有一个重要的好处,那就是能够正确识别四个字节的 Unicode 字符。

'x\uD83D\uDE80y'.length // 4
[...'x\uD83D\uDE80y'].length // 3

上面代码的第一种写法,JavaScript 会将四个字节的 Unicode 字符,识别为 2 个字符,采用扩展运算符就没有这个问题。因此,正确返回字符串长度的函数,可以像下面这样写。

function length(str) {
  return [...str].length;
}

length('x\uD83D\uDE80y') // 3

凡是涉及到操作四个字节的 Unicode 字符的函数,都有这个问题。因此,最好都用扩展运算符改写。

let str = 'x\uD83D\uDE80y';

str.split('').reverse().join('')
// 'y\uDE80\uD83Dx'

[...str].reverse().join('')
// 'y\uD83D\uDE80x'

上面代码中,如果不用扩展运算符,字符串的reverse()操作就不正确。

(5)实现了 Iterator 接口的对象

任何定义了遍历器(Iterator)接口的对象(参阅 Iterator 一章),都可以用扩展运算符转为真正的数组。

let nodeList = document.querySelectorAll('div');
let array = [...nodeList];

上面代码中,querySelectorAll()方法返回的是一个NodeList对象。它不是数组,而是一个类似数组的对象。这时,扩展运算符可以将其转为真正的数组,原因就在于NodeList对象实现了 Iterator。

Number.prototype[Symbol.iterator] = function*() {
  let i = 0;
  let num = this.valueOf();
  while (i < num) {
    yield i++;
  }
}

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

上面代码中,先定义了Number对象的遍历器接口,扩展运算符将5自动转成Number实例以后,就会调用这个接口,就会返回自定义的结果。

对于那些没有部署 Iterator 接口的类似数组的对象,扩展运算符就无法将其转为真正的数组。

let arrayLike = {
  '0': 'a',
  '1': 'b',
  '2': 'c',
  length: 3
};

// TypeError: Cannot spread non-iterable object.
let arr = [...arrayLike];

上面代码中,arrayLike是一个类似数组的对象,但是没有部署 Iterator 接口,扩展运算符就会报错。这时,可以改为使用Array.from方法将arrayLike转为真正的数组。

(6)Map 和 Set 结构,Generator 函数

扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,比如 Map 结构。

let map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

let arr = [...map.keys()]; // [1, 2, 3]

Generator 函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。

const go = function*(){
  yield 1;
  yield 2;
  yield 3;
};

[...go()] // [1, 2, 3]

上面代码中,变量go是一个 Generator 函数,执行后返回的是一个遍历器对象,对这个遍历器对象执行扩展运算符,就会将内部遍历得到的值,转为一个数组。

如果对没有 Iterator 接口的对象,使用扩展运算符,将会报错。

const obj = {a: 1, b: 2};
let arr = [...obj]; // TypeError: Cannot spread non-iterable object

2.6.2 重要的实例方法

2.6.2.1 fill()方法

定义和用法

fill方法使用给定值,填充一个数组。

['a', 'b', 'c'].fill(7)
// [7, 7, 7]

new Array(3).fill(7)
// [7, 7, 7]

上面代码表明,fill方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。

fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。

['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']

上面代码表示,fill方法从 1 号位开始,向原数组填充 7,到 2 号位之前结束。

注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。

实例

let arr = new Array(3).fill({name: "Mike"});
arr[0].name = "Ben";
arr
// [{name: "Ben"}, {name: "Ben"}, {name: "Ben"}]

let arr = new Array(3).fill([]);
arr[0].push(5);
arr
// [[5], [5], [5]]
2.6.2.2 forEach() 方法

定义和用法

forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。

注意: forEach() 对于空数组是不会执行回调函数的。

语法

array.forEach(callbackFn(currentValue, index, arr), thisValue)

参数

参数

描述

callbackFn(currentValue, index, arr)

必需。 数组中每个元素需要调用的函数。
函数参数:

参数

描述

currentValue

必需。当前元素

index

可选。当前元素的索引值。

arr

可选。当前元素所属的数组对象。

thisValue

可选。传递给函数的值一般用 "this" 值。
如果这个参数为空, "undefined" 会传递给 "this" 值

其他形式的语法格式:

// 箭头函数
forEach((element) => { /* … */ })
forEach((element, index) => { /* … */ })
forEach((element, index, array) => { /* … */ })

// 回调函数
forEach(callbackFn)
forEach(callbackFn, thisArg)

// 内联回调函数
forEach(function(element) { /* … */ })
forEach(function(element, index) { /* … */ })
forEach(function(element, index, array){ /* … */ })
forEach(function(element, index, array) { /* … */ }, thisArg)

实例

var arr = [1, 2, 3, 4, 5];
arr.forEach(function (item) {
    if (item === 3) {
        return;
    }
    console.log(item);
});
2.6.2.3 filter()

定义和用法

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

注意: filter() 不会对空数组进行检测。filter() 不会改变原始数组。

语法

array.filter(function(currentValue,index,arr), thisValue) 

参数说明

参数

描述

function(currentValue, index,arr)

必须。函数,数组中的每个元素都会执行这个函数
函数参数:

参数

描述

currentValue

必须。当前元素的值

index

可选。当前元素的索引值

arr

可选。当前元素属于的数组对象

thisValue

可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。
如果省略了 thisValue ,"this" 的值为 "undefined"

实例

var ages = [32, 33, 12, 40];

function checkAdult(age) {
    return age >=18 ;
}

function myFunction() {
    console.log(ages.filter(checkAdult));
}
2.6.2.4 map() 方法

定义和用法

map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

map() 方法按照原始数组元素顺序依次处理元素。

注意: map() 不会对空数组进行检测。 map() 不会改变原始数组。

语法

array.map(function(currentValue,index,arr), thisValue) 

参数

描述

function(currentValue, index,arr)

必须。函数,数组中的每个元素都会执行这个函数
函数参数:

参数

描述

currentValue

必须。当前元素的值

index

可选。当前元素的索引值

arr

可选。当前元素属于的数组对象

thisValue

可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。
如果省略了 thisValue,或者传入 null、undefined,那么回调函数的 this 为全局对象。

实例

var numbers = [4, 9, 16, 25];

function myFunction() {
  x = document.getElementById("demo")
  x.innerHTML = numbers.map(Math.sqrt);
}

输出结果为:

2,3,4,5

2.6.2.5 reduce() 方法

定义和用法

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

reduce() 可以作为一个高阶函数,用于函数的 compose。

注意: reduce() 对于空数组是不会执行回调函数的。

语法

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

参数

参数

描述

function(total,currentValue, index,arr)

必需。用于执行每个数组元素的函数。
函数参数:

参数

描述

total

必需。初始值, 或者计算结束后的返回值。

currentValue

必需。当前元素

currentIndex

可选。当前元素的索引

arr

可选。当前元素所属的数组对象。

initialValue

可选。传递给函数的初始值

实例

var numbers = [65, 44, 12, 4];
 
function getSum(total, num) {
    return total + num;
}
function myFunction(item) {
    const sum = numbers.reduce(getSum);
    console.log(sum)
}
2.6.2.6 some() 方法

定义和用法

some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。

some() 方法会依次执行数组的每个元素:

  • 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
  • 如果没有满足条件的元素,则返回false。

注意: some() 不会对空数组进行检测。some() 不会改变原始数组。

语法

array.some(function(currentValue,index,arr),thisValue)

参数

参数

描述

function(currentValue, index,arr)

必须。函数,数组中的每个元素都会执行这个函数
函数参数:

参数

描述

currentValue

必须。当前元素的值

index

可选。当前元素的索引值

arr

可选。当前元素属于的数组对象

thisValue

可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。
如果省略了 thisValue ,"this" 的值为 "undefined"

实例

var ages = [3, 10, 18, 20];
 
function checkAdult(age) {
    return age >= 18;
}
 
function myFunction() {
    document.getElementById("demo").innerHTML = ages.find(checkAdult);
}
2.6.2.7 find() 方法

定义和用法

find() 方法返回通过测试(函数内判断)的数组的第一个元素的值。

find() 方法为数组中的每个元素都调用一次函数执行:

  • 当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。
  • 如果没有符合条件的元素返回 undefined

注意: find() 对于空数组,函数是不会执行的。注意: find() 并没有改变数组的原始值。

语法

array.find(function(currentValue, index, arr),thisValue)

参数

参数

描述

function(currentValue, index,arr)

必需。数组每个元素需要执行的函数。
函数参数:

参数

描述

currentValue

必需。当前元素

index

可选。当前元素的索引值

arr

可选。当前元素所属的数组对象

thisValue

可选。 传递给函数的值一般用 "this" 值。
如果这个参数为空, "undefined" 会传递给 "this" 值

实例

var ages = [3, 10, 18, 20];
 
function checkAdult(age) {
    return age >= 18;
}
 
function myFunction() {
    document.getElementById("demo").innerHTML = ages.find(checkAdult);
}

相关方法(略)

findIndex(),findLast(),findLastIndex()

2.6.2.8 sort() 方法

定义和用法

sort() 方法用于对数组的元素进行排序。

排序顺序可以是字母或数字,并按升序或降序。

数组的默认排序顺序为按字母升序【即使是数字数组,默认亦按此排序】。

注意:当数字是按字母顺序排列时"40"将排在"5"前面。

使用数字排序,你必须通过一个函数作为参数来调用。

函数指定数字是按照升序还是降序排列。

这些说起来可能很难理解,你可以通过本页底部实例进一步了解它。

注意: 这种方法会改变原始数组!。

语法

array.sort(sortfunction)

参数 Values

参数

描述

sortfunction

可选。规定排序顺序。必须是函数。

返回值

Type

描述

Array

对数组的引用。请注意,数组在原数组上进行排序,不生成副本。

实例

var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.sort();
// Apple,Banana,Mango,Orange

var points = [40,100,1,5,25,10];
console.log( points.sort(function(a,b){return a-b}));
//  结果:
1,5,10,25,40,100

var points = [40,100,1,5,25,10];
points.sort(function(a,b){return b-a});
// 100,40,25,10,5,1

var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.sort();
fruits.reverse();
// Orange,Mango,Banana,Apple
2.6.2.9 copyWithin() 方法(了解)

copyWithin() 方法用于从数组的指定位置拷贝元素到数组的另一个指定位置中。

语法

array.copyWithin(target, start, end)

参数

参数

描述

target

必需。复制到指定目标索引位置。

start

可选。元素复制的起始位置。

end

可选。停止复制的索引位置 (默认为 array.length)。如果为负值,表示倒数。

实例

 var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.copyWithin(2, 0);
// Banana,Orange,Banana,Orange

// 复制数组的前面两个元素到第三和第四个位置上
var fruits = ["Banana", "Orange", "Apple", "Mango", "Kiwi", "Papaya"];
fruits.copyWithin(2, 0, 2);
// Banana,Orange,Banana,Orange,Kiwi,Papaya
2.6.2.A every() 方法

定义和用法

every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。

every() 方法使用指定函数检测数组中的所有元素:

  • 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测。
  • 如果所有元素都满足条件,则返回 true。

注意: every() 不会对空数组进行检测。

注意: every() 不会改变原始数组。

语法

array.every(function(currentValue,index,arr), thisValue)

参数

参数

描述

function(currentValue, index,arr)

必须。函数,数组中的每个元素都会执行这个函数
函数参数:

参数

描述

currentValue

必须。当前元素的值

index

可选。当前元素的索引值

arr

可选。当前元素属于的数组对象

thisValue

可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。
如果省略了 thisValue ,"this" 的值为 "undefined"

实例

var ages = [32, 33, 16, 40];

function checkAdult(age) {
    return age >= 18;
}

function myFunction() {
    document.getElementById("demo").innerHTML = ages.every(checkAdult);
}
//  false

// 检测数组 ages 的所有元素是否都大于等于输入框中指定的数字:

<p>最小年龄: <input type="number" id="ageToCheck" value="18"></p>
<button onclick="myFunction()">点我</button>
<p>是否所有年龄都符号条件? <span id="demo"></span></p>
<script>
  var ages = [32, 33, 12, 40];
  
  function checkAdult(age) {
      return age >= document.getElementById("ageToCheck").value;
  }
  
  function myFunction() {
      document.getElementById("demo").innerHTML = ages.every(checkAdult);
  }
</script>

2.6.3 Array扩展(了解)

2.6.3.1 from()函数 定义和用法

from() 方法用于通过拥有 length 属性的对象或可迭代的对象来返回一个数组。

如果对象是数组返回 true,否则返回 false。

语法

Array.from(object, mapFunction, thisValue)参数

实例

//  通过字符串创建一个数组 
let  myArr = Array.from("Neuedu");

// 下面的实例返回集合中包含的对象数组
var setObj = new Set(["a", "b", "c"]);
var objArr = Array.from(setObj);
objArr[1] == "b";  // true

// 下面的实例演示如何使用箭头语法和映射函数更改元素的值
var arr = Array.from([1, 2, 3], x => x * 10);
// arr[0] == 10;
// arr[1] == 20;
// arr[2] == 30;
2.6.3.2 of() 方法

定义和用法

of() 方法用于将一组值转换为数组,不考虑参数的数量或类型。

Array.of() 和 Array() 构造函数之间的区别在于对单个参数的处理:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个 length 为 7 的空数组。

如果对象是数组返回 true,否则返回 false。

语法

Array.of(element0)
Array.of(element0, element1)
Array.of(element0, element1, /* … ,*/ elementN)

参数

参数

描述

element0

必需,要转换为数组的元素。

element1, /* … ,*/ elementN

可选,要转换为数组的元素。

实例

var myArr = Array.of("Runoob","Google","Taobao");

Array.of(7); // [7]
Array(7); // array of 7 empty slots

Array.of(1, 2, 3); // [1, 2, 3]
Array(1, 2, 3);    // [1, 2, 3]

Array.of(1);         // [1]
Array.of(1, 2, 3);   // [1, 2, 3]
Array.of(undefined); // [undefined]

2.6.3.3 flat() 方法

定义和用法

flat() 方法方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

flat() 方法返回一个包含将数组与子数组中所有元素的新数组。

语法

flat()flat(depth)

参数

参数

描述

depth

指定要提取嵌套数组的结构深度,默认值为 1。

实例

// 扁平化嵌套数组

var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

//使用 Infinity,可展开任意深度的嵌套数组
var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 扁平化与数组空项
var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]

2.6.4 Map操作相关(了解)

2.6.4.1 entries()方法

定义和用法

entries() 方法返回带有键/值对的 Array Iterator 对象。

对于原始数组var fruits = ["Banana", "Orange", "Apple", "Mango"] 中的每一项,新的迭代对象将包含一个以索引为键,以项值为值的数组:

  • [0, "Banana"]
  • [1, "Orange"]
  • [2, "Apple"]
  • [3, "Mango"]

注释:entries() 不会更改原始数组。

语法

array.entries()

实例

// 创建一个 Array Iterator 对象,并创建一个循环来迭代每个键/值对:
var fruits = ["Banana", "Orange", "Apple", "Mango"];
var f = fruits.entries();

for (x of f) {
  document.getElementById("demo").innerHTML += x;
}

2.6.4.2 keys()方法

定义和用法

keys() 方法用于从数组创建一个包含数组键的可迭代对象。

如果对象是数组返回 true,否则返回 false。

语法

array.keys()

实例

// 从数组 fruit 创建一个数组迭代对象, 该对象包含了数组的键:
var fruits = ["Banana", "Orange", "Apple", "Mango"];
// 
console.log(fruits.keys());
// 结果: 0,1,2,3

3.类

3.1. 类的基本语法

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

//定义类
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return '(' + this.x + ', ' + this.y + ')';
    }
}
//创建对象
let p = new Point(1,2);
console.log(p.toString());

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

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

注意:生成类的实例对象的写法,要使用new命令。如果忘记加上new,像ES5函数那样调用Class,将会报错。

// 报错
let point = Point(2,3);
// 正确
let point = new Point(2,3);

3.2.constructor方法

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

constructor() {}

注意:constructor方法默认返回实例对象(即this)

扩展:constructor方法默认返回实例对象(即this),但也可以指定返回另外一个对象。

class Point {
    constructor(x, y) {
        return new Date();
        //return Object.create(null);
    }
}
let p = new Point();   //返回为Date对象
console.log(p);

Constructor与普通构造函数的区别: 类的构造函数,不使用new是没法调用的,会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return '(' + this.x + ', ' + this.y + ')';
    }
}
let p = Point(); //TypeError: Class constructor Point cannot be invoked without 'new'

3.3.this与class(原型)

class:类,是对象的模板。(或者叫:原型) this:当前对象。 以上定义,与java中的概念是相同的。 但是,由于javascript语言自身的特点,关于class与this,还需要做进一步说明。

注意:在javascript中,在定义类完毕之后,还可以再添加属性和方法。

3.3.1.属性与方法添加到this上

class Point {
    constructor() { }
}
let p = new Point();
p.username = 'zhangsan';
console.log(p.username);    //zhangsan

上面代码中,Point类定义之后,给对象 p 又添加了一个username属性。这是完全可以的。 但是要注意:此种方式,仅仅是给对象p添加一个属性,而不是给类添加一个属性。 或者说: 此种方式,仅仅是给对象p的this添加一个属性,而不是给类添加一个属性。

class Point {
    constructor() { }
}
let p1 = new Point();
p1.username = 'zhangsan';
console.log(p1.username);   //zhangsan
let p2 = new Point();
console.log(p2.username);   //undefined

上面例子中,只给对象p1添加了username属性,但没有给类添加属性。 所以,p2对象中并没有username属性。

3.3.2.属性与方法添加到类上

class Point {
    constructor() { }
}
Point.prototype.username = 'zhangsan';
let p1 = new Point();
console.log(p1.username);   //zhangsan
let p2 = new Point();
console.log(p2.username);   //zhangsan

上面代码中,使用 Point.prototype方式,给类添加属性。 这样,类的所有对象(p1、p2)就都有username属性了。

在javascript中,每一个类都有一个prototype,它就是类的原型,类的所有实例共享一个原型。如果想访问这个原型,可以使用proto指针

class Point {
 constructor() { }
}
let p = new Point();
console.log(p.__proto__);      //注意proto指针的写法
/**
 * 输出结果:
 * {constructor: ƒ}
 *  constructor: class Point  //这里证明:proto指针指向类的原型
 *  __proto__: Object
 */
class Point {
    constructor() { }
}
let p1 = new Point();
let p2 = new Point();
console.log(p1.__proto__===p2.__proto__); //这里证明:类的所有实例共享一个原型

实际上,由于ES5历史原因,程序员更喜欢称之为:属性与方法添加到原型上。

3.3.3.类的静态成员

class Point {
    constructor() { }
}
Point.username = 'zhangsan';
let p = new Point();
console.log(p.username);       //undefined
console.log(Point.username);   //zhangsan

上面代码中,直接使用 Point.username 方式给类添加一个属性,此种方式只是添加了一个静态属性,访问时,只能通过类名才能访问。

4.ES6中的模块化编程

4.1.为什么使用模块化

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。

<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>

上面是最原始的 JavaScript 文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在 window 对象中,不同模块的接口调用都是一个作用域中,一些复杂的框架,会使用命名空间的概念来组织这些模块的接口。

这种原始的加载方式暴露了一些显而易见的弊端

  1. 全局作用域下容易造成变量冲突
  2. 文件只能按照javaScripe的书写顺序进行加载
  3. 开发人员必须主观解决模块和代码库的依赖关系
  4. 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。

在 ES6 之后,ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

4.2.ES6模块化实现

模块功能主要由两个命令构成:export和import。 export命令用于规定模块的对外接口 import命令用于输入其他模块提供的功能。

4.2.1.export 命令

export let firstName = 'Michael';
export let lastName = 'Jackson';
export let year = 1958;

上面代码中,使用export命令对外部输出了三个模块。

let firstName = 'Michael';
let lastName = 'Jackson';
let year = 1958;
export { 
    firstName as f,   //可以使用as关键字,重命名对外接口
    lastName, 
    year 
};

上面代码是export命令的另外一种写法。 在export命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在let语句前)是等价的,但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。

export function multiply(x, y) {
    return x * y;
};
export class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return this.x + ',' + this.y;
    }
};

export命令除了输出变量,还可以输出函数、类(class)。

4.2.2.import命令

  1. 使用export命令定义模块对外接口后,其他 JS 文件就可以通过import命令加载这个模块。
import { name, age, sex } from './module1.js‘
console.log(name + '\t' + age + '\t' + sex);
  1. 上面代码的import命令,用于加载module1.js文件,并从中输入变量。import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块( module1.js)对外接口的名称相同。
  2. import命令可以使用as关键字,将输入的变量重命名。
import { lastName as surname } from './profile.js';
  1. import命令具有提升效果,会提升到整个模块的头部,首先执行
foo();
import { foo } from './my_module.js';
  1. 上面的代码不会报错,因为import的执行早于foo的调用。这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。
  2. import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
// 报错
import { 'f' + 'oo' } from './my_module.js';
// 报错
let module = ‘./my_module.js';
import { foo } from module;
// 报错
if (x === 1) {
  import { foo } from './module1.js';
} else {
  import { foo } from './module2.js';
}
  1. 如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次
import './lodash.js';
import './lodash.js';
  1. 上面代码加载了两次lodash,但是只会执行一次。
  2. import语句是 Singleton 模式
import { foo } from './my_module.js';
import { bar } from './my_module.js';
// 等同于
import { foo, bar } from './my_module.js';
  1. 上面代码中,虽然foo和bar在两个语句中加载,但是它们对应的是同一个my_module实例。也就是说,import语句是 Singleton 模式。

4.2.3.完整实例

(注意:当使用本地file协议打开文件时,type="module" 会产生跨越请求,从而导致跨越失败。所以下面代码需要在服务器环境下运行。 )

  1. profile.js文件
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
export function jia(x, y) {
    return x + y;
}
export function jian(x, y) {
    return x - y;
}
export class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() {
        return this.x + '\t' + this.y;
    }
}
  1. app.js文件:
import {firstName,lastName,year,jia,jian,Point} from './profile.js'
console.log(firstName + '\t' + lastName + '\t' + year);
console.log(jia(1,2));
console.log(jian(1,2));
let obj = new Point(1,2)
console.log(obj.toString());

注意: from './profile.js' 即使是在同一个文件夹中,也必须要写 ./

  1. index.html文件:
<script src="js/app.js" type="module"></script>

4.3.模块的整体加载

除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

// circle.js文件
export function area(radius) {
    return Math.PI * radius * radius;
}
export function circumference(radius) {
    return 2 * Math.PI * radius;
}
//main.js整体加载
import * as circle from './circle.js';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));

4.4.export default 命令

使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。

为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

// export-default.js
export default function  () {
    console.log('foo');
}

上面代码是一个模块文件export-default.js,它的默认输出是一个函数。

// import-default.js
import customName from './export-default.js';
customName(); // 'foo'

上面代码的import命令,可以用任意名称指向export-default.js输出的方法,这时就不需要知道原模块输出的函数名。

注意:

  1. 一个js文件中,不能有两个export default命令。
  2. import命令后面,不使用大括号。因为只能写一个模块名。

export default命令用在非匿名函数前,也是可以的:

// export-default.js
export default function foo() {
    console.log('foo');
}
// 或者写成
function foo() {
    console.log('foo');
}
export default foo;

上面代码中,foo函数的函数名foo,在模块外部是无效的。加载的时候,视同匿名函数加载。


http://www.kler.cn/news/330682.html

相关文章:

  • 外贸网站怎么搭建对谷歌seo比较好?
  • 努力却未获收益的心理困境及其改善策略--研究生心理健康课程结课论文
  • 设计模式之访问者
  • 一个简单的摄像头应用程序1
  • 画质与体验双升, 海信AI电视从此更懂你
  • AI 搜索引擎工具集合
  • ts类型体操-简单篇
  • Java对象头
  • Dijkstra算法,动态规划和滑动窗口
  • 【分别为微服务云原生】9分钟ActiveMQ延时消息队列:定时任务的革命与Quartz的较量
  • MES(软件)系统是什么?MES系统为何如此重要呢?
  • 【RabbitMQ 项目】服务端:消费者管理模块
  • c#增删改查 (数据操作的基础)
  • Python 从入门到实战32(数据库MySQL)
  • VMware中Ubuntu系统Docker正常运行但网络不通(已解决)
  • java 的三种IO模型(BIO、NIO、AIO)
  • 蓝桥杯备赛---2.新建工程
  • 论文不同写作风格下的ChatGPT提示词分享
  • Linux学习之路 -- 线程 -- 线程池
  • RabbitMq生产者可靠性