博客 & 代码整理目录 & 书签整理 & 文章整理
文章整理都通过 issues 方式整理
规划
- 读书笔记, 只建一个汇总(目录)issue, 在专门的书籍库中保留更详细的读书笔记, 汇总issue中尽量包含详细的关键字,通过汇总issue, 找到其他的内容
:fist: 博客 & 前端代码内容
文章整理都通过 issues 方式整理
更新时间 | 创建时间 | 是否原创 |
---|---|---|
2019/01/15 | 2018/10/22 | 部分原创 |
Flex布局中,子元素不能height填充满--(详细说明)
解决方案: 使用flex的父元素(flex-container) 设置 height=100%
子元素即使设置了 with
但是没有设置 flex
宽度,会导致元素挤压。
有问题效果:
有问题代码:( Tips: 项目属于微信小程序开发,故使用了单位rpx
)
.parent_element {
height:40rpx;
line-height:40rpx;
font-size:0;
border-bottom:1px solid #153369;
padding-bottom:20rpx;
font-weight:400;
width:auto;
display:flex;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
word-wrap:normal;
word-wrap:break-word;
word-break:break-all;
}
.child1_element {
display:inline-block;
width:37rpx;
height:100%;
}
.child2_element {
width:auto;
font-size:15px;
color:#060946;
margin-left:10rpx;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
word-wrap:normal;
word-wrap:break-word;
word-break:break-all;
}
问题分析: 由于父元素使用了flex
布局。子元素 .child2_element
使用了 width: auto
那么是不是这个造成的呢?实际测试发现 width: auto
并不是造成这个问题的原因。子元素 .child2_element
添加 flex: 1
可以解决
解决方案:
.child2_element {
flex: 1;
}
References
-- 参考,关联操作
#xxx
xxx为issue
的编号username/repository#xxx
像这样 Jesonhu/keep#4
Cannot read property 'i18nDriver' of undefined
版本 | 报错原因 |
---|---|
2.1.0-beta.11 | 2.1.0-beta.09 的配置在 2.1.0-beta.11 中使用, 而 beta.11 改了使用方式 |
先进行注册 Vue.use(),然后再进行相关配置
# 报错码 内容描述
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Arg list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
#define EDEADLK 35 /* Resource deadlock would occur */
#define ENAMETOOLONG 36 /* File name too long */
#define ENOLCK 37 /* No record locks available */
#define ENOSYS 38 /* Function not implemented */
#define ENOTEMPTY 39 /* Directory not empty */
#define ELOOP 40 /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define ENOMSG 42 /* No message of desired type */
#define EIDRM 43 /* Identifier removed */
#define ECHRNG 44 /* Channel number out of range */
#define EL2NSYNC 45 /* Level 2 not synchronized */
#define EL3HLT 46 /* Level 3 halted */
#define EL3RST 47 /* Level 3 reset */
#define ELNRNG 48 /* Link number out of range */
#define EUNATCH 49 /* Protocol driver not attached */
#define ENOCSI 50 /* No CSI structure available */
#define EL2HLT 51 /* Level 2 halted */
#define EBADE 52 /* Invalid exchange */
#define EBADR 53 /* Invalid request descriptor */
#define EXFULL 54 /* Exchange full */
#define ENOANO 55 /* No anode */
#define EBADRQC 56 /* Invalid request code */
#define EBADSLT 57 /* Invalid slot */
#define EDEADLOCK EDEADLK
#define EBFONT 59 /* Bad font file format */
#define ENOSTR 60 /* Device not a stream */
#define ENODATA 61 /* No data available */
#define ETIME 62 /* Timer expired */
#define ENOSR 63 /* Out of streams resources */
#define ENONET 64 /* Machine is not on the network */
#define ENOPKG 65 /* Package not installed */
#define EREMOTE 66 /* Object is remote */
#define ENOLINK 67 /* Link has been severed */
#define EADV 68 /* Advertise error */
#define ESRMNT 69 /* Srmount error */
#define ECOMM 70 /* Communication error on send */
#define EPROTO 71 /* Protocol error */
#define EMULTIHOP 72 /* Multihop attempted */
#define EDOTDOT 73 /* RFS specific error */
#define EBADMSG 74 /* Not a data message */
#define EOVERFLOW 75 /* Value too large for defined data type */
#define ENOTUNIQ 76 /* Name not unique on network */
#define EBADFD 77 /* File descriptor in bad state */
#define EREMCHG 78 /* Remote address changed */
#define ELIBACC 79 /* Can not access a needed shared library */
#define ELIBBAD 80 /* Accessing a corrupted shared library */
#define ELIBSCN 81 /* .lib section in a.out corrupted */
#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
#define ELIBEXEC 83 /* Cannot exec a shared library directly */
#define EILSEQ 84 /* Illegal byte sequence */
#define ERESTART 85 /* Interrupted system call should be restarted */
#define ESTRPIPE 86 /* Streams pipe error */
#define EUSERS 87 /* Too many users */
#define ENOTSOCK 88 /* Socket operation on non-socket */
#define EDESTADDRREQ 89 /* Destination address required */
#define EMSGSIZE 90 /* Message too long */
#define EPROTOTYPE 91 /* Protocol wrong type for socket */
#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
#define EPFNOSUPPORT 96 /* Protocol family not supported */
#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
#define EADDRINUSE 98 /* Address already in use */
#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
#define ENETDOWN 100 /* Network is down */
#define ENETUNREACH 101 /* Network is unreachable */
#define ENETRESET 102 /* Network dropped connection because of reset */
#define ECONNABORTED 103 /* Software caused connection abort */
#define ECONNRESET 104 /* Connection reset by peer */
#define ENOBUFS 105 /* No buffer space available */
#define EISCONN 106 /* Transport endpoint is already connected */
#define ENOTCONN 107 /* Transport endpoint is not connected */
#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS 109 /* Too many references: cannot splice */
#define ETIMEDOUT 110 /* Connection timed out */
#define ECONNREFUSED 111 /* Connection refused */
#define EHOSTDOWN 112 /* Host is down */
#define EHOSTUNREACH 113 /* No route to host */
#define EALREADY 114 /* Operation already in progress */
#define EINPROGRESS 115 /* Operation now in progress */
#define ESTALE 116 /* Stale NFS file handle */
#define EUCLEAN 117 /* Structure needs cleaning */
#define ENOTNAM 118 /* Not a XENIX named type file */
#define ENAVAIL 119 /* No XENIX semaphores available */
#define EISNAM 120 /* Is a named type file */
#define EREMOTEIO 121 /* Remote I/O error */
#define EDQUOT 122 /* Quota exceeded */
#define ENOMEDIUM 123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
#define ECANCELED 125 /* Operation Canceled */
#define ENOKEY 126 /* Required key not available */
#define EKEYEXPIRED 127 /* Key has expired */
#define EKEYREVOKED 128 /* Key has been revoked */
#define EKEYREJECTED 129 /* Key was rejected by service */
/* for robust mutexes */
#define EOWNERDEAD 130 /* Owner died */
#define ENOTRECOVERABLE 131 /* State not recoverable */
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
// 执行顺序
// script start
// script end
// promise1
// promise2
// setTimeout
为了搞清楚缘由,你需要明白 事件循环(event loop)
是如何处理 任务(tasks)
和 微任务(microtasks)
的.当这些名词第一次出现的时候,你可能会感到头疼,没关系,深呼吸...
每一个线程都拥有属于自己的事件循环(event loop)
,也就意味着每一个web worker都会存在自有的事件循环,这样它就能独立运作互不干扰。然而对于同源窗口而言他们共享一个事件循环(event loop),这样他们就可以互相通信了(译者注:根据HTML5.2规范,事件循环分两种,一种是浏览器上下文的,一种是web worker的)。事件循环(event loop)循环执行着进入队列的任务。一个事件循环存在多个任务源,这确保了任务源的执行顺序(译者注:同一个任务源的任务将被添加到相同任务队列,不同任务源的任务可能被添加到不同任务队列),但是在每一次的循环中,浏览器会自主选择哪个源的任务优先执行,这确保了一些性能敏感的任务的优先级,比如用户输入。
任务(tasks,译者注:也叫macro-task)是被调度的,这样浏览器就能很好的将这些任务注入到JavaScript或者Dom中,并且确保这些操作有序执行。在任务之间,浏览器可能会更新视图,从鼠标点击到事件回调,再到html的渲染,以及上面例子提到的setTimeout这些都需要任务调度。
setTimeout等待了给定的延迟时间之后就会为它的回调创建一个新的任务。这就是为什么setTimeout打印在script end之后,因为script end是第一个任务的一部分,而setTimeout对应的是另一个任务,至此,我们快要搞清楚了,我需要你们有足够的耐心看完下一个部分
微任务(Microtasks)通常被安排在当前执行脚本之后,比如对一些批量操作或者一些不会产生新的任务队列的异步处理的反应。每次事件循环中,如果没有其他JavaScript运行并且任务(task)都执行完毕了,那么微任务就会在回调之后被执行。在微任务中排队的任何其他微任务将被添加到队列的末尾并进行处理。微任务包括 MutationObserver、Promise的回调(译者注:微任务包括:process.nextTick(Nodejs), Promises, Object.observe, MutationObserver;任务(tasks)包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering)。
一旦promise执行或者已经执行就会在队列中插入这个微任务的回调。这确保了promise回调是异步的,即使是这个promise已经执行。因此调用.then(yey,nay)是不会立即执行的,这就是为什么promise1和promise2会在script end后打印出来,因为只有在当前脚本执行完成之后微任务才会被执行。也因为微任务通常在下一个任务(tasks)执行之前被执行,所以promise1和promise2会在setTimeout之前打印。
百分比方式显示
/**
* 格式为`百分比方式显示`
*
* @param {number} molecular 分子 `横线`上面的数
* @param {number} denominator 分母
* @see [百度百科-分数](https://baike.baidu.com/item/%E5%88%86%E6%95%B0/2067623?fr=aladdin)
* @see [a](http://www.css88.com/archives/7340)
*/
function getPercentNumber(molecular, denominator) {
// 这种写法, 牵扯了浮点型小数问题
// const quotient = Number((molecular / denominator).toFixed(2)) * 100;
// 方式1:移位运算
// console.log( (molecular / denominator) * 100 >> 2 );
// return (molecular / denominator) * 100 >> 2;
// 方式2:换成整数方式处理
// console.log( parseInt(String(molecular / denominator * 100)) );
// console.log(denominator, molecular);
return parseInt(String(molecular / denominator * 100));
}
console.log( getPercentNumber(1, 3) ); // 33 -- number
console.log( getPercentNumber(23, 60) ); // 38
console.log( getPercentNumber(66, 100) ); // 66
function formatWan(n) {
if (n <= 10000) return `${n}`;
n = n.toString(); // 返回字符串表示
return (n / 10000).toFixed(1) + '万'; // toFixed(1)四舍五入保留指定小数位小数
}
console.log( formatWan(100) ); // '100'
console.log( formatWan(10000) ); // '10000'
console.log( formatWan(100000) ); // '10.0万'
/**
* 判断某个数字有几位小数.
*
* @param {number} num 需要判断的数字
* @param {number} len 指定小数点位数
* @return {boolean} 数字判断位数后的结果
*
* @see https://blog.csdn.net/yanlingdi/article/details/52849314
*/
function isNumLen(num, len) {
const x = String(num).indexOf('.') + 1; // 小数点的位置
const y = String(num).length - x; // 位数
if (x >= len) { // 不少于指定位数
return true;
} else {
return false;
}
}
/**
* 浮点值处理为至多两位小数.
*
* @param {number} num 需要格式化的数字.
* @return {number|string} 格式化后的数字或字符串.
* @see https://www.jb51.net/article/120127.htm
* @see https://www.runoob.com/w3cnote/javascript-two-decimal.html
*/
function getNum(num) {
// console.log(typeof num, num);
// if (num == 0) return 0;
// console.log('浮动处理');
// const reg = /([0-9]+\.[0-9]{2})[0-9]*/;
// return num.replace(reg, '$1');
const isThan2 = isNumLen(num, 2);
if (isThan2) { // 至少两位小数
return num.toFixed(2);
} else {
return num;
}
}
console.log( getNum(100) ); // 100
console.log( getNum(100.1) ); // 100.10
console.log( getNum(100.20) ); // 100.20
console.log( getNum(100.25) ); // 100.25
console.log( getNum(100.253) ); // 100.25
console.log( getNum(100.255) ); // 100.25
[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
vue 版本 | cli 版本 | 类型 | 依赖 |
---|---|---|---|
2.5.17 | 3.0.0-rc.12 | 普通单页面 |
vue-cli2.x 升级到 3.x 的项目,初始化时正常,插件方式安装 CubeUI 也正常
package.json
(无效)由于是 cli 2.x
升级到 3.x
的项目,前段时间有个新项目也是使用了 cli 3.x
没有出现这个问题,尝试使用正常项目的 package.json
在这个报错的项目中使用
cli 3.x
项目按需一个个安装依赖) 参考#12
或者配置文件单独放置,不全部放置在 package.json
中。习惯是单独放置
问题:
订单页面有三个接口A, B, C。A接口获取初始预付款订单,B接口使用优惠券,C接口提交订单。页面加载先执行 A, 选择了优惠券执行B, 为了订单数据实时更新,刷新了接口A, 但由于选择了优惠券。A接口的某些数据全部使用(折扣,实缴 不能直接使用)导致的问题。详情
更新时间 | 创建时间 | 来源 |
---|---|---|
2019/01/28 | 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]];
代码分析:不传参数,返回的是一个空数组;传了一个参数,且为数字,返回一个指定长度的空数组。每一项填充内容为空(Tips: 不是undefined
, 只不过访问空数组的某一项返回 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值会裁减或扩充数组 | number | - | - |
示例
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 更新为 6
console.log(c); // ["one", "two", "three", empty, "four", "blastoff"]
console.log(c[3]); // undefined 空的项被 undefined 填充
console.log(c.length); // 返回 c 的长度,这里为 6
const arr = [];
console.log(arr.length); // 0
// 原数组内容增加了一项
arr.length = 1;
console.log(arr); // [empty]
arr[0] = 1;
console.log(arr); // [1]
console.log(arr.length); // 1
arr[1] = 2;
console.log(arr); // [1, 2]
console.log(arr.length); // 2
// 原数组内容不改变.
arr.length = 2;
console.log(arr); // [1, 2]
// 原数组内容被删除了.
arr.length = 1;
console.log(arr); // [1]
代码分析:读取
数组的 length
属性时,返回当前数组的长度。设置
数组的 length
属性不仅会设置数组的长度,还会增(当原数组长度小于length的值时)、 删(当原数组长度大于设置的length的值时)数组内容,或者数组内容不变(当原数组长度等于设置的length的值时)。
// @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.from | 从一个类似数组或可迭代对象中创建一个新的数组实例 | ES6 创建 新数组 |
Array.isArray | 用于确定传递的值是否是一个 Array | ES5 检测 |
Array.of | 创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型 | ES6 创建 |
从一个类似数组或可迭代对象中创建一个新的数组实例
语法
Array.from(arrayLike[, mapFn[, thisArg]])
参数
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
arrayLike | 想要转换成数组的伪数组对象或可迭代对象 | any | - | - |
mapFn | 可选 如果指定了该参数,新数组中的每个元素会执行该回调函数 |
Function | - | - |
thisArg | 可选 执行回调函数 mapFn 时 this 对象 |
Object | - | - |
返回值
一个新的数组实例
示例
Case1:字符串
Array.from('Hello'); // => ["H", "e", "l", "l", "o"]
代码分析:字符串Hello
是一个可以迭代的。
Case2: Set
对象(实例对象)
//
let s = new Set(['Hello', window]);
Array.from(s); // => ["Hello", Window]
代码分析:Set实例对象可以迭代
Map
对象(实例对象)
// Map
let m = new Map([[1, 2], [3, 4], [5, 6]]);
Array.from(m) // => [[1, 2], [3, 4], [5, 6]]
伪数组对象(拥有一个 length 属性和若干索引属性的任意对象)。伪数组对象不是一个数组
//
function fn() {
return Array.from(arguments);
}
fn(1, 2, 3); // => [1, 2, 3]
使用箭头函数
const arr = [1, 2, 3];
Array.from(arr, x => x + x); // => [2, 4, 6]
数组去重合并
function combine() {
// 没有去重复的新数组 feat1
const arr = [].concat.apply([], arguments);
// feat2
return Array.from(new Set(arr));
}
const arr1 = [1, 2, 2];
const arr2 = [2, 3, 3];
console.log(combine(arr1, arr2)); // [1, 2, 3];
代码分析:cobmine
函数的功能是将多个参数数组合并,并返回一个合并后没有重复的新数组。feat1
:将所有的参数合并在一起,并返回合并后的新组,这个数组里面可能会包含重复的项。feat2
: 将合并在一起的数组通过 Set
去除重复的。
确定传递的值是否是一个 Array
语法
Array.isArray(obj)
参数
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
obj | 需要检测的值 | any | - | - |
返回值
布尔类型。如果对象是 Array,则为true; 否则为false
示例
基础示例
// 下面的函数调用都返回 true
Array.isArray([]);
Array.isArray([1]);
Array.isArray(new Array());
// 鲜为人知的事实:其实 Array.prototype 也是一个数组。
Array.isArray(Array.prototype);
// 下面的函数调用都返回 false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray({ __proto__: Array.prototype });
// Q1
Array.isArray(Array);
代码分析:Q1
中的 Array
是数组的构造函数,类型是函数对象,所以不是一个数组。
当检测Array实例时, Array.isArray
优于 instanceof
, 因为 Array.isArray
能检测 iframes
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const xArray = window.frames[window.frames.length-1].Array;
const arr = new xArray(1, 2, 3);
Array.isArray(arr); // => true
arr instanceof Array; // => false
const arr1 = [1, 2, 3];
Array.isArray(arr1); // => true
arr1 instanceof Array; // => true
创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型
语法
Array.of(element0[, element1[, ...[, elementN]]])
参数
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
elementN | 任意个参数,将按顺序成为返回数组中的元素 | any | - | - |
返回值
新的 Array 实例
示例
Array.of()
与 Array构造函数
的区别
Array.of(7); // => [7]
Array.of(1, 2, 3); // => [1, 2, 3]
Array(7); // => [ , , , , , , ]
Array(1, 2, 3); // => [1, 2, 3]
const arr1 = Array(7);
console.log(arr1[0]); // => undefined
代码分析:Array.of(7)
创建一个具有单个元素 7
的数组,而 Array(7)
创建一个长度为 7
的 空数组
(注意:这是指一个有7个空位的数组,而不是由7个undefined组成的数组), 因为空数组里面什么都没有,当访问一个什么都没有的对象时返回 undefined
;
基础示例
Array.of(1); // => [1]
Array.of(1, 2, 3); // => [1, 2, 3]
Array.of(undefined); // => [undefined]
方法名 | 说明 | 标签 |
---|---|---|
Array.prototype.concat | 用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。 | ES6 合并 新数组 |
Array.prototype.copyWithin | 浅复制数组的一部分到同一数组中的另一个位置,并返回它,而不修改其大小 | ES6 |
Array.prototype.entries | 返回一个新的 Array Iterator 对象,该对象包含数组中每个索引的键/值对 |
ES6 |
Array.prototype.every | 测试数组的所有元素是否都通过了指定函数的测试 | ES5 |
Array.prototype.fill | 用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引 | ES6 |
Array.prototype.filter | 创建一个新数组, 其包含通过所提供函数实现的测试的所有元素 | ES5 |
Array.prototype.find | 返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined | ES6 |
Array.prototype.findIndex | 返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1 | ES6 |
Array.prototype.forEach | 对数组的每个元素执行一次提供的函数 | ES5 |
Array.prototype.includes | 用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false | ES6 |
Array.prototype.indexOf | 返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1 | ES5 |
Array.prototype.join | 将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串 | ES5 |
Array.prototype.keys | 返回一个包含数组中每个索引键的Array Iterator对象 | ES6 |
Array.prototype.lastIndexOf | 返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始 | ES5 |
Array.prototype.map | 创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果 | ES5 |
Array.prototype.pop | 从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度 | ES5 |
Array.prototype.push | 将一个或多个元素添加到数组的末尾,并返回该数组的新长度 | ES5 |
Array.prototype.reduce | 对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值 | ES5 |
Array.prototype.reduceRight | 接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值 | ES5 |
Array.prototype.reverse | 将数组中元素的位置颠倒。第一个数组元素成为最后一个数组元素,最后一个数组元素成为第一个 | ES5 |
Array.prototype.shift | 从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度 | ES5 |
Array.prototype.slice | 返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变 | ES5 |
Array.prototype.some | 测试是否至少有一个元素通过由提供的函数实现的测试 | ES5 |
Array.prototype.sort | 用原地算法对数组的元素进行排序,并返回数组。排序算法现在是稳定的。默认排序顺序是根据字符串Unicode码点 | ES5 |
Array.prototype.splice | 通过删除现有元素和/或添加新元素来修改数组,并以数组返回原数组中被修改的内容 | ES5 |
Array.prototype.toLocalString | 返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ",")隔开 | Pending |
Array.prototype.toString | 返回一个字符串,表示指定的数组及其元素 | ES5 |
Array.prototype.toSource | 返回一个字符串,代表该数组的源代码 | 非标准 |
Array.prototype.unshift | 将一个或多个元素添加到数组的开头,并返回该数组的新长度 | ES5 |
Array.prototype.values | 返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值 | ES6 |
Array.prototype[@@iterator] | 返回数组的 iterator 方法,默认情况下与 values() 返回值相同 | ES6 |
用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组
语法
var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])
参数
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
valueN | 将数组和/或值连接成新数组 | 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 方法里面的参数如果存在 多维数组
,将多维数组作为整体组合到新数组中.
const thisArr = [0];
// feat1
const arr1 = [].concat.apply(thisArr, [1, 2, 3]);
// or
// const arr1 = [].concat.call(thisArr, 1, 2, 3);
const arr2 = thisArr.concat(1, 2, 3);
// or
// const arr2 = thisArr.concat([1, 2, 3]);
console.log(thisArr); // [0]
console.log(arr1); // [0, 1, 2, 3]
console.log(arr2); // [0, 1, 2, 3]
代码分析: feat1
的代码相当于 thisArr.concat(1, 2, 3)
功能类似的其他方法
Array.join() Array.push() Array.splice()
ES5
测试
断言函数:predicateFn
是否对每个元素为真
语法
参数
参数 | 是否必需 | 描述 |
---|---|---|
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
Array.filter() Array.forEach() Array.some()
ES5
返回通过断言的数组元素
predicate
判断array中每项是否需要包含在返回数组中的调用函数 predicate(array[i], i, array)
o
调用断言函数predicate时可选的this值
一个新数组,只包含那些让predicate返回真值的数组元素
文字待整理
let result = [1, 2, 3].filter( x => x > 1 ) // => result = [2, 3];
Array.every() Array.forEach() Array.indexOf() Array.map() Array.reduce()
ES5
为每个数组元素调用一个函数
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];
Array.every() Array.filter() Array.indexOf() Array.map() Array.reduce()
ES5
查找数组
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
Array.lastIndexOf() String.indexOf()
将数组元素衔接为字符串
spearator
在返回的字符串中,用来分割数组某个元素与下一个元素的可选字符或字符串。如果省略,默认是英文逗号(,)
一个字符串。将array的每个元素转换为字符串。然后用separator字符串分隔开。最后衔为返回的字符串
文字待整理
let a = new Array(1, 2, 3, 'testing');
s = a.join('+'); // => s == '1+2+3+testing'
String.split()
ES5
反向查找数组
value
要在array中查找的值
start
开始查找的可选数组序号。如果省略,则从最后一个元素开始查找
一个小于等于start的最大序号值,该序号值处的array元素与value相等。如果不存在匹配的元素时,返回-1。
文字待整理
Array.indexOf() String.lastIndexOf()
ES5
从数值元素中计算新值
f
为array的每个元素调用的函数。它的返回值会成为数组的元素
o
f调用时可选的this值
一个新数组,由函数f计算出的元素组成
文字待整理
let result = [1, 2, 3].map( x => x*x ); // => result == [1, 4, 9]
Array.every() Array.filter() Array.forEach() Array.indexOf() Array.reduce()
移除并返回数组的最后一个元素
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
Array.push()
给数组追加元素
array.push(value, ...);
vlaue, ...
追加到array尾部的一个或多个值
把指定值追加到数组后数组的新长度
文字待整理
Array.pop()
ES5
从数组元素中计算出一个值
f
一个函数,可以合并两个值(比如两个数组元素),并返回一个‘缩减’的新值
inital
用来缩减数组的可选初始值。如果指定该参数, reduce()的行为会像是把该参数插入array的头部一样
数组的简化值,该值是最后一次调用f时的返回值
文字待整理
let r = [1, 2, 3, 4].reduce((x, y) => x*y); // => r == 24
Array.forEach() Array.map() Array.reduceRight()
ES5
从右到做缩减数组
f
一个函数,可以合并两个值(比如两个数组元素),并返回一个“缩减”的新值
initial
用来缩减数组的可选初始值,如果指定该参数,reduceRight()的行为就会是把该参数插入array的尾部一样
数组的缩减值,该值是最有一次调用f时的返回值
文字待整理
let r = [2, 10, 60].reduceRight((x, y) => x / y); // => r == 3
Array.reduce()
颠倒数组中的元素顺序
文字待整理
let a = new Array(1, 2, 3); // => a[0] == 1, a[2] == 3;
a.reverse(); // => a[0] == 3, a[2] == 1;
移除数组的第一个元素
数组原来的第一元素
文字待整理
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]
Array.pop() Array.unshift()
返回数组的一部分
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]
在IE4中start参数不能为负数, 在IE的后续版本中已经修复该bug
Array.splice()
ES5
测试是否有元素满足断言函数
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
Array.every() Array.filter() Array.forEach()
对数组元素进行排序
orderFunc
用来指定如何排序的可选函数
该数组的引用。注意是在原数组中进行排序,没有新建数组
文字待整理
// 用来数组排序的排序函数
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] 数值排序
插入、删除或替换数组元素
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]
Array.slice()
将数组转化为本地化字符串
array.toLocalString()
数组的本地化字符串表示
TypeError
调用该方法时,如果对象不是Array,则抛出该异常
文字待整理
Array.toString() Object.toLocalString()
将数组转化为字符串
array.toString()
array的字符串表示
TypeError
调用该方法时,如果对象不是Array,则抛出该异常
文字待整理
Array.toLocalString() Object.toString()
在数组头部插入元素
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];
Array.shift()
扩展运算符(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
方法,将数组转为函数的参数了。
// 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-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(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
方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三个参数。
这三个参数都应该是数值,如果不是,会自动转为数值。
[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
方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为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
方法使用给定值,填充一个数组。
['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号位之前结束。
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']
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
区分。
has
方法,是用来查找键名的,比如Map.prototype.has(key)
、WeakMap.prototype.has(key)
、Reflect.has(target, propertyKey)
。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
,而undefined
和null
会被处理成空字符串。// 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
由于空位的处理规则非常不统一,所以建议避免出现空位。
ES2015 Proxy
放在 ./src/assets/style
目录下
├─scss
| ├─index.scss
| ├─_animate.scss
| ├─_function.scss
| ├─_iconfont.scss
| ├─_mixin.scss
| ├─_one-px-border.scss
| ├─_reboot.scss
| └_variables.scss
@import './_variables.scss';
@import './_mixin.scss';
@import './_function.scss';
@import './_reboot.scss';
@import './_one-px-border.scss';
@import './iconfont.scss';
@import './ui-change-style.scss';
组件中使用 @import '../../assets/style/scss/index';
// 返回数据 res or res.data 兼容性处理 start ==================
let _res = (res && res.data) ? res.data : res;
// 返回数据 res or res.data 兼容性处理 end ==================
npm install
版本安装规则~
:比如~1.2.2,表示安装1.2.x的最新版本(不低于1.2.2),但是不安装1.3.x,也就是说安装时不改变大版本号和次要版本号。^
:比如ˆ1.2.2,表示安装1.x.x的最新版本(不低于1.2.2),但是不安装2.x.x,也就是说安装时不改变大版本号。需要注意的是,如果大版本号为0,则插入号的行为与波浪号相同,这是因为此时处于开发阶段,即使是次要版本号变动,也可能带来程序的不兼容。// Notice:
`_this.slideProgress` ji
/**
* 粉色255,72,145 过渡到 灰色51,51,51
*/
// 放大平方(2次方)倍的变化速率
r = Math.floor((255 - 51) * (1 - Math.pow(Math.abs(_this.slideProgress), 2)) + 51)
g = Math.floor((72 - 51) * (1 - Math.pow(Math.abs(_this.slideProgress), 2)) + 51)
b = Math.floor((145 - 51) * (1 - Math.pow(Math.abs(_this.slideProgress), 2)) + 51)
// 正常变化速率
r = Math.floor((255 - 51) * (1- Math.abs(_this.slideProgress)) + 51)
g = Math.floor((72 - 51) * (1 - Math.abs(_this.slideProgress)) + 51)
b = Math.floor((145 - 51) * (1 - Math.abs(_this.slideProgress)) + 51)
更新时间 | 创建时间 | 来源 |
---|---|---|
2019/01/28 | 2019/01/28 | 搜集整理 |
for
优化方案// bad
const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
// do something
}
// good
for (let i = 0, len = arr.length; i < len; i++) {
// do something
}
针对上面的优化方案,可以通过如下代码证明.
// bad way
const list = [1, 2, 3];
const proxyList = new Proxy(list, {
get(target, key) {
if (key === 'length') {
console.log('Get Length');
return target.length;
} else {
return target[key];
}
}
});
for (let i = 0; i < proxyList.length; i++) {
console.log(proxyList[i]);
}
// 输出:
// Get Length
// 1
// Get Length
// 2
// Get Length
// 3
// Get Length
// god way
const list = [1, 2, 3];
const proxyList = new Proxy(list, {
get(target, key) {
if (key === 'length') {
console.log('Get Length');
return target.length;
} else {
return target[key];
}
}
});
for (let i = 0, len = proxyList.length; i < len; i++) {
console.log(proxyList[i]);
}
// 输出:
// Get Length
// 1
// 2
// 3
更新时间 | 创建时间 | 来源 |
---|---|---|
2019/01/29 | 2019/01/29 | 原创 |
step1: 域名管理平台
处添加 CNAME
解析 指向 userName.github.io
step2: github
指定的库。Settings
栏目 > GitHub Pages
分类 > 选择一个分支 > ok
step3: github
指定的库,添加 CNAME
内容为 自定义域名
CNAME
重复填写.The page build completed successfully, but returned the following warning for the `master` branch:
The CNAME `xxx.xxx.xxx` is already taken. Check out https://help.github.com/articles/troubleshooting-custom-domains/#cname-already-taken for more information.
For information on troubleshooting Jekyll see:
https://help.github.com/articles/troubleshooting-jekyll-builds
If you have any questions you can contact us by replying to this email.
以上问题的解决方案: 将重复库里面的 CNAME
改为一个没有重复的域名.
github
上可以使用多个库,作为多个静态站点吗?github
提供的域名为 username.github.io
。例如我们新建了多个库 a
、b
。当将这个两个库都作为静态站点时,分别对应的地址为 username.github.io/a
、username.github.io/b
example,一般分支 gh-pages
为静态站点分支。更新时间 | 创建时间 | 来源 |
---|---|---|
2019/01/28 | 2019/01/28 | 收集整理 |
指南内容节选自 Vue 官方风格指南
组件的 data 必须是一个函数。
// bad
export default {
data: {
foo: 'bar'
}
}
// good
export default {
data () {
return {
foo: 'bar'
}
}
}
单文件组件的文件名应该要么始终是单词大写开头 (PascalCase)
,要么始终是横线连接 (kebab-case)
。
// bad
mycomponent.vue
myComponent.vue
// good
my-component.vue
MyComponent.vue
和父组件紧密耦合的子组件应该以父组件名作为前缀命名。
// bad
components/
|- TodoList.vue
|- TodoItem.vue
└─ TodoButton.vue
// good
components/
|- TodoList.vue
|- TodoListItem.vue
└─ TodoListItemButton.vue
在单文件组件中没有内容的组件应该是自闭合的。
<!-- bad -->
<my-component></my-component>
<!-- good -->
<my-component />
在声明 prop 的时候,其命名应该始终使用 驼峰方式(camelCase)
,而在模板中应该始终使用 kebab-case
。
// bad
export default {
props: {
'greeting-text': String
}
};
// good
export default {
props: {
greetingText: String
}
}
<!-- bad -->
<welcome-message greetingText="hi" />
<!-- good -->
<welcome-message greeting-text="hi" />
多个 Props 的元素应该分多行撰写,每个 Props 一行,闭合标签单起一行。
<!-- bad -->
<my-component foo="a" bar="b" baz="c" />
<!-- good -->
<my-component
foo="a"
bar="b"
baz="c"
/>
指令缩写,用 :
表示 v-bind:
,用 @
表示 v-on:
<!-- bad -->
<input
v-bind:value="value"
v-on:input="onInput"
>
<!-- good -->
<input
:value="value"
@input="onInput"
>
标签的 Props 应该有统一的顺序,依次为指令、属性和事件。
<my-component
v-if="if"
v-show="show"
v-model="value"
ref="ref"
:key="key"
:text="text"
@input="onInput"
@change="onChange"
/>
组件选项应该有统一的顺序。
export default {
name: '',
mixins: [],
components: {},
props: {},
data() {},
computed: {},
watch: {},
created() {},
mounted() {},
destroyed() {},
methods: {}
};
组件选项较多时,建议在属性之间添加空行。
export default {
computed: {
formattedValue() {
// ...
},
styles() {
// ...
}
},
methods: {
onInput() {
// ...
},
onChange() {
// ...
}
}
};
-
连接,文件夹下的 .vue
文件统一命名为 index.vue
import xxx from '@/components/Toast'
views
或者 pages
文件夹下,引入的时候像这样 @/pages/home/index
├──home
├── index.vue # home 页面
└── children
├── home-order.vue # home 子页面,不存在子页面
└── home-usercenter # home 子页面,存在子页面
└── children
└── home-usercenter-userinfo.vue
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
单文件组件应该总是让顶级标签的顺序保持一致,且标签之间留有空行。
<template>
...
</template>
<script>
/* ... */
</script>
<style>
/* ... */
</style>
:style="{backgroundImage:'url(' + item.videopic + ')'}" 或者
:style="{ backgroundImage: `url(${item.videopic})` }"
list: [
require('@/assets/img/index/banner.png')
]
const Util = {
/**
* 是否大于指定的随机数.
*
* @param {parNum} 百分比字符串
* @return true/false 中: true, 未中: false
*/
isPass(perNum) {
// 获取小数
const _decimals = 1 - parseInt(`${perNum}`) / 100;
const _randomNum = Math.random();
return (_randomNum >= _decimals) ? true : false;
}
}
// 使用
Util.isPass('50%') // 判断50%的概率是否中
问题描述 | 浏览器 | 代码 |
---|---|---|
微信内置浏览器, 页面显示空白,无任何报错提示 | 2.6.5.38 (win pc) | 使用了const let 箭头函数 |
babel-polyfill
use strict
严格模式js
源码内联和外联箭头函数
use strict
VConsole
要输出错误,最前面开启const
let
use strict
模式Uncaught SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
相关链接
@media (-webkit-max-device-pixel-ratio: 1) {
::-webkit-scrollbar-track-piece {
background-color:#FFF
}
::-webkit-scrollbar {
width: 6px;
height: 6px
}
::-webkit-scrollbar-thumb {
background-color: #c2c2c2;
background-clip: padding-box;
min-height: 28px
}
::-webkit-scrollbar-thumb:hover {
background-color: #A0A0A0
}
}
人性本善,少个别别有用心的人,将支付成功的页面添加个书签,下次多次访问这个书签。
vue -V
以下操作在 3.0
以上才行
vue create project-name
选择的 features
有: Babel, Router, Vuex, CSS Pre-processors
CSS 预处理器为 SCSS
vue add cube-ui
npm install axios fastclick qs vant vee-validate vue-awesome-swiper vue-lazyload -S
vant具体配置参考: #13
资料整理,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正
基本形象的比喻 原文
进程
是一个工厂,工厂有它的独立资源-工厂之间相互独立
线程
是工厂中的工人,多个工人协作完成任务-工厂内有一个或多个工人-工人之间共享空间
完善概念 (Tips: -> 相当于)
工厂的资源 -> 系统分配的内存(独立的一块内存)
工厂之间的相互独立 -> 进程之间相互独立
多个工人协作完成任务 -> 多个线程在进程中协作完成任务
工厂内有一个或多个工人 -> 一个进程由一个或多个线程组成
工人之间共享空间 -> 同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)
通过 windows
电脑的 任务管理器
可以更形象的理解:
进程是cpu资源分配的最小单位(系统会给它分配内存)
问题: 线程和进程的区别是什么?
答: 进程和线程都是一个时间段的描述,是CPU工作时间段的描述 (详情)
进程:上下文切换的程序执行时间总和= CPU加载上下文 + CPU执行 + CPU保存上下文
官方术语介绍:
tips:
页面的渲染,JS的执行,事件的循环,都在这个进程内进行
请牢记,浏览器的渲染进程是多线程的(这点如果不理解,请回头看进程和线程的区分)
浏览器渲染进程(列举一些主要常驻线程):
GUI 渲染线程
JS引擎线程
JS内核
,负责处理Javascript脚本程序。(例如V8引擎)事件触发线程
定时触发器线程
异步http请求线程
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.