Js practical tips

Collect some common JS skills

Terminate loop traversal without using for loops and break

With the popularity of the ES6 specification, it is rare to use the for loop for traversal. We are used to using forEach and map for traversal.

But when we want to end the loop prematurely, using forEach is not enough.

If you want to terminate the loop during traversal, you can use find and some methods to control the return value of the callback function.

let arr = [
  {index: 1.value: 111},
  {index: 2.value: 222},
  {index: 3.value: 333},
  {index: 4.value: 444},];/ / find way
arr.find(item= > {
  if (item.index > 2) return true; // equivalent to break in the for loop;
  // Specify the business logic within the loop
  console.log(item.index);
  return false;
});
/ / some way
arr.some(item= > {
  if (item.index > 2) return true; // equivalent to break in the for loop;
  // Specify the business logic within the loop
  console.log(item.index);
  return false;
});
Copy the code

When a qualifying data item is found, return true, and the traversal stops

Quickly create an m by N two-dimensional array

There are several ways to create a two-dimensional array with m rows and n columns

  • Original method:
function makeMatrix(m, n) {
  let result = [];
  for (let i = 0; i < m; i++) {
    result.push(new Array(n).fill(0));
  }
  console.log(result);
}
Copy the code
  • Using es6’s array methods:
// Approach 1: use the new Array and map methods
function makeMatrix1(m, n) {
  return new Array(m).fill(0).map(() = > new Array(n).fill(0));
}

// Method 2: array. from can create an Array by passing in a control function that processes each item and returns the value of each item (equivalent to a map method).
function makeMatrix2(m, n) {
  return Array.from(new Array(m), () = > new Array(n).fill(0));
}
Copy the code

String.raw

String.raw ‘aaa\ NBBB’ can be used to output template strings as they are. This is usually used to write documents containing code so that the source code can be output without escaping

Object property chain get

An obj object is nested with many layers. Given a string key = ‘first.second. Third ‘, the final value of obj.first.second

let obj = {
  first: {
    second: {
      third: 'message'}}};function getResultValue(obj, key) {
  return key.split('. ').reduce((o, i) = > {
    if (o) return o[i];
  }, obj);
}

let key = 'first.second.third';
let result = getResultValue(obj, key);
console.log(result);
Copy the code

Clever bit operation

1. Parity judgment — bitwise and (&)

  • Bitwise and operation converts two operands to binary and compares them bitwise by bit. the current bit is 1 and the result is 1, otherwise it is 0.
  • And the parity of all binary numbers is just looking at the end, the odd mantissa is 1, the even mantissa is 0.
if ( a & 1 ) {
  alert('A' is odd! ');
} else {  
  alert('A is even! ');
}
Copy the code

2, interchangeably two integers — bitwise xor (^)

Plus and minus are inversions of each other, and xor and xor are inversions of each other.

  • Using addition and subtraction:
var a = 1;
var b = 2;
a = a + b;
b = a - b;
a = a - b;
console.log(a, b);
Copy the code
  • Bitwise xor:
var a = 1;
var b = 2;
a = a ^ b;
b = a ^ b;
a = a ^ b;
console.log(a, b);
Copy the code

Interchangeably two integers can declare a third intermediate variable for temporary storage implementation, but declaring variables can be memory intensive and unfriendly. And addition and subtraction are not as efficient as bits.

3, the number of -1 judgment — according to the reverse (~)

IndexOf () returns -1 when a string is not found. Many programs, plugins, and framework error status values return -1 by default.

/ / regular
if ( str.indexOf('a') != -1 ) {
  alert('A is in the string STR');
}
/ / to take back
if ( ~str.indexOf('a') ) {
  alert('A is in the string STR');
}
Copy the code

4. Round the numbers — ~~

Most of the time we need to get the integer part of a number (not rounded), we have the following methods:

let num = 13.14;
// parseInt() 
console.log(parseInt(num)); // NaN, null, undefined, etc
// Math.floor()
console.log(Math.floor(num)); // If the argument is a string, NaN, undefined, etc., NaN will be obtained. If the argument is null, the result will be 0
/ / ~ ~
console.log(~~num); // Non-numbers are converted to 0
Copy the code

N & (n-1)

The existing binary number 10101100, the last 1 is the third from the bottom, to change the last 1 to 0, you can use n & (n-1), the result is 10101000

N & (n-1) :

Problem: Write a function that takes an unsigned integer as an input (in the form of a binary string) and returns the number of digits ‘1’ in its binary expression (also known as hamming weight). For example, find the number of 1’s in 1001, 1100, 1010, 1100, 101, 0011, 0110, 1000

var hammingWeight = function(n) {
  let count = 0;
  while(n ! =0) {
    count++;
    n = n & (n - 1);
  }

  return count;
};
Copy the code

6. Displacement operation — >> and >>>

>> is a signed right-shift operator that moves the object to the left of the operator to the right by a specified number of digits. If it’s a positive number, it adds 0; If it’s a negative number, we add 1 in the high number. >>> is an unsigned right-shift operator that moves the object to the left of the operator to the right by the specified number of digits and adds 0 >>>0 at the high level to remove the negative sign.

It can be concluded that:

  • When I do a positive shift,>>and>>>The result is the same;
  • When I do a negative shift,>>and>>>The operation results are different.>>I’m going to fill in the bits with 1’s, and>>>You fill in the bits with zeros, and that leads to>>>The shift of a negative number becomes a positive number because the high number is filled with zeros

Application examples:

Problem: Write a function that takes an unsigned integer as an input (in the form of a binary string) and returns the number of digits ‘1’ in its binary expression (also known as hamming weight). For example, find the number of 1’s in 1001, 1100, 1010, 1100, 101, 0011, 0110, 1000

This problem is very convenient to use n & (n-1) and can also be implemented using >> and >>>

/ / > > implementation
var hammingWeight = function(n) {
  let count = 0;
  
  // Since '>>' is a high-complement one, we cannot use the while loop
  for (let i = 0; i < 32; i++) {
    if ((n >> i) & 1) count++;
  }

  return count;
};

/ / > > > implementation
var hammingWeight = function(n) {
  let count = 0;
  // '>>>' is a high-order zeroing operation, does not affect the increment of 1 in the binary number, every time the right shift, n == 0, indicating that 1 has not existed, the count is complete
  while (n) {
    if (n & 1) count++;
    n = n >>> 1; // n >> 1 does not apply to negative numbers, resulting in a while loop that requires n >>> 1 to shift the zero complement
  }

  return count;
};

Copy the code

Dynamically sets properties/values for objects

1. Determine which attribute to set the value to according to the condition

var obj = { top: 60 };
// obj['left'] = 20 when the element is on the left of the box, obj['right'] = 20 when the element is on the right of the box
obj[['left'.'right'][+(posLeft > containerWidth  / 2=)]]20;
Copy the code

2. Determine whether to add an attribute or value based on the condition

You can use the expansion operator to conditionally add attributes to an object:

const edit = this.$route.query.edit; // Whether to edit the status
const params = {  
  id: editData.id,
  title: editData.title, ... (edit ==1 && { type: 'edit' }), // If the state is edited, set the type attribute to 'edit'.
};
Copy the code

Parse if edit is 1, the params object is given the property type value of ‘edit’; If the edit value is not 1, it expands false and has no effect on the object

Conditionally add elements to an array

This is the CRA Webpack configuration source:

module.exports = {
  plugins: [
    new HtmlWebpackPlugin(),
    isEnvProduction && new MiniCssExtractPlugin(),
    useTypeScript && new ForkTsCheckerWebpackPlugin(),
  ].filter(Boolean),}Copy the code

If isEnvProduction is true, MiniCssExtractPlugin will be added. When isEnvProduction is false, false is added to the array. Finally, filter is used to filter the array with a value of true

If/else optimization

There is a business handler function that performs a different method to get the corresponding value depending on the parameter type

function getValue(type) {
  if (type == 'a') {
    return getValue_a();
  } else if (type == 'b') {
    return getValue_b();
  } else if (type == 'c') {
    return getValue_c();
  } else if (type == 'd') {
    return getValue_d();
  } else {
    returngetValue_default(); }}Copy the code

This method is recommended to use switch/case or collection of objects instead:

  • Switch/case:
function getValue(type) {
  switch(type) {
    case 'a':
      return getValue_a();
    case 'b':
      return getValue_b();
    case 'c':
      return getValue_c();
    case 'd':
      return getValue_d();
    default:
      return getValue_default();
}
Copy the code
  • Object collection form:
const handlers = {
  'a': () = > getValue_a(),
  'b': () = > getValue_b(),
  'c': () = > getValue_c(),
  'd': () = > getValue_d(),
  'default': () = > getValue_default(),
}

function getValue(type) {
  return handlers[type || 'default'] (); }Copy the code

Determines whether an attribute exists in the object

We know that the in keyword can be used to determine whether an attribute is present in an object:

const params = { id: 111.title: 'edit'.type: 'edit' };
console.log('type' in params); // true
console.log('content' in person); // false
Copy the code

But the in keyword retrieves a stereotype property, so there is no way to tell whether the property is defined in the current object or in the stereotype. For example:

"hasOwnProperty" in {}; // true
"toString" in {}; // true
Copy the code

A lot of things we don’t care about the prototype on whether there is a property, you can through the Object. The prototype. The hasOwnProperty method, such as:

Object.prototype.hasOwnProperty.call(params, 'type'); // true
Object.prototype.hasOwnProperty.call(params, 'content'); // false
Copy the code

The above code has to write a long string of code for each judgment. You can define a global method and use it as needed.

const hasOwnProperty = Object.prototype.hasOwnProperty
function hasOwn(target, key) {
  return hasOwnProperty.call(target, key);
}

console.log(hasOwn(params, 'type')); // true
Copy the code

Async /await exception handling encapsulation

Normal async/await is really easy and concise to use and avoids callback hell

function getUserInfoApi(params) {
  return new Promise((resolve, reject) = > {
    axios.get(url, { params }).then(res= > {
      if (res.status == 200) {
        resolve(res.data.data);
      } else {
        reject(null);
      }
    }).catch(err= > {
      reject(null);
    });
  });
}
async getUserInfo() {
  const userInfo = await getUserInfoApi({ userId: 123 }); // { name: 'zhangsan', age: 30 }
  console.log(userInfo.name);
}
Copy the code

However, if an exception occurs during the execution of the getUserInfoApi, an error will be reported affecting subsequent code execution;

So we need an exception handling solution:

Plan 1

Resolve an empty object after exception catching directly in the getUserInfoApi method, without affecting subsequent userInfo operations

However, in the actual development process, there will be a lot of interface requests. If each interface is processed in this way, it will take time and energy and affect efficiency.

function getUserInfoApi(params) {
  return new Promise((resolve, reject) = > {
    axios.get(url, { params }).then(res= > {
      if (res.status == 200) {
        resolve(res.data.data);
      } else {
        resolve({});
      }
    }).catch(err= > {
      resolve({});
    });
  });
}
Copy the code

Scheme 2

Encapsulate a common exception handler that wraps the return value.

function getUserInfoApi(params) {
  return new Promise((resolve, reject) = > {
    axios.get(url, { params }).then(res= > {
      if (res.status == 200) {
        resolve(res.data.data);
      } else {
        reject(null);
      }
    }).catch(err= > {
      reject(null);
    });
  });
}
/ * * *@descriptionWrap asynchronous, exception handling (avoid try/catch handling when using async/await) *@param {*} The request (typically a Promise) that THE SERVER needs to handle the exception@return {Promise} Return a Promise destructed with async/await at the place of the call: Let [err, result] = await this.wrappedAwait(error), error */
wrappedAwait(awaited) {
  let p = Promise.resolve(awaited); // Non-promise becomes Promise
  return p.then(
    res= > {
      return [null, res];
    },
    err= > {
      return [err, null]; }); }async function test() {
  const [err, res] = await wrappedAwait(getUserInfoApi({ userId: 123 }));
  console.log('__err__', err);
  console.log('__res__', res);
}

test();
Copy the code

The return value of the wrappedAwait function is an array type. The first item is the error message in the abnormal case, and the second item is the interface data returned in the normal case. The corresponding logical processing is performed by judging the returned value

JS randomly scrambles the array

function shuffle(arr) {
  let i = arr.length;
  while (i) {
    let j = Math.floor(Math.random() * i--);
    [arr[j], arr[i]] = [arr[i], arr[j]];
  }
  return arr;
}
Copy the code

Sort method is used to implement:

const list = [-34567.5.8.110, -321.567.200.234, -654, -2.1.654321];
list.sort(() = > Math.random() - 0.5);
console.log(list);
Copy the code

Array flattening

There are several ways to implement array flattening:

  • 1. Recursive implementation (common)

    const arr = [1[2[3.4], [5[6.7]]]];
    
    function flatten(arr) {
      let result = [];
    
      arr.forEach(item= > {
        if (Array.isArray(item) {
          result = result.concat(flatten(item));
        } else{ result.push(item); }});return result;
    }
    
    console.log(flatten(arr));
    Copy the code
  • 2. Reduce implementation (common)

    const arr = [1[2[3.4], [5[6.7]]]];
    
    function flatten(arr) {
      return arr.reduce((prev, curr) = > {
        return prev.concat(Array.isArray(curr) ? flatten(curr) : curr); } []); }console.log(flatten(arr));
    Copy the code
  • 3. Es10-flat () function (recommended)

    New to ES10 is the flat() method, which is used to flatten an array (” flatten “) and returns a new array.

    The flat() method only flattens one layer by default. It takes an argument indicating the number of layers to flatten, which defaults to 1 if no argument is passed.

    If you want to convert to a one-dimensional array regardless of the number of nesting levels, you can use the Infinity keyword as an argument.

    const arr = [1[2[3.4], [5[6.7]]]];
    
    console.log(arr.flat(Infinity));
    Copy the code

    For an introduction to Flat (), check out Ruan Yifeng’s introduction to ECMAScript 6

  • 4

    let arr = [1[2[3.4], [5[6.7]]]];
    let str = JSON.stringify(arr);
    console.log(str);
    let result = str.replace(/(\[|\])/g.' ').split(', ');
    console.log(result);
    Copy the code

Since ES6 already gives us a proprietary method, why not use it?

Date formatting

Here is a common way to format dates

// Format 1 according to the custom format (only replace once)
function formatDateAndTime(value, fmt) {
  let date = value ? new Date(value) : new Date(a);let o = {
    'M+': date.getMonth() + 1.'d+': date.getDate(),
    'H+': date.getHours(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds(),
    'S+': date.getMilliseconds()
  };

  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + ' ').substr(4 - RegExp.$1.length));
  }
  for (var k in o) {
    if (new RegExp(` (${k}) `).test(fmt)) {
      fmt = fmt.replace(RegExp. $1,RegExp.$1.length == 1)? (o[k]) : (('00' + o[k]).substr(String(o[k]).length))); }}return fmt;
}
Copy the code

FormatDateAndTime method above can only be replaced once the information such as date, usually meets the requirement, but if the FMT parameter value is similar to the ‘yyyy/MM/dd hh: MM: ss – on dd MM yyyy years’ this format, Since yyyy, MM, and dd are repeated, the re replaces only the first occurrence of YYYY and does not convert the second occurrence of YYYY.

Regular replacements can be optimized to use global replacements

// Format 2 according to custom format (global substitution)
function formatDateAndTime(value, fmt) {
  let date = value ? new Date(value) : new Date(a);let o = {
    'M+': date.getMonth() + 1.'d+': date.getDate(),
    'H+': date.getHours(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds(),
    'S+': date.getMilliseconds()
  };

  let reg = new RegExp('(y+)'.'g');
  if (reg.test(fmt)) {
    fmt = fmt.replace(reg, (_, curr) = > (date.getFullYear() + ' ').substring(4 - curr.length));
  }
  for (var k in o) {
    let reg_k = new RegExp(` (${k}) `.'g');
    if (reg_k.test(fmt)) {
      fmt = fmt.replace(reg_k, (_, curr) = > {
        return curr.length == 1 ? (o[k]) : (00 `${o[k]}`).substring(String(o[k]).length) }); }}return fmt;
}
Copy the code

Zero padding operation

Method 1: Loop to add zero until the specified length

function zerofill(num, n) {
  let str = num.toString();
  let len = str.length;
  while (len < n) {
    str = '0' + str;
    len++;
  }

  return str;
}
Copy the code

Method 2: Fill in enough zeros, and then intercept the string from the end of the specified length

// The first method
function zerofill(num, n) {
  return (Array(n).fill(0) + num).slice(-n);
}
Copy the code

Mode 3: ES2017 introduces string completion length (padStart, padEnd)

'1'.padStart(4.'0') / / '0001'
'1'.padStart(4) // '1' (if the second argument is omitted, padding is used by default)
Copy the code

Generating random strings

Generating random strings is a common requirement, and you can customize the output to suit your needs.

Method 1

The math. random method is used to randomly extract one character at a time from the target array

/ * * *@description: Generates a random string *@param {Number} The length of the random string len wants to generate *@param {Boolean} IsPlainNumber Random string whether pure digits *@return {String} The string */ to be output
function randomHandler(len = 16, isPlainNumber = false) {
  let chars = isPlainNumber ? '1234567890' : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
  let result = ' '; 
  
  for (let i = 0; i < len; i++) {
    let currIndex = ~~(Math.random() * chars.length);
    result += chars[currIndex];
  }

  return result;
}

console.log(randomHandler(20));
Copy the code

Way 2

Random strings are cleverly generated using math.random () and toString() methods

  • Math.random()Can generate a [0, 1) range of random number
  • toString(radix)Method can convert the number toradixA string in base
console.log(Math.random()) / / 0.1852958957954327
console.log(Math.random()) / / 0.19654440821013286
console.log(Math.random()) / / 0.862253082562344
console.log(Math.random().toString()) / / 0.8898541967454725
console.log(Math.random().toString()) / / 0.21295312937219646
console.log(Math.random().toString(36).substring(2)) // xkji8078cgr
console.log(Math.random().toString(36).substring(2)) // 0e8jhas9zxft
console.log(Math.random().toString(36).substring(2)) // yxnudwel5iq
Copy the code

Great, one line of code.

Oh, wait a minute, there seems to be a glitch: Math.random() generates an arbitrary number after the decimal point

Then optimize it a little bit: if a random fails to reach the desired length, splice the random several times

Let’s look at the implementation:

/ * * *@description: Generates a random string *@param {Number} The length of the random string len wants to generate *@param {Boolean} IsPlainNumber Random string whether pure digits *@return {String} The string */ to be output
function randomHandler(len = 16, isPlainNumber = false) {
  let result = ' ';
  // If pure digits are required, convert to base 10, otherwise convert to base 36 (26 letters +10 digits)
  let radix = isPlainNumber ? 10 : 36;
  // Use substring(2) to keep the decimal place, leaving out the integer and decimal point
  let getOnce = () = > Math.random().toString(radix).substring(2);
  while(result.length < len) {
    result += getOnce();
  }
  
  return result.substring(0, len);
}

console.log(randomHandler(20));
Copy the code

Finally, math.random ().tostring (16) seems to be a straightforward way to implement a common little function: generating random colors

function getRandomColor() { 
  return ` #The ${Math.random().toString(16).substr(2.6)}`
}

console.log(getRandomColor())
Copy the code

Conditional judgment simplification

There is such a service judgment that the user role value role has [1,2,3,4,5,6,7,8,9,10], a total of ten roles. When role is 1,2,5,8,10, the delete button will be displayed

A normal way to judge might be like this:

if (role == 1 || role == 2 || role == 5 || role == 8 || role == 10) {
  this.showDelBtn = true;
}
Copy the code

It can be optimized like this:

if ([1.2.5.8.10].includes(role)) {
  this.showDelBtn = true;
}
Copy the code