Giter Club home page Giter Club logo

js-tutorial's Introduction

JavaScript

资料来源各前端大佬和自己整理,仅作为个人学习参考用,转载传递请注明来源

联系E-mail:[email protected]

js-tutorial's People

Contributors

gitbook-bot avatar jesonhu avatar

Watchers

 avatar

js-tutorial's Issues

Date-时间相关

时间间隔计算

计算年日时分秒

/**
 * 获取时分秒格式化后的字符串
 * 
 * @param {number} totalSeconds 相差的秒(单位:秒)
 * 
 * @example
 * ```
 * getYearToSeconds(17240);
 * => '4小时47分20秒'
 * 
 * getYearToSconds(1527684151664);
 * => 
 * ```
 */
function getYearToSeconds(totalSeconds) {
  /** 格式化的字符串 */
  let timeStr = '';

  // 年
  const year = Math.floor(totalSeconds / (365 * 24 * 60 * 60));
  if (year > 0) {
    timeStr += `${year}年`;
  }

  // 天
  const lastDay = Math.floor(totalSeconds % (365 * 24 * 60 * 60));  
  const day = Math.floor(lastDay / (24 * 60 * 60));
  
  if (day > 0) {
    timeStr += `${day}天`;
  }

  // 小时
  const lastHour = Math.floor(totalSeconds % (24 * 60 * 60));
  const hours = Math.floor(lastHour / (60 * 60));
  if (hours > 0) {
    timeStr += `${hours}时`;
  }

  // 分
  const lastMinutes = Math.floor(lastHour % (60 * 60));
  const minutes = Math.floor(lastMinutes / 60);

  if (minutes > 0) {
    timeStr += `${minutes}分`;
  }

  // 秒
  const lastSeconds = Math.floor(lastMinutes % 60);
  const seconds = lastSeconds;
  if (seconds > 0) {
    timeStr += `${seconds}秒`;
  }

  console.log(timeStr);
}
getYearToSeconds(17240);

// new Date().getTime() 返回值的单位是毫秒数
const initToNowSeconds = Math.floor(1527684151664 / 1000);
getYearToSeconds(initToNowSeconds);

// 5月30天

// 某一天距离现在的时间搓
const date1 = new Date().getTime() - new Date('2015/05/30 21:00:00');
// console.log(date1);
getYearToSeconds( Math.floor(94694453666 / 1000) );

// 指定某两天的间隔
const g1 = new Date('2019/05/30 21:00:00') - new Date('2017/05/30 21:00:00');
getYearToSeconds( Math.floor( g1 / 1000 ) )

// 1527685963162 2018/05/30 21:13
// 1527686110907 2018/05/30 21:15
getYearToSeconds( Math.floor((1527686110907 - 1527685963162) / 1000) );

/**
 * 参考资料
 * + [闰年-百度百科](https://baike.baidu.com/item/%E9%97%B0%E5%B9%B4/27098?fr=aladdin) 
 * + [MDN-Date](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date)
 */

(概念) ECMAScript 包含 DOM BOM ?

我的理解:

JS 是包含 DOM BOM ECMAScript。ECMAScript 定义了 JS 的基本语法和用法,但是不包括DOM、BOM

DOM 和 BOM 并不属于 JavaScript(ECMAScript ) 语言的一部分。DOM 和 BOM 是 JavaScript 的运行平台(浏览器)提供的,比如在 nodejs 中就没有 DOM 和 BOM。

JavaScript 类型分为 2 大类:原生类型和对象类型。而 DOM 和 BOM 都是对象类型。

比如 html 中的段落 p 映射为 JavaScript 对象是 HTMLParagraphElement,顾名思义 Paragraph 就是英语“段落”的意思。
我们看看 HTMLParagraphElement 的继承关系:

参考文章

知乎: ECMAScript中的对象和DOM BOM对象是一个概念么?

事件回调函数中的this

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
    <button id="btn1">btn1</button>
    <button id="btn2">btn2</button>
    <button id="btn3">btn3</button>
    <button id="btn4">btn4</button>

  <script>
    function autoRun() {
      const oBtn1 = document.getElementById('btn1');
      oBtn1.addEventListener('click', function() {
        console.log('oBtn1 this:', this); // => Dom #btn1
      });
    }
    autoRun();

    const oBtn2 = document.getElementById('btn2');
    oBtn2.addEventListener('click', function() {
      console.log('oBtn2 this:', this); // => Dom #btn2
    });


    const obj1 = {x: 1, y: 2};
    obj1.onClick = function() {
      console.log('oBtn3 this:', this); // => Dom #btn3
    }
    const oBtn3 = document.getElementById('btn3');
    oBtn3.addEventListener('click', obj1.onClick);

    function autoGo() {
      const a = 111;
      const obj2 = {x: 2};
      obj2.onClick = function() {
        console.log('oBtn4 this:', this);
      }
      const oBtn4 = document.getElementById('btn4');
      oBtn4.addEventListener('click', obj2.onClick); // => Dom #btn4
    }
    autoGo();

  </script>
</body>

</html>

02. JS基础:数组(array)的方法

JS基础:数组相关的知识点

更新时间 是否原创
2019/01/10 原创

构造函数方式 创建数组语法

// method1
new Array()

// method2
new Array(size)

// method3
new Array(elem0, elem1, ..., elem)

参数

参数 说明 类型 可选值 默认值
无参数 创建一个长度为0的空数组 - - -
只一个参数 创建数组的长度, Array.length = size number - -
多参数: 参数 返回的数组填充的项 any - -

示例

const arr1 = new Array();
const arr2 = new Array(1);
const arr3 = new Array(0, 1, 2, 3, 4);
const arr4 = new Array(0, [1, 3], [4, 5]);

console.log(arr1);  // []
console.log(arr2);  // [empty]
console.log(arr3);  // [0, 1, 2, 3, 4];
console.log(arr4);  // [0, [1, 3], [4, 5]];

上面代码分析:不传参数,返回的是一个空数组;传了一个参数,且为数字,返回一个指定长度的空数组。空数组每一项被 undefined 填充;传了多个参数,且每项都为数字,返回一个包含所有参数的数组; 传了多个参数且某项为数组时,会将数组作为一个整体,放入返回的新数组中。

const arr = new Array(5).fill(1);
console.log(arr);  // [1, 1, 1, 1, 1]

上面代码分析: 通过上面代码,可以快速创建指定长度和指定每项内容的数组。


直接量 语法创建数组

ES3规定了数组的直接量语法

let a = [1, true, 'abc'];
let b = [a[0], a[0]*2, f(x)];

返回值

创建后的数组

异常

当size为负值,或者大于 2^32 - 1 时会抛出 RangeError 异常


属性
length

一个可读/写的整数,用来指明数组中的元素个数。数组不连续时,length = 数组中最后一个元素的序号加一。改变length值会裁减或扩充数组

描述

读: 返回数组的长度。写(赋值): 改变数组的长度

示例

let a = new Array();                       // a.length 初始化为 0

let b = new Array(3);                      // b.length 初始化为 3

let c = new Array('one', 'two', 'three');  // c.length 初始化为 3
c[4] = 'four';                             // c.length 更新为 5
c[5] = 'blastoff'; // c.length 变成 11     // c.length 更新为 6
console.log(c);                            // ["one", "two", "three", empty, "four", "blastoff"]
console.log(c[3]);                         // undefined 空的项被 undefined 填充 

ES5 以前的方法(包含ES5)

方法列表


给 Array 动态扩展自定义方法(还需理解,这里可以选择性忽略)

    // @desc 通过给Function.prototype增加方法来使得该方法对所有函数都可用
    //       通过给Function.prototype增加一个method方法,我们下次给对象增加
    //       方法的时候就不必键入prototype这一个字符,省去了麻烦,
    //       这样添加以后所有的构造函数(这里是给函数添加的方法)由于构造函数
    //       也是函数,所以所有的函数都可以用
    // @example 
    // ```
    // Array.method('eReduce', func)
    // ```
    if (Function.prototype.method !== 'function') {
        Function.prototype.method = function(name, func) {
            // TODO: 扩展函数的功能
        }
    }

Array.prototype.concat()

把元素衔接到数组中

语法

array.concat(value, ...)

参数

参数 说明 类型 可选值 默认值
value 衔接的数组, 数字类型就表示也表示数组,非必需 `number array` -
... 若干需要衔接的数组,非必需 `number array` -

返回值

一个 新数组,不改变 数组,包含 array 的元素,以及衔接的新元素

描述

产生一个新数组,包含一份浅复制(shadow copy)并把一个或多个参数item附加在其后

示例

// 字面量数组方式
let a = [1, 2, 3];
let a1 = a.concat(4, 5);            // => a1 = [1, 2, 3, 4, 5];        a不变
let a2 = a.concat([4, 5]);          // => a2 = [1, 2, 3, 4, 5];         a不变
let a3 = a.concat([4, 5], [6, 7]);  // => a3 = [1, 2 ,3 ,4, 5, 6, 7];   a不变
let a4 = a.concat(4, [5, [6, 7]]);  // => a4 = [1, 2, 3, 4, 5, [6, 7]]; a不变

通过以上代码发现:concat的返回结果为一个新的数组,不改变源数组

// 数组变量方式
const a = [1, 2, 3],
      b = [4, 5],
      c = [6, [0, 0]];

let d = [],
    e = [];

d = a.concat(a, b);

console.log(d); // [ 1, 2, 3, 1, 2, 3, 4, 5 ]
console.log(a); // [ 1, 2, 3 ]
console.log(b); // [ 4, 5 ]

以上代码发现:d为 [ 1, 2, 3, 1, 2, 3, 4, 5 ]。 a, b数组并未发生改变,d中由两个a一个b 构成

const a = [1, 2, 3],
      b = [4, 5],
      c = [6, [0, 0]];

let d = [],
    e = [];
e = a.concat(b, c);

console.log(e); // [ 1, 2, 3, 4, 5, 6, [ 0, 0 ] ] 
console.log(c); // [ 6, [ 0, 0 ] ]

以上代码发现:concat 方法里面的参数如果存在 多维数组,将多维数组作为整体组合到新数组中.

功能类似的其他方法
Array.join() Array.push() Array.splice()


Array.prototype.every() ES5

测试 断言函数:predicateFn 是否对每个元素为真

语法

  • array.every(predicateFn)
  • array.every(predicateFn, o)

参数

参数 是否必需 描述
predicateFn 非必需 用来测试数组元素的断言函数 predicateFn(array[i], i, array)
o 非必需 调用断言函数predicateFn时可选的 this

返回值

全部满足断言函数返回true,否则返回false;不改变当前数组

概述

文字待整理

示例
let result1 = [1, 2, 3].every( x => x < 5 ); // => result1 = true
let result2 = [1, 2, 3].every( x => x < 3 ); // => result2 = false
let result3 = [].every( x => false ); // true: []总返回true
关联 BackTop

Array.filter() Array.forEach() Array.some()

Array.prototype.filter() ES5

返回通过断言的数组元素

语法
  • array.filter(predicate)
  • array.filter(predicate, o)
参数

predicate

判断array中每项是否需要包含在返回数组中的调用函数 predicate(array[i], i, array)

o

调用断言函数predicate时可选的this值

返回值

一个新数组,只包含那些让predicate返回真值的数组元素

描述

文字待整理

示例
let result = [1, 2, 3].filter( x => x > 1 ) // => result = [2, 3];
关联 BackTop

Array.every() Array.forEach() Array.indexOf() Array.map() Array.reduce()

Array.prototype.forEach() ES5

为每个数组元素调用一个函数

语法
  • array.forEach(f)
  • array.forEach(f, o)
参数

f

为array的每项调用的函数 array.forEach(array[i], i, array)

o

调用f时的可选this值

返回值

该方法无返回值

描述

文字待整理

示例
let a = [1, 2, 3];
a.forEach( (x, i, a) => { a[i]++; }); // a = [1, 4, 9];
关联 BackTop

Array.every() Array.filter() Array.indexOf() Array.map() Array.reduce()

Array.prototype.indexOf() ES5

查找数组

语法
  • array.indexOf(value)
  • array.indexOf(value, start)
参数

value

要在array中查找的值

start

开始查找的可选数组序号。如果省略则为0

返回值

从start开始最小序号值,该序号值处的array元素与value全等。如果不存在匹配的元素,返回-1

概述

文字待整理

示例
let result1 = ['a', 'b', 'c'].indexOf('b'); // => 1 即 result1 == 1
let result2 = ['a', 'b', 'c'].indexOf('d'); // => -1 即 result2 == -1
let result3 = ['a', 'b', 'c'].indexOf('a', 1); // => -1 即 result3 == -1
关联 BackTop

Array.lastIndexOf() String.indexOf()

Array.prototype.join()

将数组元素衔接为字符串

语法
  • array.join()
  • array.join(separator)
参数

spearator

在返回的字符串中,用来分割数组某个元素与下一个元素的可选字符或字符串。如果省略,默认是英文逗号(,)

返回值

一个字符串。将array的每个元素转换为字符串。然后用separator字符串分隔开。最后衔为返回的字符串

描述

文字待整理

示例
let a = new Array(1, 2, 3, 'testing');
s = a.join('+'); // => s == '1+2+3+testing'
关联 BackTop

String.split()

Array.prototype.lastIndexOf() ES5

反向查找数组

语法
  • array.lastIndexOf(value)
  • array.lastIndexOf(value, start)
参数

value

要在array中查找的值

start

开始查找的可选数组序号。如果省略,则从最后一个元素开始查找

返回值

一个小于等于start的最大序号值,该序号值处的array元素与value相等。如果不存在匹配的元素时,返回-1。

描述

文字待整理

示例
关联 BackTop

Array.indexOf() String.lastIndexOf()

Array.prototype.map() ES5

从数值元素中计算新值

语法
  • array.map(f)
  • array.map(f, 0)
参数

f

为array的每个元素调用的函数。它的返回值会成为数组的元素

o

f调用时可选的this值

返回值

一个新数组,由函数f计算出的元素组成

描述

文字待整理

示例
let result = [1, 2, 3].map( x => x*x ); // => result == [1, 4, 9]
关联 BackTop

Array.every() Array.filter() Array.forEach() Array.indexOf() Array.reduce()

Array.prototype.pop()

移除并返回数组的最后一个元素

语法
  • array.pop()
返回值

array的最后一元素

描述

文字待整理

示例
// pop()与伴随的push()方法。可以提供先进后出(FILO)的栈功能
let stack = []; // stack: []
let s1 = stack.push(1, 2); // stack: [1, 2] s1 == 2
let s2 = stack.pop(); // stack: [1] s2 == 2
let s3 = stack.push([4, 5]); // stack: [1, [4, 5]] s3 == 2
let s4 = stack.pop(); // stack: [1] s4 == [4, 5]
let s5 = stack.pop(); // stack: []  s5 == 1
关联 BackTop

Array.push()

Array.prototype.push()

给数组追加元素

语法

array.push(value, ...);

参数

vlaue, ...

追加到array尾部的一个或多个值

返回值

把指定值追加到数组后数组的新长度

描述

文字待整理

关联 BackTop

Array.pop()

Array.prototype.reduce() ES5

从数组元素中计算出一个值

语法
  • array.reduce(f)
  • array.reduce(f, inital)
参数

f

一个函数,可以合并两个值(比如两个数组元素),并返回一个‘缩减’的新值

inital

用来缩减数组的可选初始值。如果指定该参数, reduce()的行为会像是把该参数插入array的头部一样

返回值

数组的简化值,该值是最后一次调用f时的返回值

描述

文字待整理

示例
let r = [1, 2, 3, 4].reduce((x, y) => x*y); // => r == 24
关联 BackTop

Array.forEach() Array.map() Array.reduceRight()

Array.prototype.reduceRight() ES5

从右到做缩减数组

语法
  • array.reduceRight(f)
  • array.reduceRight(f, initial)
参数

f

一个函数,可以合并两个值(比如两个数组元素),并返回一个“缩减”的新值

initial

用来缩减数组的可选初始值,如果指定该参数,reduceRight()的行为就会是把该参数插入array的尾部一样

返回值

数组的缩减值,该值是最有一次调用f时的返回值

描述

文字待整理

示例
let r = [2, 10, 60].reduceRight((x, y) => x / y); // => r == 3
关联 BackTop

Array.reduce()

Array.prototype.reverse()

颠倒数组中的元素顺序

语法
  • array.reverse()
描述

文字待整理

示例 BackTop
let a = new Array(1, 2, 3); // => a[0] == 1, a[2] == 3;
a.reverse(); // => a[0] == 3, a[2] == 1;

Array.prototype.shift()

移除数组的第一个元素

语法
  • array.shift()
返回值

数组原来的第一元素

描述

文字待整理

示例
let a = [1, [2, 3], 4];
let s = a.shift(); // => s == 1; a == [[2, 3], 4];
let s2 = a.shift(); // => s2 == [2, 3]; a == [4]
关联 BackTop

Array.pop() Array.unshift()

Array.prototype.slice()

返回数组的一部分

语法

array.slice(start, end)

参数

start

数组片段开始处的数组序号。如果为负数,则表示从数组的尾部开始计算,也就是说-1代表最有一个元素, -2代表倒数第二个元素。以此类推

end

数组片段结束处的后一个元素的数组序号。如果没有指定,该片段会包含从start开始到数组尾部的所有数组元素。如果为负数,则表示从数组的尾部开始计算

返回值

一个新数组,包含array中从start一直到end直接的所有元素(包含start指定的元素,但不包含end指定的元素)

描述

文字待整理

示例
let a = [1, 2, 3, 4, 5];
let s1 = a.slice(0, 3); // => s1 == [1, 2, 3]
let s2 = a.slice(3); // s2 == [4, 5]
let s3 = a.slice(1, -1); // s3 == [2, 3, 4]
let s4 = a.slice(-3, -2); // => s4 == [3] ;但在ie4下有误,返回[1, 2, 3]
bug

在IE4中start参数不能为负数, 在IE的后续版本中已经修复该bug

关联 BackTop

Array.splice()

Array.prototype.some() ES5

测试是否有元素满足断言函数

语法
  • array.some(predicate)
  • array.some(predicate, o)
参数

predicate

用来测试数组元素的断言函数

o

调用predicate时的可选this值

返回值

如果array中至少有一个元素调用predicate时返回真值,则返回true.如果所有元素调用predicate时都返回假值,则返回false

描述

文字待整理

示例
let s1 = [1, 2, 3].some(x => x > 5); // => s1 == false
let s2 = [1, 2, 3].some(x => x > 2); // => s2 == true
let s3 = [1, 2, 3].some(x => false); // => s3 = false; []总是返回false
关联 BackTop

Array.every() Array.filter() Array.forEach()

Array.prototype.sort()

对数组元素进行排序

语法
  • array.sort()
  • array.sort(orderFunc)
参数

orderFunc

用来指定如何排序的可选函数

返回值

该数组的引用。注意是在原数组中进行排序,没有新建数组

描述

文字待整理

示例 BackTop
// 用来数组排序的排序函数
function numberOreder(a, b) { return a - b };
a = new Array(33, 4, 111, 222);
let s1 = a.sort(); // => s1 == [1111, 222, 33, 4] 字母排序
let s2 = a.sort( numberOrder ); // => s2 == [4, 33, 222, 1111] 数值排序

Array.prototype.splice()

插入、删除或替换数组元素

语法
  • array.splice(start, deleteCount, value, ...)
参数

start

开始插入和(或)删除处的数组元素的序号

deleteCount

要删除的元素个数,从start开始,并包含start开始出的元素,如果指定为0,表示插入元素,而不是删除元素

value, ...

要插入数组中的零个或多个值,从start序号处开始插入

返回值

如果从array中删除了元素,则返回一个新数组,包含这些删除的元素

示例
var a = [1, 2, 3, 4, 5, 6, 7, 8];
let s1 = a.splice(1, 2); // => s1 == [2, 3]; a == [1, 4, 5, 6, 7, 8];
let s2 = a.splice(1, 1); // => s2 == [4]; a == [1, 5, 6, 7, 8];
let s3 = a.splice(1,0,2,3) // => s3 == []; a == [1,2,3,5,6,7,8]
关联 BackTop

Array.slice()

Array.prototype.toLocalString()

将数组转化为本地化字符串

语法

array.toLocalString()

返回值

数组的本地化字符串表示

异常

TypeError

调用该方法时,如果对象不是Array,则抛出该异常

描述

文字待整理

关联 BackTop

Array.toString() Object.toLocalString()

Array.prototype.toString()

将数组转化为字符串

语法

array.toString()

返回值

array的字符串表示

异常

TypeError

调用该方法时,如果对象不是Array,则抛出该异常

描述

文字待整理

关联 BackTop

Array.toLocalString() Object.toString()

Array.prototype.unshift()

在数组头部插入元素

语法

array.unshift(value, ...)

参数

value, ...

要插入array头部的一个或多个值

返回值

数组的新长度

描述

文字待整理

示例
// unshift()经常与shift()一起使用
let a = [];
let s1 = a.unshift(1); // => s1 == 1; a == [1]
let s2 = a.unshift(22); // => s2 == 2; a == [22, 1]
let s3 = a.shift(); // => s3 == 1; a == [1];
let s4 = a.unshift(33, [4, 5]); // => s4 == 3 ; a == [33, [4, 5], 1];
关联 BackTop

Array.shift()

资源参考


  • ES6数组的扩展

扩展运算符

含义

扩展运算符(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;
}

var numbers = [4, 38];
add(...numbers) // 42

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

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

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

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

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

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

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

替代数组的 apply 方法

由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。

// ES5 的写法
function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f.apply(null, args);

// ES6的写法
function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f(...args);

下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法。

// ES5 的写法
Math.max.apply(null, [14, 3, 77])

// ES6 的写法
Math.max(...[14, 3, 77])

// 等同于
Math.max(14, 3, 77);

上面代码中,由于 JavaScript 不提供求数组最大元素的函数,所以只能套用Math.max函数,将数组转为一个参数序列,然后求最大值。有了扩展运算符以后,就可以直接用Math.max了。

另一个例子是通过push函数,将一个数组添加到另一个数组的尾部。

// ES5的 写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);

// ES6 的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);

上面代码的 ES5 写法中,push方法的参数不能是数组,所以只好通过apply方法变通使用push方法。有了扩展运算符,就可以直接将数组传入push方法。

下面是另外一个例子。

// ES5
new (Date.bind.apply(Date, [null, 2015, 1, 1]))
// ES6
new Date(...[2015, 1, 1]);

扩展运算符的应用

(1)合并数组

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

// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]

var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];

// ES5的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

(2)与解构赋值结合

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

// 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];
// 报错

(3)函数的返回值

JavaScript 的函数只能返回一个值,如果需要返回多个值,只能返回数组或对象。扩展运算符提供了解决这个问题的一种变通方法。

var dateFields = readDateFields(database);
var d = new Date(...dateFields);

上面代码从数据库取出一行数据,通过扩展运算符,直接将其传入构造函数Date

(4)字符串

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

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

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

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

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

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

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

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

let str = 'x\uD83D\uDE80y';

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

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

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

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

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

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

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

对于那些没有部署 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 函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。

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

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

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

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

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

Array.from()

Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。

下面是一个类似数组的对象,Array.from将它转为真正的数组。

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

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组。

// NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).forEach(function (p) {
  console.log(p);
});

// arguments对象
function foo() {
  var args = Array.from(arguments);
  // ...
}

上面代码中,querySelectorAll方法返回的是一个类似数组的对象,可以将这个对象转为真正的数组,再使用forEach方法。

只要是部署了Iterator接口的数据结构,Array.from都能将其转为数组。

Array.from('hello')
// ['h', 'e', 'l', 'l', 'o']

let namesSet = new Set(['a', 'b'])
Array.from(namesSet) // ['a', 'b']

上面代码中,字符串和Set结构都具有Iterator接口,因此可以被Array.from转为真正的数组。

如果参数是一个真正的数组,Array.from会返回一个一模一样的新数组。

Array.from([1, 2, 3])
// [1, 2, 3]

值得提醒的是,扩展运算符(...)也可以将某些数据结构转为数组。

// arguments对象
function foo() {
  var args = [...arguments];
}

// NodeList对象
[...document.querySelectorAll('div')]

扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。Array.from方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。

Array.from({ length: 3 });
// [ undefined, undefined, undefined ]

上面代码中,Array.from返回了一个具有三个成员的数组,每个位置的值都是undefined。扩展运算符转换不了这个对象。

对于还没有部署该方法的浏览器,可以用Array.prototype.slice方法替代。

const toArray = (() =>
  Array.from ? Array.from : obj => [].slice.call(obj)
)();

Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);

Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]

下面的例子是取出一组DOM节点的文本内容。

let spans = document.querySelectorAll('span.name');

// map()
let names1 = Array.prototype.map.call(spans, s => s.textContent);

// Array.from()
let names2 = Array.from(spans, s => s.textContent)

下面的例子将数组中布尔值为false的成员转为0

Array.from([1, , 2, , 3], (n) => n || 0)
// [1, 0, 2, 0, 3]

另一个例子是返回各种数据的类型。

function typesOf () {
  return Array.from(arguments, value => typeof value)
}
typesOf(null, [], NaN)
// ['object', 'object', 'number']

如果map函数里面用到了this关键字,还可以传入Array.from的第三个参数,用来绑定this

Array.from()可以将各种值转为真正的数组,并且还提供map功能。这实际上意味着,只要有一个原始的数据结构,你就可以先对它的值进行处理,然后转成规范的数组结构,进而就可以使用数量众多的数组方法。

Array.from({ length: 2 }, () => 'jack')
// ['jack', 'jack']

上面代码中,Array.from的第一个参数指定了第二个参数运行的次数。这种特性可以让该方法的用法变得非常灵活。

Array.from()的另一个应用是,将字符串转为数组,然后返回字符串的长度。因为它能正确处理各种Unicode字符,可以避免JavaScript将大于\uFFFF的Unicode字符,算作两个字符的bug。

function countSymbols(string) {
  return Array.from(string).length;
}

Array.of()

Array.of方法用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。

Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]

上面代码中,Array方法没有参数、一个参数、三个参数时,返回结果都不一样。只有当参数个数不少于2个时,Array()才会返回由参数组成的新数组。参数个数只有一个时,实际上是指定数组的长度。

Array.of基本上可以用来替代Array()new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。

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

Array.of总是返回参数值组成的数组。如果没有参数,就返回一个空数组。

Array.of方法可以用下面的代码模拟实现。

function ArrayOf(){
  return [].slice.call(arguments);
}

数组实例的 copyWithin()

数组实例的copyWithin方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。

Array.prototype.copyWithin(target, start = 0, end = this.length)

它接受三个参数。

  • target(必需):从该位置开始替换数据。
  • start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。
  • end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。

这三个参数都应该是数值,如果不是,会自动转为数值。

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

上面代码表示将从3号位直到数组结束的成员(4和5),复制到从0号位开始的位置,结果覆盖了原来的1和2。

下面是更多例子。

// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]

// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]

// 将3号位复制到0号位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}

// 将2号位到数组结束,复制到0号位
var i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]

// 对于没有部署 TypedArray 的 copyWithin 方法的平台
// 需要采用下面的写法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]

数组实例的 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

这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。

另外,这两个方法都可以发现NaN,弥补了数组的IndexOf方法的不足。

[NaN].indexOf(NaN)
// -1

[NaN].findIndex(y => Object.is(NaN, y))
// 0

上面代码中,indexOf方法无法识别数组的NaN成员,但是findIndex方法可以借助Object.is方法做到。

数组实例的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号位之前结束。

数组实例的 entries(),keys() 和 values()

ES6 提供三个新的方法——entries()keys()values()——用于遍历数组。它们都返回一个遍历器对象(详见《Iterator》一章),可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历。

let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']

数组实例的 includes()

Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法。

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true

该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。

[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true

没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。

if (arr.indexOf(el) !== -1) {
  // ...
}

indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判。

[NaN].indexOf(NaN)
// -1

includes使用的是不一样的判断算法,就没有这个问题。

[NaN].includes(NaN)
// true

下面代码用来检查当前环境是否支持该方法,如果不支持,部署一个简易的替代版本。

const contains = (() =>
  Array.prototype.includes
    ? (arr, value) => arr.includes(value)
    : (arr, value) => arr.some(el => el === value)
)();
contains(['foo', 'bar'], 'baz'); // => false

另外,Map 和 Set 数据结构有一个has方法,需要注意与includes区分。

  • Map 结构的has方法,是用来查找键名的,比如Map.prototype.has(key)WeakMap.prototype.has(key)Reflect.has(target, propertyKey)
  • Set 结构的has方法,是用来查找值的,比如Set.prototype.has(value)WeakSet.prototype.has(value)

数组的空位

数组的空位指,数组的某一个位置没有任何值。比如,Array构造函数返回的数组都是空位。

Array(3) // [, , ,]

上面代码中,Array(3)返回一个具有3个空位的数组。

注意,空位不是undefined,一个位置的值等于undefined,依然是有值的。空位是没有任何值,in运算符可以说明这一点。

0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false

上面代码说明,第一个数组的0号位置是有值的,第二个数组的0号位置没有值。

ES5 对空位的处理,已经很不一致了,大多数情况下会忽略空位。

  • forEach(), filter(), every()some()都会跳过空位。
  • map()会跳过空位,但会保留这个值
  • join()toString()会将空位视为undefined,而undefinednull会被处理成空字符串。
// forEach方法
[,'a'].forEach((x,i) => console.log(i)); // 1

// filter方法
['a',,'b'].filter(x => true) // ['a','b']

// every方法
[,'a'].every(x => x==='a') // true

// some方法
[,'a'].some(x => x !== 'a') // false

// map方法
[,'a'].map(x => 1) // [,1]

// join方法
[,'a',undefined,null].join('#') // "#a##"

// toString方法
[,'a',undefined,null].toString() // ",a,,"

ES6 则是明确将空位转为undefined

Array.from方法会将数组的空位,转为undefined,也就是说,这个方法不会忽略空位。

Array.from(['a',,'b'])
// [ "a", undefined, "b" ]

扩展运算符(...)也会将空位转为undefined

[...['a',,'b']]
// [ "a", undefined, "b" ]

copyWithin()会连空位一起拷贝。

[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]

fill()会将空位视为正常的数组位置。

new Array(3).fill('a') // ["a","a","a"]

for...of循环也会遍历空位。

let arr = [, ,];
for (let i of arr) {
  console.log(1);
}
// 1
// 1

上面代码中,数组arr有两个空位,for...of并没有忽略它们。如果改成map方法遍历,空位是会跳过的。

entries()keys()values()find()findIndex()会将空位处理成undefined

// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]

// keys()
[...[,'a'].keys()] // [0,1]

// values()
[...[,'a'].values()] // [undefined,"a"]

// find()
[,'a'].find(x => true) // undefined

// findIndex()
[,'a'].findIndex(x => true) // 0

由于空位的处理规则非常不统一,所以建议避免出现空位。

01. JavaScript 语句

JavaScript 语句

表达式语句

循环语句(looping statement)

JavaScript 存在四种循环语句:whiledo/whileforfor/in

JS units -- 常用工具函数整理

  • 分享前端开发常用代码片段
    • 图片预加载
    • 检查图像是否加载
    • 自动修复破坏的图像
    • 悬停切换
    • 淡入淡出/显示隐藏
    • 鼠标滚轮
    • 鼠标坐标
    • 禁止移动端浏览器页面滚动
    • 阻止默认行为
    • 阻止冒泡
    • 检测浏览器是否支持svg
    • 检测浏览器是否支持canvas
    • 检测是否是微信浏览器
    • 检测是否移动端及浏览器内核
    • 检测是否电脑端/移动端
    • 检测浏览器内核
    • 强制移动端页面横屏显示
    • 电脑端页面全屏显示
    • 获得/失去焦点
    • 获取上传文件大小
    • 限制上传文件类型
    • 正则表达式
    • 限制字符数
    • 验证码倒计时
    • 时间倒计时
    • 倒计时跳转
    • 时间戳、毫秒格式化
    • 当前日期
    • 判断周六/周日
    • AJAX调用错误处理
    • 链式插件调用

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.