返回

JavaScript中的那些鲜为人知的骚操作,你都知道吗?

前端

1. 数组去重

正常我们实现数组去重大多都是通过双层遍历或者indexOf的方式。

// 双层for循环去重
function unique1(arr) {
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    let isUnique = true;
    for (let j = 0; j < result.length; j++) {
      if (arr[i] === result[j]) {
        isUnique = false;
        break;
      }
    }
    if (isUnique) {
      result.push(arr[i]);
    }
  }
  return result;
}

// 利用indexOf去重
function unique2(arr) {
  const result = [];
  for (let i = 0; i < arr.length; i++) {
    if (result.indexOf(arr[i]) === -1) {
      result.push(arr[i]);
    }
  }
  return result;
}

但其实有一种更简单的方式:利用Array.from与set去重

function unique3(arr) {
  return Array.from(new Set(arr));
}

这种代码的实现原理是:利用Set的数据结构,Set可以自动去重,然后利用Array.from()方法将Set转换为数组。

2. 函数柯里化

函数柯里化是指将一个函数拆分成多个子函数,每个子函数都接受一个参数,并将结果传递给下一个子函数,最终得到最终结果。

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn(...args);
    } else {
      return (...rest) => curried(...args, ...rest);
    }
  };
}
const sum = (a, b, c) => a + b + c;
const curriedSum = curry(sum);

curriedSum(1)(2)(3); // 6
curriedSum(1, 2)(3); // 6
curriedSum(1, 2, 3); // 6

函数柯里化的好处是可以将一个复杂函数拆分成多个简单的函数,方便代码的复用和维护。

3. 事件委托

事件委托是指将事件监听器注册到父元素上,而不是子元素上。当子元素触发事件时,事件会向上冒泡到父元素,父元素的事件监听器就会被触发。

const parent = document.getElementById('parent');
parent.addEventListener('click', (event) => {
  // 事件委托,可以处理子元素的点击事件
  console.log(event.target);
});

事件委托的好处是可以减少事件监听器的数量,提高代码的性能。

4. 深拷贝

深拷贝是指将一个对象的所有属性和子属性都复制一份,而浅拷贝只复制一层。

// 浅拷贝
const obj1 = {
  name: 'John',
  age: 20,
  address: {
    street: 'Main Street',
    city: 'New York',
  },
};

const obj2 = { ...obj1 };

obj2.address.street = 'Wall Street';

console.log(obj1); // { name: 'John', age: 20, address: { street: 'Wall Street', city: 'New York' } }
console.log(obj2); // { name: 'John', age: 20, address: { street: 'Wall Street', city: 'New York' } }
// 深拷贝
const obj1 = {
  name: 'John',
  age: 20,
  address: {
    street: 'Main Street',
    city: 'New York',
  },
};

const obj2 = JSON.parse(JSON.stringify(obj1));

obj2.address.street = 'Wall Street';

console.log(obj1); // { name: 'John', age: 20, address: { street: 'Main Street', city: 'New York' } }
console.log(obj2); // { name: 'John', age: 20, address: { street: 'Wall Street', city: 'New York' } }

深拷贝的好处是可以保证两个对象完全独立,互不影响。

5. 防抖

防抖是指在事件连续触发的情况下,只执行一次函数。

const debounce = (fn, delay) => {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn(...args);
    }, delay);
  };
};
const input = document.getElementById('input');
const debouncedInput = debounce((event) => {
  console.log(event.target.value);
}, 500);

input.addEventListener('input', debouncedInput);

防抖的好处是可以防止函数被频繁调用,提高代码的性能。

6. 节流

节流是指在一定时间内只执行一次函数。

const throttle = (fn, delay) => {
  let lastTime = 0;
  return (...args) => {
    const now = Date.now();
    if (now - lastTime >= delay) {
      fn(...args);
      lastTime = now;
    }
  };
};
const scrollHandler = throttle((event) => {
  console.log(window.scrollY);
}, 100);

window.addEventListener('scroll', scrollHandler);

节流的好处是可以防止函数被频繁调用,提高代码的性能。

7. 箭头函数

箭头函数是ES6中引入的新语法,它可以替代传统的函数表达式。

// 传统函数表达式
const sum = function(a, b) {
  return a + b;
};

// 箭头函数
const sum = (a, b) => a + b;

箭头函数的好处是语法更简洁,而且可以自动绑定this。

8. Promise

Promise是ES6中引入的新语法,它可以表示一个异步操作的结果。

const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    resolve('Hello, world!');
  }, 1000);
});

promise.then((result) => {
  console.log(result); // Hello, world!
});

Promise的好处是可以让代码更加易读和可维护。

9. 原型链

原型链是JavaScript中一个重要的概念,它可以实现继承。

// 父类
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

// 子类
class Student extends Person {
  constructor(name, age, school) {
    super(name, age);
    this.school = school;
  }

  study() {
    console.log(`I am studying at ${this.school}.`);
  }
}

// 创建一个学生对象
const student = new Student('John', 20, 'Harvard University');

// 调用greet方法
student.greet(); // Hello, my name is John