Skip to content
On This Page

JavaScript实战案例

1. 工具函数

1.1 如何判断一个对象是不是空对象

可以使用以下几种判断对象是否为空的方法。

  1. Object.keys()

    在使用这个方法之前,我们需要对传入的对象进行判空,Object.keys() 返回一个包含对象所有可枚举属性的数组。如果这个数组的长度为 0,那么对象就是空的。

    javascript
    function isEmptyObject(obj) {
      return obj && typeof obj === 'object' && Object.keys(obj).length === 0;
    }
    
    let emptyObj = {};
    console.log(isEmptyObject(emptyObj)); // true
    
    let nonEmptyObj = { key: 'value' };
    console.log(isEmptyObject(nonEmptyObj)); // false
  2. for...in 循环

    如果你需要兼容较老的浏览器,可以使用 for...in 循环。这个方法会遍历对象的所有可枚举属性。如果找到任何一个属性,就说明对象不为空。

    javascript
    function isEmptyObject(obj) {
      for (let prop in obj) {
        if (Object.hasOwn(obj, prop)) {
          return false;
        }
      }
      return true;
    }
  3. JSON.stringify()

    这个方法将对象转换为 JSON 字符串,然后比较是否等于 '{}'。不过要注意,这个方法可能会有一些边界情况,比如对象中包含 undefined、函数等值时可能会出现问题。

    javascript
    function isEmptyObject(obj) {
      return JSON.stringify(obj) === '{}';
    }
  4. Object.getOwnPropertyNames()

    这个方法比 Object.keys() 更严格,因为它会检查所有属性,而不仅仅是可枚举属性。

    javascript
    function isEmptyObject(obj) {
      return Object.getOwnPropertyNames(obj).length === 0;
    }

1.2 如何判断网页元素是否到达可视区域

判断网页元素是否到达可视区域是一个常见的需求,尤其在实现懒加载、无限滚动或者触发动画等场景中非常有用。有以下几种几种:

  1. Intersection Observer API

    这是我最推荐的方法,因为它既高效又简单。Intersection Observer API 可以异步地观察目标元素与其祖先元素或视口的交叉状态。

    javascript
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          console.log('元素进入可视区域');
          // 在这里执行你的逻辑
        }
      });
    });
    
    const target = document.querySelector('#your-element');
    observer.observe(target);

    这种方法的优点是性能好,不会阻塞主线程,而且使用起来相对简单。

  2. getBoundingClientRect() 方法

    如果需要更精确的控制或者需要兼容旧版浏览器,我们可以使用 getBoundingClientRect() 方法。这个方法返回元素的大小及其相对于视口的位置。

    javascript
    function isElementInViewport(el) {
      const rect = el.getBoundingClientRect();
      return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
      );
    }
    
    // 使用
    const element = document.querySelector('#your-element');
    if (isElementInViewport(element)) {
      console.log('元素在可视区域内');
    }

    这种方法的优点是可以精确控制元素的可见范围,但需要注意的是,如果在滚动事件中频繁调用,可能会影响性能。

    在使用 getBoundingClientRect() 方法时,可以使用节流(throttle)或防抖(debounce)技术来限制函数的调用频率,以避免过度消耗性能。

  3. Element.checkVisibility() 方法

    这是一个相对较新的 API,可以直接检查元素是否可见:

    javascript
    const element = document.querySelector('#your-element');
    if (element.checkVisibility()) {
      console.log('元素可见');
    }

    这个方法还可以接受一个选项对象,用于进行更细粒度的检查。

    javascript
    element.checkVisibility({
      checkOpacity: true,  // 检查 opacity 是否为 0
      checkVisibilityCSS: true  // 检查 visibility CSS 属性
    });

    虽然这个方法使用起来很简单,但需要注意浏览器兼容性问题 实际开发中。

如果是现代浏览器环境,我会优先选择 Intersection Observer API,因为它既高效又易用。如果需要更精确的控制或者需要兼容旧版浏览器,我会使用 getBoundingClientRect() 方法。

2. 原理学习

2.1 使用 async/await 模拟阻塞

虽然 JavaScript 本身不支持真正的阻塞操作,但我们可以使用 async/awaitPromise 来模拟阻塞行为。例如,我们可以创建一个 Promise,该 Promise 在满足某个条件之前不会解决,从而模拟阻塞。

以下是一个简单的例子,其中 sleep 函数模拟了一个阻塞操作:

javascript
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function blockingOperation() {
  console.log('Starting blocking operation...');
  await sleep(2000); // 模拟阻塞 2 秒
  console.log('Blocking operation completed.');
}

blockingOperation();

在这个例子中,sleep 函数返回一个在指定毫秒数后解决的 PromiseblockingOperation 函数使用 await 关键字等待 sleep 函数完成,从而模拟了阻塞行为。

javascript
// 定义一个返回 Promise 的函数,模拟异步操作(如网络请求)
function fetchData() {
  return new Promise((resolve) => {
    // 使用 setTimeout 模拟网络延迟
    setTimeout(() => {
      resolve("数据已获取");
    }, 2000); // 2秒后解决 Promise
  });
}

// 定义一个 async 函数,使用 await 关键字等待 fetchData 函数的 Promise 解决
async function getData() {
  console.log("开始获取数据...");
  // await 会暂停 getData 函数的执行,直到 fetchData 返回的 Promise 解决
  const data = await fetchData();
  // 当 Promise 解决后,继续执行 getData 函数
  console.log(data); // 输出 "数据已获取"
  console.log("数据获取完成。");
}

// 调用 async 函数
getData();