15

Offer arrives, dig friends to pick up! I am participating in the 2022 Spring Recruitment Series - Experience Review, click to view the details of the event and to be considered a participant

Hello everyone, my name is Shanyue. This article is also available on my blog Roadmap .

One of the frequent questions is: How do I practice programming problems? Shanyue once again summed up a practice route about handwritten code.

前端手写代码

To make sure the code runs properly, debug and test it.

All handwritten codes below are pasted in my codepen

All handwritten codes below are posted in my codepen

All handwritten codes below are posted in my codepen

Ready to work

API Design Thinking

As an old front-end who has worked for more than three years, he will understand one thing: the design of the API is more important than the implementation .

Why?

For example compose function is commonly used in various middleware designs, such as redux and so on. The implementation of the redux function is extremely simple, even in one line, but it is even more difficult to be the first to think of compose .

Therefore, many interview questions in the front-end interview are mainly based on the simulation implementation of ES API and lodash API, so you need to be familiar with lodash and ES6+ documents before writing code.

code specification

When examining code during the interview process, in addition to examining the candidate's logical ability, secondly, you can examine the candidate's code ability, such as

  1. Is there a consistent code specification
  2. Are there clear and readable variable names
  3. Is there a more brief code

For the ability to develop elegant code, see Clean Code concepts adapted for JavaScript. , with 50+ K stars on Github.

For example, about naming optimization

// BAD
// What the heck is 86400000 for?
setTimeout(blastOff, 86400000);

// GOOD
// Declare them as capitalized named constants.
const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000; //86400000;

setTimeout(blastOff, MILLISECONDS_PER_DAY);

space complexity and time complexity

  • Space Complexity describes the size of the storage space temporarily occupied by the algorithm during operation
  • Time complexity describes the running time of the algorithm, usually expressed in big O notation

时间复杂度

This image is from the article How to find time complexity of an algorithm?
  • O(1): Constant Complexity, constant complexity, for example, the final conclusion can be obtained by calculating twice
  • O(logn): Logarithmic Complexity, the most common is binary search
  • O(n): Linear Complexity, generally a for loop, but half or two for loops are also regarded as O(n)
  • O(n^2): Usually nested for loops, such as bubble sort

Handwritten Code Roadmap

The following are the code questions that I have summarized in many major workshops. I will summarize them into different parts according to the difficulty and module attributes.

Remarks: summarizes all the interviews with big factories, please click here

Therefore, I divided the questions into the following categories. You can prepare according to the number of stars and the order in which I listed all the code questions. Find three questions for coding every day, and stick to it. After three months, the coding stage of the interview with the big factory will not change. Problems will arise.

  1. ES API
  2. lodash API
  3. programming logic questions
  4. Algorithms and Data Structures (leetcode)

All the questions below can be found in Shanyue's warehouse, and most of the code questions can be found on codepen , find my problem solving test and execute it directly.

01 ES API

Many big factory interview questions are obsessed with simulating the implementation of the native API. Although most of the time it is not helpful, sometimes it can further deepen the understanding of the API.

For example, when you write the end Promise.all , it will be easier to write a Promises for concurrency control.

bind/call/apply ⭐⭐⭐⭐⭐️️️️

High frequency problem, medium frequency realization.

Function.prototype.fakeBind = function(obj, ...args) {
  return (...rest) => this.call(obj, ...args, ...rest)
}

sleep/delay ⭐⭐⭐⭐⭐

sleep function is not only a code question often asked in interviews, but also a tool function commonly used in daily work, especially in testing.

const sleep = (seconds) => new Promise(resolve => setTimeout(resolve, seconds))

function delay (func, seconds, ...args) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      Promise.resolve(func(...args)).then(resolve)
    }, seconds)
  })
}

Promise.all ⭐️⭐️⭐️⭐️⭐️

Simple at first glance, but difficult to implement.

function pAll (_promises) {
  return new Promise((resolve, reject) => {
    // Iterable => Array
    const promises = Array.from(_promises)
    // 结果用一个数组维护
    const r = []
    const len = promises.length
    let count = 0
    for (let i = 0; i < len; i++) {
      // Promise.resolve 确保把所有数据都转化为 Promise
      Promise.resolve(promises[i]).then(o => { 
        // 因为 promise 是异步的,保持数组一一对应
        r[i] = o;

        // 如果数组中所有 promise 都完成,则返回结果数组
        if (++count === len) {
          resolve(r)
        }
        // 当发生异常时,直接 reject
      }).catch(e => reject(e))
    }
  })
}

Array.isArray ⭐️⭐️⭐️⭐️⭐️

Interviews are often asked, but simple enough.

Array.prototype.flat ⭐️⭐️⭐️⭐️⭐️

reduce and concat are a perfect match

function flatten (list, depth = 1) {
  if (depth === 0) return list
  return list.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b, depth - 1) : b), [])
}

Note that flatten has a second parameter depth

Promise ⭐️⭐️⭐️⭐️

Needless to say, it is not easy to implement a real Promise that conforms to the specification.

Array.prototype.reduce ⭐️⭐️⭐️

const reduce = (list, fn, ...init) => {
  let next = init.length ? init[0] : list[0]
  for (let i = init.length ? 0 : 1; i < list.length; i++) {
    next = fn(next, list[i], i)
  }
  return next
}

This topic looks simple, but in practice there are many boundary issues that need to be paid attention to, such as

  1. What is the first Index in the callback function?
  2. How to deal with arrays as sparse arrays?

String.prototype.trim ⭐️⭐️⭐️

In regular expressions, \s means to match a whitespace character, including spaces, tabs, form feeds, and newlines. Equivalent to [ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff] .

const trim = str => str.trim || str.replace(/^\s+|\s+$/g, '')

Generally, the API will be examined when examining regularities

02 lodash API

throtle/debounce ⭐⭐⭐⭐⭐️️

The necessary means of reducing rendering in performance optimization, and the code is easy enough, it is often mentioned in interview questions.

function throttle (f, wait) {
  let timer
  return (...args) => {
    if (timer) { return }
    timer = setTimeout(() => {
      f(...args)
      timer = null
    }, wait)
  }
}
function debounce (f, wait) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      f(...args)
    }, wait)
  }
}

cloneDeep ⭐⭐️⭐⭐⭐

Deep copy, whether in performance optimization at work or in interviews, is very popular.

Using JSON serialization and deserialization cannot solve the problem of copying some complex objects. The difficulty lies in the processing of different data types.

isEqual ⭐⭐⭐⭐⭐

In-depth comparison is also commonly used in performance optimization, and it is less difficult than cloneDeep .

get ⭐️⭐️⭐️⭐️⭐️

In ES6+, use the optional chaining operator ?. to further reduce implementation difficulty

function get (source, path, defaultValue = undefined) {
  // a[3].b -> a.3.b -> [a, 3, b]
  const paths = path.replace(/\[(\w+)\]/g, '.$1').replace(/\["(\w+)"\]/g, '.$1').replace(/\['(\w+)'\]/g, '.$1').split('.')
  let result = source
  for (const p of paths) {
    result = result?.[p]
  }
  return result === undefined ? defaultValue : result 
}

compose(flowRight) ⭐️⭐️⭐️⭐️⭐️

const compose = (...fns) =>
  // 注意 f、g 的位置,如果实现从左到右计算,则置换顺序
  fns.reduce((f, g) => (...args) => f(g(...args)))

shuffle ⭐️⭐️⭐️⭐️⭐️

Probably extremely simple to implement a simple shuffle .

const shuffle = (list) => list.sort((x, y) => Math.random() - 0.5)

Without the help of Array.prototype.sort , it can be achieved by the following code

function shuffle (list) {
  const len = list.length
  let result = [...list]
  for (let i = len - 1; i > 0; i--) {
    const swapIndex = Math.floor(Math.random() * (i + 1));
    [result[i], result[swapIndex]] = [result[swapIndex], result[i]]
  }
  return result
}

In production practice, shuffle is used in many scenarios, such as to randomly generate a six-digit mobile phone verification code that does not repeat

sample ⭐️⭐️⭐️⭐️⭐️

The Math.random() function returns a floating point, pseudo-random number in the range from 0 to less than 1, which is expressed in mathematics as [0, 1), which can be used to implement the sample function

Array.prototype.sample = function () { return this[Math.floor(Math.random() * this.length)] }

sampleSize ⭐️⭐️⭐️⭐️⭐️

A simple shuffle can be implemented according to 0623884f2b06bd

const shuffle = (list) => list.sort((x, y) => Math.random() - 0.5)
const sampleSize = (list, n) => shuffle(list).slice(0, n)

maxBy ⭐⭐⭐⭐⭐

keyBy ⭐⭐⭐⭐

groupeBy ⭐⭐⭐⭐

chunk ⭐️⭐️⭐️⭐️

function chunk (list, size) {
  const l = []
  for (let i = 0; i < list.length; i++ ) {
    const index = Math.floor(i / size)
    l[index] ??= [];
    l[index].push(list[i])
  }
  return l
}

chunk ⭐️⭐️⭐️⭐️

const f = x => x

const onceF = once(f)

//=> 3
onceF(3)

//=> 3
onceF(4)

template ⭐⭐⭐⭐️️️️️

A slightly more difficult programming problem.

const template = '{{ user["name"] }},今天你又学习了吗 - 用户ID: {{ user.id }}';

const data = {
  user: {
    id: 10086,
    name: '山月',
  }
};

//=> "山月,今天你又学习了吗 - 用户ID: 10086"
render(template, data); 

Notice:

  1. Watch out for deeply nested data
  2. Note user['name'] attribute

pickBy/omitBy ⭐⭐⭐⭐

camelCase ⭐️⭐⭐⭐

difference ⭐️⭐️⭐️

03 Programming logic questions

Regarding programming logic questions, it refers to some data processing often encountered in work

FizzBuzz, is it divisible by 3 or 5 ⭐️⭐️⭐️⭐️⭐️

Input an integer, if it is divisible by 3, output Fizz

If divisible by 5, output Buzz

If it is divisible by both 3 and 5, output FizzBuzz

//=> 'fizz'
fizzbuzz(3)

//=> 'buzz'
fizzbuzz(5)

//=> 'fizzbuzz'
fizzbuzz(15)

//=> 7
fizzbuzz(7)

Although this question is very simple, there are still many people who can't get it right in the interview.

Implement Promise.map to control concurrency ⭐️⭐️⭐️⭐️⭐️

For Promise concurrency control, it is often asked in interviews and often involved in work. Knowing the implementation of [Promise.all]() will help a lot in implementing concurrency control before getting started.

In addition, the most popular Promise library bluebird implements Promise.map and is heavily used in the project.

Async sum/add ⭐️⭐️⭐️⭐️⭐️

The master of coding questions , from the headlines, promise serial, parallel, binary, concurrency control, progressive.

How to implement a publish subscribe pattern using JS ⭐️⭐️⭐️⭐️⭐️

How to implement infinite accumulation sum function ⭐️⭐️⭐️⭐️⭐️

Implement a sum function as follows:

sum(1, 2, 3).valueOf() //6
sum(2, 3)(2).valueOf() //7
sum(1)(2)(3)(4).valueOf() //10
sum(2)(4, 1)(2).valueOf() //9
sum(1)(2)(3)(4)(5)(6).valueOf() // 21

This is still the most preferred topic of Byte, Kuaishou, and Ali. In fact, there is a little technical problem.

This is still the most preferred topic of Byte, Kuaishou, and Ali. In fact, there is a little technical problem.

This is still the most preferred topic of Byte, Kuaishou, and Ali. In fact, there is a little technical problem.

This is a function about lazy calculation, use sum to collect all accumulated items, use valueOf to calculate

  • sum returns a function that collects all accumulated items, implemented using recursion
  • The return function has a valueOf attribute for unified calculation
function sum (...args) {
  const f = (...rest) => sum(...args, ...rest)
  f.valueOf = () => args.reduce((x, y) => x + y, 0)
  return f
}

Count the largest number/second largest number in an array ⭐️⭐️⭐️⭐️⭐️

Find the largest value:

function max (list) {
  if (!list.length) { return 0 }
  return list.reduce((x, y) => x > y ? x : y)
}

Find the largest two values:

See code Find the largest two values in an array - codepen
function maxTwo (list) {
  let max = -Infinity, secondMax = -Infinity
  for (const x of list) {
    if (x > max) {
      secondMax = max
      max = x
    } else if (x > secondMax) {
      secondMax = x
    }
  }
  return [max, secondMax]
}

If you want TopN, you can use large top heap and small top heap to achieve, see another question

Count the most frequently occurring characters in a string ⭐️⭐️⭐️⭐️⭐️

function getFrequentChar (str) {
  const dict = {}
  for (const char of str) {
    dict[char] = (dict[char] || 0) + 1
  }
  const maxBy = (list, keyBy) => list.reduce((x, y) => keyBy(x) > keyBy(y) ? x : y)
  return maxBy(Object.entries(dict), x => x[1])
}

The following scheme performs counting and statistics once for size comparison, and only needs one algorithm complexity of O(n)

function getFrequentChar2 (str) {
  const dict = {}
  let maxChar = ['', 0]
  for (const char of str) {
    dict[char] = (dict[char] || 0) + 1
    if (dict[char] > maxChar[1]) {
      maxChar = [char, dict[char]]
    }
  }
  return maxChar
}

Encode compression of the following numbers ⭐️⭐️⭐️⭐️⭐️

This is a code question that is often tested by major manufacturers

  • Input: 'aaaabbbccd'
  • Output: 'a4b3c2d1', which means a appears four times in a row, b appears three times in a row, c appears twice in a row, and d appears once in a row

There are following test cases

//=> a4b3c2
encode('aaaabbbcc')

//=> a4b3a4
encode('aaaabbbaaaa')

//=> a2b2c2
encode('aabbcc')

If the code is written correctly, you can go further:

  • If it only appears once, do not encode the number, such as aaab -> a3b
  • If it only appears twice, do not encode, such as aabbb -> aab3
  • How to solve if the decoding number conflict

Write function encode to realize this function

For the code, see [Q412] Compress the following characters - codepen
function encode (str) {
  const l = []
  let i = 0
  for (const s of str) {
    const len = l.length
    const lastChar = len > 0 ? l[len - 1][0] : undefined
    if (lastChar === s) {
      l[len - 1][1]++
    } else {
      l.push([s, 1])
    }
  }
  return l.map(x => x.join('')).join('')
}

Test passed

> encode('aaab')
< "a3b1"

But interviewers tend to go deeper

  1. If it occurs only once, do not encode the number, such as aaab -> a3b
  2. If it appears only twice, do not encode, such as aabbb -> aab3
  3. If decoding, how to deal with numbers?

Below is further coding besides numbers

function encode (str) {
  const l = []
  let i = -1;
  let lastChar
  for (const char of str) {
    if (char !== lastChar) {
      lastChar = char
      i++
      l[i] = [char, 1];
    } else {
      l[i][1]++
    }
  }
  return l.map(([x, y]) => {
    if (y === 1) {
      return x
    }
    if (y === 2) {
      return x + x
    }
    return x + y
  }).join('')
}

LRU Cache ⭐️⭐️⭐️⭐️⭐️

Implement a function to encode and decode URL querystring ⭐️⭐️⭐️⭐️⭐️

What is the principle of JSONP and how to implement it ⭐️⭐️⭐️⭐️

JSONP , full name JSON with Padding , appeared to solve cross-domain problems. Although it can only handle GET cross-domain, although CORS cross-domain is basically used now, it is still necessary to know it, after all interview will ask .

JSONP based on two principles:

  1. Dynamically create script , use script.src to load requests across domains
  2. The content of the script loaded by script.src is JSONP: ie PADDING(JSON) format
function jsonp ({ url, onData, params }) {
  const script = document.createElement('script')

  // 一、为了避免全局污染,使用一个随机函数名
  const cbFnName = `JSONP_PADDING_${Math.random().toString().slice(2)}`

  // 二、默认 callback 函数为 cbFnName
  script.src = `${url}?${stringify({ callback: cbFnName, ...params })}`

  // 三、使用 onData 作为 cbFnName 回调函数,接收数据
  window[cbFnName] = onData;

  document.body.appendChild(script)
}

// 发送 JSONP 请求
jsonp({
  url: 'http://localhost:10010',
  params: { id: 10000 },
  onData (data) {
    console.log('Data:', data)
  }
})

How to generate a random string using JS ⭐️⭐️⭐️⭐️⭐️

const random = (n) => Math.random().toString(36).slice(2, 2 + n)

Add thousands to numbers ⭐️⭐️⭐️

Thousands character replacement can be matched by the regular /(\d)(?=(\d\d\d)+(?!\d))/

function numberThousands (number, thousandsSeperator = ',') {
  return String(number).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + thousandsSeperator)
}

04 Algorithms and Data Structures (leetcode)

200/100 simple and intermediate difficulty questions, mainly simple questions. Just brush it directly.

In my question bank, I also collected multi-algorithm questions summed up in many big factories. The summary is as follows

Output the Fibonacci sequence up to 100

TopK problem

Typical binary heap problem

  1. Take the first k numbers in the array as a small top heap, heap
  2. The other numbers in the array are compared one by one with the top element of the heap, and if it is greater than the top element of the heap, the number is inserted

Time complexity O(nlg(k))

Find two numbers whose sum is N in an array of positive integers growing in positive order

Find the sum of N numbers in the given array to sum all possible sets

Find the sum of N numbers in the given array to sum all possible sets, please add the following code

function fn(arr, n, sum) {}

How to check if two linked lists intersect

  • Topic: 】How to judge whether two linked lists intersect1623884f2bad77

classic problem

finally

Code programming questions appear frequently in the interview process, and can largely examine the coding ability and coding style of candidates.


shanyue
4.7k 声望707 粉丝

暮从碧山下,山月(shanyue)随人归。