There are some issues that get discussed a lot in JavaScript, and everyone has different ideas about them, and the best way to understand them is to implement them yourself, without saying a word, and get to the point.

Array flattening

There are many ways to flatten an array, but ultimately the best approach is to recurse and implement a flatten method with a specified depth so that the basics are understood.

function flattenDepth(array, depth = 1) {
  let result = []
  array.forEach(item= > {
    let d = depth
    if (Array.isArray(item) && d > 0) { result.push(... (flattenDepth(item, --d))) }else {
      result.push(item)
    }
  })
  return result
}

console.log(flattenDepth([1[2[3[4]], 5]])) // [1, 2, [3, [4]], 5]
console.log(flattenDepth([1[2[3[4]], 5]], 2)) // [1, 2, 3, [4], 5]
console.log(flattenDepth([1[2[3[4]], 5]], 3)) // [1, 2, 3, 4, 5]
Copy the code

The recursive implementation is pretty straightforward, just iterate through each item, and if an item is an array, let that item continue to be called. Depth is specified as the flattening depth, because this parameter is applied to each item in the array, so it is placed in the loop.

Currie,

Everyone has their own way of understanding and implementing it. In a word, the explanation is to execute when there are enough arguments, return a function when there are not enough arguments, and save the previous arguments until there are enough.

function curry(func) {
  var l = func.length
  return function curried() {
    var args = [].slice.call(arguments)
    if(args.length < l) {
      return function() {
        var argsInner = [].slice.call(arguments)
        return curried.apply(this, args.concat(argsInner))
      }
    } else {
      return func.apply(this, args)
    }
  }
}

var f = function(a, b, c) {
  return console.log([a, b, c])
};

var curried = curry(f)
curried(1) (2) (3) // => [1, 2, 3]
curried(1.2) (3) // => [1, 2, 3]
curried(1.2.3) // => [1, 2, 3]
Copy the code

If the number of parameters is less than the number of currified parameters, the function is returned; otherwise, the function is executed.

Image stabilization

The way I understand it is that no matter how many times you trigger, you wait until a certain amount of time has passed after you finally trigger. Follow this explanation and write a basic version.

function debounce(func, wait) {
  var timer
  return function() {
    var context = this
    var args = arguments
    clearTimeout(timer)
    timer = setTimeout(function() {
      func.apply(context, args)
    }, wait)
  }
}
Copy the code

Now there is a requirement is the beginning of the trigger, the last trigger, and can be configured, write a test page to facilitate the test function, each press the space bar will let the number plus 1, to test the anti-shake and throttling function.


      
<html lang="zh-cmn-Hans">
<head>
    <style>
        #container{text-align: center; color: # 333; font-size: 30px; }</style>
</head>
<body>
    <div id="container"></div>
    <script>
      var count = 1
      var container = document.getElementById('container')
      function getUserAction(e) {
        / / space
        if (e.keyCode === 32) {
          container.innerHTML = count++
        }
      }
      // document.onkeydown = debounce(getUserAction, 1000, false, true)
      document.onkeydown = throttle(getUserAction, 1000.true.true)
      function debounce(func, wait, leading, trailing) {}
      function throttle(func, wait, leading, trailing) {}
    </script>
</body>
</html>
Copy the code

Use the leading and trailing parameters to determine whether the start and end are executed. If leading is true, each space press is executed once. If trailing is true, each end is executed for the last time. If the trailing value is false, the getUserAction does not execute after the trailing value is set. If the trailing value is false, the getUserAction does not execute.

function debounce(func, wait, leading, trailing) {
  var timer, lastCall = 0, flag = true
  return function() {
    var context = this
    var args = arguments
    var now = + new Date(a)if (now - lastCall < wait) {
      flag = false
      lastCall = now
    } else {
      flag = true
    }
    if (leading && flag) {
      lastCall = now
      return func.apply(context, args)
    }
    if (trailing) {
      clearTimeout(timer)
      timer = setTimeout(function() {
        flag = true
        func.apply(context, args)
      }, wait)
    }
  }
}
Copy the code

If it is smaller than the interval, it will not be executed after the first time. If it is larger than the interval or after the interval, it will reset the flag, which can be compared with the basic version above.

The throttle

Throttling means that no matter how it is triggered, it is performed at specified intervals, also given in the basic version.

function throttle(func, wait) {
  var timer
  return function() {
    var context = this
    var args = arguments
    if(! timer) { timer = setTimeout(function () {
        timer = null
        func.apply(context, args)
      }, wait)
    }
  }
}
Copy the code

The same as the anti-shake function with two parameters, can also use the above example to test, in fact, the code is very similar.

function throttle(func, wait, leading, trailing) {
  var timer, lastCall = 0, flag = true
  return function() {
    var context = this
    var args = arguments
    var now = + new Date()
    flag = now - lastCall > wait
    if (leading && flag) {
      lastCall = now
      return func.apply(context, args)
    }
    if(! timer && trailing && ! (flag && leading)) { timer = setTimeout(function () {
        timer = null
        lastCall = + new Date()
        func.apply(context, args)
      }, wait)
    } else {
      lastCall = now
    }
  }
}
Copy the code

Copy the object

Object copy is known as deep copy and shallow copy, black technology means is to use

JSON.parse(JSON.stringify(obj))
Copy the code

Another way is to use recursion

function clone(value, isDeep) {
  if (value === null) return null
  if (typeofvalue ! = ='object') return value
  if (Array.isArray(value)) {
    if (isDeep) {
      return value.map(item= > clone(item, true))}return [].concat(value)
  } else {
    if (isDeep) {
      var obj = {}
      Object.keys(value).forEach(item= > {
        obj[item] = clone(value[item], true)})return obj
    }
    return { ...value }
  }
}

var objects = { c: { 'a': 1.e: [1, {f: 2}},d: { 'b': 2}}var shallow = clone(objects, true)
console.log(shallow.c.e[1]) // { f: 2 }
console.log(shallow.c === objects.c) // false
console.log(shallow.d === objects.d) // false
console.log(shallow === objects) // false
Copy the code

For primitive types, return directly, and for reference types, the clone method is iterated recursively.

conclusion

In fact, for the above methods, the general idea is the use of recursion and higher-order functions, including the use of closures, front-end love to ask these questions, it is best to implement their own again, so as to help understand.