数组扩展方法(二)

以下将对Array.prototype上的方法进行整理,es5中数组遍历的方法在 数组扩展方法(一)可以查看

会改变原始数组

以下方法都在Array.prototype原型上

  1. push 数组尾部插入元素
  2. shift 数组首部删除元素
  3. unshift 向数组首部添加元素
  4. pop 数组尾部删除
  5. reverse 反转数组
  6. splice 删、插、替换数组
  7. sort 数组排序
  8. toString 数组转换为字符串

不会改变原数组

以下方法都在Array.prototype原型上
indexOf 获取元素索引值
lastIndexOf 获取元素索引值
conat 拼接数组
slice 截取数组
join 数组按指定符号转为字符串

工具方法

Array.prototype.indexOf

indexOf的英文意思为索引值为。
Array.prototype.indexOf:返回在数组中可以找到给定元素的第一个索引。
indexOf方法的注意事项:

  1. indexOf方法使用的是===全等运算。
  2. 注意是指定元素在数组中第一次出现的索引值。
  3. 如果指定元素不存在的话,返回-1
  4. 返回值是指定元素在数组中第一次出现的索引值,不存在时返回-1
  5. 参数问题:
    1. searchElement:指定查找的元素。
    2. fromIndex:开始查找的位置。如果该索引值大于数组长度,意味着不会在数组中查找,返回-1。如果参数中提供的索引值是一个负值,则意味将其作为数组末尾的一个抵消,即-1相当于arr.length - 1-2相当于arr.length - 2。注意:虽然fromIndex可能是负数,但是查询的顺序依旧是从前往后。如果抵消后的索引值小于0,则整个数组都会被查询,默认值为0。
const array = [2, 9, 9];

array.indexOf(2);     // 0
array.indexOf(7);     // -1
array.indexOf(9, 2);  // 2
array.indexOf(2, -1); // -1
array.indexOf(2, -3); // 0
重写indexOf
Array.prototype.myIndexOf = function(){
  let arr = this,
      len = arr.length,
      item = null,
      arg1 = arguments[0],
      arg2 = arguments[1] || 0,
      i = 0;
  
  //   fromIndex:开始查找的位置。如果该索引值大于数组长度,意味着不会在数组中查找,返回-1。
  //   如果参数中提供的索引值是一个负值,则意味将其作为数组末尾的一个抵消,即-1相当于arr.length - 1,-2相当于arr.length - 2。
  //  注意:虽然fromIndex可能是负数,但是查询的顺序依旧是从前往后。如果抵销后的索引值小于0,则整个数组都会被查询,默认值为0。
  if(arg2 > len) return -1;
  if(!arg1) return -1;
  i  = arg2 < 0 ? (arg2 + len < 0 ? 0 : arg2 + len) : arg2;
  // 这里说明一点,使用for...i...遍历,会遍历出稀松数组,建议使用for...in...遍历
  for(i ; i < len ; i++){
    if(arr[i] === arg1 ){
      return i;
    }
    if(i === len - 1 && arg1 !== arr[i]){
      return -1;
    }
  }
}
const array = [2, 9, 9];
console.log(array.myIndexOf(2));   // 0
console.log(array.myIndexOf(7));     // -1
console.log(array.myIndexOf(9, 2));  // 2
console.log(array.myIndexOf(2, -1)); // -1
console.log(array.myIndexOf(2, -3)); // 0
Array.prototype.lastIndexOf

lastIndexOf的英文意思是最后的索引值。
Array.prototype.lastIndexOf:方法返回指定元素在数组中最后一个索引值。如果不存在则返回-1。从数组的后面项向前查找,从fromIndex处开始。
lastIndexOf需要我们注意的有以下几点:

  1. 参数的问题
    1. searchElement:被查找的元素。
    2. fromIndex:从此位置开始逆向查找。默认为数组的长度减1(arr.length - 1 ),即整个数组都被查找。如果该值大于或等于数组的长度,则整个数组会被查找。如果为负值,数组仍然会从后向前查找。如果该负值的绝对值大于数组长度,则方法返回-1,即数组不会被查找。
  2. 返回值是数组在该元素最后一次出现的索引值,如未找到返回-1.
  3. lastIndexOf使用严格相等===比较searchElement和数组中元素。
var array = [2, 5, 9, 2];
var index = array.lastIndexOf(2);
// index is 3
index = array.lastIndexOf(7);
// index is -1
index = array.lastIndexOf(2, 3);
// index is 3
index = array.lastIndexOf(2, 2);
// index is 0
index = array.lastIndexOf(2, -2);
// index is 0
index = array.lastIndexOf(2, -1);
// index is 3
重写 lastIndexOf
Array.prototype.myLastIndexOf = function(){
  let arr = this,
      len = arr.length,
      arg1 = arguments[0],
      arg2 = arguments[1] || len,
      i = 0;
  if(!arg1 || Math.abs(arg2) > len) return -1;
  // if(arg2 >= len) i = len;
  // if(arg2 < 0 ) i = arg2 + len;
  // if(0 <= arg2 < len) i = arg2;
  i = arg2 >= len ? len : (arg2 < 0 ?(arg2 + len):arg2);

  for (i; i >= 0; i--) {
    if(arg1 === arr[i]) return i;
    if(i == 0 && arg1 !== arr[i]) return -1;
  }
}

var array = [2, 5, 9, 2];
console.log(array.myLastIndexOf(2)); // 3
console.log(array.myLastIndexOf(7)); // -1
console.log(array.myLastIndexOf(2, 3)); // 3
console.log(array.myLastIndexOf(2, 2)); // 0
console.log(array.myLastIndexOf(2, -2)); // 0
console.log(array.myLastIndexOf(2, -1)); // 3
比较新颖的示例

使用lastIndexOf查找到一个元素在数组中所有的索引,并使用push将所有添加到另一个数组中。

var indices = [];
var array = ['a', 'b', 'a', 'c', 'a', 'd'];
var searchElement = 'a';
var idx = array.lastIndexOf(searchElement);
while(idx !== -1) {
	indices.push(idx);
	idx = (idx > 0 ? array.lastIndexOf(searchElement, idx - 1) : -1);
}
console.log(indices);
Array.prototype.join

Array.prototype.join:将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。
join方法需要我们注意的有以下几点:

  1. 参数问题:
    1. separator分隔符,如果需要,将分隔符转换为字符串。如果省略该值,数组元素用,分隔。如果separator是空字符串"",则所有元素之间都没有任何字符
  2. 返回值是一个数组元素连接的字符串。如果arr.length为0,则返回空字符串。
  3. 如果一个元素是undefinednull,它被转换空字符串。
  4. 这是数组转为字符串的一种方式。
示例

连接类数组对象。

function f(a, b, c) {
	var s = Array.prototype.join.call(arguments);
	console.log(s); // '1, a, true'
}
f(1, 'a', true)

undefined/null被转换为空字符串。

var arr = [undefined, null, 1];
console.log(arr.join()); // ,,1
数组扁平化

join方法带参数的话,只能将数组扁平化一层
join方法不带参数的话,直接将数组扁平化

// 带参数的话,只能将数组扁平化一层
console.log([1,[2,[3,4,5]]].join('-')); // 1-2, 3, 4, 5

// 不带参数的话,直接数据扁平化,默认分隔
console.log([1,[2,[3,4,5]]].join()); // 1, 2, 3, 4, 5
重写join方法
Array.prototype.myJoin = function (/*separator*/) {
	var t = this,
		// 保存数组索引值
		index = t.length - 1,
		// 分隔符
		separator = arguments[0] !== undefined ? arguments[0] : ',',
		// 结果字符串
		restr = '';

	// 遍历需要分割的数组
	for (var key in t) {
		if (t.hasOwnProperty(key)) {
			// 处理undefined和null问题,转为空字符串
			if (t[key] === undefined || t[key] === null) {
				restr += separator;
				continue;
			}
			// 字符串最后一个separator问题
			if (key == index) {
				restr += t[key]
				return restr;
			}
      console.log(t[key]);
			restr += t[key] + separator;
		}
	}
}

// 当一个数组被作为文本值或者进行字符串拼接操作时,将会自动调用其 toString 方法。
Array.prototype.toString

Array.prototype.toString:方法返回一个字符串,表示指定的数组及其元素。
toString方法需要我们注意的有以下几点:

  1. 无参数。
  2. 返回值是一个表示数组所有元素的字符串。
toString 方法描述

Array对象覆盖了ObjecttoString方法。对于数组对象,toString方法在内部调用join方法拼接数组中的元素中并返回一个字符串,其中包含逗号分割的每个数组元素。如果join方法不可用,或者它不是一个函数,将使用Object.prototype.toString代替,并返回[object Array]
当一个数组被作为文本值或者进行字符串拼接操作时,将会自动调用其 toString 方法。

const arr = [];
arr.join = 1;
console.log(arr.toString());

console.log(Array.prototype.toString.call({join: () => 1})); // 1
数组扁平化

toString方法能够将多维数组进行扁平化。

console.log(Array.prototype.toString.call([1, 2, 3, 4])); // 1,2,3,4
console.log(Array.prototype.toString.call([[1, 2], [3, 4]])); // 1,2,3,4
Array.from

Array.from()方法对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例对象。
Array.from()需要注意的有以下几点

  1. 参数问题
    1. arrayLike:想要转为数组的伪数组对象或可迭代对象。
    2. mapFn:如果制定了该参数,新数组中的每个元素都会执行该回调函数。
    3. thisArg:可选参数,执行回调mapFnthis对象。

返回值:一个新的数组实例。

// 下面是一个类似数组的对象,Array.from将它转为真正的数组。
let arrayLike = {
  '0':'a',
  '1':'b',
  '2':'c',
  length:3
}
// es6 写法
let arr2 = Array.from(arrayLike) ; // ['a','b','c']

实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组。

let dom = document.querySelectorAll('div');
let domArray = Array.from(dom)

只要是部署了 Iterator 接口的数据结构,Array.from都能将其转为数组。

console.log(Array.from('hello')); //  ['h', 'e', 'l', 'l', 'o']
console.log('hello'.__proto__);

image.png

let namesSet = new Set(['a','b','b']);
console.log(Array.from(namesSet));
// ['a', 'b']

上面代码中,字符串和 Set 结构都具有 Iterator 接口,因此可以被Array.from转为真正的数组。
如果参数是一个真正的数组,Array.from会返回一个一模一样的新数组。
值得提醒的是,扩展运算符(…)也可以将某些数据结构转为数组。

function foo(){
  const arg = [...arguments]
}

[...document.querySelectorAll('div')]

扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。Array.from方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。

console.log(Array.from({length:3}));
// [undefined, undefined, undefined]

上面代码中,Array.from返回了一个具有三个成员的数组,每个位置的值都是undefined。扩展运算符转换不了这个对象。

  1. 重写from方法
Array.myFrom = function (iteratorObj, callback) {
	// this指向
	var thisArg = arguments[2] !== undefined ? arguments[2] : window;
	var isIterable = iteratorObj[Symbol.iterator] || iteratorObj.hasOwnProperty('length');

	// 创建一个数组
	var resArr = [];
	// 计步器
	var index = 0;
	if (isIterable) {
		if (iteratorObj.hasOwnProperty('length')) {
			for (var key in iteratorObj) {
				if (typeof callback === 'function') {
					key !== 'length' ? resArr[index] = callback.apply(thisArg, [iteratorObj[key], index++]) : '';
				}else{
          key !== 'length' ? resArr[index++] = iteratorObj[key] : ''
        }
			}
		} else {
			for (var value of iteratorObj) {
				if (typeof callback === 'function') {
					resArr[index] = callback.apply(thisArg, [value, index++]);
				}else{
          resArr[index++] = value;
        }
			}
		}
		index = 0;
	}
	return resArr;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

️不倒翁

你的鼓励就是我前进的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值