前端面试-手搓代码篇

文章目录

  • [一、JavaScript 基础](#一、JavaScript 基础)
    • [1. 手写 Object.create](#1. 手写 Object.create)
    • [2. 手写 instanceof 方法](#2. 手写 instanceof 方法)
    • [3. 手写 new 操作符](#3. 手写 new 操作符)
    • [4. 手写 Promise](#4. 手写 Promise)
    • [5. 手写 Promise.then](#5. 手写 Promise.then)
    • [8. 手写防抖函数](#8. 手写防抖函数)
    • [9. 手写节流函数](#9. 手写节流函数)
    • [10. 手写类型判断函数](#10. 手写类型判断函数)
    • [11. 手写 call 函数](#11. 手写 call 函数)
    • [12. 手写 apply 函数](#12. 手写 apply 函数)
    • [13. 手写 bind 函数](#13. 手写 bind 函数)
    • [14. 函数柯里化的实现](#14. 函数柯里化的实现)
    • [15. 实现AJAX请求](#15. 实现AJAX请求)
    • [16. 使用Promise封装AJAX请求](#16. 使用Promise封装AJAX请求)
    • [17. 实现浅拷贝](#17. 实现浅拷贝)
    • [18. 实现深拷贝](#18. 实现深拷贝)
  • 二、数据处理
    • [1. 实现数组的扁平化](#1. 实现数组的扁平化)
    • [2. 实现数组去重](#2. 实现数组去重)
    • [3. 实现数组的flat方法](#3. 实现数组的flat方法)
    • [4. 实现数组的push方法](#4. 实现数组的push方法)
    • [5. 实现数组的filter方法](#5. 实现数组的filter方法)
    • [6. 实现数组的map方法](#6. 实现数组的map方法)
    • [7. 实现字符串的repeat方法](#7. 实现字符串的repeat方法)
    • [8. 实现非负大整数相加](#8. 实现非负大整数相加)
    • [9. 实现类数组转化为数组](#9. 实现类数组转化为数组)
  • 三、场景应用
    • [1. 用Promise实现图片的异步加载](#1. 用Promise实现图片的异步加载)
    • [2. 实现发布-订阅模式](#2. 实现发布-订阅模式)
    • [3. 查找文章中出现频率最高的单词](#3. 查找文章中出现频率最高的单词)
    • [4. 封装异步的fetch,使用async await方式来使用](#4. 封装异步的fetch,使用async await方式来使用)
    • [5. 实现prototype继承](#5. 实现prototype继承)
    • [6. 实现双向数据绑定](#6. 实现双向数据绑定)
    • [7. 实现简单路由](#7. 实现简单路由)
    • [8. 实现斐波那契数列](#8. 实现斐波那契数列)
    • [9. 实现 jsonp](#9. 实现 jsonp)

一、JavaScript 基础

1. 手写 Object.create

Object.create() 用于创建一个新对象,使用现有的对象来提供新创建的对象的 proto。本质上,是对传入的对象执行了一次浅复制

javascript 复制代码
// 原Objet.create
let person = { 
 name: "Nicholas", 
 friends: ["Shelby", "Court", "Van"] 
}; 
let anotherPerson = Object.create(person, { 
 name: { // 对数据属性name描述特性
 value: "Greg" 
 } 
}); 
console.log(anotherPerson.name); // "Greg"

// 手写
function create(obj) {
	function Fun(){}
	Fun.prototype = obj
	return new Fun()
}
console.log(create(person).name) // Nicholas

2. 手写 instanceof 方法

测试一个对象是否是由某个特定的构造函数创建的实例。

步骤:

1、获取特定构造函数的 prototype 对象

2、通过Object.getPrototypeOf()获取测试对象的原型,一直往原型链上找直到顶层Object的对象原型Null

javascript 复制代码
function myInstanceof(obj,fun) {
	let prototype = fun.prototype // 获取特定构造函数的 prototype 对象
	let proto = Object.getPrototypeOf(obj) // 获取测试对象的原型,[[Prototype]]通过Object.getPrototypeOf访问
	while(true) {
		if(proto === null) {return false}
		if (prototype === proto ) {return true}
		proto = Object.getPrototypeOf(proto)
	}
}

3. 手写 new 操作符

在调用 new 的过程中会发生以上四件事情:

1、创建一个新的空对象。

2、将新对象的[[Prototype]](内部原型)设置为构造函数的prototype对象。

3、将构造函数的this指向新创建的对象。

4、如果构造函数返回一个对象,则new表达式的结果就是这个对象;否则,结果就是this所指向的对象

javascript 复制代码
// 实现目标myNew
function Fun(name,age) {
	this.name = name
	this.age = age
}
let newData = myNew(Fun,'lida',16)
console.log(newData.name) // lida 

// 实现myNew
function myNew(fun,...args) {
	let newObj = Object.create(fun.prototype)
	let result = fun.apply(newObj,args)
	return result instanceof Object ? result : obj; 
}

4. 手写 Promise

javascript 复制代码
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function MyPromise(fn) {
  // 保存初始化状态
  let self = this;

  // 初始化状态
  this.state = PENDING;

  // 用于保存 resolve 或者 rejected 传入的值
  this.value = null;

  // 用于保存 resolve 的回调函数
  this.resolvedCallbacks = [];

  // 用于保存 reject 的回调函数
  this.rejectedCallbacks = [];

  // 状态转变为 fulfilled 方法
  function resolve(value) {
    // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
    if (value instanceof MyPromise) {
      return value.then(resolve, reject);
    }

    // 保证代码的执行顺序为本轮事件循环的末尾
    setTimeout(() => {
      // 只有状态为 pending 时才能转变,
      if (self.state === PENDING) {
        // 修改状态
        self.state = FULFILLED;

        // 设置传入的值
        self.value = value;

        // 执行回调函数
        self.resolvedCallbacks.forEach(callback => {
          callback(value);
        });
      }
    }, 0);
  }

  // 状态转变为 rejected 方法
  function reject(value) {
    // 保证代码的执行顺序为本轮事件循环的末尾
    setTimeout(() => {
      // 只有状态为 pending 时才能转变
      if (self.state === PENDING) {
        // 修改状态
        self.state = REJECTED;

        // 设置传入的值
        self.value = value;

        // 执行回调函数
        self.rejectedCallbacks.forEach(callback => {
          callback(value);
        });
      }
    }, 0);
  }

  // 将两个方法传入函数执行
  try {
    fn(resolve, reject);
  } catch (e) {
    // 遇到错误时,捕获错误,执行 reject 函数
    reject(e);
  }
}

MyPromise.prototype.then = function(onResolved, onRejected) {  
  const self = this;  

  return new MyPromise((resolve, reject) => {  
    // 处理 onResolved
    let resolved = () => {
      try {  
        if (typeof onResolved === 'function') {  
          const result = onResolved(self.value);  
          // 如果结果仍然是 Promise,则等待它解决后再 resolve  
          result instanceof MyPromise ? result.then(resolve, reject) : resolve(result);  
        } else {  
          resolve(self.value);  
        }  
      } catch (error) {  
        reject(error);  
      }  
    };  
  
    // 处理 onRejected  
    let rejected = () => {  
      try {  
        if (typeof onRejected === 'function') {  
          const result = onRejected(self.value);  
          result instanceof MyPromise ? result.then(resolve, reject) : resolve(result);  
        } else {  
          reject(self.value);  
        }  
      } catch (error) {  
        reject(error);  
      }  
    };  
  
    // 根据当前 Promise 的状态,决定调用哪个回调  
    switch (self.state) {  
      case FULFILLED:  
        resolved();  
        break;  
      case REJECTED:  
        rejected();  
        break;  
      case PENDING:  
        self.resolvedCallbacks.push(resolved);  
        self.rejectedCallbacks.push(rejected);  
        break;  
    }  
  });  
};  


// 示例使用  
let promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('Success!');
    }, 1000);
});

promise.then(
    (value) => {
        console.log(value)
        return 2
    },
)
    .then((erro) => {
        console.log(erro)
    })

5. 手写 Promise.then

javascript 复制代码
MyPromise.prototype.then = function(onResolved, onRejected) {  
  const self = this;  

  return new MyPromise((resolve, reject) => {  
    // 处理 onResolved
    let resolved = () => {
      try {  
        if (typeof onResolved === 'function') {  
          const result = onResolved(self.value);  
          // 如果结果仍然是 Promise,则等待它解决后再 resolve  
          result instanceof MyPromise ? result.then(resolve, reject) : resolve(result);  
        } else {  
          resolve(self.value);  
        }  
      } catch (error) {  
        reject(error);  
      }  
    };  
  
    // 处理 onRejected  
    let rejected = () => {  
      try {  
        if (typeof onRejected === 'function') {  
          const result = onRejected(self.value);  
          result instanceof MyPromise ? result.then(resolve, reject) : resolve(result);  
        } else {  
          reject(self.value);  
        }  
      } catch (error) {  
        reject(error);  
      }  
    };  
  
    // 根据当前 Promise 的状态,决定调用哪个回调  
    switch (self.state) {  
      case FULFILLED:  
        resolved();  
        break;  
      case REJECTED:  
        rejected();  
        break;  
      case PENDING:  
        self.resolvedCallbacks.push(resolved);  
        self.rejectedCallbacks.push(rejected);  
        break;  
    }  
  });  
};  

8. 手写防抖函数

javascript 复制代码
// 函数防抖的实现
function debounce(fn, wait) {
  let timer = null;

  return function() {
    let context = this,
        args = arguments;

    // 如果此时存在定时器的话,则取消之前的定时器重新记时
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }

    // 设置定时器,使事件间隔指定事件后执行
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, wait);
  };
}

// 使用示例  
const myEfficientFn = debounce(function() {  
  // 一些高开销的操作,例如 DOM 操作、网络请求等  
  console.log('Function called!');  
}, 250); // 设置防抖间隔为 250 毫秒  
  
// 假设我们有一个频繁触发的事件处理器,例如滚动事件  
window.addEventListener('scroll', myEfficientFn);

9. 手写节流函数

javascript 复制代码
function throttle(func, limit) {  
  let inThrottle;  
  return function() {  
    const context = this;  
    const args = arguments;  
    if (!inThrottle) {  
      func.apply(context, args);  
      inThrottle = true;  
      setTimeout(() => inThrottle = false, limit);  
    }  
    // 使用示例  
const handleScroll = throttle(function() {  
  console.log('Scrolling...');  
  // 更新页面内容  
}, 200);  
  
// 绑定到窗口的 scroll 事件  
window.addEventListener('scroll', handleScroll);

10. 手写类型判断函数

javascript 复制代码
function getType(value) {
  // 判断数据是 null 的情况
  if (value === null) {
    return value + "";
  }
  // 判断数据是引用类型的情况
  if (typeof value === "object") {
    let valueClass = Object.prototype.toString.call(value),
      type = valueClass.split(" ")[1].split("");
    type.pop();
    return type.join("").toLowerCase();
  } else {
    // 判断数据是基本数据类型的情况和函数的情况
    return typeof value;
  }
}

Object.prototype.toString.call(value): 通过间接的方式调用 toString 方法,这样可以确保我们得到的是对象的内部类字符串,而不是对象自己定义的 toString 方法返回的结果。

11. 手写 call 函数

1、判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。

2、判断传入上下文对象是否存在,如果不存在,则设置为 window 。

3、处理传入的参数,截取第一个参数后的所有参数。

4、将函数作为上下文对象的一个属性。

5、使用上下文对象来调用这个方法,并保存返回结果。

6、删除刚才新增的属性。

返回结果。

javascript 复制代码
Function.prototype.myCall = function(context, ...args) {  
  // 确保context不是null或undefined,否则设置为全局对象(window或global)  
  context = context ? Object(context) : window;  
    
  // 临时给context添加一个唯一的属性,并将函数本身赋值给它  
  const fnSymbol = Symbol('fn');  
  context[fnSymbol] = this;  
    
  // 使用context来调用这个函数,并传入参数args  
  const result = context[fnSymbol](...args); // 调用函数,this绑定到 context
    
  // 调用完成后,删除临时添加的属性  
  delete context[fnSymbol];  
    
  // 返回函数调用的结果  
  return result;  
};

function greet(name, age) {  
  console.log(`Hello, my name is ${name} and I'm ${age} years old.`);  
  return this.value;  
}  
const obj = { value: 'Test' };    
// 使用手写的 myCall 方法  
greet.myCall(obj, 'Alice', 30); // 输出: Hello, my name is Alice and I'm 30 years old.  
// 并且会返回 'Test',因为 this 在 greet 函数内部指向了 obj 对象

12. 手写 apply 函数

javascript 复制代码
Function.prototype.myApply = function(context, args) {  
  // 确保context不是null或undefined,否则设置为全局对象(window或global)  
  context = context ? Object(context) : window;  
    
  // 如果args不是数组或类似数组对象,则抛出错误  
  if (!Array.isArray(args)) {  
    throw new Error('Second argument to myApply must be an array or array-like object');  
  }  
    
  // 将args转换为真正的数组(如果它是类似数组的对象)  
  args = Array.prototype.slice.call(args);  
    
  // 临时给context添加一个唯一的属性,并将函数本身赋值给它  
  const fnSymbol = Symbol('fn');  
  context[fnSymbol] = this;  
    
  // 使用context来调用这个函数,并传入args中的参数  
  const result = context[fnSymbol](...args);  
    
  // 调用完成后,删除临时添加的属性  
  delete context[fnSymbol];  
    
  // 返回函数调用的结果  
  return result;  
};

13. 手写 bind 函数

javascript 复制代码
Function.prototype.myBind = function(context, ...initialArgs) {  
  // 保存对原函数的引用  
  const self = this;  
  
  // 返回一个新的函数  
  return function F(...args) {  
    // 如果F作为构造函数被new调用,则绑定context到新的实例对象上  
    if (this instanceof F) {  
      return new self(...initialArgs, ...args);  
    }  
      
    // 调用原函数,并传入之前保存的initialArgs和当前函数的args  
    return self.apply(context, [...initialArgs, ...args]);  
  };  
};
//调用
function greet(name, age) {  
  console.log(`Hello, my name is ${name} and I'm ${age} years old.`);  
  console.log(this.value);  
}  
  
const obj = { value: 'Test' };  
  
// 使用手写的 myBind 方法  
const boundGreet = greet.myBind(obj, 'Alice');  
boundGreet(30); // 输出: Hello, my name is Alice and I'm 30 years old.  
                 // 并且会输出: Test,因为 this 在 greet 函数内部指向了 obj 对象

14. 函数柯里化的实现

javascript 复制代码
function curry(fn) {  
  return function curried(...args) {  
      if (args.length >= fn.length) {  
          return fn.apply(this, args);  
      } else {  
          return function(...args2) {  
              return curried.apply(this, args.concat(args2));  
          }  
      }  
  };  
} 

// 使用示例  
function sum(a, b, c) {  
    return a + b + c;  
}  
  
const curriedSum = curry(sum);  
const sumTwo = curriedSum(1, 2); // 返回一个函数,等待第三个参数  
const result = sumTwo(3); // 调用这个函数,返回 6
console.log(result)

15. 实现AJAX请求

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

创建AJAX请求的步骤:

1、创建一个 XMLHttpRequest 对象。

2、在这个对象上使用 open 方法创建一个 HTTP 请求,open 方法所需要的参数是(请求的方法、请求的地址、是否异步和用户的认证信息)。

3、在发起请求前,可以为这个对象添加一些信息和监听函数。比如说可以通过 setRequestHeader 方法来为请求添加头信息。还可以为这个对象添加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变化时会触发onreadystatechange 事件,可以通过设置监听函数,来处理请求成功后的结果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接收完成,这个时候可以通过判断请求的状态,如果状态是 2xx 或者 304 的话则代表返回正常。这个时候就可以通过 response 中的数据来对页面进行更新了。

4、当对象的属性和监听函数设置完成后,最后调用 send 方法来向服务器发起请求,可以传入参数作为发送的数据体。

javascript 复制代码
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);

16. 使用Promise封装AJAX请求

javascript 复制代码
// promise 封装实现:
function getJSON(url) {
  // 创建一个 promise 对象
  let promise = new Promise(function(resolve, reject) {
    let xhr = new XMLHttpRequest();
    // 新建一个 http 请求
    xhr.open("GET", url, true);
    // 设置状态的监听函数
    xhr.onreadystatechange = function() {
      if (this.readyState !== 4) return;
      // 当请求成功或失败时,改变 promise 的状态
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    // 设置错误监听函数
    xhr.onerror = function() {
      reject(new Error(this.statusText));
    };
    // 设置响应的数据类型
    xhr.responseType = "json";
    // 设置请求头信息
    xhr.setRequestHeader("Accept", "application/json");
    // 发送 http 请求
    xhr.send(null);
  });
  return promise;
}

17. 实现浅拷贝

浅拷贝意味着只有对象的顶层属性和值被复制。如果对象的属性值是一个对象或数组(即引用类型),那么实际上复制的是这个对象或数组在内存中的地址引用,而不是对象或数组本身的内容。

(1)Object.assign()

javascript 复制代码
let target = {a: 1};
let object2 = {b: 2,obj: {c: 4}};
Object.assign(target,object2);  
object2.obj.c = 6
object2.b = 9
console.log(target);  // { a: 1, b: 2, obj: { c: 6 } }

注意:

如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。

如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。

因为null 和 undefined 不能转化为对象,所以第一个参数不能为null或 undefined,会报错。

(2)扩展运算符

javascript 复制代码
let obj1 = {a:1,b:{c:1}}
let obj2 = {...obj1};
obj1.a = 2;
console.log(obj1); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}
obj1.b.c = 2;
console.log(obj1); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}

(3)数组方法实现数组浅拷贝

1、Array.prototype.slice

slice() 方法返回一个新的数组对象,这一对象是一个由 start 和 end 决定的原数组的浅拷贝(包括 start,不包括 end)

javascript 复制代码
let arr = [{a: 1},{b: 2}];
let a = arr.slice(); // [{a: 1},{b: 2}];
arr[0].a = 5
console.log(arr,a) // [ { a: 5 }, { b: 2 } ] [ { a: 5 }, { b: 2 } ]

2、Array.prototype.concat

javascript 复制代码
let arr = [{a: 1},{b: 2}];
let a = arr.concat(); // [{a: 1},{b: 2}];
arr[0].a = 5
console.log(arr,a) // [ { a: 5 }, { b: 2 } ] [ { a: 5 }, { b: 2 } ]

(4)手写实现浅拷贝

javascript 复制代码
function shallowCopy(object) {
  // 只拷贝对象
  if (!object || typeof object !== "object") return;

  // 根据 object 的类型判断是新建一个数组还是对象
  let newObject = Array.isArray(object) ? [] : {};

  // 遍历 object,并且判断是 object 的属性才拷贝
  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      newObject[key] = object[key];
    }
  }

  return newObject;
}// 浅拷贝的实现;

18. 实现深拷贝

(1)JSON.stringify()

使用JSON.parse(JSON.stringify(obj)),但存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过JSON.stringify()进行处理之后,都会消失。

(2)手写实现深拷贝函数

javascript 复制代码
// 深拷贝的实现
function deepCopy(object) {
  if (!object || typeof object !== "object") return;

  let newObject = Array.isArray(object) ? [] : {};

  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      newObject[key] =
        typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
    }
  }

  return newObject;
}

二、数据处理

1. 实现数组的扁平化

(1)递归实现

javascript 复制代码
function fun (arr) {
  if (!Array.isArray(arr)) {
    return
  }
  let result = []
  for (let item of arr) {
    if (Array.isArray(item)) {
      result = result.concat(fun(item))
    } else {
      result.push(item)
    }
  }
  return result
}

let arr = [1, [2, [3, 4, 5]]];
console.log(fun(obj))

(2)reduce 函数迭代

javascript 复制代码
let arr = [1, [2, [3, 4]]];
function flatten(arr) {
    return arr.reduce(function(prev, next){
        return prev.concat(Array.isArray(next) ? flatten(next) : next)
    }, [])
}
console.log(flatten(arr));//  [1, 2, 3, 4,5]

2. 实现数组去重

javascript 复制代码
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];

Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8]

3. 实现数组的flat方法

javascript 复制代码
function _flat(arr, depth) {
  if(!Array.isArray(arr) || depth <= 0) {
    return arr;
  }
  return arr.reduce((prev, cur) => {
    if (Array.isArray(cur)) {
      return prev.concat(_flat(cur, depth - 1))
    } else {
      return prev.concat(cur);
    }
  }, []);
}

4. 实现数组的push方法

javascript 复制代码
let arr = [];
Array.prototype._push = function() {
    for( let i = 0 ; i < arguments.length ; i++){
        this[this.length] = arguments[i] ;
    }
    return this.length;
}

5. 实现数组的filter方法

javascript 复制代码
Array.prototype._filter = function(fn) {
    if (typeof fn !== "function") {
        throw Error('参数必须是一个函数');
    }
    const res = [];
    for (let i = 0, len = this.length; i < len; i++) {
        fn(this[i]) && res.push(this[i]);
    }
    return res;
}

6. 实现数组的map方法

javascript 复制代码
Array.prototype._map = function(fn) {
   if (typeof fn !== "function") {
        throw Error('参数必须是一个函数');
    }
    const res = [];
    for (let i = 0, len = this.length; i < len; i++) {
        res.push(fn(this[i]));
    }
    return res;
}

7. 实现字符串的repeat方法

javascript 复制代码
function repeat(s, n) {
    return (new Array(n + 1)).join(s);
}

8. 实现非负大整数相加

javascript 复制代码
function sumBigNumber(a, b) {
  let res = '';
  let temp = 0;
  
  a = a.split('');
  b = b.split('');
  
  while (a.length || b.length || temp) {
    temp += ~~a.pop() + ~~b.pop();
    res = (temp % 10) + res;
    temp  = temp > 9
  }
  return res.replace(/^0+/, '');
}

9. 实现类数组转化为数组

通过 call 调用数组的 slice 方法来实现转换

javascript 复制代码
Array.prototype.slice.call(arrayLike);

通过 call 调用数组的 splice 方法来实现转换

javascript 复制代码
Array.prototype.splice.call(arrayLike, 0);

通过 apply 调用数组的 concat 方法来实现转换

javascript 复制代码
Array.prototype.concat.apply([], arrayLike);

通过 Array.from 方法来实现转换

javascript 复制代码
Array.from(arrayLike);

三、场景应用

1. 用Promise实现图片的异步加载

javascript 复制代码
let imageAsync=(url)=>{
            return new Promise((resolve,reject)=>{
                let img = new Image();
                img.src = url;
                img.οnlοad=()=>{
                    console.log(`图片请求成功,此处进行通用操作`);
                    resolve(image);
                }
                img.οnerrοr=(err)=>{
                    console.log(`失败,此处进行失败的通用操作`);
                    reject(err);
                }
            })
        }
        
imageAsync("url").then(()=>{
    console.log("加载成功");
}).catch((error)=>{
    console.log("加载失败");
})

2. 实现发布-订阅模式

javascript 复制代码
class EventBus {  
    constructor() {  
        this.subscribers = {};  
    }  
  
    subscribe(event, callback) {  
        if (!this.subscribers[event]) {  
            this.subscribers[event] = [];  
        }  
        this.subscribers[event].push(callback);  
    }  
  
    publish(event, data) {  
        if (this.subscribers[event]) {  
            this.subscribers[event].forEach(callback => {  
                callback(data);  
            });  
        }  
    }  
  
    unsubscribe(event, callback) {  
        if (this.subscribers[event]) {  
            this.subscribers[event] = this.subscribers[event].filter(subCallback => subCallback !== callback);  
        }  
    }  
}  
  
// 使用示例:  
const bus = new EventBus();  
  
function handleMessage(data) {  
    console.log('Received message:', data);  
}  
  
bus.subscribe('message', handleMessage);  
bus.publish('message', 'Hello, world!'); // 输出: Received message: Hello, world!  
bus.unsubscribe('message', handleMessage);

3. 查找文章中出现频率最高的单词

javascript 复制代码
function findMostWord(article) {
  // 合法性判断
  if (!article) return;
  // 参数处理
  article = article.trim().toLowerCase();
  let wordList = article.match(/[a-z]+/g),
    visited = [],
    maxNum = 0,
    maxWord = "";
  article = " " + wordList.join("  ") + " ";
  // 遍历判断单词出现次数
  wordList.forEach(function(item) {
    if (visited.indexOf(item) < 0) {
      // 加入 visited 
      visited.push(item);
      let word = new RegExp(" " + item + " ", "g"),
        num = article.match(word).length;
      if (num > maxNum) {
        maxNum = num;
        maxWord = item;
      }
    }
  });
  return maxWord + "  " + maxNum;
}

4. 封装异步的fetch,使用async await方式来使用

javascript 复制代码
(async () => {
    class HttpRequestUtil {
        async get(url) {
            const res = await fetch(url);
            const data = await res.json();
            return data;
        }
        async post(url, data) {
            const res = await fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(data)
            });
            const result = await res.json();
            return result;
        }
        async put(url, data) {
            const res = await fetch(url, {
                method: 'PUT',
                headers: {
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify(data)
            });
            const result = await res.json();
            return result;
        }
        async delete(url, data) {
            const res = await fetch(url, {
                method: 'DELETE',
                headers: {
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify(data)
            });
            const result = await res.json();
            return result;
        }
    }
    const httpRequestUtil = new HttpRequestUtil();
    const res = await httpRequestUtil.get('http://golderbrother.cn/');
    console.log(res);
})();

5. 实现prototype继承

所谓的原型链继承就是让新实例的原型等于父类的实例:

javascript 复制代码
//父方法
function SupperFunction(flag1){
    this.flag1 = flag1;
}

//子方法
function SubFunction(flag2){
    this.flag2 = flag2;
}

//父实例
var superInstance = new SupperFunction(true);

//子继承父
SubFunction.prototype = superInstance;

//子实例
var subInstance = new SubFunction(false);
//子调用自己和父的属性
subInstance.flag1;   // true
subInstance.flag2;   // false

6. 实现双向数据绑定

javascript 复制代码
let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
// 数据劫持
Object.defineProperty(obj, 'text', {
  configurable: true,
  enumerable: true,
  get() {
    console.log('获取数据了')
  },
  set(newVal) {
    console.log('数据更新了')
    input.value = newVal
    span.innerHTML = newVal
  }
})
// 输入监听
input.addEventListener('keyup', function(e) {
  obj.text = e.target.value
})

7. 实现简单路由

javascript 复制代码
// hash路由
class Route{
  constructor(){
    // 路由存储对象
    this.routes = {}
    // 当前hash
    this.currentHash = ''
    // 绑定this,避免监听时this指向改变
    this.freshRoute = this.freshRoute.bind(this)
    // 监听
    window.addEventListener('load', this.freshRoute, false)
    window.addEventListener('hashchange', this.freshRoute, false)
  }
  // 存储
  storeRoute (path, cb) {
    this.routes[path] = cb || function () {}
  }
  // 更新
  freshRoute () {
    this.currentHash = location.hash.slice(1) || '/'
    this.routes[this.currentHash]()
  }
}


// 创建 Route 类的实例  
const router = new Route();  
  
// 为 '/' 路径注册一个回调函数  
router.storeRoute('/', function() {  
  console.log('Home page!');  
  // 这里可以执行当访问 '/' 路径时需要的操作  
});  
  
// 为 '/about' 路径注册一个回调函数  
router.storeRoute('about', function() {  
  console.log('About page!');  
  // 这里可以执行当访问 '/about' 路径时需要的操作  
});  
  
// 手动触发 freshRoute 方法,模拟 hash 变化或页面加载完成  
// router.freshRoute();  
  
// 或者,你可以通过改变 location.hash 来触发 hashchange 事件  
location.hash = '#about'; // 这将触发路由到 '/about' 的回调函数

8. 实现斐波那契数列

1、递归方式:

递归方法比较直观,但是在大规模的计算中,可能会因为重复计算导致性能问题。

javascript 复制代码
function fibonacci(n) {  
    if (n <= 0) {  
        return 0;  
    } else if (n === 1) {  
        return 1;  
    } else {  
        return fibonacci(n - 1) + fibonacci(n - 2);  
    }  
}  
  
// 使用方法  
console.log(fibonacci(10));  // 输出: 55

2、迭代方式:

javascript 复制代码
function fibonacci(n) {  
    if (n <= 0) {  
        return 0;  
    } else if (n === 1) {  
        return 1;  
    } else {  
        let a = 0;  
        let b = 1;  
        let temp;  
        for (let i = 2; i <= n; i++) {  
            temp = a + b;  
            a = b;  
            b = temp;  
        }  
        return b;  
    }  
}  
  
// 使用方法  
console.log(fibonacci(10));  // 输出: 55

9. 实现 jsonp

javascript 复制代码
// 动态的加载js文件
function addScript(src) {
  const script = document.createElement('script');
  script.src = src;
  script.type = "text/javascript";
  document.body.appendChild(script);
}
addScript("http://xxx.xxx.com/xxx.js?callback=handleRes");
// 设置一个全局的callback函数来接收回调结果
function handleRes(res) {
  console.log(res);
}
// 接口返回的数据格式
handleRes({a: 1, b: 2});
相关推荐
excel1 分钟前
webpack 核心编译器 十四 节
前端
excel8 分钟前
webpack 核心编译器 十三 节
前端
腾讯TNTWeb前端团队7 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰10 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪11 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪11 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy11 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom12 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom12 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom12 小时前
React与Next.js:基础知识及应用场景
前端·面试·github