Array.isArray,Array.from, Array.entries,Array.incledes的实现原理,分别手写方法

本文围绕ES6中数组的相关方法展开。介绍了Array.isArray的基本与非基本使用,还给出了手写实现方法;阐述了Array.from的三个参数;讲解了Array.entries等方法的作用及手写实现;最后介绍了Array.prototype.includes判断数组元素的功能及手写方法。

1、Array.isArray

基本使用

const arr = ["1"];
console.log("isArray:", Array.isArray(arr)); // true

非基本使用:

const arr = ["1"];
const proxy = new Proxy(arr, {});
console.log(proxy); // Proxy {0: '1'}
console.log("isArray:", Array.isArray(proxy)); // true

为什么上面 Array.isArray 判断代理对象是否数组返回 true 呢?
我们可以下面看看打印得到的状态

const arr = ["1"];
const proxy = new Proxy(arr, {});

console.log("__proto__:", proxy.__proto__ === Array.prototype); // __proto__: true
console.log("instanceof:", proxy instanceof Array); // instanceof: true
console.log("Proxy toString:", Object.prototype.toString.call(Proxy)); // Proxy toString: [object Function]
console.log("proxy toString:", Object.prototype.toString.call(proxy)); // proxy toString: [object Array]
console.log("Proxy.prototype:", Proxy.prototype); // Proxy.prototype: undefined
console.log("proxy instanceof Proxy:", proxy instanceof Proxy); // 报错

实际Array.isArray判断的是Proxy里面的target属性
在这里插入图片描述

如果使用的语法不支持Array.isArray,我们可以手写一个isArray的方法: Object.prototype.toString

Array.isArray = function(obj) {
	return Object.prototype.toString.call(obj) === "[object Array]";
}
const arr = ["1"];
const proxy = new Proxy(arr, {});
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(proxy)); // true

instanceof 可以正确判断对象的类型,其内部运行原理是:判断其原型链中是否能找到该类型的原型
注意instanceof 只能正确判断引用类型数据,而不能判断基本类型。 instanceof可以用来测试一个对象在其原型链上是否存在一个构造函数的prototype属性。

console.log(2 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false 
 
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                   // true

根据instanceof的原理,可以使用instanceof来手写Array.isArray方法

Array.isArray = function (obj) {
  if (typeof obj !== "object" || obj === null) {
    return false;
  }
  return obj instanceof Array;
};
const arr = ["1"];
const proxy = new Proxy(arr, {});
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(proxy)); // true

Array.from

有三个参数

  • arrayLike:类数组对象或者可遍历对象(Map、Set)等
  • mapFn:可选参数,在最后生成数组后执行一次map方法后返回
  • thisArg:可选参数,实际是Array.from(obj).map(mapFn, thisArg)
console.log("Array.from1:", Array.from({}));  // Array.from1: []
console.log("Array.from2:", Array.from("")); // Array.from2: []
console.log("Array.from3:", Array.from({ a: 1, length: "10" }));
 // Array.from3: (10) [undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]
console.log("Array.from4:", Array.from({ a: 1, length: "ss" })); // Array.from4: []
console.log("Array.from5:", Array.from([NaN, null, undefined, 0])); // Array.from5: (4) [NaN, null, undefined, 0]

根据之前文章《数组高级用法》里面写的类数组特征,可以做如下处理:

// 最大的整数
let maxSafeInteger = Math.pow(2, 32) - 1; 

// 获取合法有效的整数
let ToInterOrInfinity = function(value) {
	let number = Number(value);
	if (isNaN(number)) return 0;
	if (number === 0 || !isFinite(number)) return number;
	return (number > 0 ? 1 : -1) *Math.floor(Math.abs(bumber));
}

// 获取长度
let ToLength = function (value) {
	let len = ToInterOrInfinity (value);
	return Math.min(Math.max(len, 0), maxSafeInteger);
}

let isCallable = function(fn) {
	return typeof fn === 'function' || Object.toString.call(fn) === '[object Function]'
}

Array.from = function(arrayLink, mapFn, thisArg) {
	let C = this;
	//判断对象是否为空
	if (arrayLike == null) {
	    throw new TypeError("Array.from requires an array-like object - not null or undefined");
	}
	//检查mapFn是否是方法
    if (typeof mapFn !== "function" && typeof mapFn !== "undefined") {
		throw new TypeError(mapFn + "is not a function");
	}
	let items = Object(arrayLike);
	//判断 length 为数字,并且在有效范围内。
	let len = ToLength(items.length);
	if (len <= 0) return [];
	let A = isCallable(C) ? Object(new C(len)) : new Array(len);
	for (let i = 0; i < len; i++) {
	    let value = items[i];
	    if (mapFn) {
	      A[i] = typeof thisArg === "undefined" ? mapFn(value, i) : mapFn.call(thisArg, value, i);
	    } else {
	      A[i] = value;
	    }
	}
  	return A;
}

Array.entries、Array.prototype.entries

  • 作用:返回一个新的 Array Iterator 对象,该对象包含数组中每个索引的键/值对
const arr = ["a", "b", "c"];

const iter = arr.entries();
console.log("iter:", iter); // iter: Array Iterator {}
// next函数访问
console.log("iter.next():", iter.next());
console.log("iter.next():", iter.next());
console.log("iter.next():", iter.next());
console.log("iter.next():", iter.next());
// for of迭代
for (let [k, v] of arr.entries()) {
  console.log(k, v);
}

在这里插入图片描述
done 表示遍历是否结束,value 返回当前遍历的值
自己来实现下这个方法:

Array.prototype.entries = function () {
	 // 转换对象(引用数据类型返回自身)
  const O = Object(this);
  let index = 0;
  const length = O.length;
  return {
    next() {
      if (index < length) {
        return { value: [index, O[index++]], done: false };
      }
      return { value: undefined, done: true };
    },
  };
}

上面手写的方法无法使用for of 不能正常执行,我们需要添加Symbol.iterator方法返回next即可实现

Array.prototype.entries = function () {
	 const O = Object(this);
	 let index = 0;
	 const length = O.length;
	 
	 function next() {
	    if (index < length) {
	      return { value: [index, O[index++]], done: false };
	    }
	    return { value: undefined, done: true };
	  }
	return {
	    next,
	    [Symbol.iterator]() {
	      return {
	        next,
	      };
    },
  };
}

数组还有 Array.prototype.keys,Array.prototype.keys,如果我们像上面这样写等于每个方法里面都要实现[Symbol.iterator],我们可以抽离其逻辑,代码如下:

Array.prototype[Symbol.iterator] = function () {
  const O = Object(this);
  let index = 0;
  const length = O.length;

  function next() {
    if (index < length) {
      return { value: O[index++], done: false };
    }
    return { value: undefined, done: true };
  }

  return {
    next,
  };
};

Array.prototype.entries = function () {
  const O = Object(this);
  const length = O.length;
  let entries = [];

  for (let i = 0; i < length; i++) {
    entries.push([i, O[i]]);
  }

  const itr = this[Symbol.iterator].bind(entries)();
  return {
    next: itr.next,
    [Symbol.iterator]() {
      return itr;
    },
  };
};
Array.prototype.keys = function () {
  const O = Object(this);
  const length = O.length;
  let keys = [];

  for (let i = 0; i < length; i++) {
    keys.push([i]);
  }

  const itr = this[Symbol.iterator].bind(keys)();
  return {
    next: itr.next,
    [Symbol.iterator]() {
      return itr;
    },
  };
};

Array.prototype.values = function () {
  const O = Object(this);
  const length = O.length;
  let keys = [];

  for (let i = 0; i < length; i++) {
    keys.push([O[i]]);
  }

  const itr = this[Symbol.iterator].bind(keys)();
  return {
    next: itr.next,
    [Symbol.iterator]() {
      return itr;
    },
  };
};

Array.prototype.includes

  • 判断数组是否含有某值,可判断NaN
const arr = [1, 2, 3, { a: 1 }, null, undefined, NaN, ""];

console.log("includes null:", arr.includes(null)); // includes null: true
console.log("indexOf null:", arr.indexOf(null)); // indexOf null: 4

console.log("includes NaN:", arr.includes(NaN)); // includes NaN: true
console.log("indexOf NaN:", arr.indexOf(NaN)); // indexOf NaN: -1

手写该方法

Number.isNaN = function (param) {
  if (typeof param === "number") {
    return isNaN(param);
  }
  return false;
};
Array.prototype.includes = function (item, fromIndex) {
  // call, apply调用,严格模式
  if (this == null) {
    throw new TypeError("无效的this");
  }

  let O = Object(this);
  let len = O.length >> 0;
  if (len <= 0) {
    return false;
  }

  const isNAN = Number.isNaN(item);
  for (let i = 0; i < len; i++) {
    if (O[i] === item) {
      return true;
    } else if (isNAN && Number.isNaN(O[i])) {
      return true;
    }
  }
  return false;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值