前端要会的手写原生方法

call: 简单来讲就是改变执行方法当前的this,能够传入不定参数

Function.prototype.myCall = function(context, ...args) {
  context.fn = this;
  const result = context.fn(...args);
  delete context.fn;
  return result;
}

getMessage.myCall(obj, 'name'); 

当即执行getMessage方法,不过是以obj.getMessage的方式,传入参数'name'。(obj可能压根就没有getMessage方法)

复制代码

apply: 和call做用一致,传递参数格式不一样,需是数组

Function.prototype.myApply = function(context, args) {
  context.fn = this;
  const result = context.fn(...args);
  delete context.fn;
  return result;
}

getMessage.myApply(obj, ['name']); 

复制代码

bind: 改变指定方法的this后执行,以函数的形式返回执行结果

Function.prototype.myBind = function(context, ...args) {
  return () => {
    return this.myApply(context, args);
  }
}

const result = getMessage.myBind(obj) 

result是一个函数,再执行一次才是getMessage方法的执行结果

复制代码

new:将构造函数实例化,将参数建立为对象以及赋值原型方法

function createNew(Ctur, ...args) {
  const obj = {};
  obj.__proto__ = Ctur.prototype;
  const ret = Ctur.apply(obj, args);
  return ret instanceof Object ? ret : obj;
}

1. 建立一个空对象。
2. 将构造函数的原型继承给这个空对象的隐式原型。
3. 在obj下执行构造函数,并传入参数,这个时候构造函数内的this就是obj。
4. 若是这个'构造函数'没有return对象格式的结果,返回新建立的obj。

function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.getName = function() {
  console.log(this.name);
}

const xm = createNew(Person, 'xiaoming', 22);

复制代码

instanceof: 判断一个变量是不是某个类型

function myInstanceOf(left, right) {
  while(1) {
    if(left.__proto__ === null) {
      return false;
    }
    if(left.__proto__ === right.prototype) {
      return true;
    }
    left = left.__proto__;
  }
}

instanceof的原理就是经过原型链查找,因此一直向上查找左侧的隐式原型__ptoto__是否等于右侧显式原型,
原型链的尽头是null,没找到就返回false。

myInstanceOf([1,2], Array);  // true

复制代码

forEach: 遍历数组,这个你们常常用,想必不说都懂

Array.prototype.myForEach = function(fn) {
  const arr = this;
  for(let i = 0; i < arr.length; i++) {
    fn(arr[i], i, arr);
  }
}

接受一个fn回调函数,传递给回调函数三个参数:每项的值,下标,自身。第二个参数有人用么?

const arr = ['a','b','c'];
arr.myForEach(item => {
  console.log(item);  // a   b   c
})

复制代码

map: 返回通过处理的数组

Array.prototype.myMap = function(fn) {
  const arr = this;
  const ret = [];
  for(let i = 0; i < arr.length; i++) {
    ret.push(fn(arr[i], i, arr));
  }
  return ret;
}

和forEach相似也是接受一个fn回调,不过会将回调处理的结果放入一个新的数组,
因此map回调内的每一项须要return,由于要组成新的数组结果。

const arr = ['a', 'b', 'c'];
const newArr = arr.myMap(item => {  // a1   b1   c1
  return item + 1; // 要return结果
})

复制代码

filter: 返回回调处理结果为true的新数组

Array.prototype.myFilter = function(fn) {
  const arr = this;
  const ret = [];
  for(let i = 0; i < arr.length; i++) {
    if(fn(arr[i], i, arr)) {
      ret.push(arr[i]);
    }
  }
  return ret;
}

大同小异,过滤出处理条件为true的值。

返回数组中不重复的值:
function repeat(arr) {
  return arr.myFilter((v, i, a) => {
	return a.indexOf(v) === a.lastIndexOf(v);
  })
}

const arr = [1,2,3,4,1,2,3,5,6,8,3];
repeat(arr); // [4,5,6,8]

复制代码

find:返回处理条件第一个为true的数组项

Array.prototype.myFind = function(fn) {
  const arr =this;
  for(let i = 0; i < arr.length; i++) {
    if(fn(arr[i], i, arr)) {
      return arr[i];
    }
  }
}

不然返回undefined

复制代码

findIndex: 返回处理条件第一个为true的数组下标

你们本身写下咯~

复制代码

every:若是数组每一项都符合处理条件。返回true,不然返回false

Array.prototype.myEvery = function(fn) {
  const arr = this;
  for(let i = 0; i < arr.length; i++) {
    if(!fn(arr[i], i, arr)) {
      return false;
    }
  }
  return true;
}

复制代码

some:只要数组有一项符合处理条件。返回true,都不知足返回false。

这个相信你们都知道怎么写了~

复制代码

reduce: 通常为数组作累计结果使用。

Array.prototype.myReduce = function(fn, accumulator) {
  const arr = this;
  let index = 0;
  if(typeof accumulator === undefined) { // 没传第二个参数
    index = 1;
    accumulator = arr[0];
  }
  for(let i = index; i < arr.length; i++) {
    let invokedReturn = fn(accumulator, arr[i], i, arr);
    accumulator = invokedReturn;
  }
  return accumulator;
}

通常会传入第二个参数做为初始值,若是没有传入,初始值就是数组的第一项,将处理的结果进行累计,最后返回累计的结果。

返回数组中指定参数重复的次数:
function count(arr, value) {
  return arr.myReduce((f, s) => {
    return Object.is(s, value) ? f + 1 : f + 0;
  }, 0)
}

const arr = [1,2,3,4,1,2,3,2,1];
count(arr, 2); // 3

复制代码

JSON.stringify: 将对象转为json字符串

function jsonStringify(obj) {
  const type = typeof obj;
  if (type !== 'object') {
    if (type === 'string') {
      obj = '"' + obj + '"';
    }
    return String(obj);
  } else {
    const json = [];
    const arr = Array.isArray(obj);
    for (const k in obj) {
      let v = obj[k];
      const type = typeof v;
      if (type === 'string') {
        v = '"' + v + '"';
      } else if (v === null) { // 处理null状况
        v = null
      } else if (/function|undefined/.test(type)) { // 原生方法会移除function和undefined
        delete obj[k];
      } else {
        v = jsonStringify(v); // 递归
      }
      json.push((arr ? "" : '"' + k + '":') + String(v));
    }
    return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
  }
}

const obj = {
  a: 'a1',
  b: [1, 2, 3],
  c: 22,
  d: function () {},
  e: Date.now(),
  f: null,
  g: /str/ig,
  h: undefined
}

const str = jsonStringify(obj); 
// {"a":"a1","b":[1,2,3],"c":22,"e":1562815128952,"f":null,"g":{}}

复制代码

JSON.parse: 将字符串格式的对象转为对象。

function jsonParse(str) {
  return new Function('return ' + str)(); // return后有一个空格
}

很神奇有木有,直接在字符串前面加上'return '关键字就能够转为对象。
在组件精讲小册里有一个实例,在线vue单文件编辑器。原理就是将编辑器内的vue单文件字符串使用正则分割,
js部分将‘export default’替换为'return '。经过new Function转为js对象使用。

const sum = new Function('a','b','return a + b');
sum(1, 2); // 3

const str = '{"a":"a1","b":[1,2,3],"c":22,"e":1562815128952,"f":null,"g":{}}';
jsonParse(str); //
a: "a1",
b: [1, 2, 3],
c: 22,
e: 1562815128952,
f: null,
g: {}

复制代码

setInterval: 你们懂的

function mySetInterval(fn, interval) {
  const now = Date.now;
  let startTime = now();
  const loop = () => {
    const timer = requestAnimationFrame(loop);
    if (now() - startTime >= interval) {
      startTime = now();
      fn(timer);
    }
  }
  loop();
}

通常来讲是不建议使用setInterval的,如内部函数复杂就不能保证必定在规定时间内自动执行。
通常是经过setTimeout模仿setInterval。那为何要实现setInterval,
由于它内部的实现是使用requestAnimationFrame实现的,该方法自带函数节流。
若有持续的动画须要执行,基本会保证在16.6毫秒内执行一次,提升动画性能并延时也是精确的。

mySetInterval(timer => {
  console.log('a');
  // cancelAnimationFram(timer) 能够取消当前定时器
})

复制代码

setTimeout: 本身实现下吧

定时执行一次回调就取消掉,本身实现下吧~

复制代码
写到最后

​ 还有例如Promise,节流/防抖, jsonp,深拷贝等方法要不代码太长或者不是原生的方法,他们每个均可以写一个单篇,就不列在这了。若有谬误之处,万望斧正。vue

相关文章
相关标签/搜索