前端面试-手搓代码篇

文章目录

  • [一、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});
相关推荐
神夜大侠12 分钟前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱14 分钟前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号1 小时前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy72931 小时前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲1 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6
究极无敌暴龙战神X1 小时前
前端学习之ES6+
开发语言·javascript·ecmascript
王解1 小时前
【模块化大作战】Webpack如何搞定CommonJS与ES6混战(3)
前端·webpack·es6
欲游山河十万里1 小时前
(02)ES6教程——Map、Set、Reflect、Proxy、字符串、数值、对象、数组、函数
前端·ecmascript·es6
明辉光焱1 小时前
【ES6】ES6中,如何实现桥接模式?
前端·javascript·es6·桥接模式
PyAIGCMaster1 小时前
python环境中,敏感数据的存储与读取问题解决方案
服务器·前端·python