JS监听document是否加载完成

跨浏览器且纯JavaScript检测document是否加载完成的方法是使用readyState.

这样可以在document完全加载时监测到…

或者使用onreadystatechange

使用document.readyState === 'interactive'监听DOM是否加载完成。

JavaScript标准库之数组

学习任何编程语言,数组都是绕不过去的坎,每个编程语言都在其标准库里面内置了功能强大的Array对象。通过参考阮一峰教程和MDN,我把数组常见的方法以及一些误区总结如下,内容较多,而且会继续补充,希望这一篇文章可以把数组的知识一网打尽。

1. 数组的简单概念

1.1 数组是什么呢?

编程总要和数据打交道,常见的普通的数据由基本数据类型可以定义,一些具有多重属性、内容的数据就需要复杂的数据类型去定义,也就是对象来定义,数组也是对象的一种。

  • 为了方便理解,我们可以认为数组是具有一定顺序的复杂数据的组合(与对象的无序区别),每个位置对应一个索引,索引从0开始,具有length属性,而且length属性是可变的

1.2 数组如何定义

  • 第一种方法是通过Array构造函数来定义(该方法并不常用)

以上是控制台打印结果,构造了一个长度为3的、每个元素为空的数组。


以上的写法有个小bug 虽然元素为空,但是正常来说,索引应该存在的,但是事实是 索引竟然不存在

索引0、1、2处是undefined,完全合理,但是索引不存在,很奇怪


而且new不写也是一样的结果。

  • 但是采用构造函数的这种方法容易产生一些歧义,不同的参数个数,会产生如下五种不同的结果。
1.2.1 构造函数不写参数

此时构造出空的数组,而且发现构造函数的()写不写都可以

1.2.2 构造函数写1个正整数参数

那这个正整数参数就是构造出来的数组的长度。

1.2.3 构造函数参数是一个非正整数(字符串、boolean、对象等其他值)

这个非正整数就是数组的内容

1.2.4 构造函数写多个参数

此时直接构造出0索引是元素1、1索引是元素2的数组对象。

即多参数时,所有参数都是返回的新数组的成员

1.2.5 构造函数参数是非正整数,报错

1.2.6 数组定义的正确方法

为了避免上述的各种奇怪理解,实际中直接用字面量定义数组

2. 数组的length属性解疑

如果你是初学者,一定要记住数组的length属性和这个数组的元素个数无关,爱几个元素几个元素,length并不是计数的作用。这是我自学时对数组长度最大的误解。 正确的理解是:数组的length属性等于最大正整数索引 + 1 而且数组的索引可以改变,那么length属性也是一个动态的值,可以变化。

这个例子一开始是个空数组,长度是0,直接给他一个索引10,可以发现长度立马变为11。

通过以上的例子,我们反向推理,可以明白数组长度根本不连续,是动态变化的,即数组长度是可写的。唯一的不变真理是,它的长度永远等于最大正整数索引+1。

2.1 把数组清空的方法

由以上知识可以知道数组长度可以人为改变,进而大胆的猜想,改变长度会不会把数组清空呢?

把数组length设为0,证明可以清空数组。

2.2 有趣的一点

由于数组本质上是对象的一种,所以我们可以为数组添加属性,但是这不影响length属性的值。 一定不要有思维定式,以为添加几个新元素,长度就会加几个。

通过这个例子,一开始元素长度为0,只要你没添加一个正整数的索引,无论你添加多少其他元素,长度永远不会变化。

  • 注意:方括号运算符里面一定要用引号,我总是手抖忘了加。

3. 伪数组(array-like object)

如果一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,语法上称为“类似数组的对象”

上面的对象,看着结构特别像数组,但是绝对不是数组。 因为__proto__指向的就不是Array的prototype,没有指向Array的共有属性,再怎么像也只是模仿,本质不同。不具备数组的其他方法(第四部分将要列举的方法)。

3.1 数组的本质

由伪数组的问题引出真正的数组应该具备什么特点

数组.png

__proto__必须指向数组的公有属性才是真正的数组对象。

4. 数组实例的常见简单的方法(可以无参或者参数很简单)

4.1 判断数组还是对象

Array.isArray()方法可以判断是不是数组对象,以前学过的instanceOf也可以判断。

所以现在有两个方法可以区分是数组还是对象了。

4.2 valueOf(),toString()

  • valueOf()返回数组本身

  • toString()返回数组的字符串形式

4.3 push()

向数组的末尾添加元素,返回添加成功后的数组的长度 会改变原数组

4.4 pop()

删除数组的最后一个元素,并返回删除的这个元素。

  • 注意:对空数组使用pop方法,不会报错,而是返回undefined。 这个方法会改变原数组

push() 和pop()方法一起使用可以模拟栈的数据结构


4.5 join()

以某种形式把数组的所有成员以字符串的形式返回

以上是以中划线的形式连接起来

如果没有规定格式,则以逗号分隔

  • 注意:如果字符串中有null和undefined的,会被转成空字符串。 该方法不会改变原数组

4.6 concat()

是一个专业合并数组的方法。

把一个新数组添加到旧数组的后面,返回生成的新数组。 不会改变原数组

4.7 shift()

删除数组的第一个元素,并返回删除的那个元素

会改变原数组


push()与shift()方法结合,可以模拟队列的数据结构


4.8 unshift()

在数组的第一个位置添加元素,并返回添加新元素后的数组长度

和shift()方法的作用正好相反。 一定会改变原数组

4.9 reverse()

反转数组,返回反转后的数组

会改变原数组

4.10 slice()

提取原数组的一部分,返回一个新的数组

arr.slice(1,3)从索引为1的位置开始截取,到索引3停止,但是不包括索引3。 arr.slice()无参是原数组的拷贝 arr.slice(1)从索引为1的位置开始截取,到末尾。

如果slice方法的参数是负数,则表示倒数计算的位置。 上面代码中,-2表示倒数计算的第二个位置,-1表示倒数计算的第一个位置。


slice()方法可以把伪数组变成真的数组


不会改变原数组

4.11 splice()

删除原数组的一部分成员,返回被删的元素。

arr.splice(1,3),从索引1开始删除,删3个元素!!! 一定要注意和slice区分:splice的第一个参数是删除的起始位置,第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。

arr.splice(1, 3, ‘wu’, 999),从索引1开始删了3个元素,然后加上两个元素,’wu’和999。 负数参数同样表示数组倒数第几个位置 会改变原数组
splice()有两个变式

  • 变式1:我只是想单纯的插入一个元素

把第二个参数设为0,就可以在第2个位置插入一个元素了

  • 变式2:我只给一个参数,就是拆分数组,变为两个新数组

a.splice(2)从第三个索引处拆分这个数组。

4.12 indexOf(),lastIndexOf()

indexOf(),返回括号里面 的元素第一次出现的位置。 如果有两个参数则是表示搜索的位置从第二个参数开始。 如果找不到该元素,则返回-1。 lastIndexOf()返回括号里面的元素最后一次出现的位置。

  • 一个MDN的实战例子:获得数组里面某个元素出现的所有位置(利用循环和返回值-1的特点)


注意:这里有个例外 数组里面包含NaN时无法判断

arr数组的第四个位置是NaN,但是无法获得索引。 因为indexOf(),lastIndexOf()是严格按照===操作符来检测的,而NaN是唯一的不与自身相等的值。

奇葩啊,NaN与自己都不相等


5. 数组实例的常见复杂的方法(参数是另一个函数)

5.1 sort()

下面MDN的解释非常棒

sort() 方法在适当的位置对数组的元素进行排序,并返回数组。 sort 排序不一定是稳定的。默认排序顺序是根据字符串Unicode码点。

上述代码两点注意

  • 第一点是 上述代码中的第二部分的[1, 10, 2, 21]是因为 10的Unicode编码是\u0031\u0030,2的Unicode编码是\u0032,所以10排在2的前面
  • 第二点是上述代码中的第三部分的['1 Word', '2 Words', 'Word', 'word']是因为
    'Word'的Unicode编码是

'word'的Unicode编码是

所以 'Word'排在'word'前面。 各种编码查询站长工具
sort方法明显的会改变原数组啊

  • 我们通常不想使用默认的升序排列,sort方法可以传入函数来改变顺序。 MDN的语法是arr.sort(compareFunction) compareFunction这个函数用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的诸个字符的Unicode位点进行排序。 compareFunction这个函数基本的规则是传入两个参数

a,b参数比较代表的意思
compareFunction(a, b) < 0a在b之前
compareFunction(a, b) > 0b在a之前

降序排列。

  • 也可以根据具体需求来根据属性来排列

以上是把三个学生根据成绩从大到小排列的

5.2 map()

map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。 不影响原数组。

以上代码中map()方法里面传入的函数是一个把数组每个值都加上一个’f’。 每个元素末尾都加上一个’f’,然后返回这个新的数组,原数组没有任何变化的。 我初学的时候,看到上述代码反正很懵逼,这玩意咋出来的这个结果呢。琢磨了很久,还是觉得MDN的解释明白,只不过需要看个3、4遍就能明白了。 语法规范是:

callback 生成新数组元素的函数,使用三个参数: currentValue callback 的第一个参数,数组中正在处理的当前元素。 index callback 的第二个参数,数组中正在处理的当前元素的索引。 array callback 的第三个参数,map 方法被调用的数组。 thisArg 可选的。执行 callback 函数时 使用的this 值。 返回值 一个新数组,每个元素都是回调函数的结果。

其实callback 的第三个参数可以不写,也知道调用的到底是哪个Array。

当你用map()方法的时候,callback 函数会被自动传入三个参数:数组的每一个元素,元素索引,原数组本身。既然原数组本身可以省略,那么由剩下的两个特点我们发散一下,会想到前面我们讲过,伪数组(比如字符串)也具备这两个特点会不会也能用map()方法呢,接下来做个实验。
哈哈哈哈,愚蠢的人类,你想的美,怎么可能直接使用呢,必须把伪数组转换一下的。

  • 第一种转换方法

以上是通过map函数的call方法间接使用

  • 第二种转换方法

'abc'.split('')把字符串转成数组["a", "b", "c"]


至此,字符串和数组相互转化的方法,都学到了,总结如下。

  • 数组转字符串 三种方法

  • 字符串转数组 一种方法


在map()的最后,要注意数组的空位问题。 我们先看一个map()处理含有空位的数组的奇怪现象

可以发现[1, , 2].map(f)空位未执行map()。map方法不会跳过undefined和null,但是会跳过空位。

  • 用一个更直观的例子来证明map方法会跳过空位

本文一开始就讲了Array[2]始构造了长度为2的空数组,没有打印出enter,说明未执行map()方法。

使用 map 方法处理数组时,数组元素的范围是在 callback 方法第一次调用之前就已经确定了。在 map 方法执行的过程中:原数组中新增加的元素将不会被 callback 访问到;若已经存在的元素被改变或删除了,则它们的传递到 callback 的值是 map 方法遍历到它们的那一时刻的值;而被删除的元素将不会被访问到。


以上引入了数组的空位(hole)概念,那什么才是数组的空位呢 var a= [1, , 2] 中间就是一个空位

可以看到,空位计入数组长度,空位可读取,但是这个空位的值是undefined。 delete命令可以删除数组内的一个元素

delete命令删除成功,返回true,但是length不变,说明空位可以被读取到,所以用delete命令无法清空数组。目前把数组清空的唯一方法就是把length属性改为0。 换句话说length属性不能过滤空位。 当使用length属性进行数组遍历时,一定要非常小心。

数组的某个位置是空位,与某个位置是undefined,是不一样的。 为什么不一样呢。

  • 如果是空位,使用数组的forEach方法(接下来重点研究)、for…in结构、以及Object.keys方法进行遍历,空位都会被跳过。

  • 如果是undefined,使用数组的forEach方法(接下来重点研究)、for…in结构、以及Object.keys方法进行遍历,不会被跳过。

上面的对比可以知道,空位就是数组没有这个元素,所以不会被遍历到,而undefined则表示数组有这个元素,值是undefined,所以遍历不会跳过。


5.3 forEach()

该方法与map()类似,都是使数组的每个元素执行一个函数。与map()的最大区别是没有返回值,而map()返回一个新的数组。forEach()只关心数据的操作,而不关心返回值。forEach()方法传入的函数,实际上是有3个值。 MDN的语法规范

参数列表的含义与map()方法的每个参数含义相同。 callback()函数的array参数,通常省略,自己要脑补上。

谁去调用的forEach()方法,那么callback()里面的array就会自动传入那个数组,但是是隐藏的。和我一样的初学者,都曾怀疑过,哪里传进来的数组呢,最好的答案都在MDN的callback()函数的语法规则里面,具体的细节分析和map()的分析一样。

  • 注意: 用forEach()方法遍历数组,无法再某个条件时停止遍历,此时应该用普通的for循环

上面代码中,执行到数组的第二个成员时,就会中断执行。forEach方法做不到这一点。

  • 与map()方法一样,forEach方法会跳过数组的空位。而不会跳过undefined和null。

  • 当然了,forEach方法也可以用于类似数组的对象和字符串。

对象和字符串使用foreach一定要用Array.prototype.forEach.call()的。

forEach 遍历的范围在第一次调用 callback 前就会确定。调用forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift()) ,之后的元素将被跳过

ε=(´ο`*)))唉,上面这段话啊,可以看出forEach()和map()函数如此的相似啊。

  • 举一个MDN上面的例子,一旦数组被修改了,遍历不受修改的影响

我们发现遍历出了原来的所有元素,在forEach()开始之后的添加的'aaa'并不会遍历到。 不过MDN的原始例子比我的难度大多了啊。

当到达包含值”two”的项时,整个数组的第一个项被移除了,这导致所有剩下的项上移一个位置。因为元素 “four”现在在原数组的第三个位置,three跑到了第二个位置,而此时要去遍历第三个位置,所以不会打印three。

5.4 filter()

filter方法的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。 通俗的理解就是过滤器。callback()函数与以上两个一样,也是传入三个参数。 第一个参数是当前数组成员的值,这个是必须的。

可以理解为给filter()传入的函数一个规则,满足规则的才能返回。

5.5 reduce()

reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值。

以上是MDN的解释,挺难理解字面意思的。直接用实例来理解吧。

  • 累加求和

reduce()函数传入一个函数作为参数,函数里面传入两个参数,preSum默认是数组的第一个元素,每次都把数组的两个元素相加并返回,ele就是每个数组元素。 你也快成规定起始的累加值

起始的累加值是10,那么加上数组的20就是30。

  • 用reduce表示map()

利用reduce()完成了map()一样的功能

  • 用reduce表示filter()

如果原数组里面的值大于3,就放到新的数组里面。和filter()道理一样。

  • 计算数组里面技术的和 var a = [1,2,3,4,5,6,7,8,9] 计算所有奇数的和

先判断一下,再把奇数相加

5.6 几个方法组合使用

  • 计算数组的偶数和 给定一个 数组 var a = [1,2,3,4,5,6,7,8,9]
  1. 获取所有偶数
  2. 得到所有偶数的平方

先调用filter()获得所有偶数,再调用map()获得所有偶数平方和

5.7 some(),every()

some() 方法测试数组中的某些元素是否通过由提供的函数实现的测试。 传入的参数也是一个callback()函数,callback 被调用时传入三个参数:元素的值,元素的索引,被遍历的数组。其实一般只要发现时传入callback()函数,基本都是这些参数。

some()方法的作用是只要数组中的某个元素满足传入的函数的要求就返回true

every() 方法测试数组的所有元素是否都通过了指定函数的测试。

every()是要求数组的所有元素都满足传入的函数的要求才返回true

  • 注意:对于空数组,some方法返回false,every方法返回true,回调函数都不会执行。

对上面的结果,我又有什么办法呢,只能选择背过呗。 这两个方法都不改变原数组

6. 上述数组的方法的使用总结

数组的上述方法种类繁多,不过有几个特点很明显,一些方法会改变原数组,一些方法不会改变原数组,我以这个细节把上述方法分类如下

6.1 改变原数组的方法

方法名字方法作用
push()在元素末尾添加元素,返回添加新元素后的数组长度
pop()删除数组末尾的元素,返回删除的那个元素。与push()方法一起模拟栈这个数据结构
shift()删除数组的第一个元素,返回删除的那个元素。与push()方法结合,模拟队列这个数据结构
unshift()在数组的起始位置添加新元素,返回添加新元素后的数组长度
reverse()把数组的每一个元素的位置互换,返回翻转后的数组
splice()根据方法传入的参数删除原数组的部分元素,返回被删除的元素。可以用来拆分数组
indexOf(),lastIndexOf()返回括号里面 的元素第一次出现和最后一次出现的位置。NaN元素无法获得位置
sort()默认按照数组元素的Unicode码点排序,可以自己传入函数,规定排序准则

6.2 不改变原数组的方法

方法名字方法作用
join()以某种形式把数组的所有元素以字符串的形式返回,默认以逗号分隔,返回生成的新数组
concat()专业合并数组,把新数组添加到旧数组的后面,返回生成的新数组
slice()根据方法传入的参数提取原数组的部分,返回提取的这个新数组也可以用来把伪数组变成真数组
map()必须传入一个callback()函数,数组的每一个元素执行这个函数,返回执行回调函数后的新数组。该方法会跳过空位
forEach()必须传入一个callback()函数,数组的每一个元素执行这个函数。没有返回值,无法终止循环
filter()必须传入一个callback()函数,数组的每一个元素执行这个函数,返回结果为true的成员组成一个新数组返回
reduce()对数组中的每个元素(从左到右)应用一个函数,将其减少为单个值。具体理解看例子吧
some()只要数组中的某个元素满足传入的函数的要求就返回true
every()数组的所有元素都满足传入的函数的要求才返回true

正是因为以上的方法对原数组不造成影响,所以我们可以组合使用filter()、map()先过滤再匹配。

6.3 数组的遍历

对于有序、无序的数据,我们有时候会希望获得所有的key或者value,数组对这个需求尤甚。 一般来说,数组的遍历有三种方法

  • for…in循环

  • 切忌把arr[key]手抖写成了arr.key。因为arr.key等同于arr[‘key’],很明显数组没有这个名字叫key的键。 for…in循环有个弊端就是它会把非数字的索引也打印出来

如果我们只关心数组的数字索引,用传统的下面的传统for循环

  • 传统for循环

这种方法其实是我们人为规定了只遍历数字索引,O(∩_∩)O哈哈~

  • forEach()循环

这种方法也不会遍历非数字的索引。

作者:codevvvv9
链接:https://juejin.im/post/5a4db0ef5188257d692a00b4
来源:掘金

19 个 JavaScript 有用的简写技术

1.三元操作符

当想写if…else语句时,使用三元操作符来代替。

简写:
const answer = x > 10 ? 'is greater' : 'is lesser';

也可以嵌套if语句:
const big = x > 10 ? " greater 10" : x

2.短路求值简写方式

当给一个变量分配另一个值时,想确定源始值不是null,undefined或空值。可以写撰写一个多重条件的if语句。

或者可以使用短路求值方法:
const variable2 = variable1 || 'new';

3.声明变量简写方法

简写方法:
let x, y, z=3;

4.if存在条件简写方法

if (likeJavaScript === true)

简写:
if (likeJavaScript)

只有likeJavaScript是真值时,二者语句才相等

如果判断值不是真值,则可以这样:

简写:

5.JavaScript循环简写方法

for (let i = 0; i < allImgs.length; i++)

简写:
for (let index in allImgs)
也可以使用Array.forEach:

6.短路评价

给一个变量分配的值是通过判断其值是否为null或undefined,则可以:

简写:
const dbHost = process.env.DB_HOST || 'localhost';

7.十进制指数

当需要写数字带有很多零时(如10000000),可以采用指数(1e7)来代替这个数字:
for (let i = 0; i < 10000; i++) {}
简写:

8.对象属性简写

如果属性名与key名相同,则可以采用ES6的方法:
const obj = { x:x, y:y };

简写:
const obj = { x, y };

9.箭头函数简写

传统函数编写方法很容易让人理解和编写,但是当嵌套在另一个函数中,则这些优势就荡然无存。

简写:

10.隐式返回值简写

经常使用return语句来返回函数最终结果,一个单独语句的箭头函数能隐式返回其值(函数必须省略{}为了省略return关键字)

为返回多行语句(例如对象字面表达式),则需要使用()包围函数体。

简写:

11.默认参数值

为了给函数中参数传递默认值,通常使用if语句来编写,但是使用ES6定义默认值,则会很简洁:

简写:

12.模板字符串

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

ES6可以使用反引号和${}简写:

13.解构赋值简写方法

在web框架中,经常需要从组件和API之间来回传递数组或对象字面形式的数据,然后需要解构它

简写:

也可以分配变量名:

14.多行字符串简写

需要输出多行字符串,需要使用+来拼接:

使用反引号,则可以达到简写作用:

15.扩展运算符简写

扩展运算符有几种用例让JavaScript代码更加有效使用,可以用来代替某个数组函数。

简写:

不像concat()函数,可以使用扩展运算符来在一个数组中任意处插入另一个数组。

也可以使用扩展运算符解构:

16.强制参数简写

JavaScript中如果没有向函数参数传递值,则参数为undefined。为了增强参数赋值,可以使用if语句来抛出异常,或使用强制参数简写方法。

简写:

17.Array.find简写

想从数组中查找某个值,则需要循环。在ES6中,find()函数能实现同样效果。

简写:

18.Object[key]简写

考虑一个验证函数

假设当需要不同域和规则来验证,能否编写一个通用函数在运行时确认?

现在可以有适用于各种情况的验证函数,不需要为了每个而编写自定义验证函数了

19.双重非位运算简写

有一个有效用例用于双重非运算操作符。可以用来代替Math.floor(),其优势在于运行更快,可以阅读此文章了解更多位运算。
Math.floor(4.9) === 4 //true

简写:
~~4.9 === 4 //true

本文翻译于SitePoint:www.sitepoint.com/sho…

jquery 实现元素拖动

 

web前端全景图功能实现

资源下载:pannellum

预览图:

未命名1509430566

利用pannellum.js实现全景图功能