Giter Club home page Giter Club logo

keep's Introduction

keep's People

Contributors

jesonhu avatar

Stargazers

 avatar

Watchers

 avatar

keep's Issues

03. Flexbox 相关

Flexbox 语法与使用记录

更新时间 创建时间 是否原创
2019/01/15 2018/10/22 部分原创

基本语法

父容器(设置 display:flex 的容器)


Flexbox 相关的坑

  • 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;
}

正常效果: 输入图片说明

参考文章

04. Github issue 的操作

Github issue 的相关操作

添加 References -- 参考,关联

image

操作

  • 当前的库 #xxx xxx为issue的编号
  • 其他的库 username/repository#xxx 像这样 Jesonhu/keep#4

相关文章

14. Linux Error 报错码定义

#       报错码                 内容描述
#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 */  

参考资料

19. 任务(Tasks), 微任务(microtasks), 队列(queues) 和 调度(schedules)

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之前打印。

参考资料

21. 常用数字(number)格式化

cTools工具库

格式为百分比方式显示

    /**
     * 格式为`百分比方式显示` 
     * 
     * @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

概率获取 #5

15. vue 项目报错整理

[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.

image

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

方法可用,无错误源码 报错误源码

初始化项目

image
image
或者配置文件单独放置,不全部放置在 package.json 中。习惯是单独放置

一些不重要的依赖安装过程省略....

27. JavaScript(js)基础: 数组相关语法基础

JS基础:数组相关语法基础

更新时间 创建时间 来源
2019/01/28 2019/01/10 搜集整理

创建数组的方式:

方式1:构造函数方式 创建数组

// 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]

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

方式2:数组 字面量 方式创建

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的值时)。


数组的内置方法

给 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.from 从一个类似数组或可迭代对象中创建一个新的数组实例 ES6 创建 新数组
Array.isArray 用于确定传递的值是否是一个 Array ES5 检测
Array.of 创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型 ES6 创建

Array.from()文档

从一个类似数组或可迭代对象中创建一个新的数组实例

语法

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.isArray()文档

确定传递的值是否是一个 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()文档

创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型

语法

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

Array.prototype.concat()文档

用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组

语法

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 方法里面的参数如果存在 多维数组,将多维数组作为整体组合到新数组中.

apply() 或者 call() 方式执行

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()


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

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

09. Vue 项目 SCSS 文件目录结构

vue 单页面文件中使用 scss 目录结构规划

放在 ./src/assets/style 目录下

├─scss
|  ├─index.scss
|  ├─_animate.scss
|  ├─_function.scss
|  ├─_iconfont.scss
|  ├─_mixin.scss
|  ├─_one-px-border.scss
|  ├─_reboot.scss
|  └_variables.scss

index.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';

参考资料和关联

07. Vue 单页面项目请求(Request-API)封装

// 返回数据 res or res.data 兼容性处理 start ==================
let _res = (res && res.data) ? res.data : res;
// 返回数据 res or res.data 兼容性处理 end ==================

16. package.json 相关说明

npm install 版本安装规则

  • 指定版本:比如1.2.2,遵循“大版本.次要版本.小版本”的格式规定,安装时只安装指定版本。
  • 波浪号(tilde)+指定版本 ~:比如~1.2.2,表示安装1.2.x的最新版本(不低于1.2.2),但是不安装1.3.x,也就是说安装时不改变大版本号和次要版本号。
  • 插入号(caret)+指定版本 ^:比如ˆ1.2.2,表示安装1.x.x的最新版本(不低于1.2.2),但是不安装2.x.x,也就是说安装时不改变大版本号。需要注意的是,如果大版本号为0,则插入号的行为与波浪号相同,这是因为此时处于开发阶段,即使是次要版本号变动,也可能带来程序的不兼容。
    latest:安装最新版本。

参考资料

06. 两个颜色间过渡

// 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)

参考资料

25. JS 代码容易被忽略的优化方案

JS 代码容易被忽略的优化方案

更新时间 创建时间 来源
2019/01/28 2019/01/28 搜集整理

1. 基础类型

2. 引用类型

2.1 数组

2.1.1 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

28. Github Page 页面使用及跳坑集锦

Github Page 页面使用及跳坑集锦

更新时间 创建时间 来源
2019/01/29 2019/01/29 原创

Github Page 自定义域名

操作步骤

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。例如我们新建了多个库 ab。当将这个两个库都作为静态站点时,分别对应的地址为 username.github.io/ausername.github.io/b example,一般分支 gh-pages 为静态站点分支。

08. Vue 项目规范化: 风格指南

风格指南

指南内容节选自 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 名大小写

在声明 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 的元素应该分多行撰写,每个 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 顺序

标签的 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>

参考资料

11. Vue 背景图片 background-image 使用

指令(行内样式)方式使用

:style="{backgroundImage:'url(' + item.videopic + ')'}" 或者
:style="{ backgroundImage: `url(${item.videopic})` }"

vue-cli 3.x 以后可以通过 require引入

list: [
 require('@/assets/img/index/banner.png')
]

05. 概率获取

方式1

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%的概率是否中

02. 微信PC内置浏览器页面显示空白

问题相关

问题描述 浏览器 代码
微信内置浏览器, 页面显示空白,无任何报错提示 2.6.5.38 (win pc) 使用了const let 箭头函数

尝试解决方案

  • 引入babel-polyfill
  • 使用 use strict 严格模式
  • js 源码内联和外联
  • 不使用 箭头函数
  • 调试工具 VConsole 调试手机端

最终解决方案

  • use strict
  • VConsole 要输出错误,最前面开启
  • 不使用箭头函数,保留 const let

说明

  • 为什么使用 use strict 模式

Uncaught SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

相关链接

  • VConsole
  • 停车缴费商场选择指引

18. 支付结果页面漏洞

纯静态支付结果页面

产生的原因

  • 支付结果页面没有授权, 也没有访问任何需要授权的页面,整个一个静态页面。通过地址上的参数判断支付结果,显示支付状态

问题来了

人性本善,少个别别有用心的人,将支付成功的页面添加个书签,下次多次访问这个书签。

解决方案

  • 配合后端 请求支付结果API, 根据API的状态绑定到视图上。具体实现

12. vue-cli 3.x 移动端普通单页面项目使用指南

vue-cli 3.x 使用指引

准备工作

  • 查看 vue-cli 版本 vue -V

以下操作在 3.0 以上才行

初始化一个新项目

vue create project-name

选择的 features 有: Babel, Router, Vuex, CSS Pre-processors
CSS 预处理器为 SCSS

安装插件(plugins)

CubeUI

vue add cube-ui

image

安装常用依赖

npm install axios fastclick qs vant vee-validate vue-awesome-swiper vue-lazyload -S

vant具体配置参考: #13

20. JS 单线程运行机制,浏览器渲染机制 相关概念梳理

资料整理,如有描述不当之处,请帮忙及时指出,如有错误,会及时修正

大纲

  • 区分进程和线程
  • 浏览器是多进程的
  • 浏览器都包含哪些进程?
  • 浏览器多进程的优势
  • 重点是浏览器内核(渲染进程)
  • Browser进程和浏览器内核(Renderer进程)的通信过程
  • 梳理浏览器内核中线程之间的关系
  • GUI渲染线程与JS引擎线程互斥
  • JS阻塞页面加载
  • WebWorker,JS的多线程?
  • WebWorker与SharedWorker
  • 简单梳理下浏览器渲染流程
  • load事件与DOMContentLoaded事件的先后
  • css加载是否会阻塞dom树渲染?
  • 普通图层和复合图层
  • 从Event Loop谈JS的运行机制
  • 事件循环机制进一步补充
  • 单独说说定时器
  • setTimeout而不是setInterval
  • 事件循环进阶:macrotask与microtask

区分进程和线程

基本形象的比喻 原文

进程 是一个工厂,工厂有它的独立资源-工厂之间相互独立
线程 是工厂中的工人,多个工人协作完成任务-工厂内有一个或多个工人-工人之间共享空间

完善概念 (Tips: -> 相当于)

工厂的资源 -> 系统分配的内存(独立的一块内存)
工厂之间的相互独立 -> 进程之间相互独立
多个工人协作完成任务 -> 多个线程在进程中协作完成任务
工厂内有一个或多个工人 -> 一个进程由一个或多个线程组成
工人之间共享空间 -> 同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)

通过 windows 电脑的 任务管理器 可以更形象的理解:

进程是cpu资源分配的最小单位(系统会给它分配内存)

问题: 线程和进程的区别是什么?
答: 进程和线程都是一个时间段的描述,是CPU工作时间段的描述 (详情)
进程:上下文切换的程序执行时间总和= CPU加载上下文 + CPU执行 + CPU保存上下文

官方术语介绍:

  • 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
  • 线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)

tips:

  • 不同进程之间也可以通信,不过代价较大
  • 现在,一般通用的叫法:单线程与多线程,都是指在一个进程内的单和多。(所以核心还是得属于一个进程才行)
  • js 执行是单线程 参考

浏览器是多进程的 原文

浏览器多进程的优势 原文

浏览器内核(渲染进程)原文

页面的渲染,JS的执行,事件的循环,都在这个进程内进行

请牢记,浏览器的渲染进程是多线程的(这点如果不理解,请回头看进程和线程的区分)

浏览器渲染进程(列举一些主要常驻线程):

  • GUI 渲染线程

    • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
    • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
    • 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
  • JS引擎线程

    • 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
    • JS引擎线程负责解析Javascript脚本,运行代码。
    • JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
    • 同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
  • 事件触发线程

    • 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
    • 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
    • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
    • 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
  • 定时触发器线程

    • 传说中的setInterval与setTimeout所在线程
    • 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)
    • 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
    • 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
  • 异步http请求线程

    • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
    • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

来个图解理解下
image

参考资料

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.