前端面试必会的手撕代码题,四天带你学完(第三天)

98 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第16天,点击查看活动详情

  • 手写 call 函数
  • 手写 apply 函数
  • 手写 bind 函数
  • 函数柯里化的实现
  • 实现AJAX请求

1. 手写 call 函数

call 函数的实现步骤:

  1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
  2. 判断传入上下文对象是否存在,如果不存在,则设置为 window 。
  3. 处理传入的参数,截取第一个参数后的所有参数。
  4. 将函数作为上下文对象的一个属性。
  5. 使用上下文对象来调用这个方法,并保存返回结果。
  6. 删除刚才新增的属性。
  7. 返回结果。
 // call函数实现
 Function.prototype.myCall = function(context) {
   // 判断调用对象
   if (typeof this !== "function") {
     console.error("type error");
   }
   // 获取参数
   let args = [...arguments].slice(1),
       result = null;
   // 判断 context 是否传入,如果未传入则设置为 window
   context = context || window;
   // 将调用函数设为对象的方法
   context.fn = this;
   // 调用函数
   result = context.fn(...args);
   // 将属性删除
   delete context.fn;
   return result;
 };

2. 手写 apply 函数

apply 函数的实现步骤:

  1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
  2. 判断传入上下文对象是否存在,如果不存在,则设置为 window 。
  3. 将函数作为上下文对象的一个属性。
  4. 判断参数值是否传入
  5. 使用上下文对象来调用这个方法,并保存返回结果。
  6. 删除刚才新增的属性
  7. 返回结果
 // apply 函数实现
 Function.prototype.myApply = function(context) {
   // 判断调用对象是否为函数
   if (typeof this !== "function") {
     throw new TypeError("Error");
   }
   let result = null;
   // 判断 context 是否存在,如果未传入则为 window
   context = context || window;
   // 将函数设为对象的方法
   context.fn = this;
   // 调用方法
   if (arguments[1]) {
     result = context.fn(...arguments[1]);
   } else {
     result = context.fn();
   }
   // 将属性删除
   delete context.fn;
   return result;
 };

3. 手写 bind 函数

bind 函数的实现步骤:

  1. 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
  2. 保存当前函数的引用,获取其余传入参数值。
  3. 创建一个函数返回
  4. 函数内部使用 apply 来绑定函数调用,需要判断函数作为构造函数的情况,这个时候需要传入当前函数的 this 给 apply 调用,其余情况都传入指定的上下文对象。
 // bind 函数实现
 Function.prototype.myBind = function(context) {
   // 判断调用对象是否为函数
   if (typeof this !== "function") {
     throw new TypeError("Error");
   }
   // 获取参数
   var args = [...arguments].slice(1),
       fn = this;
   return function Fn() {
     // 根据调用方式,传入不同绑定值
     return fn.apply(
       this instanceof Fn ? this : context,
       args.concat(...arguments)
     );
   };
 };

4. 函数柯里化的实现

函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。

 function curry(fn, args) {
   // 获取函数需要的参数长度
   let length = fn.length;
 ​
   args = args || [];
 ​
   return function() {
     let subArgs = args.slice(0);
 ​
     // 拼接得到现有的所有参数
     for (let i = 0; i < arguments.length; i++) {
       subArgs.push(arguments[i]);
     }
 ​
     // 判断参数的长度是否已经满足函数所需参数的长度
     if (subArgs.length >= length) {
       // 如果满足,执行函数
       return fn.apply(this, subArgs);
     } else {
       // 如果不满足,递归返回科里化的函数,等待参数的传入
       return curry.call(this, fn, subArgs);
     }
   };
 }
 ​
 // es6 实现
 function curry(fn, ...args) {
   return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
 }

5. 实现AJAX请求

AJAX是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的 异步通信,从服务器获取 XML 文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。

创建AJAX请求的步骤:

  • 创建一个 XMLHttpRequest 对象。
  • 在这个对象上使用 open 方法创建一个 HTTP 请求,open 方法所需要的参数是请求的方法、请求的地址、是否异步和用户的认证信息。
  • 在发起请求前,可以为这个对象添加一些信息和监听函数。比如说可以通过 setRequestHeader 方法来为请求添加头信息。还可以为这个对象添加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变化时会触发onreadystatechange 事件,可以通过设置监听函数,来处理请求成功后的结果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接收完成,这个时候可以通过判断请求的状态,如果状态是 2xx 或者 304 的话则代表返回正常。这个时候就可以通过 response 中的数据来对页面进行更新了。
  • 当对象的属性和监听函数设置完成后,最后调用 sent 方法来向服务器发起请求,可以传入参数作为发送的数据体。
 const SERVER_URL = "/server";
 let xhr = new XMLHttpRequest();
 // 创建 Http 请求
 xhr.open("GET", SERVER_URL, true);
 // 设置状态监听函数
 xhr.onreadystatechange = function() {
   if (this.readyState !== 4) return;
   // 当请求成功时
   if (this.status === 200) {
     handle(this.response);
   } else {
     console.error(this.statusText);
   }
 };
 // 设置请求失败时的监听函数
 xhr.onerror = function() {
   console.error(this.statusText);
 };
 // 设置请求头信息
 xhr.responseType = "json";
 xhr.setRequestHeader("Accept", "application/json");
 // 发送 Http 请求
 xhr.send(null);