前端面试题大合集2----基础篇

目录

1、事件模型

2、什么是事件委托/事件代理

3、说一下Commonjs、AMD和CMD

4、Ajax原理

5、说一下XHR和Fetch的区别

6、实现一个once函数,传入函数只执行一次

7、js监听对象属性的改变

8、如何解决跨域问题

9、介绍js有哪些内置对象

10、介绍js有哪些方法定义对象


1、事件模型

W3C 中定义事件的发生经历三个阶段:捕获阶段( capturing )、目标阶段

( targetin )、冒泡阶段( bubbling )

冒泡型事件:当你使用事件冒泡时,子级元素先触发,父级元素后触发

捕获型事件:当你使用事件捕获时,父级元素先触发,子级元素后触发

DOM 事件流:同时支持两种事件模型:捕获型事件和冒泡型事件

阻止冒泡:在 W3c 中,使用 stopPropagation() 方法;在IE下设置 cancelBubble =true

阻止捕获:阻止事件的默认行为,例如 click - a 后的跳转。在 W3c 中,使用preventDefault() 方法,在 IE 下设置 window.event.returnValue = false


2、什么是事件委托/事件代理

事件代理( Event Delegation ),又称之为事件委托。是 JavaScript 中常用的绑定事件的常用技巧。顾名思义,"事件代理"即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。使用事件代理的好处是可以提高性能

可以大量节省内存占用,减少事件注册,比如在 table 上代理所有 td 的 click 事件就非常棒

可以实现当新增子对象时无需再次对其绑定


3、说一下Commonjs、AMD和CMD

Javascript的模块化编程经历了: 控制script标签顺序 ⇒ CommonJs ⇒ AMD ⇒ CMD ⇒ ES6模块 这样的过程。

一个模块是能实现特定功能的文件,有了模块就可以方便的使用别人的代码,想要什么功能就能加载什么模块。

  • Commonjs:开始于服务器端的模块化,同步定义的模块化,每个模块都是一个单独的作用域,模块输出,modules.exports,模块加载require()引入模块。

根据CommonJS规范,一个单独的文件就是一个模块;

Nodejs,webpack等是Commonjs的实践者;

Commonjs主要用于服务器模块化开发;

Commonjs是同步加载模块;

  • AMD:

全称Asynchronous Module Definition,中文名异步模块定义的意思;

AMD是浏览器模块化开发的规范;

AMD是Requirejs在推广的过程中对模块定义的规范化产出;

AMD推崇依赖前置,在定义模块的时候要先声明其依赖的模块,如下:

复制代码
// 定义模块 myModule.js
// 在定义时需要预先声明其依赖
define(['dependency'], function(){
    var name = 'Byron';
    function printName(){
        console.log(name);
    }

    return {
        printName: printName
    };
});

// 加载模块
require(['myModule'], function (my){
  my.printName();
});

AMD可以直接知道某个模块依赖的模块有哪些;

AMD异步加载模块,加载完成立即执行,也就是提前执行;

AMD用户体验好,因为模块提前执行了;

requireJS实现了AMD规范,主要用于解决下述两个问题。

(1)多个文件有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器;

(2)加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应的时间越长。

语法:requireJS定义了一个函数define,它是全局变量,用来定义模块。

  • CMD

全称是Common Module Definition,即通用模块定义;

CMD是浏览器模块化开发的规范;

CMD是SeaJS在推广过程中对模块定义的规范化产出;

CMD推崇依赖就近,如:

复制代码
// 定义模块  myModule.js
// 不需要预先申明依赖的模块
define(function(require, exports, module) {
  // 需要某个模块再require
  var $ = require('jquery.js')
  $('div').addClass('active');
});

// 加载模块
seajs.use(['myModule.js'], function(my){

});

CMD需要等到所有的模块变为字符串,解析一遍之后才知道他们之间的依赖关系;

CMD异步加载模块,但不会立即执行,而是遇到require语句时再执行,也就是延迟执行;

CMD性能好,因为只有用户需要的时候才执行。

  • UMD

全称Universal Module Definition,是一种通用的模块化规范,旨在兼容不同的环境。UMD可以同时支持AMD、Commonjs和全局变量的方式来导入和导出模块。

  • ESM

全称ECMAScript Modules,是ECMAScript提供的官方模块化规范,从ECMAScript6开始引入。ESM使用import和exprot关键字来导入和导出模块。ESM支持静态分析,可以在编译时进行模块依赖的静态解析,提供更好的性能和可靠性。

AMD和CMD主要用于浏览器环境,强调异步加载。Commonjs主要用于服务器端开发,采用同步加载。UMD是通用的模块化规范。ESM是官方标准的模块化规范,具有静态分析和更好的性能。


4、Ajax原理

Ajax 的原理简单来说是在用户和服务器之间加了---个中间层( AJAX 引擎),通过XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后用 javascript来操作 DOM 而更新页面。使用户操作与服务器响应异步化。这其中最关键的一步就是从服务器获得请求数据。

Ajax 的过程只涉及 JavaScript 、 XMLHttpRequest 和 DOM 。 XMLHttpRequest 是ajax的核心机制。

  • 将原生的ajax封装成promise:

    复制代码
      let myNewAjax = function(url: string, method: string, data?: any){
    
        return new Promise(function(resolve,reject){
    
          var xhr = new XMLHttpRequest();
    
          xhr.open(method || 'get', url);
    
          xhr.send(data);
    
          xhr.onreadystatechange = function(){
    
            if(xhr.status === 200 && xhr.readyState === 4){
    
              const json=JSON.parse(xhr.responseText);
    
              resolve(json)
    
            }else if(xhr.readyState == 4 && xhr.status != 200){
    
              reject('error');
    
            }
    
          }
    
        })
    
      }
    
      // 此处放一个可以访问的链接就可以了
      myNewAjax('https://www.baidu.com/', 'get').then((res: any) => {
        console.log('Success!', res);
      }).catch(err => {
        console.log('Failed!', err);
      })

5、说一下XHR和Fetch的区别

由于现在网站的开发模式普遍采用前后端分离的模式,数据交互成了不可或缺的关键环节。而XHR和Fetch是两种最常见的方法,用于从web服务器获取数据,XHR是一种传统的数据请求方式,而Fetch则代表了现在Web开发的新兴标准。

  • XMLHttpRequest

通常简称XHR,特点:

  1. 异步请求:XHR 允许进行异步请求,它可以在后台执行,而不会阻止页面的其他操作。
  2. 支持跨域请求:通过服务器端设置允许跨域请求,从不同域的服务器获取数据。
  3. 事件驱动:提供了 onload、onerror、onprogress 等一系列事件来监听请求的状态变化。
  4. 灵活性:提供了对请求头、响应头以及请求方法的完全控制,使其非常灵活。

XHR的工作原理主要为:

  1. 创建 XHR 对象实例:通过new XMLHttpRequest()创建一个 XHR 对象。
  2. 配置请求:使用open()方法设置请求方法(GET、POST 等)、URL,以及是否要异步执行请求。
  3. 设置回调函数:设置事件处理程序来处理请求完成、成功、失败等不同的状态。
  4. 发起请求:使用send()方法发送请求。
  5. 处理响应:在事件处理程序中处理响应数据,通常使用responseText或responseXML来访问响应内容。

具体看代码比较直观,可以一目了然的看出来:

复制代码
// 创建一个新的XHR对象
const xhr = new XMLHttpRequest();

// 配置请求
xhr.open("GET", "https://api.baidu.com/test", true);

// 设置响应处理函数
xhr.onload = function() {
  if (xhr.status === 200) {
    // 请求成功
    const responseData = xhr.responseText;
    console.log("成功获取数据:", responseData);
  } else {
    // 请求失败
    console.error("请求失败,状态码:" + xhr.status);
  }
};

// 发起请求
xhr.send();
  • Fetch

可以理解为XHR的升级版,提供了更强大,更灵活的方式来处理HTTP请求。

特点:

  1. Promise 风格:Fetch API 使用 Promise 对象来处理异步请求,使代码更具可读性和可维护性。
  2. 更简单的语法:相较于 XHR,Fetch API 的语法更加简单明了,通常只需要几行代码来完成请求。
  3. 默认不接受跨域请求:为了安全性,Fetch API 默认不接受跨域请求,但可以通过 CORS(跨域资源共享)来进行配置。
  4. 更现代的架构:Fetch API 是建立在 Promise 和 Stream 之上的,支持更灵活的数据处理和流式传输。

工作原理:

  1. 使用fetch()函数创建请求:传入要请求的 URL,以及可选的配置参数,例如请求方法、请求头等。
  2. 处理响应:fetch()返回一个 Promise,您可以使用.then()链式调用来处理响应数据,例如使用.json()方法解析 JSON 数据或.text()方法获取文本数据。
  3. 错误处理:您可以使用.catch()方法来捕获任何请求或响应的错误。
  4. 使用async/await:如果需要,您还可以使用async/await来更清晰地处理异步操作。

具体来看代码示例:

复制代码
    fetch("https://api.baidu.com/test")
      .then(response => {
        if (!response.ok) {
          throw new Error("请求失败,状态码:" + response.status);
        }
        return response.json();
      })
      .then(data => {
        // 请求成功,处理响应数据
        console.log("成功获取数据:", data);
      })
      .catch(error => {
        // 请求失败,处理错误
        console.error(error);
      });
  • XHR和Fetch的区别

如下是区别:

  • 语法: Fetch 使用 Promise,更直观和易于理解。
  • 跨域请求: Fetch 在跨域请求方面更灵活,支持 CORS。
  • 流式传输: Fetch 支持可读流,适用于大文件下载。
  • 维护性: Fetch 更容易维护和扩展。
  • 常用库和插件

基于XHR封装的库:

  • jquery:一个 JavaScript 库,提供了用于处理 DOM 操作、事件处理和 XHR 请求的便捷方法。
  • axios:一个流行的 HTTP 请求库,基于 XHR 开发,支持浏览器和 Node.js。

基于Fetch封装的库:

  • redaxios:它具有与 axios 类似的 API,但更轻量级且适用于现代 Web 开发。
  • umi-request:由 Umi 框架维护的网络请求库,提供了强大的拦截器、中间件和数据转换功能。

6、实现一个once函数,传入函数只执行一次
复制代码
    function once(func: Function){
      // 用来记录函数是否已经被调用过
      let tag = true;
      return function(){
        if(tag){
          func.apply(null,arguments);
          tag = false;
        }
        return undefined
      }
    }
    function myFn() {
      console.log(2);
    }
    const fn = once(myFn);

    fn() // 打印出来2,后续再次执行fn就不会再打印任何东西了
    fn()
    fn()

7、js监听对象属性的改变
  • 在ES5中,可以通过Object.defineProperty来实现已有属性的监听

    复制代码
      function observe(obj: any) {
        for (let key in obj) {
          let val = obj[key];
          Object.defineProperty(obj, key, {
            enumerable: true,
            configurable: true,
            get: function () {
              console.log(`有地方获取val了${val}`);
              return val;
            },
            set: function (newVal) {
              if (newVal !== val) {
                console.log(`属性 ${key} 从 ${val} 变成了 ${newVal}`);
                val = newVal;
              }
            }
          });
        }
      }
    
      let obj = { count: 0 };
      observe(obj);
      // 此处走set方法
      obj.count = 1;
      // 此处走get方法
      const str = `正式具体的数量:${obj.count}`
  • 在ES6中使用Proxy

    复制代码
      function observe(obj) {
        return new Proxy(obj, {
          get(target, propKey, receiver) {
            console.log(`此处获取了属性${propKey}`);
            return Reflect.get(target, propKey, receiver);
          },
          set(target, propKey, value, receiver) {
            let oldValue = target[propKey];
            if (value !== oldValue) {
              console.log(`属性 ${propKey} 从  ${oldValue} 变成了 ${value}`);
            }
            return Reflect.set(target, propKey, value, receiver);
          }
        });
      }
    
      let obj = { name: 'zs', age: 20, gender: 'male' };
      obj = observe(obj);
      // 此处走的是 set 方法
      obj.age = 25;
      // 此处走的是 get 方法
      const str = `我的名字是${obj.name}`;

8、如何解决跨域问题
  • 什么是跨域

指的是浏览器不能执行其他网站的脚本,简单来说就是浏览器同源策略的限制,浏览器针对于ajax的限制;

  • 首先了解浏览器的同源策略

同源策略:同协议,同端口,同域名;有一个不同,就需要跨域了;如果缺少了同源策略,浏览器很容易收到XSS、CSRF攻击;所以同源策略的产生是为了保护用户信息安全,防止一些网站盗取用户信息。

  • 跨域解决方案

1、通过jsonp跨域

2、CORS(Cross-Origin Resource Sharing)跨域资源共享

3、document.domain + iframe跨域

4、location.hash + iframe跨域

5、window.name + iframe跨域

6、postMessage跨域

7、nginx反向代理跨域

8、Nodejs中间件代理跨域

9、WebSocket协议跨域

(1)jsonp跨域

原理:

  1. 动态创建一个script标签(此标签无跨域限制);
  2. 通过script标签里面的src属性进行跨域访问,属性里面写:跨域的地址 + ?callback属性作为函数,传递给后端;
  3. 服务端通过接收客户端传过来的callback属性值对应的函数
  4. 要发送过去的数据,通过函数的参数传递过去(JSON格式);
  5. 将函数名对应调用的执行代码返回给客户端(服务端响应数据一定是函数代码的调用,当浏览器对script响应内容加载完成后,会自动调用函数)
  6. 并且在服务端执行函数是字符串,字符串包裹着函数调用的代码,如果没有包裹,则就在服务端立即执行。

缺点:

只能发送get请求,易受到XSS攻击

客户端代码:

复制代码
// 1.封装一个jsonp函数;
jsonp({
    // method: 'GET',// 所有的jsonp请求都是get请求,所以这个属性可以不写了
    // data: , // 写了以后太繁琐,取消
    url: 'http://www.localhost:3006/api/jsonp',
    success: function (res) {
        console.log(res)
    }
})
 
// 封装
function jsonp(obj) {
    // 1.创建一个script标签;   2.改变src    3.给函数起名字,定义为全局函数;
    var script = document.createElement("script"); // 不要用innerHTML, 他不会自动发送请求
    // 3.给函数起名字,定义为全局函数;
    var fnName = "haha_123123";
    // window.aaa就是把aaa设置为全局变量!
    window[fnName] = obj.success;
    // 2.改变src,添加到head中
    script.src = obj.url + "?callback=" + fnName;
    // 把script标签添加到head标签中,就会发送src的请求了
    document.head.appendChild(script);
    // 代码执行完毕,把script标签删除
    script.onload = function () {
        document.head.removeChild(script);
        window[fnName] = undefined;
    }
}

服务端代码:

复制代码
app.get("/api/jsonp", (req, res) => {
    // 获取函数名,设置对象,发送给客户端
    const fnName = req.query.callback;
    // 定义发送给客户端的对象转换为json字符串
    var objStr = JSON.stringify({
        name: '张三',
        age: 18
    });
    // 字符串类型的执行函数
    res.send(`${fnName}(${objStr})`);
});

(2)CORS跨域

原理:服务端设置

同源策略默认组织跨域获取资源,但是CORS给了web服务器权限,即服务器可以选择,允许访问他们的资源。

缺点:

忽略cookie,浏览器版本有一定要求。

此方式属于跨域ajax请求的根本解决方法。相比jsonp只能发送get请求,cors允许任何类型的请求。

(3)document.domain + iframe跨域

此方案仅限主域相同,子域不同的跨域场景。

通过iframe是浏览器非同源标签,加载内容中转,传到当前页面的属性中。

实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

缺点:同一一级域名;相同协议;相同端口;

(4)location.hash + iframe跨域

实现原理:a域与b域相互通信,通过中间页c来实现。三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

具体实现::A域:a.html -> B域:b.html -> A域:c.html,a与b不同域只能通过hash值单向通信,b与c也不同域也只能单向通信,但c与a同域,所以c可通过parent.parent访问a页面所有对象。

(5)window.name + iframe跨域

window.name属性的独到之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的name值(2MB)。

缺点:页面的属性值有大小限制

(6)postMessage跨域

可以用于解决以下方面的问题:

  • a.) 页面和其打开的新窗口的数据传递
  • b.) 多窗口之间消息传递
  • c.) 页面与嵌套的iframe消息传递
  • d.) 上面三个场景的跨域数据传递

用法:postMessage(data,origin)方法接受两个参数

data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。

origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

缺点:浏览器版本要求,部分浏览器要配置放开跨域限制

(7)nginx反向代理跨域

前端向发送请求,经过代理,请求需要的服务器资源

缺点:需要额外的代理服务器

  • a、nginx配置解决iconfont跨域

浏览器跨域访问js、css、image等常规静态资源被同源策略许可,但iconfont字体文件(eot|oft|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置:

复制代码
location / {
    add_header Access-Control-Allow-Origin *;
}
  • b、nginx反向代理接口跨域

跨域原理:

同源策略是浏览器的安全策略,不是Http协议的一部分。服务器端调用http接口只是使用http协议,不会执行js脚本,不需要同源策略,也就不存在跨域问题。

实现思路:

通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

(8)nodejs中间件代理跨域

node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。

(9)WebSocket协议跨域

它实现了浏览器与服务器的全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的,同时允许跨域通讯,是server push技术的一种很好的实现。

WebSocket是一个持久化的协议。

具体可以参考如下链接:js跨域解决方案


9、介绍js有哪些内置对象

数据封装类对象:Object、Array、Boolean、Number、String

其他对象:Function、Arguments、Math、Date、RegExp、Error


10、介绍js有哪些方法定义对象

对象字面量:var obj = {};

构造函数:var obj = new Object()

Object.create():var obj = Object.create(Object.prototype)

相关推荐
腾讯TNTWeb前端团队6 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰9 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪10 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy10 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom11 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom11 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom11 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom11 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom11 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试