林大妈的JavaScript基础知识(三):JavaScript编程(4)数组
数组,是一段线性分配的,具有非常高性能的数据结构。简单地说,数组以连续的空间存储,通过整数地计算偏移量访问其中的元素,将读取修改的时间复杂度降低至O(1),我们称之为猝发式存取。是不是非常期待?没错,像这样的好东西,JavaScript没有。
1. Array简介
但作为替代,JavaScript设计者想出了一个更方便但性能相对较低的方案,打印观察Array.prototype,会发现,设计者为我们提供的是一个array-like(类数组)的对象。在检索和更新属性上,Array就和普通的对象一模一样(也就是说要遍历所有属性),只是多了一个不可枚举的属性length来记录这个对象所表示的数组的长度。尽管Array对象的性能明显比数组要差,但是搭配上弱类型的JavaScript语言(当然,JavaScript中不存在传统的数组一部分原因也是因为这一点),它在使用上非常的方便。更贴心的是,设计者还为我们提供了许多内置的方法,可以快速解决其他语言费很大劲才能解决的问题。
2. 声明
数组的声明跟对象的声明很类似。我们可以用两种方法初始化一个数组:① 直接用 var array = []; 我们称之为数组字面量的方式来初始化;② 使用构造函数 var array = new Array(); 如果参数填入一个数字,则返回一个长度为这个数字的空数组,如果参数填入多个值,则返回一个按顺序保存了这些值的Array对象。
3. 修改
上面我们已经搞清楚了Array对象总体的结构,这样修改一个数组就可以转化为我们以前学到的修改一个对象属性的知识了。由于JavaScript的灵活性,除非你定义一个大到Infinity的数组,其他情况下均不会因为越界报错(Runtime Error也许是很多人的噩梦,反正是我的噩梦)。因此,假如我们现在有这样一个数组:
var myArray = [0, '1', true]
利用JavaScript会帮我们维护length属性这一特点(但为了编写易维护的代码,不推荐这些简单粗暴的做法),我们也可以做一些在别的语言中看起来不可思议的操作,例如:① myArray[8] = undefined 会直接把Array的长度扩展到8;② myArray.length = 0; 能直接清空数组。除了可以这样清空数组,还可以通过Array.prototype.splice方法完成清空,因此我们把目光放到数组的原型方法上来。
4. 原型方法(均以上面的myArray举例)
① indexOf
indexOf方法同时存在于Array.prototype和String.prototype中,可以用它来检测数组或字符串中是否存在对应的元素,如果存在,则返回它的下标,如果不存在,则返回-1:
console.log(myArray.indexOf(0)); //0
console.log(myArray.indexOf(1)); //-1
由于在数组中,1和'1'是两个不同的值,因此第二句返回结果-1。
② push、pop、unshift、shift
push朝数组末尾推入若干新元素,返回加入后数组的length。
pop弹出数组末尾的一个元素,返回被弹出的元素。
unshift朝数组开头推入若干新元素,返回加入后数组的length。
shift弹出数组开头的一个元素,返回被弹出的元素。
console.log(myArray.push('A', 'B')); //
console.log(myArray.pop()); //'B'
console.log(myArray.unshift('A', 'B')); //
console.log(myArray.shift()); //'A'
③ sort(这时候我们重新定义一下 myArray = [5, 2, 0, 10, 17, 25]; )
sort方法在原数组上动刀,这里我们期望将数组中的数按从小到大的顺序排列,sort函数可以帮我们做到这一点,但需要注意的是,sort函数默认把这些元素转化为字符串进行比较。因此这个数组排序以后的结果是这样的:
console.log(myArray.sort()); // [0, 10, 17, 2, 25, 5]
因此通常需要填入一个比较判断函数作为参数,下面传入一个箭头函数,按照我所定义的这个函数进行判断大小再排序:
console.log(myArray.sort((x, y)=>{return x - y;})); // [0, 2, 5, 10, 17, 25]
④ reverse和join
reverse方法在原数组上动刀,返回跟原来相反的数组;join方法相当于String.prototype.split方法的反函数,填入一个字符串参数,以这个参数将每个元素分隔开,合并成一个字符串并返回。这两个方法可以搭配使用来处理反向输出字符串的问题:
var str = "Hello world!"; console.log(str.split('').reverse().join(''));
//"!dlrow olleH"
console.log(str.split(' ').reverse().join(' '));
//"world! Hello"
这里由于每个函数返回值都是与其相对应的数组或字符串,可以直接在这个返回值上进行操作,因此我们还用到了链式调用的技巧。
⑤ slice和splice
这两个方法的区别和使用非常重要,又由于它们名字之间只差一个字母,缺少练习时我们很容易会将其混淆。
slice方法可以类比String.prototype.substring。指定一个参数n,它将返回一个新数组,这个数组中含有原数组下标从n到末尾的所有元素。指定两个参数a和b时,它将返回一个新数组,这个数组含有原数组下标从a到b的所有元素(不得不说,用中文来描述真的非常蹩脚):
var myArray = [1, 2, 3, 4, 5, 6, 7, 8]; var aNewArray = myArray.slice(3);
var aNewNewArray = myArray.slice(3, 5); console.log(aNewArray);
//[4, 5, 6, 7, 8]
console.log(aNewNewArray);
//[4, 5]
当然了,这两种操作都是含头不含尾的。如果不指定参数地使用slice,它将返回一个跟原数组一模一样的数组,利用这一点,我们可以用一句代码复制一个数组。
splice是修改一个数组的“万能方法”,要注意它将直接在原数组上动刀,返回值是被删除的元素组成的数组:
var myArray = ['CapAmerica', 'IronMan', 'Hulk', 'Thor'];
// param: 从第4个元素开始操作,删除0个元素,加入新元素'BlackWidow'
myArray.splice(3, 0, 'BlackWidow');
// 由于删除0个元素,它将返回一个空数组 console.log(myArray);
// ['CapAmerica', 'IronMan', 'Hulk', 'Thor', 'BlackWidow'] // param:从第二个元素开始,删除三个元素,加入这些新元素
myArray.splice(2, 3, 'ScarletWitch', 'Vision', 'CapMarvel');
//返回['Hulk', 'Thor', 'BlackWidow'] console.log(myArray);
//['CapAmerica', 'IronMan', 'ScarletWitch', 'Vision', 'CapMarvel']
⑥ concat
concat方法不会动原数组,而是返回新数组。它返回一个将原数组和所有你填入的参数都合并在一起的新数组,因此我们想到了可以用它搭配splice方法来实现一个JavaScript版本的快速排序:
function quickSort(arr) {
if(arr.length <=1) return arr;
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1)[0];
var left = [], right = [];
for(var i = 0; i < arr.length; i++) {
if(arr[i] < pivot) left.push(arr[i]);
else right.push(arr[i]);
};
return quickSort(left).concat(pivot, quickSort(right));
}
拓展:1. ES6 Map
我们都知道,在Array对象中,每个元素就是一个对象,通过键值对存储数据。但其中的键只能是字符串类型。我们渴求能用类似Number或其他基本数据类型作为键,这样的表达会更清晰。因此ES6为我们带来了Map数据结构,它还具有极高的查询效率。且看它的使用语法:
var myMap = new Map([['name', 'MotherLyn'],['score', 51]]);
观察发现,我们完全可以把它当做是一个两列n行的,对数据类型有规范的二维Array。它还有许多内置方法:set、get、has、delete。
要注意的是,Map中所有的键类似数据库中的primary key,也就是他们都是不可重复的,填入相同的键不同的值只会把以往的数据冲掉。
2. ES6 Set
Set与Map类似但又十分不同。说它相同是因为Set对数据类型也是存在划分的(3和‘3’是不同的两个键);说它不同是因为Set只一个值而不是一个键值对。因此使用Set时只需要传入一个一维的Array即可:
var mySet = new Set(['MotherLyn', 51]);
通过使用内置方法add、delete和has可以进行增删查改操作。
3. ES6 for of遍历方法
前面我们提到,使用for in遍历会将可枚举的原型属性一块遍历到。为了解决这个问题,ES6提出了for of遍历。它在使用上和for in是相同的,只是把in改成of而已。
总结:① JavaScript中的Array对象只是一个内建的对象,并不是传统意义上的数组,它在内存中不是连续的空间,因此只能遍历元素来进行查找修改,性能较差但灵活性非常好。
② Array.prototype中有非常多的方法,常用的有以上这些:indexOf、push、pop、unshift、shift、sort、reverse、join、slice、splice和concat。还有很多其他的方法,要善用这些方法只能靠多练习,慢慢积累。
③ ES6中的Map和Set都是类似Array的数据结构,它们都严格管理数据类型,但区别是Map以键值对的方式存储,Set只能存储值而不能存储键。
林大妈的JavaScript基础知识(三):JavaScript编程(4)数组的更多相关文章
- JavaScript基础知识整理(1)数组
第一:创建. 1,var arr= new Array(); //数组为空.长度为0. arr[0]="apple"; arr[1]="orange"; arr ...
- 【javascript基础知识】javascript中的转义序列和特殊数值常量
javascript的转义序列 \0 NUL字符(\u0000) \b 退格符(\u0008) \t 水平制表符(\u0009) \n 换行符(\u000A) \v 垂直制表符(\u000B) \f ...
- Javascript 基础知识学习--javascript中的参数传递都是按值传递的
ECMAScript中所有函数的参数传递都是按值传递的,无论参数是值类型还是引用类型的.过去我跟大多数人一样觉得跟传值类型相关. 自己写了一个测试的例子,确实如此 function add(a) { ...
- 学习javascript基础知识系列第二节 - this用法
通过一段代码学习javascript基础知识系列 第二节 - this用法 this是面向对象语言中的一个重要概念,在JAVA,C#等大型语言中,this固定指向运行时的当前对象.但是在javascr ...
- 学习javascript基础知识系列第三节 - ()()用法
总目录:通过一段代码学习javascript基础知识系列 注意: 为了便于执行和演示,建议使用chrome浏览器,按F12,然后按Esc(或手动选择)打开console,在console进行执行和演示 ...
- Javascript基础知识总结一
Javascript基础知识总结一 <!DOCTYPE html> <html> <head lang="en"> <meta chars ...
- JavaScript基础知识(一)
一.JavaScript基础 1.JavaScript用法: HTML 中的脚本必须位于 <script> 与 </script> 标签之间. 脚本可被放置在 HTML 页面的 ...
- JavaScript基础(三)
十三.JS中的面向对象 创建对象的几种常用方式 1.使用Object或对象字面量创建对象 2.工厂模式创建对象 3.构造函数模式创建对象 4.原型模式创建对象 1.使用Object或对象字面量创建对象 ...
- javascript 基础知识学习1
JavaScript 是脚本语言.浏览器会在读取代码时,逐行地执行脚本代码.而对于传统编程来说,会在执行前对所有代码进行编译.基础知识:1).JavaScript 对大小写敏感.JavaScript ...
- JavaScript 基础知识 - BOM篇
前言 本篇文章是JavaScript基础知识的BOM篇,如果前面的<JavaScript基础知识-DOM篇>看完了,现在就可以学习BOM了. 注意: 所有的案例都在这里链接: 提取密码密码 ...
随机推荐
- SpringMVC_One
SpringMVC_One SpringMVC的优势 (面试) 清晰的角色划分: 前端控制器(DispatcherServlet) 请求到处理器映射器(HandlerMapping) 处理器适配器(H ...
- MyBatis从入门到精通(五):MyBatis 注解方式的基本用法
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. @Select 注解 1.1 使 ...
- 漫谈Redis分布式锁实现
在Redis上,可以通过对key值的独占来实现分布式锁,表面上看,Redis可以简单快捷通过set key这一独占的方式来实现分布式锁,也有许多重复性轮子,但实际情况并非如此.总得来说,Redis实现 ...
- 浅入深出Vue:组件
组件在 vue开发中是必不可少的一环,用好组件这把屠龙刀,就能解决不少问题. 组件是什么 官方的定义: 组件是可复用的 Vue 实例,并且可带有一个名字. 官方的定义已经非常简明了,组件就是一个实例. ...
- 并发编程-concurrent指南-ReadWriteLock-ReentrantReadWriteLock(可重入读写锁)
几个线程都申请读锁,都能获取: import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantRea ...
- POJ 3183:Stump Removal(模拟)
http://poj.org/problem?id=3183 题意:有n个树桩,分别有一个高度h[i],要用Bomb把树桩都炸掉,如果炸的位置的两边树桩高度小于Bomb炸的树桩高度,那么小于树桩高度的 ...
- HDU 5521:Meeting(最短路)
http://acm.hdu.edu.cn/showproblem.php?pid=5521 Meeting Problem Description Bessie and her friend E ...
- 使用纯js写的一个分页
上图晒效果: 网上确实有很多分页的插件以及开源代码,单本是一个后台开发猿,前台css等样式还驾驭不住,所以就开始自己去写了.其实这个分页原理很简单,就是用ajax往后台传值(当前页码),后台使用lim ...
- django基础知识之定义模板:
定义模板 模板语言包括 变量 标签 { % 代码块 % } 过滤器 注释{# 代码或html #} 变量 语法: {{ variable }} 当模版引擎遇到一个变量,将计算这个变量,然后将结果输出 ...
- C# Linq 常用查询操作符
限定操作: 1. All:用来确定是否序列中的所有元素都满足条件 2. Any:用来确定序列是否包含任何元素,有参方式用来确定序列中是否有元素满足条件 3. Contains:方法用来确定序列是否包含 ...