前端要會的手寫原生方法

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

相關文章
相關標籤/搜索