4.1 数组
今天又是学习的一天,接下来我们开始看数组,这是一个非常重要的对象,而且是我们除了原始对象以外,经常用到的一个对象,对于整个 Js 的学习非常重要。本次学习主要学习数组的定义,即如何创建数组,几种轮询数组的方法,还有就是一些数组常用的函数。
4.11 创建数组
首先,我们学习的是怎么创建一个数组并把它初始化,这就好比如我们在建房子的时候,得先了解怎么建房,建成什么规格的。底下我们来看代码示例:
1 | var arr = []; |
那么房子建好了接下去是不是就是搬进去住的问题了,这就是我们要如何访问数组元素,
1 | console.log(arr1[1]); |
在这边呢,还是要再强调一点,大家一定要把代码在自己的电脑里面重新敲一遍并且运行,这样对一些函数和方法的调用印象都会比较强,更加有助于学习。接下来,我们看下底下这句代码,大家认为是否会报错,先不要在机器里边敲啊,自己根据理解猜测下,
1 | arr[3]=5; |
很多人看到我这样问,就感觉说这边有坑了,猜测应该不会报错,自信点,把应该去掉,这边是没有报错的,运行结果是:
1 | [empty × 3, 5] |
为什么是这样的结果捏,跟我们的理解可能不太一样, arr 明明是空的,但是为什么 arr[3] = 5 ? 也就是说刚开始的数组长度是 0 ,arr[3] 时数组长度是 4 ,在位置 4 的地方赋值 5,而前面的三个仍然是空值,就是会在数组中强制塞进去。
再来看这样一个示例:
1 | arr2[2]=8; |
结合上面两段代码呢,我们可以知道,如果一个下标位置原来不存在,会添加,如果必须的话,还会添加 length ,而如果下标存在的话,就会覆盖掉原来所传进去数组的值。
使用构造函数方式生成数组,请看示例:
1 | arr = new Array();//等价于 arr = [] ; |
这块知识点我们后面会深入学习,所以这边知识先带大家了解下,就不展开来讲解了。有的同学在问,说可不可以在一个数组里边插入另外的数,首先我们不知道要插入的这个位置原先是否有其他值的存在,这就涉及到了稀疏数组,所谓的稀疏,顾名思义就是,某些数组的内存不是连续的,存在一些空隙。例如:
1 | var larr = Array(10000); |
再来看个意想不到的东西,
1 | larr[1.5]=7; |
数组里面还可以写小数???结果是肯定的,可以把数组当作 map 来用,一个 Map对象在迭代时会根据对象中元素的插入顺序来进行 一个 for…of 循环在每次迭代后会返回一个形式为[key,value]的数组。比如:
1 | var a4=[]; |
那我们要如何去判断一个变量是否是数组呢,请看示例:
1 | var arr=[]; |
介绍下轮询数组的方法:
接下来我们相继学习三种轮询数组的方法,首先先学习前面两种,例如:
1 | arr=[0,1,2,3,4]; |
上述两段代码的运行结果为下图所示:
那么,思考一下,这两者之间的区别是什么呢?可以说,for(i) 的方法适用于数组里面所包含的元素全部都是一个一个,清清楚楚地写出来那种,而相反,for(in) 就用到了 map 。我们看个示例:
1 | arr=[0,1,,,4]; |
1 | arr=[0,1,,,4]; |
相信看完这两个代码大家就可以深入理解,说的通透点就是,for(i) 方法可以透过表面读取深度数据,按照数组的方式改变数组,而 for(in) 就只能读取表面看到的数据,使用 map 的方式返回所有非稀疏节点的 key。
这边涉及下第三种轮询数组的方法,forEach(f) :返回所有数字且非稀疏的节点的 value ,示例:
1 | arr.forEach(function(x)){ |
这种呢,我们下节会具体学,在这边就先简单带过。
4.12 for in
循环、轮询怎么去访问它。
4.13 concat()
concat() 函数用于把两个数组连接起来,合并成为一个数组,该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。这个点是相对好理解的,在本例中,我们创建数组并把它们连接起来:
1 | var arr=[3,2,1]; |
4.14 join()
把数组的里边的元素全部打开,组成一个字符串,元素通过指定的分隔符进行分割。
1 | console.log(arr3.join(','));//3,2,1,4,5,6 |
4.15 sort()
对数组里边的值进行排序,并且返回数组。例:创建一个数组,实现排序并且返回功能,
1 | arr3=[1,5,3,2,9,4,0,8]; |
4.2 数据结构
4.21 栈
栈相当于一个桶,原则上是后进先出,进栈 :push ,出栈 :pop 。
示例:
1 | //push-pop 尾进尾出 |
数组中还有一个有意思的地方, unshift-shift 。shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值,而 unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度,示例:
1 | /*shift 是把东西拿走移动,而 unshift 是把东西放进栈里面 unshift-shift 头进头出*/ |
4.22 队列
队列,采用的原则是先进先出,就好比如我们排队买东西,先排队的先付款走人,如果这时候是先来的最后买单,整个秩序就会完全乱掉。
示例:
1 | arr=['a','b','c']; |
4.3 数组中的成员函数
4.31 slice() 函数
slice() 函数是一个数组的函数,主要是节选数组中的一段,原数组是不受影响的。在本例中:
1 | //slice(iStart[,iEnd]) |
上面主要是 slice() 函数的写法,大家注意下, [] 里面的数可有可不有,不会产生影响。用法示例:
1 | var arr7=[0,1,2,3,4,5,6,7,8]; |
在这边呢,要注意下,正着数的时候是从 0 开始数的,而倒着数的时候是从 1 开始数的,这是所有程序员从今往后要习惯的事情。
4.32 splice() 函数
splice 主要用来对 js 中的数组进行操作,包括删除,添加,替换等,与 slice 函数不同的是 splice() 函数操作完原数组会受到影响。splice(iIndex[, iHowmany][,item1]),从 iIndex 开始,删除元素,删除几个由 iHowmany 决定, item1 是要插入的元素。示例:
1 | arr=[0,1,2,3,4,5,6,7,8]; |
4.33 delete
在JavaScript中,delete操作符用的比较少,但是还是比较重要的,我们在这块就举个例子说明下:
1 | //delete |
4.4 内存问题
4.41 值类型 vs 引用类型
回顾下之前学的值类型都有哪些呢?值类型:number , string , Boolean , undefined , null ; 而引用类型有:array , function , object 。这些涉及的是要在栈里分配还是队列分配。
这边的栈跟之前代码里面规约四则运算式的栈是不一样的,他们相同的点的是都有相同的数据的操作方式,就是后进先出,但是,事实上,之前的栈是 javascript 的对象,是你在写业务代码的时候,提供的一个栈的功能给你,而今天要学的这个栈,是 javascript 引擎在它的代码层运用,javascript 引擎并不是存在于 javascript , 而是 C++ 。
值类型存储在栈里面,引用类型存储在堆里面。因为值类型占用空间小、大小固定,通过按值来访问,属于被频繁使用的数据,而因为引用类型占据空间大、大小不固定。 如果存储在栈中,将会影响程序运行的性能; 引用类型在栈中存储了指针,该指针指向堆中该实体的起始地址。 当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。比如说:
1 | var a=5;//key 是变量名 a,value 是 5 |
这个变量 a ,arr 是存在于栈内存里边的,在栈内存里面的如果是值类型的话,就可以一起放进去,但是,如果后面是数组的话,数组放在堆内存里边,然后把它的地址放到栈里边,即 arr 的位置,可以说是,栈内存小,堆内存大。如下所示:
1 | // 基本数据类型-栈内存 |
4.42 栈 vs 堆
堆内存中对象,有一个引用计数,就是 GC ,就是用来垃圾收集,通俗点说就是知道什么东西已经不用,而什么东西还要用,内存回收的机制,最早是由 MS COM 实现的。GC 的作用时间,第一是,堆内存达到一定的门槛,第二是,定期。
谁的引用数是0,就被摧毁。而栈内存是当代码运行到栈内变量的作用域以外时,变量将被摧毁(就是内存里边删除了),如果变量被摧毁,引用数-1。来看个底下代码:
1 | //循环引用 |