JS基础--Array

数组

JavaScript中对数组的定义

数组的标准定义是:一个存储元素的线性集合(collection),元素可以通过索引来任意存 取,索引通常是数字,用来计算元素之间存储位置的偏移量。

创建数组的几种方法
  • var numbers = [];
  • var numbers = [1,2 ,3];
  • var numbers = new Array(); //调用 Array 的构造函数创建数组
  • var numbers = new Array(1, 2 , 3); //[1, 2, 3]
  • var numbers = new Array(3); //[undefined, undefined, undefined]

用Array 的构造函数创建数组时,不传入参数是创建一个空数组,传入一组元素时是为这个数组设置了初始值,传入一个参数时是指定创建的这个数组的长度。

判断是否是一个数组

Array.isArray() 来判断一个对象是否是数组

延伸知识点—-类型检测

JavaScript有四种方法,可以确定一个值到底是什么类型。

  • typeof运算符 —-返回的都是字符串
    • ‘number’
    • ‘string’
    • ‘boolean’
    • ‘undefined’
    • ‘object’
    • ‘function’
  • instanceof运算符 —-表示指定对象是否为某个构造函数的实例

    instanceof只能用来判断对象和函数,不能用来判断字符串和数字等

    instanceof运算符的左边是实例对象,右边是构造函数。它会检查右边构建函数的原型对象,是否在左边对象的原型链上。因此,下面两种写法是等价的。

      var v = new Vehicle();
      v instanceof Vehicle // true
    
      v instanceof Vehicle
      // 等同于
      Vehicle.prototype.isPrototypeOf(v)
    
      eg:
      v instanceof Function
      v instanceof Object
      v instanceof Array
      v instanceof Date
      ......
    
  • constructor

    每一个对象实例都可以通过 constrcutor 对象访问它的构造函数

    但是constructor是不可靠的,在继承的时候会出现问题,看代码

      function A () {}
      function B () {}
      A.prototype = new B(); //A继承B
      var aObj = new A();
    
      AObj.constructor === B // true
      AObj.constructor === A // false
    
      而instanceof方法不会出现该问题,对象直接继承和间接继承的都会报true:
    		
      aObj instanceof A // true 
      aObj instanceof B // true
    

    解决construtor的问题通常是让对象的constructor手动指向自己

  • Object.prototype.toString方法 —-用来判断一个对象是否是另一个对象的原型

    通用但很管繁锁的方法

    
      Object.prototype.toString.call(a) === '[object String]';
      Object.prototype.toString.call(b) === '[object Number]';
      Object.prototype.toString.call(c) === '[object Array]';
      Object.prototype.toString.call(d) === '[object Date]';
      Object.prototype.toString.call(e) === '[object Function]';
    
    

一个检测数据类型的对象:

var valide = (function(){
    // 是否是字符串
    function isString(value){
        return Object.prototype.toString.call(value) == "[object String]";
    }
    // 是否是数字
    function isNumber(value){
        return Object.prototype.toString.call(value) == "[object Number]";
    }
    // 是否是布尔值
    function isBoolean(value){
        return Object.prototype.toString.call(value) == "[object Boolean]";
    }
    // 是否undefined
    function isUndefined(value){
        return Object.prototype.toString.call(value) == "[object Undefined]";
    }
    // 是否是null
    function isNull(value){
        return Object.prototype.toString.call(value) == "[object Null]";
    }
    // 是否数组
    function isArray(value){
        return Object.prototype.toString.call(value) == "[object Array]";
    }
    // 是否是函数
    function isFunction(value){
        return Object.prototype.toString.call(value) == "[object Function]";
    }
    // 是否是对象
    function isObject(value){
        return Object.prototype.toString.call(value) == "[object Object]";
    }
    // 是否是正则表达式
    function isRegExp(value){
        return Object.prototype.toString.call(value) == "[object RegExp]";
    }
    // 是否是日期对象
    function isDate(value){
        return Object.prototype.toString.call(value) == "[object Date]";
    }
    return {
        isString: isString,
        isNumber: isNumber,
        isBoolean: isBoolean,
        isUndefined: isUndefined,
        isNull: isNull,
        isArray: isArray,
        isFunction: isFunction,
        isObject: isObject,
        isRegExp: isRegExp,
        isDate: isDate
    };
})();

valide.isArray([])   //true
字符串转数组
  • split()
数组的浅拷贝
var nums = [];
     for (var i = 0; i < 10; ++i) {
        nums[i] = i+1;
     }
 var samenums = nums;

nums[0] = 400; 
console.log(samenums[0]) // 显示 400

当把一个数组赋给另外一个数组时,只是为被赋值的数组增加了一个新的引用。当 你通过原引用修改了数组的值,另外一个引用也会感知到这个变化。这种行为被称为浅复制,新数组依然指向原来的数组。

数组的深拷贝
function copy(arr1, arr2) {
   for (var i = 0; i < arr1.length; ++i) {
      arr2[i] = arr1[i];
   }
}

var nums = [];
for (var i = 0; i < 10; ++i) {
   nums[i] = i+1;
}

var samenums = [];
copy(nums, samenums);

nums[0] = 400;
console.log(samenums[0]) // 显示 1
存取函数
  • indexOf()

    如果目标数组包含该参数,就返回该元素在数组中的索引;如果不包含,就返回 -1,如果在数组中有多个重复的值 ,indexOf只会返回重复的第一个值的索引。

  • lastIndexOf()

    返回相同元素中最后一个元素的索引,如果没找到相同元素,则返回 -1

数组转字符串
  • join()
  • toString()
由已有数组创建新数组
  • concat 合并多个数组生成一个新的数组
  • splice 截取一个数组的子集创建一个新数组
为数组添加元素
  • push
  • unshift

也可以使用数组的 length 属性为数组添加元素,但 push() 方法看起来更直观

var nums = [1,2,3,4,5];
nums[nums.length] = 6;
console.log(nums); // 1,2,3,4,5,6

和在数组的末尾添加元素比起来,在数组的开头添加元素更难。如果不利用数组提供的可 变函数,则新的元素添加进来后,需要把后面的每个元素都相应地向后移一个位置。下面的代码展示了这一过程:

var nums = [1,2,3,4,5];
for(var i = nums.length; i >= 0; --i) {
	nums[i] = nums[i-1]
}
console.log(nums)   //(6) [undefined, 1, 2, 3, 4, 5]
nums[0] = 1; 
console.log(nums)  //(6) [1, 1, 2, 3, 4, 5]

随着数组中存储的元素越来越多,上述代码将会变得越来越低效。

从数组中删除元素
  • pop
  • shift

用一代码实现shift()方法

var nums = [1,2,3,4,5];
for (var i = 0; i < nums.length; i++) {
	nums[i] = nums[i +1]; 
}
nums.length = nums.length - 1;
console.log(nums);   // (5) [2, 3, 4, 5]
从数组中间位置添加和删除元素
  • splice
为数组排序
  • reverse() //反转数组
  • sort()
    • 如果元素是字符串类型,那么数组的可变方法 sort() 就非常好使:
    • 如果数组元素是数字类型,sort() 需要传一个比较函数
 function compare(num1, num2) {
     return num1 - num2;
}
var nums = [3,1,2,100,4,200];
nums.sort(compare);
console.log(nums); // 1,2,3,4,100,200

迭代器方法

不生成新数组的迭代器方法
  • forEach

    它们要么对于数组中的每个元素执行某种操作,要么返回一个值

    该方法接受一个函数作为参数,对数组中的每个元素 使用该函数。

function square(num) {
	document.write(num, num * num);
}
var nums = [1,2,3,4,5,6,7,8,9,10];
nums.forEach(square);
  • every()

    该方法接受一个返回值为布尔类型的函数,对数组中的每 个元素使用该函数。如果对于所有的元素,该函数均返回 true,则该方法返回 true。

  • some()

    该方法接受一个返回值为布尔类型的函数,只要有一个元素使得该函数返回 true,该方法就返回 true。

  • reduce()

    reduce() 方法接受一个函数,返回一个值。该方法会从一个累加值开始,不断对累加值和 数组中的后续元素调用该函数,直到数组中的最后一个元素,最后返回得到的累加值。

function add(num1, num2) {
	return num1 + num2;
}
var nums = [1,2,3,4,5,6,7,8,9,10]; 
var sum = nums.reduce(add); 
console.log(sum)  // 55

reduce() 方法和 add() 函数一起,从左到右,依次对数组中的元素求和,其执行过程如下 所示:

add(1,2) -> 3
add(3,3) -> 6
add(6,4) -> 10
add(10,5) -> 15
add(15,6) -> 21
add(21,7) -> 28
add(28,8) -> 36
add(36,9) -> 45
add(45,10) -> 55
  • reduceRight()

    和 reduce() 方法不同,它是从右到左执行, reduceRight() 方法可以将数组中的元素进行翻转。

生成新数组的迭代器方法
  • map()

map() 和 forEach() 有点儿像,对数组中的每个元素使用某个函数。两者的区别是 map() 返回一个新的数组,新数组的元素是对原有元素应用某个函数得到的结果。

  • filter()

filter() 和 every() 类似,传入一个返回值为布尔类型的函数。

和 every() 方法不同的是, 当对数组中的所有元素应用该函数,结果均为 true 时,该方法并不返回 true,而是返回 一个新数组,该数组包含应用该函数后结果为 true 的元素。

二维和多维数组

创建二维数组

二维数组类似一种由行和列构成的数据表格。在 JavaScript 中创建二维数组,需要先创建 一个数组,然后让数组的每个元素也是一个数组。

Array.matrix = function(numrows, numcols, initial) {
    var arr = [];
    for (var i = 0; i < numrows; ++i) {
        var columns = [];
        for (var j = 0; j < numcols; ++j) {
           columns[j] = initial;
        }
        arr[i] = columns;
    }
    return arr;
}

var names = Array.matrix(3,4,0);
console.log(names);

//输出结果:
(3) [Array(4), Array(4), Array(4)]
0:(4) [0, 0, 0, 0]
1:(4) [0, 0, 0, 0]
2:(4) [0, 0, 0, 0]

这种循环同理做一个九九乘法表:

document.write('<table>');
 for (var i = 1; i < 10; i++) {
    document.write('<tr>');
    for (var j = 1; j <= i; j++) {
	document.write('<td>' + i  + '*' + j + '=' + (i * j) + '</td>');
    }
    document.write('</tr>');
}
document.write('</table>');

结果如下图:

99乘法表

处理二维数组的元素

有两种最基本的方式

  • 按行访问
var grades = [[89, 77, 78],[76, 82, 81],[91, 94, 89]];
 for (var row = 0; row < grades.length; row++) {
 	var num = 0;
 	var average = 0;
 	for (var col = 0; col < grades[row].length; col++) {
 		num = num + grades[row][col];
 	}
 	average = num / grades[row].length;
 	document.write("Student " + parseInt(row+1) + " average: " +
      average.toFixed(2));
}

Student 1 average: 81.33
Student 2 average: 79.67
Student 3 average: 91.33
  • 按列访问
var grades = [[89, 77, 78],[76, 82, 81],[91, 94, 89]];
		 
for (var col = 0; col < grades.length; col++) {
	var num = 0;
	var average = 0;
	for (var row = 0; row < grades[col].length; row++) {
		num = num + grades[row][col];
	}
	average = num / grades[col].length;
	document.write("Student " + parseInt(col+1) + " average: " + average.toFixed(2));
}

Student 1 average: 85.33
Student 2 average: 84.33
Student 3 average: 82.67
参差不齐的数组–同按行访问
var grades = [[89, 77],[76, 82, 81],[91, 94, 89, 99]];
for (var row = 0; row < grades.length; ++row) {
    var total = 0;
    var average = 0.0;
    for (var col = 0; col < grades[row].length; ++col) {
       total += grades[row][col];
    }
    average = total / grades[row].length;
    document.write("Student " + parseInt(row+1) + " average: " + average.toFixed(2));
}
对象数组
function Point(x,y) {
    this.x = x;
    this.y = y;
}

function displayPts(arr) {
    for (var i = 0; i < arr.length; ++i) {
       print(arr[i].x + ", " + arr[i].y);
    }
}

var p1 = new Point(1,2);
var p2 = new Point(3,5);
var p3 = new Point(2,8);
var p4 = new Point(4,4);
var points = [p1,p2,p3,p4];

for (var i = 0; i < points.length; ++i) {
   print("Point " + parseInt(i+1) + ": " + points[i].x + ", " + points[i].y);
}

var p5 = new Point(12,-3);

points.push(p5);
print("After push: ");
displayPts(points);
points.shift();
print("After shift: ");
displayPts(points);



Point 1: 1, 2 Point 2: 3, 5 Point 3: 2, 8 Point 4: 4, 4 After push: 1,2
3,5
2,8
4,4
12, -3 
After shift: 3,5
2,8 
4,4 
12, -3
对象中的数组
function weekTemps() {
   this.dataStore = [];
   this.add = add;
   this.average = average;
}

function add(temp) {
   this.dataStore.push(temp);
}

function average() {
   var total = 0;
   for (var i = 0; i < this.dataStore.length; ++i) {
      total += this.dataStore[i];
   }
   return total / this.dataStore.length;
}
var thisWeek = new weekTemps(); thisWeek.add(52);
thisWeek.add(55);
thisWeek.add(61);
thisWeek.add(65);
thisWeek.add(55);
thisWeek.add(50);
thisWeek.add(52);
thisWeek.add(49); 

document.write(thisWeek.average()); // 显示 54.875