慕课前端售1299元的面试题【第一阶段】面试真题 day05

上一篇http面试模块,内涵部分面试真题

- 我的博客需要缩宽页面观看,图片无法均放,很抱歉。

1. 事件代理(委托)是什么?

在这里插入图片描述

2. 如何阻止事件冒泡 和 默认行为?

在这里插入图片描述

3. 查找,添加,移动,删除 DOM节点

在这里插入图片描述

4. 如何减少DOM操作

在这里插入图片描述

5. 解释 jsonp原理,为何它不是真正的ajax ?

- 浏览的同源策略(服务端没有同源策略)和跨域

JSONP(JSON with Padding)是一种跨域访问的技术手段,用于在不同域名之间传输数据,在前端应用中非常常见。JSONP的原理如下:

  • 1. 前端页面向后端服务器发送一个带有 callback 参数的请求,例如:

    http://www.example.com/data?callback=handleResponse
    
  • 2. 后端服务器会根据请求中 callback 参数指定的函数名,构造一个以该函数名为函数体的 JavaScript 代码,并将需要传递的 JSON 数据包裹在其中,返回给前端页面,例如:

    handleResponse({
          
          "id": "123", "name": "John Doe", "age": 35});
    
  • 3. 前端页面接收返回的 JavaScript 代码,直接以 script 标签的形式添加到页面中。后端返回的 JavaScript 代码会调用页面上定义的 handleResponse 函数,并以 JSON 数据作为参数传入。

  • JSONP 之所以不是真正意义上的 AJAX 技术,是因为 AJAX 基于 XmlHttpRequest 对象进行通信,而 JSONP 是通过添加 script 标签实现的,是一种与 AJAX 类似但不同的实现方式。JSONP 也存在一些安全和性能上的问题,因此逐渐被 CORS(跨域资源共享)所取代。

  • 以下列举一些常用的可以绕过跨域的标签:

  • img:可以通过引入图片的方式跨域访问。

  • link:可以通过引入样式表的方式跨域访问。

  • script:可以通过引入JavaScript文件,或者JSONP的方式跨域访问。

需要注意的是,虽然这些标签可以绕过跨域限制,但也应该遵守安全限制,尽量避免跨域攻击和其他安全风险。同时,CORS 技术也提供了更加灵活和安全的跨域通信方式。
在这里插入图片描述

6. 闭包是什么,有什么新特性,有盒影响?

Closure(闭包)是指一个函数能够访问其词法作用域之外的变量的能力。也就是说,当一个内部函数在其外部函数之外被调用时,它仍然可以访问到该外部函数的变量。闭包可以使用变量和函数保护,避免与其他代码发生冲突。
-闭包具有以下特性:

  1. 可以访问包含该函数的外部函数的变量。
  2. 闭包函数在其外部函数被调用后仍然可以访问外部函数中的变量。
  3. 闭包函数可以访问外部函数中的实例变量,即使该变量在实例化之后进行了改变。

闭包可以带来一些影响,如内存泄露和性能问题,直接影响程序的执行效率和稳定性。需要注意在编码中合理运用闭包,避免出现问题。

下面是一个闭包的示例:

function outerFunction() {
    
    
  var outerVar = 'I am outside!';

  function innerFunction() {
    
    
    console.log(outerVar);
  }

  return innerFunction; // 返回闭包函数
}
// 执行 outerFunction() 函数,把返回的内部函数赋值给 myFunc
var myFunc = outerFunction();
myFunc(); // 执行 myFunc() 函数,输出 "I am outside!"

6. 函数声明和函数表达式的区别?

  • 主要在于定义函数的方式不同。
  • 函数声明(Function Declaration)直接定义了一个新的函数,而函数表达式(Function Expression)将函数定义为一个值,可以在其他函数中使用。具体来说,函数声明可以被提升到当前作用域的顶部,所以在函数声明定义之前调用该函数也不会出错;
  • 而函数表达式按照普通变量的方式定义,需要在声明之后才能使用。

下面是一个函数声明和函数表达式的示例:

// 函数声明
function sum(a, b) {
    
    
  return a + b;
}

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

7. new Object() 和 Object.create() 区别?

  • new Object() 用于创建一个新的Object对象,
  • Object.create() 则是利用现有的对象来创建一个新对象,使得新对象可以继承原型对象的属性和方法,具体差异如下:
  1. new Object() 使用 Object 构造函数来创建新对象,使用 {} 语法糖更为常见,返回的是一个空对象,不继承任何属性和方法。
  2. Object.create() 方法只能通过 Object.prototype 调用,可以接收一个参数作为新对象的原型,返回一个新对象,继承了原型中的属性和方法。
  3. { }等同于new Object(),有原型Object.prototype,Object.create(null) 没有原型,Object.create({...}) 可指定原型

以下是一个使用 Object.create() 方法创建新对象的示例:

// 创建一个发动机对象
var engine = {
    
    
  power: '400kw',
  brand: 'BMW'
};

// 创建一个车辆对象,继承发动机对象
var car = Object.create(engine);
car.color = 'red';
car.model = 'X5';

console.log(car.power); // "400kw"
console.log(car.color); // "red"

在上面的示例中,car 对象通过 Object.create 方法被创建,并继承了 engine 对象,因此可以直接访问 engine 对象的 powerbrand 属性。

8. 手写trim 保证浏览器兼容性

在这里插入图片描述

9. 获取多个数字中最大值

  • 在JavaScript中,arguments是一个与函数相关的特殊变量,它包含了函数被调用时传递的所有实参。然而,arguments并不是一个真正的数组,它只是一个类数组对象,因此它没有像数组那样的方法(如slice()、forEach()等)。

  • 为了能够在arguments上使用数组方法,可以使用Array.prototype.slice.call(arguments)将其转换为真正的数组。这行代码的作用是将arguments对象转换为一个数组,并将该数组的副本存储在nums变量中。

  • 具体来说,Array.prototype.slice()是一个用于从现有数组中返回选定元素的方法。当该方法被调用时,它会返回一个新的数组,该数组包含了被选元素的副本。在这个例子中,我们使用call()方法来将slice()方法应用到arguments对象上,并将其转换为一个数组
    在这里插入图片描述

10. 如何使用 js 实现继承

11.如何捕获 js 中的异常?

在这里插入图片描述

12. 什么是JSON?

  • JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,基于 JavaScript 语言的一个子集。JSON 格式描述了一种结构化的数据,用于网站前后端之间的数据传输与交互,也常被用于数据存储和配置文件中。
    在这里插入图片描述

13. 获取当前页面url参数

  • JavaScript 可以使用 location 对象来获取当前页面的 URL,并使用正则表达式。
  • 传统方式:location.search
  • 新API , URLSearchParams 对象来获取 URL 参数。
    在这里插入图片描述

在这里插入图片描述

14. 手写深拷贝

注意,Object.assign不是深拷贝!!!
在这里插入图片描述

在这里插入图片描述

  • 深拷贝指的
  • 是创建一个新对象,这个新对象与原始对象的所有属性都具有相同的值,但是这两个对象在内存中是独立的,修改其中一个对象的属性不会影响到另一个对象的属性可以使用递归或循环遍历对象的属性来实现深拷贝
    在这里插入图片描述

下面是一个更为优化的深拷贝函数:

  • 下面对代码的每一行进行解释:
  1. function deepClone(obj, map = new WeakMap()) { :声明一个函数 deepClone,参数为 obj 和可选参数 map,用于实现深拷贝。将 map 的初始值设置为一个空的弱映射表。
  2. if (typeof obj !== 'object' || obj === null) { return obj; }:判断 obj 是否为对象或数组,或者是否为 null,如果不是则直接返回 obj。
  3. if (map.has(obj)) { return map.get(obj); }:如果 map 中已经存在当前要拷贝的对象(即已经遍历过该对象),则直接返回对应的克隆对象,避免进入死循环。
  4. const cloned = Array.isArray(obj) ? [] : {};:声明一个变量 cloned,如果 obj 是数组则将 cloned 赋值为一个空数组,如果 obj 是对象则将 cloned 赋值为一个空对象。
  5. map.set(obj, cloned);:将当前要拷贝的对象和对应的克隆对象的映射关系记录在 map 中。
  6. Object.keys(obj).forEach(key => { cloned[key] = deepClone(obj[key], map); });:使用 Object.keys 方法获取 obj 所有可枚举属性的 key,然后遍历每个 key,对 cloned 对象的这个 key 赋值为递归调用 deepClone 函数后返回的值,并传入当前的 map 映射表。
  7. return cloned;:返回最终生成的克隆对象。
    在这里插入图片描述
  • 优化点如下:
  1. 添加了一个可选参数 map,用于记录每个需要拷贝的对象和对应的克隆对象的映射关系,用于处理循环引用。这里使用了 JavaScript 的弱引用 WeakMap,如果某个拷贝对象已经在该映射表中有对应的克隆对象,则直接返回该克隆对象,避免进入死循环。
  2. 使用 Object.keys 替换循环遍历对象的方式,更加简洁高效。

15. 介绍一下 RAF requestAnimationFrame

在这里插入图片描述

  • requestAnimationFrame(简称RAF)是一个浏览器提供的API,用于在执行动画时实现更高效、更平滑的帧率。它告诉浏览器在下一次重绘前执行指定的回调函数。这样,浏览器可以在最佳时间执行动画,从而提高动画性能。

  • requestAnimationFrame具有以下特点和优势:
    1.帧率与浏览器刷新率同步:requestAnimationFrame会自动调整回调函数的执行频率,以匹配浏览器的刷新率。这可以确保动画更加流畅,减少闪烁和卡顿现象
    2. 节省资源:当浏览器标签页处于非激活状态或最小化时,RAF 会暂停回调函数的执行。这可以节省CPU和GPU资源,降低设备功耗
    3.适用于各种动画类型:RAF 适用于各种类型的动画,如CSS属性变化、Canvas绘制、SVG动画等。
    4.简化动画代码:使用requestAnimationFrame可以简化动画代码,替代传统的setTimeoutsetInterval方法。

css
 #div1 {
      width: 100px;
      height: 50px;
      background-color: cadetblue;
 }
html 中
<div id="div1"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"/>

在这里插入图片描述

// 使用 RAF 实现动画效果
function animate() {
   requestAnimationFrame(animate);
   // 在这里更新动画效果
}
// 停止动画效果
function stopAnimate() {
   cancelAnimationFrame(animate);
}
  • 需要注意的是,RAF 并不是在所有浏览器中都实现了,因此可能需要使用polyfill来确保兼容性。不过,现代浏览器中大多已实现了requestAnimationFrame

16. 前端性能如何优化,一般从哪几个方面考虑?

  • 1. 资源压缩与合并:
  • 压缩 CSS、JavaScript 文件,减小文件大小,加快加载速度。例如使用 UglifyJS(JavaScript)CSSNano(CSS)等工具进行压缩。
  • 合并多个 CSS、JavaScript 文件,减少 HTTP 请求数。可以使用 Webpack、Gulp 等构建工具实现。
  • 图片优化:
  • 选择合适的图片格式,如使用 WebP 格式可以显著减小图片体积。
  • 图片进行压缩,可以使用 TinyPNG、ImageOptim 等工具
  • 使用 CSS sprites(雪碧图)将多个小图标合并到一张图片中,减少 HTTP 请求数。
  • 懒加载图片:只在图片进入可视区域时开始加载,例如使用 IntersectionObserver API 或第三方库如 lozad.js 实现。
  • 使用响应式图片,根据设备屏幕大小和分辨率加载合适尺寸的图片,可以使用<picture>和srcset属性实现。
  • 缓存策略:
  • 利用 HTTP 缓存,设置合适的 Cache-Control、ETag 等响应头,避免不必要的资源重新请求。
  • 使用 Service Workers 缓存资源,实现离线访问。
  • 代码优化:
  • 删除无用的 CSS、JavaScript 代码,减小文件体积。
  • 使用 CSS、JavaScript 的最新特性,提高性能。
  • 避免使用耗性能的 CSS 属性,如 box-shadow、border-radius 等
  • 优化 JavaScript 代码,避免长时间的阻塞操作,使用 Web Workers 处理复杂任务。
  • 优化 DOM 操作和渲染:
  • 减少 DOM 操作次数,尽量避免不必要的 DOM 重排(reflow)和重绘(repaint)。
  • 使用虚拟 DOM 技术,如 React、Vue 等框架,避免直接操作 DOM
  • 使用 requestAnimationFrame 实现动画效果,避免使用 setInterval setTimeout
  • 对于大量数据的列表展示,使用虚拟列表(virtual list)或窗口化(windowing)技术,如 react-window 等库,仅渲染可视区域内的元素
  • 网络优化:
  • 使用 CDN(内容分发网络)加速静态资源的访问。
    尽量减少重定向次数。
  • 使用 HTTP/2 协议,实现多路复用、头部压缩等优化。
  • 预加载(preload)预获取(prefetch)关键资源,提升性能。
  • 服务端渲染(SSR):
  • 对于首屏内容较多的应用,使用服务端渲染可以减少首屏加载时间,提高用户体验。

这些方面的优化需要根据具体项目进行权衡和取舍,以达到最佳的性能表现。

17. Map 和 Set

  • 是 JavaScript 中的两个非常有用的数据结构。它们分别用于存储键值对唯一值
  • Map示例
// 创建一个空的 Map
let myMap = new Map();

// 添加键值对
myMap.set('name', 'John Doe');
myMap.set('age', 30);

// 获取键值
console.log(myMap.get('name')); // 输出:John Doe
console.log(myMap.get('age')); // 输出:30

// 删除一个键值对
myMap.delete('name');

// 获取 Map 的大小
console.log(myMap.size); // 输出:1

// 检查 Map 是否包含某个键
console.log(myMap.has('name')); // 输出:false

// 遍历 Map
myMap.forEach((value, key) => {
    
    
  console.log(key + ' = ' + value);
});
  • Set示例:
// 创建一个空的 Set
let mySet = new Set();

// 添加唯一值
mySet.add(1);
mySet.add(2);
mySet.add(3);

// 检查 Set 中是否包含某个值
console.log(mySet.has(1)); // 输出:true

// 获取 Set 的大小
console.log(mySet.size); // 输出:3

// 删除某个值
mySet.delete(1);

// 遍历 Set
mySet.forEach((value) => {
    
    
  console.log(value);
});

两者的 区别 和特点

  • Map 的特点:
    1. 存储键值对。
    1. 键可以是任何类型(例如、数字、对象、函数等)。
    1. 键的插入顺序保留。
    1. 提供内置方法,如 set、get、has、delete 等。
  • Set 的特点
    1. 存储唯一值。
    1. 值可以是任何类型。
    1. 值的插入顺序保留。
    1. 提供内置方法,如 add、has、delete 等。
  • 从上面的示例中,我们可以看到 Map 和 Set 在使用和操作上有很大的不同。
    1. Map 适用于需要存储键值对并根据键查找值的场景
    1. Set适用于需要存储唯一值并检查某个值是否存在的场景。

18. Map 与 Object 的区别?

  • 1. 键类型:Map 可以接受任何类型的键(例如对象、函数等),而 Object 只接受字符串Symbol 类型的键
  • 2. 遍历顺序:Map 中的键值对按照插入顺序进行遍历,而 Object 的遍历顺序可能因引擎实现而有所不同。
  • 3. 性能:在需要频繁地添加和删除键值对的场景下,Map 的性能通常优于 Object
  • 4. 内置方法:Map 提供了一些内置方法,如 size、has、delete 等,而 Object 需要使用其他方法(如 Object.keys())获取类似的功能

Map 与 Object 的示例:

// 使用 Map
let map = new Map();
map.set('name', 'John Doe');
map.set(123, 'A Number');
map.set({
    
     key: 'aKey' }, 'An Object Key');

console.log(map.get(123)); // 输出:A Number

// 使用 Object
let obj = {
    
    };
obj['name'] = 'John Doe';
obj['123'] = 'A Number';
obj[{
    
     key: 'aKey' }] = 'An Object Key'; // 注意:这里对象作为键会被转换为字符串 '[object Object]'

console.log(obj['123']); // 输出:A Number

Set 使用场景

(集合)是ES6中新增的一种数据类型,用于存储唯一的值。Set中的值不能重复,类似于数学中的集合概念。使用Set可以简单、高效地进行去重操作。

以下是一个使用Set实现数组去重的例子:

const arr = [1, 2, 3, 2, 1];
const set = new Set(arr);
const uniqueArr = Array.from(set); // [1, 2, 3]

在这个例子中,我们首先创建了一个包含重复元素的数组arr,然后使用new Set()方法将其转换为一个Set对象。由于Set不允许重复的值存在,所以重复的元素被自动过滤掉了。最后,我们将Set对象转换回数组uniqueArr,得到了去重后的结果

18. Set 和 数组的区别

  • Set 和数组(Array)都是用于存储数据的数据结构,但它们有一些重要的区别。
  • 1. 数据类型和顺序:数组可以包含不同类型的元素,并且元素按照它们在数组中出现的顺序进行排序。而 Set 只能包含唯一的值,这些值无序排列

例如,下面是一个数组,其中包含三个不同类型的元素:

[1, "string", true]

而下面是一个 Set,其中包含两个唯一的数字:

Set {
    
    1, 2}
  • 2. 操作和方法:Set 提供了一些适用于唯一值的方法,如 add、has 和 delete。另一方面,数组提供了许多有用的方法,例如 push、pop、shift 和 unshift 等。

例如,以下代码演示了如何使用 Set 的基本操作:

const mySet = new Set();
mySet.add(1);
mySet.add(2);
mySet.add(3);

console.log(mySet.has(2)); // 输出 true

mySet.delete(2);

console.log(mySet.has(2)); // 输出 false

而以下代码演示了如何使用数组的基本操作:

const myArray = [1, 2, 3];

myArray.push(4);
myArray.pop();

console.log(myArray); // 输出 [1, 2, 3]
  • 3. 用例:由于 Set 只能包含唯一的值,它通常用于检测重复项或过滤重复项。另一方面,数组通常用于存储和操作大量元素。

以下代码演示了如何使用 Set 来删除重复项:

const myArray = [1, 2, 3, 2, 1];
const mySet = new Set(myArray);

console.log([...mySet]); // 输出 [1, 2, 3]

以下代码演示了如何在数组中搜索一个元素:

const myArray = [1, 2, 3];
console.log(myArray.indexOf(2)); // 输出 1

19. WeakMap 和 WeakSet 区别?

在这里插入图片描述

  • WeakMap 和 WeakSet 都是 ES6 新增的数据结构,它们与 Map 和 Set 类似,但具有一些特殊的行为。

  • WeakMap 是一个键值对集合,其中的键只能是对象。与 Map 不同的是,当被引用的对象被垃圾回收后,WeakMap 中对应的键值对也将被自动删除,这就意味着 WeakMap 中的键是弱引用,不会阻止垃圾回收器回收它们所引用的对象。

以下是使用 WeakMap 的一个简单示例:

let wm = new WeakMap();

let obj1 = {
    
    };
let obj2 = {
    
    };
let obj3 = {
    
    };

wm.set(obj1, "value associated with obj1");
wm.set(obj2, "value associated with obj2");

console.log(wm.get(obj1)); // "value associated with obj1"
console.log(wm.get(obj2)); // "value associated with obj2"

obj1 = null;
console.log(wm.get(obj1)); // undefined
  • 在上面的示例中,我们创建了一个 WeakMap,并使用 obj1 和 obj2 添加了一些键值对。

  • 然后,我们将 obj1 变量设置为 null,这将导致它引用的对象被垃圾回收。

  • -由于 obj1 不再引用任何对象,WeakMap 中对应的键值对也将被自动删除。

  • WeakSet 与 WeakMap 类似,但是它存储的是对象的集合,而不是键值对。WeakSet 的对象也是弱引用,它也会自动删除被垃圾回收的对象。

以下是使用 WeakSet 的一个示例:

let ws = new WeakSet();

let obj1 = {
    
    };
let obj2 = {
    
    };
let obj3 = {
    
    };

ws.add(obj1);
ws.add(obj2);

console.log(ws.has(obj1)); // true
console.log(ws.has(obj2)); // true

obj1 = null;
console.log(ws.has(obj1)); // false
  • 在上面的示例中,我们创建了一个 WeakSet,并添加了一些对象。
  • 然后,我们将 obj1 变量设置为 null,这将导致它引用的对象被垃圾回收。
  • 由于 obj1 不再引用任何对象,WeakSet 中对应的对象也将被自动删除。

20. 使用 reduce 求数组和

  • sum:累加器,它保存了上一次回调函数的返回值,或者在第一次调用时使用传递给 reduce 方法的初始值(本例中为 0)。
    在这里插入图片描述
    使用 reduce 记录重复的数值
  //  使用reduce 计数
  const arr = [10, 20, 30, 40, 50, 10, 20, 30, 20]
  const n = 30
  const count = arr.reduce((count, val) => {
    
    
    // count 参数累加器,count +1 只要匹配到 n数值,就累加一个
    return val === n ? count + 1 : count
  }, 0)
  console.log('count', count);

传统方式输出字符串

 // 传统输出字符串
  const arr = [
    {
    
     name: '章三', age: '20' },
    {
    
     name: '测试', age: '数据' },
    {
    
     name: '传统', age: '方式' }
  ]
  const str = arr.map(item => {
    
    
    return `${
      
      item.name} - ${
      
      item.age}`
  }).join('\n')
  console.log(str);  // 打印结果如下
   /* 章三 - 20
   测试 - 数据
   传统 - 方式 */
  • 使用数组的map方法将原始数组arr中的每个对象映射为一个字符串。
  • 在每个字符串中包含对象的name和age属性,并以“-”连接。
  • 然后使用join方法将所有字符串合并为一个字符串,各个字符串之间用换行符分隔。 最终得到的字符串赋值给变量str。

使用 reduce 输出字符串

// 使用 reduce 输出字符串
  const arr = [
    {
    
     name: '章三', age: '20' },
    {
    
     name: '测试', age: '数据' },
    {
    
     name: '传统', age: '方式' }
  ]

  const str = arr.reduce((s, item) => {
    
    
    return `${
      
      s}${
      
      item.name} - ${
      
      item.age}\n`
  }, '')
  console.log(str);// 打印结果如下
  /* 章三 - 20
 测试 - 数据
 传统 - 方式 */

const str = arr.reduce((s, item) => { return ${s}${item.name} - ${item.age}\n },‘’)

  • 这里使用了 reduce() 方法对数组进行遍历,并将每个对象的 name 和 age 属性拼接成字符串。
  • 在这个函数中,第一个参数是回调函数,它会接收两个参数:累加器 s 和当前正在遍历的元素 item。
  • 回调函数会返回一个字符串,这个字符串会被传递给下一个元素作为累加器 s 的值。
  • 最后,reduce() 方法返回累加器的最终值。
    注意,reduce() 方法的第二个参数是初始值,用于初始化累加器 s。在这里,初始值为空字符串 ‘’。

这里打印出了最终拼接好的字符串。该字符串包含了数组中所有对象的 name 和 age 属性。每个对象之间用换行符 \n 分隔开来。

猜你喜欢

转载自blog.csdn.net/weixin_46426412/article/details/130517902