手写常见javascript原生库实现

1、实现instanceOf操作符

/** 手写实现instanceof */
function myInstanceOf(origin, target) {
  // 1. 获取对象的prototype
  let proto = Object.getPrototypeOf(origin);
  // 2. 获取目标构造函数的原型对象
  let prototype = target.prototype;
  // 3. 循环对比对象的原型链是否和构造函数的原型对象相等,遇到相等则返回true,否则遍历结束返回false
  while(proto) {
    if(proto === prototype) {
      return true;
    }
    proto = Object.getPrototypeOf(proto);
  }
  return false;
}

let a = new Array();
console.log(a instanceof Object); // true
console.log(myInstanceOf(a, Object)); // true
console.log(myInstanceOf(a, Array)); // true
console.log(myInstanceOf(a, String)); // false

2、实现new操作符

/** 手写实现new操作符 */

function myNew(Fn) {
  // 1. 创建一个空的对象
  let obj = function() {};
  // 2. 将构造函数的原型链赋值给对象
  obj = Object.create(Fn.prototype);
  // 3. 将构造函数的this指针指向对象
  let args = [...arguments].slice(1);
  let result = Fn.apply(obj, args);
  // 4. 判断result是否是引用类型,是的话返回result,否则返回obj;
  if(typeof result === 'function' || result instanceof Object) {
    return result;
  }
  return obj;
}

function Foo (name, age){
  this.name = name;
  this.age = age;
}

Foo.prototype.sayAge = function () {
  return this.age;
}

const foo = myNew(Foo, 'allesa', 32);
const bar = myNew(Foo, 'jason', 33);
console.log(foo); // Foo { name: 'allesa', age: 32 }
console.log(bar); // Foo { name: 'jason', age: 33 }
console.log(foo.name); // allesa
console.log(foo.sayAge()); // 32
console.log(bar.name); // jason
console.log(bar.sayAge()); // 33

3、实现浅拷贝函数

/** 手写浅拷贝 shallowClone */
/**
 * 已有浅拷贝实现:
 * 1. Array.slice()、Array.concat()
 * 2. Object.assign()
 * 3. 扩展运算符:obj2 = { ...obj1 };
 */

function shallowClone (obj) {
  if(!obj || typeof obj !== 'object') return;
  let newObj = Array.isArray(obj) ? [] : {};
  // 遍历对象属性,进行浅拷贝
  for(let key in obj) {
    if(obj.hasOwnProperty(key)) {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}

let obj1 = {
  a: 1,
  b: [1, 3, 5],
  c: { d: 3 }
};
let obj2 = shallowClone(obj1);

console.log(obj1.b === obj2.b); // true

4、实现深拷贝函数

/** 手写深拷贝函数 deepClone */
/**
 * 已有深拷贝实现:
 * 1. 使用JSON函数:JSON.parse(JSON.stringfy(obj))
 * 2. 使用lodash:_.deepClone(obj)
 */

function deepClone (obj) {
  if(!obj || typeof obj !== 'object') return;
  let newObj = Array.isArray(obj) ? [] : {};
  // 遍历对象进行深拷贝
  for(let key in obj) {
    if(obj.hasOwnProperty(key)) {
      newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];
    }
  }
  return newObj;
}

let obj1 = {
  a: 2,
  b: [2,3,4],
  c: {
    d: 1,
  }
}

let obj2 = deepClone(obj1);

console.log(obj1.b === obj2.b); // false

5、实现防抖函数

/** 手写防抖函数debounce:事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时 */

function debounce(Fn, timeout) {
  let timer = null;
  return function() {
    // 如何计时器存在的话,则清除之前已有的计时器,重新创建新的定时器
    if(timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      Fn.apply(this, arguments);
    }, timeout);
  }
}

function click(count) {
  console.log(count, Date.now());
}

let debounceFunc = debounce(click, 2000);
debounceFunc(1);  // 无输出
// 在2s内再次出触发事件,只执行最后一次
setTimeout(() => debounceFunc(2), 1000); // 2 1715827126184
// 在2s外触发,执行
setTimeout(() => debounceFunc(3), 4000); // 3 1715827129183

6、实现节流函数

/** 手写节流 throttle: 一段时间内,事件只触发一次 */

// 时间戳对比实现
function timestampThrottle(Fn, wait) {
  let lastTimestamp = null;
  return function() {
    let nowTimestamp = Date.now();
    if(lastTimestamp === null || nowTimestamp - lastTimestamp >= wait) {
      lastTimestamp = Date.now();
      Fn.apply(this, arguments);
    }
  }
}

// 定时器实现
function throttle(Fn, wait) {
  let timer = null;
  return function() {
    if(!timer) {
      Fn.apply(this, arguments);
      timer = setTimeout(() => {
        timer = null;
      }, wait);
    }
  }
}

function click(count) {
  console.log(count, Date.now());
}

let throttleFunc = timestampThrottle(click, 2000);
throttleFunc(1); // 1 1715827197169
// 在2s内再次出触发事件,不执行
setTimeout(() => throttleFunc(2), 1000); // 无输出
// 在2s外触发,执行
setTimeout(() => throttleFunc(3), 4000); // 3 1715827201174

7、实现Promise.all函数

/** 手写Promise.all函数 */

function myPromiseAll(promises) {
  if(!(promises instanceof Array)) {
    throw new Error('params must be array');
  }
  let resultList = [];
  return new Promise((resolve, reject) => {
    for(let i=0; i<promises.length; i++) {
      Promise.resolve(promises[i]).then(value => {
        resultList.push(value);
        if(resultList.length === promises.length) {
          resolve(resultList);
        }
      }).catch(reason => reject(reason));
    }
  })
}

// 成功
Promise.all([1, 2, Promise.resolve(3), Promise.resolve(4)]).then(result => console.log(result)).catch(reason => console.log(reason)); // [ 1, 2, 3, 4 ]
myPromiseAll([1, 2, Promise.resolve(3), Promise.resolve(4)]).then(result => console.log(result)).catch(reason => console.log(reason)); // [ 1, 2, 3, 4 ]
// 失败
Promise.all([1, 2, Promise.resolve(3), Promise.reject(4)]).then(result => console.log(result)).catch(reason => console.log(reason)); // 4
myPromiseAll([1, 2, Promise.resolve(3), Promise.reject(4)]).then(result => console.log(result)).catch(reason => console.log(reason)); // 4

8、实现柯里化函数

/** 手写柯里化函数curry */
// 柯里化函数的作用是将fn(a, b, c)方式的函数调用转换为 fn(a)(b)(c)形式
// es6实现
function curryEs6(fn, ...args) {
  return fn.length <= args.length ? fn(...args) : curryEs6.bind(null, fn, ...args);
}
// 普通函数实现
function curryCommon(fn, args) {
  let len = fn.length;
  args = args || [];
  return function () {
    let newArgs = args.concat([...arguments]);
    if(newArgs.length >= len) {
      return fn.apply(this, newArgs);
    } else {
      return curryCommon.call(this, fn, newArgs);
    }
  }
}

function sum(a, b, c) {
  return a+b+c;
}

let commonCurry = curryCommon(sum);
console.log(commonCurry(1)(2)(3)); // 6

let esCurry = curryEs6(sum, 1);
console.log(esCurry(2)(3)); // 6

9、实现Function.prototype.bind函数

/** 手写Function.prototype.bind函数 */

Function.prototype.bind = function (context) {
  // 1. 限制调用方必须是函数类型
  if(typeof this !== 'function') {
    throw 'Caller must be function';
  }
  // 2. 预存this和入参
  let fn = this, args = [...arguments].slice(1);
  // 3. 返回一个新的函数
  return function Fn() {
    // 4. 调用apply修改this的指向,此时需要判断是否使用new 操作符创建,new 的话要返回新对象this,否则使用context
    return fn.apply(this instanceof Fn ? this : context, args.concat(...arguments));
  }
}

function Foo(name, age) {
  this.name = name;
  this.age = age;
}

function Bar(){}

Bar.prototype.sayHello = function (sex) {
  console.log(`Hello, my name is ${this.name}, age ${this.age}, sex ${sex}`);
}

let sayHelloFoo = Bar.prototype.sayHello.bind(new Foo('张三', 22), 'male');
sayHelloFoo(); // Hello, my name is 张三, age 22, sex male

let sayHelloEmpty = Bar.prototype.sayHello.bind({name: '李四', age: 32});
sayHelloEmpty('female'); // Hello, my name is 李四, age 32, sex female

// 通过new 的对象调用
let FooBind = Foo.bind({}, '王五');
let person = new FooBind(33);
console.log(person.name, person.age); // 王五 33

10、实现Function.prototype.call函数

/** 手写Function.prototype.call函数 */

Function.prototype.call = function(context) {
  // 1. 判断调用该方法的必须为函数类型
  if(typeof this !== 'function') {
    throw 'Call must called by function';
  }
  // 2. 如果没有传递context,则默认使用window
  context = context || window;
  // 3. 在context上定义执行函数,获取context的上下文
  context.fn = this;
  // 4. 获取调用参数并调用context.fn执行调用函数逻辑
  let args = [...arguments].slice(1);
  let result = context.fn(...args);
  // 5. 获取了结果后删除context.fn
  delete context.fn;
  // 6. 返回结果
  return result;
}

function Foo() {
  this.name = '张三';
  this.age = 32;
}

function Bar() {
  this.name = '李四';
  this.age = 22;
}

Bar.prototype.sayHello = function () {
  console.log(`Hello, My name is ${this.name}, age ${this.age}`);
}

let foo = new Foo();
let bar = new Bar();
bar.sayHello(); // Hello, My name is 李四, age 22
bar.sayHello.call(foo); // Hello, My name is 张三, age 32

11、实现Function.prototype.apply函数

/** 手写Function.prototype.apply函数 */

Function.prototype.apply = function (context) {
  // 1. 先判断调用方类型是否为函数
  if(typeof this !== 'function') {
    throw 'Caller must be a function';
  }
  // 2. 如果传递了目标对象则使用目标对象,如果没传递默认使用window
  context = context || window;
  // 3. 在目标对象上添加函数指向调用方,获取目标对象的上下文
  context.fn = this;
  // 4. 获取入参,并调用context.fn将入参传入进去执行,获取结果
  let result = null;
  if(arguments[1]) {
    result = context.fn(...arguments[1]);
  } else {
    result = context.fn();
  }
  // 5. 删除context.fn定义
  delete context.fn;
  // 6. 返回执行结果
  return result;
}

function Foo() {
  this.name = '张三';
  this.age = 32;
}

function Bar() {
  this.name = '李四';
  this.age = 22;
}

Bar.prototype.sayHello = function () {
  console.log(`Hello, My name is ${this.name}, age ${this.age}`);
}

let foo = new Foo();
let bar = new Bar();

bar.sayHello(); // Hello, My name is 李四, age 22
bar.sayHello.apply(foo); // Hello, My name is 张三, age 32

12、实现ajax请求

/** 手写ajax请求 */

function ajax(url) {
  // 1. 创建xhr对象
  let xhr = new XMLHttpRequest();
  // 2. 指定get请求和url并打开链接
  xhr.open('GET', url, true);
  // 3. 监听readyState变化,判断场景处理结果
  xhr.onreadystatechange = function () {
    if(this.readyState === 4) {
      // 请求成功
      if(this.status === 200) {
        console.log(this.response);
      } else {
        console.error(this.statusText);
      }
    }
  }
  // 4. 监听错误事件
  xhr.onerror = function() {
    console.error(this.statusText);
  }
  // 5. 设置header以及发送数据
  xhr.responseType = 'json';
  xhr.setRequestHeader('Accept', 'application/json')
  xhr.send(null);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值