前端JavaScript面试八股常考题(五)

文章目录

      • 43. Cookie、Session 和 Token 的区别
        • Cookie
        • Session
        • Token
        • 区别总结
      • 44. AJAX、Axios 和 Fetch 的区别
        • AJAX (Asynchronous JavaScript and XML)
        • Fetch
        • Axios
        • 主要区别
      • 45. 常见的 DOM 操作
        • 获取元素
        • 创建元素
        • 修改元素属性
        • 修改元素内容
        • 添加/删除元素
        • 事件监听
        • 其他操作
      • 46. `"use strict"` 是什么意思?使用它有什么区别?
        • 定义
        • 主要区别
        • 总结
      • 47. JavaScript 如何判断一个对象是否属于某个类?
        • 方法
        • 总结
      • 48. `mouseover` 和 `mouseenter` 事件的区别是什么?
        • 一句话概括
        • 具体区别
        • 示例代码
      • 49. JavaScript 中 `substring` 和 `substr` 函数的区别是什么?
        • 主要区别
        • 扩展
      • 50. JavaScript 的 `splice` 和 `slice` 函数会改变原数组吗?
        • `splice()`
        • `slice()`
      • 51. JavaScript 中怎么删除数组最后一个元素?
        • 方法
        • 总结
      • 52.如何判断网页元素是否到达可视区域
        • 1. Intersection Observer API
        • 2. `getBoundingClientRect()` 方法
        • 3. `Element.checkVisibility()` 方法
        • 总结

43. Cookie、Session 和 Token 的区别

Cookie
  • 定义:存储在用户浏览器中的小型数据片段,通常用于跟踪用户会话或用户偏好设置。
  • 特点
    • 存储在客户端(浏览器)。
    • 每次请求都会自动发送给服务器。
    • 有大小限制(通常为 4KB)。
    • 可以设置过期时间。
  • 用途
    • 保存用户登录状态(如 session ID)。
    • 记录用户偏好设置(如语言、主题)。
Session
  • 定义:服务器端存储的用户会话数据,通常通过一个唯一的 session ID 来标识。
  • 特点
    • 存储在服务器端,安全性更高。
    • 依赖于 Cookie(通常通过 Cookie 中的 session ID 来识别用户)。
    • 生命周期由服务器控制。
  • 用途
    • 管理用户登录状态。
    • 存储用户会话期间的临时数据。
Token
  • 定义:一种字符串,通常用于身份验证和授权,常见的有 JWT(JSON Web Token)。
  • 特点
    • 存储在客户端(如浏览器的 localStorage 或内存)。
    • 不依赖于服务器端存储,减轻服务器负担。
    • 可以跨域使用。
    • 包含用户信息和签名,可验证身份。
  • 用途
    • 用于 API 认证。
    • 无状态(stateless)身份验证,适合分布式系统。
      前端JavaScript面试八股常考题(五)_第1张图片
区别总结
特性 Cookie Session Token
存储位置 客户端 服务器端 客户端
安全性 较低(存储在客户端) 较高(存储在服务器端) 较高(签名验证)
依赖 自动发送给服务器 依赖 Cookie 中的 session ID 需手动添加到请求头中
生命周期 可设置过期时间 由服务器控制 由 Token 的过期时间决定
用途 用户偏好、会话标识 管理会话、存储临时数据 API 认证、无状态身份验证

44. AJAX、Axios 和 Fetch 的区别

AJAX (Asynchronous JavaScript and XML)
  • 定义:一种使用现有技术(如 XMLHttpRequest)实现异步请求的方法。

  • 特点

    • 最早的异步请求方案。
    • 使用回调函数处理响应。
    • 不支持 Promise
  • 示例代码

    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://api.example.com/data', true);
    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4 && xhr.status == 200) {
        console.log(xhr.responseText);
      }
    };
    xhr.send();
    
Fetch
  • 定义:较新的 API,基于 Promise,旨在替代 XMLHttpRequest

  • 特点

    • 语法更简洁,使用更直观。
    • 基于 Promise,支持 async/await
    • 不会自动抛出错误(需要手动检查响应状态)。
  • 示例代码

    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => console.log(data))
      .catch(error => console.error('Error:', error));
    
Axios
  • 定义:一个基于 Promise 的第三方 HTTP 客户端库,适用于浏览器和 Node.js。

  • 特点

    • 提供更多功能,如拦截器、自动转换 JSON 数据。
    • 在响应状态码不在 2xx 范围内时自动抛出错误。
    • 支持取消请求。
    • 支持防御 XSRF/CSRF 攻击。
  • 示例代码

    axios.get('https://api.example.com/data')
      .then(response => {
        console.log(response.data);
      })
      .catch(error => {
        console.error('Error:', error);
      });
    
主要区别
特性 AJAX Fetch Axios
语法 回调函数 基于 Promise 基于 Promise,API 更简洁
错误处理 需手动检查状态码 不会自动抛出错误 自动抛出错误(非 2xx 状态)
请求取消 不支持 需使用 AbortController 提供取消请求的方法
浏览器支持 所有现代浏览器 不支持旧版浏览器(如 IE11) 通过 polyfill 支持更广泛
功能丰富度 功能有限 功能较简单 提供拦截器、自动转换 JSON 等

45. 常见的 DOM 操作

获取元素
  • document.getElementById():返回单个元素。
  • document.getElementsByClassName():返回元素集合。
  • document.querySelector():返回第一个匹配的元素。
  • document.querySelectorAll():返回所有匹配的元素集合。
创建元素
  • document.createElement():创建新元素。
  • 使用 appendChild()insertBefore() 将新元素插入 DOM 树。
修改元素属性
  • element.setAttribute():设置属性。
  • element.removeAttribute():移除属性。
  • 对于某些特定属性(如 classid),可以直接通过点操作符设置,例如 element.className = 'new-class'
修改元素内容
  • element.innerHTML:修改节点内的 HTML 内容。
  • element.textContent:修改节点内的纯文本内容。
  • 区别:innerHTML 可以解析 HTML 标签,而 textContent 只处理纯文本。
添加/删除元素
  • parentNode.appendChild():添加子元素。
  • parentNode.removeChild():移除子元素。
事件监听
  • element.addEventListener():给元素绑定事件。
  • 可以绑定多个事件到同一个元素上,不会覆盖之前的事件。
其他操作
  • 克隆节点element.cloneNode()
  • 查找父节点/子节点
    • element.parentNode:获取父节点。
    • element.childNodes:获取子节点集合。
    • element.firstChild:获取第一个子节点。
    • element.lastChild:获取最后一个子节点。
  • 获取或设置样式
    • element.style:操作内联样式。
    • window.getComputedStyle():获取元素的计算样式。
  • 处理样式类名
    • 使用 classList 属性:
      • element.classList.add('new-class'):添加类名。
      • element.classList.remove('new-class'):移除类名。
      • element.classList.toggle('new-class'):切换类名。
      • element.classList.contains('new-class'):判断是否包含类名。

46. "use strict" 是什么意思?使用它有什么区别?

定义

"use strict" 是 JavaScript 中的一个指令,用于启用严格模式(Strict Mode)。它必须放在脚本或函数的开头,指定代码在严格模式下运行。严格模式会限制一些不规范的语法,帮助开发者写出更安全、更规范的代码。

主要区别
  1. 变量必须声明后才能使用

    "use strict";
    x = 3.14; // 错误:x 未声明
    
  2. 禁止使用 with 语句

    "use strict";
    with (Math) { x = cos(2); } // 语法错误
    
  3. eval 作用域

    • 在严格模式下,eval() 中的代码会在自己的作用域中执行,而不是在当前作用域中。
  4. 禁止 this 关键字指向全局对象

    "use strict";
    function f() {
        return this;
    }
    f(); // 返回 `undefined`,而不是全局对象
    
  5. 函数参数不能重名

    "use strict";
    function sum(a, a, c) { // 语法错误
        return a + a + c;
    }
    
  6. 禁止八进制数字语法

    "use strict";
    var sum = 015 + 197 + 142; // 语法错误
    
  7. 禁止对只读属性赋值

    "use strict";
    var obj = {};
    Object.defineProperty(obj, "x", { value: 0, writable: false });
    obj.x = 3.14; // 抛出错误
    
  8. 禁止删除不可删除的属性

    "use strict";
    delete Object.prototype; // 抛出错误
    
总结

使用 "use strict" 可以帮助开发者避免一些常见的错误,提高代码的安全性和可维护性。它还消除了代码运行的一些不安全之处,保证代码运行的安全,提高编译器效率,增加运行速度。


47. JavaScript 如何判断一个对象是否属于某个类?

方法
  1. instanceof 运算符

    • 检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
    class Animal {}
    class Dog extends Animal {}
    
    const dog = new Dog();
    
    console.log(dog instanceof Dog);     // true
    console.log(dog instanceof Animal);  // true
    console.log(dog instanceof Object);  // true
    
  2. constructor 属性

    • 每个对象都有一个 constructor 属性,指向创建该对象的构造函数。
    console.log(dog.constructor === Dog);  // true
    
  3. Object.prototype.isPrototypeOf() 方法

    • 测试一个对象是否存在于另一个对象的原型链上。
    console.log(Dog.prototype.isPrototypeOf(dog));    // true
    console.log(Animal.prototype.isPrototypeOf(dog)); // true
    
  4. Object.getPrototypeOf() 方法

    • 返回指定对象的原型。
    console.log(Object.getPrototypeOf(dog) === Dog.prototype);  // true
    
  5. 自定义类型检查函数

    • 可以提供更精确的检查。
    function isDog(obj) {
        return obj && typeof obj === 'object' && obj.constructor === Dog;
    }
    
    console.log(isDog(dog));  // true
    
  6. Symbol.hasInstance

    • ES6 引入,允许类自定义 instanceof 的行为。
    class MyClass {
        static [Symbol.hasInstance](instance) {
            return Array.isArray(instance);
        }
    }
    
    console.log([] instanceof MyClass);  // true
    
总结
  • instanceofisPrototypeOf() 可以检查整个原型链,但可能会受到原型链被修改的影响。
  • constructor 属性可以被重写,因此不总是可靠。
  • Object.getPrototypeOf() 更可靠,但只检查直接原型。
  • 自定义函数可以提供最精确的检查,但需要为每个类型单独实现。
  • 在实际开发中,instanceof 是最常用的方法,因为它简单直观,并且能够处理继承关系。

48. mouseovermouseenter 事件的区别是什么?

一句话概括

mouseover 会冒泡,mouseenter 不会。

具体区别
  1. 触发条件
    • mouseover:鼠标指针进入元素或其子元素时触发。
    • mouseenter:仅在鼠标指针进入元素本身时触发,不会在进入子元素时触发。
  2. 事件冒泡
    • mouseover:会冒泡,即鼠标从子元素移出到父元素时也会触发。
    • mouseenter:不会冒泡,仅在进入元素时触发。
  3. 使用场景
    • 如果希望在鼠标进入元素及其子元素时都触发事件,使用 mouseover
    • 如果只想在鼠标进入元素本身时触发事件,使用 mouseenter
示例代码
const parent = document.getElementById('parent');
const child = document.getElementById('child');

parent.addEventListener('mouseover', () => {
  console.log('Parent mouseover');
});

parent.addEventListener('mouseenter', () => {
  console.log('Parent mouseenter');
});

child.addEventListener('mouseover', () => {
  console.log('Child mouseover');
});

child.addEventListener('mouseenter', () => {
  console.log('Child mouseenter');
});
  • 当鼠标从父元素移动到子元素时:
    • mouseover 会在子元素和父元素上都触发。
    • mouseenter 只会在最初进入父元素时触发一次。

49. JavaScript 中 substringsubstr 函数的区别是什么?

主要区别
  1. 参数含义

    • substring(start, end):返回从 startend(不包括 end)之间的字符串。
    • substr(start, length):返回从 start 开始的 length 个字符。
    let str = "Hello, World!";
    console.log(str.substring(0, 5)); // "Hello"
    console.log(str.substr(0, 5));    // "Hello"
    
  2. 负值参数的处理

    • substring:将负值参数转换为 0。
    • substr:允许第一个参数为负值,从字符串末尾开始计数。
    console.log(str.substring(-3)); // "Hello, World!"
    console.log(str.substr(-3));    // "ld!"
    
  3. 参数顺序

    • substring:会自动调整参数顺序,使 startIndex 总是小于等于 endIndex
    • substr:不会调整参数顺序。
    console.log(str.substring(5, 2)); // "llo"
    console.log(str.substr(5, 2));    // ", "
    
  4. 浏览器兼容性和未来发展

    • substring:在所有现代浏览器中都得到了很好的支持。
    • substr:虽然目前仍被广泛支持,但已被 MDN 标记为废弃(deprecated),未来可能会被移除。
扩展

可以使用更现代的 slice 方法来替代 substring

console.log(str.slice(0, 5)); // "Hello"
console.log(str.slice(-3));   // "ld!"

50. JavaScript 的 spliceslice 函数会改变原数组吗?

splice()
  • 功能:可以在数组中添加、删除或替换元素,并返回被删除的元素。
  • 是否会改变原数组:会改变原数组。
const arr = [1, 2, 3, 4, 5];
const removed = arr.splice(2, 2); // 从索引 2 开始删除 2 个元素
console.log(arr); // [1, 2, 5]
console.log(removed); // [3, 4]
slice()
  • 功能:从原数组中返回指定开始和结束位置的元素组成的新数组,包含原数组的一部分浅拷贝。
  • 是否会改变原数组:不会改变原数组。
const arr = [1, 2, 3, 4, 5];
const sliced = arr.slice(1, 3); // 从索引 1 开始到索引 3 结束(不包括索引 3)
console.log(arr); // [1, 2, 3, 4, 5]
console.log(sliced); // [2, 3]

51. JavaScript 中怎么删除数组最后一个元素?

方法
  1. 使用 pop() 方法

    • 最直接、最常用的方法。
    • 会删除数组的最后一个元素,并返回被删除的元素。
    let fruits = ['apple', 'banana', 'orange'];
    let lastFruit = fruits.pop();
    console.log(lastFruit); // "orange"
    console.log(fruits); // ['apple', 'banana']
    
  2. 使用 splice() 方法

    • 更灵活,但稍微复杂一些。
    let fruits = ['apple', 'banana', 'orange'];
    let removedFruits = fruits.splice(-1, 1);
    console.log(removedFruits); // ['orange']
    console.log(fruits); // ['apple', 'banana']
    
  3. 使用 slice() 方法

    • 不会修改原数组,而是创建一个新数组。
    let fruits = ['apple', 'banana', 'orange'];
    let newFruits = fruits.slice(0, -1);
    console.log(newFruits); // ['apple', 'banana']
    console.log(fruits); // ['apple', 'banana', 'orange']
    
  4. 修改数组的 length 属性

    • 简单高效,但代码可读性稍差。
    let fruits = ['apple', 'banana', 'orange'];
    fruits.length = fruits.length - 1;
    console.log(fruits); // ['apple', 'banana']
    
总结
  • 如果需要直接修改原数组,推荐使用 pop()splice()
  • 如果不想修改原数组,可以使用 slice()
  • 修改 length 属性也是一种简单高效的方法,但需注意代码的可读性。

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

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


1. 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() 方法

特点

  • 返回元素的大小及其相对于视口的位置。
  • 可以精确控制元素的可见范围。
  • 需要注意性能问题,尤其是在滚动事件中频繁调用时。

示例代码

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('元素在可视区域内');
}

优化建议

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

示例代码(节流优化)

JavaScript

复制

function throttle(func, limit) {
  let lastFunc;
  let lastRan;
  return function() {
    const context = this;
    const args = arguments;
    if (!lastRan) {
      func.apply(context, args);
      lastRan = Date.now();
    } else {
      clearTimeout(lastFunc);
      lastFunc = setTimeout(function() {
        if ((Date.now() - lastRan) >= limit) {
          func.apply(context, args);
          lastRan = Date.now();
        }
      }, limit - (Date.now() - lastRan));
    }
  };
}

window.addEventListener('scroll', throttle(() => {
  if (isElementInViewport(element)) {
    console.log('元素在可视区域内');
  }
}, 100));

3. Element.checkVisibility() 方法

特点

  • 较新的 API,可以直接检查元素是否可见。
  • 可以接受一个选项对象,用于进行更细粒度的检查。
  • 兼容性较差,仅支持部分现代浏览器。

示例代码

JavaScript

复制

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

// 使用选项对象
element.checkVisibility({
  checkOpacity: true,  // 检查 opacity 是否为 0
  checkVisibilityCSS: true  // 检查 visibility CSS 属性
});

优点

  • 使用简单,代码简洁。
  • 支持更细粒度的检查。

缺点

  • 兼容性较差,仅支持部分现代浏览器。

总结
  1. 推荐使用 Intersection Observer API
    • 性能优秀,不会阻塞主线程。
    • 使用简单,代码简洁。
    • 适用于大多数现代浏览器。
  2. 使用 getBoundingClientRect() 方法
    • 适用于需要精确控制或兼容旧版浏览器的场景。
    • 需要结合节流或防抖技术优化性能。
  3. Element.checkVisibility() 方法
    • 使用简单,但兼容性较差。
    • 仅在支持该 API 的现代浏览器中使用。

在实际开发中,优先选择 Intersection Observer API,因为它既高效又易用。如果需要兼容旧版浏览器,可以使用 getBoundingClientRect() 方法,并结合节流或防抖技术优化性能。

你可能感兴趣的:(javascript,前端,面试)