高频手写js js基础
1 数组扁平化
方法一:使用flat()
const res1 = arr.flat(Infinity)
方法二:利用正则
const res2 = JSON.stringify(arr).replace(/\[|\]/g,'').split(',');
但数据类型都会变为字符串
方法三:正则改良版本
const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g,'') + ']');
方法四:使用reduce
const flatten = arr => {
return arr.reduce((pre,cur) =>{
return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
},[])
}
const res4 = flatten(arr);
方法五:函数递归
const res5 = [];
const fn = arr => {
for(let i = 0; i < arr.length; i ++){
if(Array.isArray(arr[i])){
fn(arr[i]);
} else{
res5.push(arr[i]);
}
}
}
fn(arr);
2 数组去重
方法一:利用Set
const res1 = Array.from(new Set(arr));
方法二:双层for循环+splice
const unique1 = arr => {
let len = arr.length;
for(let i = 0; i < len; i ++) {
for(let j = i + 1; j < len; j ++) {
if(arr[i] === arr[j]) {
arr.splice(j, 1);
//每删除一个树,j--保证j的值经过自加后不变,同时,len--,减少循环次数提升性能
len--;
j--;
}
}
}
return arr;
}
方法三:利用indexof
const unique2 = arr => {
const res = [];
for(let i = 0; i < arr.length; i ++) {
if(res.indexof(arr[i]) === -1) res.push(arr[i]);
}
return res;
}
也可以用单层for循环加上include、filter
方法四:利用include
const unique3 = arr => {
return res = [];
for(let i = 0; i < arr.length; i++) {
if(!res.includes(arr[i])) res.push(arr[i]);
}
return res;
}
方法五:利用filter
const unique4 = arr => {
return arr.filter((item, index) => {
return arr.indexOf(item) === index;
});
}
方法六:利用Map
const unique5 = arr => {
const map = new Map();
const res = [];
for(let i = 0; i < arr.length; i++) {
if(!map.has(arr[i])) {
map.set(arr[i], true)
res.push(arr[i]);
}
}
return res;
}
3 类数组转换为数组
类数组是具有length属性,但不具有数组原型上的方法。常见的类数组有arguments、DOM操作方法返回的结果。
方法一:Array.from
Array.from(document.querySelectorAll('div'))
方法二:Array.prototype.slice.call()
Array.prototype.slice.call(document.querySeltorAll('div'))
方法三:扩展运算符
[...document.querySelectAll('div')]
方法四:利用concat
Array.prototype.concat.apply([],document.querySeltorAll('div'));
4 Arr.prototype.filter()
语法
var newArray = arr.filter(callback(element[, index[, array]])[,thisArg])
参数:
callback
用来测试数组的每个元素的函数。返回true表示该元素通过测试,保留该元素,false则不保留。它接受以下三个参数:
element
数组中当前正在处理的元素。
index (可选)
正在处理的元素在数组中的索引.
array(可选)
调用了filter的数组本身
thisArg(可选)
执行callback时,用于this的值。
返回值
一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数字组。
Array.prototype.filter = function(callback,thisAry) {
if(this == undefined) {
throw new TypeError('this is null or not undefined');
}
if(typeof callback !== 'function') {
throw new TypeError(callback + 'is not a function');
}
const res = [];
//让0成为回调函数的对象传递(强制转换对象)
const o = Object(this);
// >>>0 保证len为number,且为正整数
const len = o.length >>> 0;
for(let i = 0; i < len; i++) {
//检查i是否在O的属性(会检查原型链)
if(i in o){
//回调函数调用传参
if(callback.call(thisArg, o[i], i, o)) {
res.push(o[i]);
}
}
}
return res;
}
5 Array.prototype.map()
参数:
callback
生成新数组元素的函数,使用三个参数:
currentValue
callback数组中正在处理的当前元素。
index(可选)
callback数组中正在处理的当前元素的索引。
array(可选)
map方法调用的数组。
thisArg(可选)
执行callback函数时值被用作this。
返回值
一个由原数组每个元素执行回调函数的结果组成的新数组。
Array.prototype.map = function(callback, thisArg) {
if(this == undefined) {
throw new TypeError('this is null or not defined');
}
if(typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const res = [];
//同理
const o = object(this);
const len = o.length >>> 0;
for(let i = 0; i < len; i ++) {
if(i in o) {
// 调用回调函数并传入新数组
res[i] = callback.call(thisArg, o[i], i, this);
}
}
return res;
}
6 Array.prototype.forEach()
参数:
callback
为数组中每个元素执行的函数,该函数接收一至三个参数:
currentValue
数组中正在处理的当前元素
index(可选)
数组中正在处理的当前元素的索引
array(可选)
forEach()方法正在操作的数组
thisArg(可选)
可选参数。当执行回调函数callback时,用作this的值。
返回值
undefined。
forEach跟map类似,唯一不同的是forEach是没有返回值的。
Array.prototype.forEach = function(callback, thisArg) {
if(this == null) {
throw new TypeError('this is null or not defined');
}
if(typeof callback != "function") {
throw new TypeError(callback + ' is not a function');
}
const o = Object(this);
const len = o.length >>> 0;
let k = 0;
while(k < len) {
if(k in o) {
callback.call(thisArg, o[k], k, o);
}
k++;
}
}
7 Array.prototype.reduce()
参数:
callback
执行数组中每个值(如果没有提供initialValue则第一个值除外)的函数,包含四个参数:
accumulator
累计器累计回调的返回值:它是上一次调用回调时返回的累积值,或initialValue。
currentValue
数组中正在处理的元素。
index(可选)
数组中正在处理的当前元素的索引。如果提供了initialValue,则初始索引号为0,否则从索引1起始。
array(可选)
调用reduce()的数组
initialValue(可选)
作为第一次调用callback函数时的第一个参数的值。如果没有提供初始值,则将使用数组中的第一个元素。在没有初始值的空数组上调用reduce将报错。
返回值
函数累计处理的结果
Array.prototype.reduce = function(callback, initialValue) {
if(this == undefined) {
throw new TypeError('this is null or not defined');
}
if(typeof callback !== 'function') {
throw new TypeError(callbackfn + ' is not a function');
}
const o = Object(this);
const len = this.length >>> 0;
let accumulator = initialValue;
let k = 0;
//如果第二个参数为undefined的情况下
//则数组的第一个有效值作为累加器的初始值
if(accumulator === undefined) {
while(k < len && !(k in o)) {
k++;
}
//如果超出数组界限还没有找到累加器的初始值,则TypeError
if(k >= len) {
throw new TypeError('Reduce of empty array with no initial value');
}
accumulator = o[k++];
}
while (k < len) {
if(k in o) {
accumutor = callback.call(undefined, accumulator, o[k], k, o);
}
k++;
}
return accumulator;
}
8 Function.prototype.apply()
第一个参数时绑定的this,默认为window,第二个参数是数组或类数组
Function.prototype.apply = function(context = window, args) {
if(typeof this !== 'function') {
throw new TypeError('Type Error');
}
const fn = Symbol('fn');
context[fn] = this;
const res = context[fn];
delete context[fn];
return res;
}
9 Function.prototype.call
于call唯一不同的是,call()方法接受的是一个参数列表
Function.prototype.call = function(context = window, ...args) {
if(typeof this !== 'function') {
throw new TypeError('Type Error');
}
const fn = Symbol('fn');
context[fn] = this;
const res = context[fn];
delete context[fn];
return res;
}
10 Function.prototype.bind
Function.prototype.bind = function(context, ...args) {
if(typeof this !== 'function') {
throw new Error("Type Error");
}
// 保存this的值
var self = this;
return function F() {
//考虑new的情况
if(this instanceof F) {
return new self(...args, ...arguments)
}
return self.apply(context, [...args, ...arguments])
}
}
11 debounce(防抖)
触发高频时间后n秒内函数只会执行一次,如果n秒内高频时间再次触发,则重新计算时间。
const debounce = (fn, time) => {
let timeout = null;
return function() {
clearTimetout(timeout)
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, time);
}
};
防抖常应用于用户搜索输入节约请求资源,window触发resize时间时进行防抖只触发一次。
12 throttle(节流)
高频时间触发,但n秒内只会执行一次,所以节流会稀释函数的执行频率。
const throttle = (fn, time) => {
let flag = true;
return function() {
if(!flag) return;
flag = false;
setTimeout(() => {
fn.apply(this, arguments);
flag = true;
}, time);
}
}
节流常应用于鼠标不断点击触发,监听滚动事件。
13 函数珂里化
指的是将一个接受多个参数的函数变为接受一个参数返回一个函数的固定形式,这样便于再次调用,例如f(1)(2)
经典面试题:实现add(1)(2)(3)(4) = 10;、add(1)(1,2,3)(2)=9;
function add() {
const _args =[...arguments];
function fn() {
_args.push(...arguments);
return fn;
}
fn.toString = function() {
return _args.reduce((sum, cur) => sum + cur);
}
return fn;
}
14 模拟new操作
三个操作:
1.以ctor.prototype为为原型创建一个对象。
2.执行构造函数并将this绑定到新创建的对象上。
3.判断构造函数执行返回的结果是否时引用数据类型,若是则返回构造函数执行的结果,否则返回创建的对象。
function newOperator(ctor, ...args) {
if(typeof ctor !== 'function') {
throw new TypeError('Type Error');
}
const obj = Object.create(ctor.prototype);
const res = ctor.apply(obj, args);
const isObject = typeof res === 'object' && res !== null;
const isFunction = typeof res === 'function';
return ifObject || isFunction ? res : obj;
}
15 instanceof
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。
const myInstanceof = (left, right) => {
//基本数据类型都返回false
if(typeof left !== 'object' || left === null) return false;
let proto = Object.getPrototypeOf(left);
while(true) {
if(proto === null) return false;
if(proto === right.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}