js小技巧

liuxiaobai
2021-09-12 / 0 评论 / 5 阅读 / 正在检测是否收录...

数组扁平化是将一个多维数组变为一个一维数组:

const arr = [1, [2, [3, [ 4,5 ]]],6 ]
// => [1,2,3,4,5,6]

方法一:使用flat()

const arr = [1, [2, [3, [ 4,5 ]]],6 ]
// => [1,2,3,4,5,6]

const res1 = arr.flat(Infinity);

方法二:利用正则

const arr = [1, [2, [3, [ 4,5 ]]],6 ]
// => [1,2,3,4,5,6]

const res2 = JSON.stringify(arr).replace(/\[|\]/g,'').split(',');
注:但数据类型都会变为字符串

方法三:正则改良版本

const arr = [1, [2, [3, [ 4,5 ]]],6 ]
// => [1,2,3,4,5,6]

const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/\[|\]/g,'') + ']');

方法四:使用reduce

const arr = [1, [2, [3, [ 4,5 ]]],6 ]
// => [1,2,3,4,5,6]
const flatten = arr => {
  return arr.reduce((pre, cur) => {
    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);
}, [])
}
const res4 = flatten(arr)

方法五:函数递归

const arr = [1, [2, [3, [ 4,5 ]]],6 ]
// => [1,2,3,4,5,6]
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);

代码和处理后结果如下:

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]

方法一:利用Set

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]
const res1 = Array.from(new Set(arr));

方法二:双层for循环+splice

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]
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 arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]
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;
}

方法四:利用include

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]
const unique3 = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!res.includes(arr[i])) res.push(arr[i]);
  }
  return res;
}

方法五:利用filter

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]
const unique4 = arr => {
  return arr.filter((item, index) => {
    return arr.indexOf(item) === index;
  });
}

方法六:利用Map

const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]
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;
}

类数组是具有length属性,但不具有数组原型上的方法。
常见的类数组有arguments、DOM操作方法返回的结果。

方法一:Array.from

Array.from(document.querySelectorAll('div'))

方法二:Array.prototype.slice.call()

Array.prototype.slice.call(document.querySelectorAll('div'))

方法三:扩展运算符

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

方法四:利用concat

Array.prototype.apply([], document.querySelectorAll('div'))

语法:
var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
参数
callback

用来测试数组的每个元素的函数。返回 true 表示该元素通过测试,保留该元素,false 则不保留。它接受以下三个参数:
element
数组中当前正在处理的元素。
index可选
正在处理的元素在数组中的索引。
array可选
调用了 filter 的数组本身。

thisArg可选

执行 callback 时,用于 this 的值。

返回值

一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。

Array.prototype.filter = function(callback, thisArg) {
  if (this == undefined) {
        throw new TypeError('this is null or not undefined');
  }
  if (typeof callback !== 'function') {
      throw new TypeError(callback + 'is null or not undefined');
  }
  const res = [];
  // 让o成为回调函数的对象传递(强制转换对象)
  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(thisAry, o[i], i, o)) {
                res.push(o[i]);
           }
        }
   }
   return res;
}

语法:
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
 // 返回一个新数组
}[, thisArg])
参数
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 undefined');
    }
  if (typeof callback !== 'function') {
      throw new TypeError(callback + 'is null or not undefined');
    }
  const res = [];
  // 让o成为回调函数的对象传递(强制转换对象)
  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) {
           // 回调函数调用传参
          res[i] = callback.call(thisAry, o[i], i , this);
        }
   }
   return res;
}

语法
myMap.forEach(callback([value][,key][,map])[, thisArg])
参数
callback

myMap 中每个元素所要执行的函数。它具有如下的参数:
value 可选
每个迭代的值。
key 可选
每个迭代的键。
map 可选
被迭代的map(上文语法框中的 myMap)。

thisArg 可选

在 callback 执行中使用的 this 的值。

返回值

undefined.
forEach跟map类似,唯一不同的是forEach是没有返回值的。

Array.prototype.forEach = function(callback, thisArg) {
  if (this == null) {
        throw new TypeError('this is null or not undefined');
  }
  if (typeof callback !== 'function') {
      throw new TypeError(callback + 'is null or not undefined');
  }
  // 让o成为回调函数的对象传递(强制转换对象)
  const o = Object(this);
  // >>>0 保证len为number,且为正整数
  const len = o.length >>> 0;
  let k = 0;
  while(k < len){
    if(k in o){
      callback.call(thisArg, o[k], k, o);
    }
    k++;
  }
}

语法
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
参数
callback

执行数组中每个值 (如果没有提供 initialValue则第一个值除外)的函数,包含四个参数:
accumulator
累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
currentValue
数组中正在处理的元素。
index 可选
数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。
array可选
调用reduce()的数组

initialValue可选

作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。

返回值

函数累计处理的结果

Array.prototype.forEach = function(callback, thisArg) {
  if (this == undefined) {
        throw new TypeError('this is null or not undefined');
  }
  if (typeof callback !== 'function') {
      throw new TypeError(callback + 'is null or not undefined');
  }
  // 让o成为回调函数的对象传递(强制转换对象)
  const o = Object(this);
  // >>>0 保证len为number,且为正整数
  const len = o.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 on initial value');
    }
    accumulator = o[k++];
  }
    while (k < len) {
      if(k in o){
        accumulator = callback.call(undefined, accumulator, o[k], k ,o);
      }
       k++;
    }
   return accumulator;
}

第一个参数是绑定的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](...args);
  delete context[fn];
  return res;
}

注意:

该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。

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](...args);
  delete context[fn];
  return res;
}


bind() 函数会创建一个新的绑定函数(bound function,BF)。绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015 中的术语),它包装了原函数对象。调用绑定函数通常会导致执行包装函数。

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])
  }
}


触发高频时间后n秒内函数只会执行一次,如果n秒内高频时间再次触发,则重新计算时间

const debounce = (fn, time) => {
  let timeout = null;
  return function() {
      clearTimeout(timeout);
      timeout = setTimeout(() => {
          fn.apply(this, arguments);
      }, time);
  }
}


高频时间触发,但n秒内只会执行一次,所以节流会稀释函数的执行频率。

const throttle = (fn, time) => {
  let flag = true;
  return function() {
      if (!flag) return;
      flag = false;
      setTimeout(() => {
          fn.apply(this, arguments);
          flag = true;
    }  , time);
  }
}

//第二种写法 非定时器写法
    function fun(fn,time){
            var activeTime=0
            return ()=>{
                var current =new Date()
                if(current-activeTime>time){
                    fn.apply(this,arguments)
                    activeTime=new Date()
                }
            }
            
        }
注: 节流常应用于鼠标不断点击触发 、 监听滚动事件


指的是将一个接受多个参数的函数变为接受一个参数返回一个函数的固定形式,这样便于再次调用,列如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 fu;
   }
   fn.toString = function() {
      return _args.reduce((sum, cur) => sum + cur);
   }
   return fn;
}


3个步骤:
以ctor.prototype为原型创建一个对象。
执行构造函数并将this绑定到新创建的对象上。
判断构造函数执行返回的结果是否引用数据类型,若是则返回构造函数执行的结果,否则返回创建的对象。

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 isObject || isFunction ? res : obj;
  }


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);
  }
}


这里只写寄生组合继承了,中间还有几个演变过来的继承但是有一定缺陷。

function Parent(){
  this.name='parent';
}
function Child(){
  Parent.call(this);
  this.type='children';
}
Child.prototype=Object.create(Parent.prototype);
Child.prototype.constructor=Child;


Object.is解决的主要的这两个问题

+0 === -0 //true
NaN === NaN //false
const is = (x, y) => {
  if (x === y) {
      // +0和-0应该不相等
      return x !== 0 || y !== 0 || 1 / x === 1 / y;
  } else {
      return x !== x && y !== y;
  }
}


Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。
它将返回目标对象(请注意这个操作是浅拷贝)

Object.defineProperty(Object, 'assign', {
  value: function(target, ...args) {
     if (target == null) {
          return new TypeError('Cannot convert undefined or null to object');
      }
      //目标对象需要统一是引用数据类型,若不是会自动转换。
      const to = Object(target);
      for (let i = 0; i < args.length; i++) {
          //每一个源对象
          const nextSource = args[i];
          if (nextSource !== null) {
              //使用for...in和hasOwnProperty双重判断,确保只拿到本身属性,方法(不包含继承的)
              for (const nextKey in nextSource) {
                  if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                      to[nextKey] = nextSource[nextKey];
                  }
              }
          }
      }
    return to;
  },
  //不可枚举
  enumerable:false,
  writable:true,
  configurable:true,
})


递归的完整版本(考虑到了Symbol属性)

const cloneDeep1 = (target, hash = new WeakMap()) => {
  //对于传入参数处理
  if (typeof target !== 'object' || target === null) {
      return target;
  }
  //哈希表中存在直接返回
  if (hash.has(target)) return hash.get(target);
  const cloneTarget = Array.isArray(target) ? [] : {};
  hash.set(target, cloneTarget);
  //针对Symbol属性
  const symKeys = Object.getOwnPropertySymbols(target);
  if (symKeys.length) {
      symKeys.forEach(Symbol => {
          if (typeof target[symKeys] === 'object' && target[symKeys] !== null) {
              cloneTarget[symKeys] = cloneDeep1(target[symKeys]);
          } else {
               cloneTarget[symKeys] = target[symKeys];
          }
      })
  }
  for (const i in target) {
      if (Object.prototype.hasOwnProperty.call(target, i)) {
          cloneTarget[i] = typeof target[i] === 'object' && target[i] !== null ? cloneDeep1(target[i].hash) : target[i]
      }
  }
  return cloneTarget;
}


扫描二维码,在手机上阅读!
0

评论 (0)

取消