javascript原生代码实现及代码总结

JsonP等实现

注意:JSONP可能会引起csrf攻击

function jsonp({url = '', params = {}, cb="cb"}) {
  if (!url) return
  const data2Url = data => {
    return Object.keys(data).reduce((acc, cur) => {
        acc.push(`${cur}=${data[cur]}`)
        return acc
    }, []).join('&')
  }
  return new Promise((resolve,reject) => {
    const cbFn = `jsonp_${Date.now()}` // 定义函数名
    const obody = document.body
    const script = document.createElement('script')

    params[cb] = cbFn // 将函数名放在回掉函数中
    window[cbFn] = function(res) {
      res ? resolve(res) : reject('error');
      obody.removeChild(script)
      window[cbFn] = null
    }

    script.src = `${url}?${data2Url(params)}`
    obody.appendChild(script)
  })
}

截流&防抖

 var COUNT = 0;
function testFn() {console.log('testFN 被调用了 ' + ++COUNT + '次')}  //被调用的函数
var throttle = function (fn, delay, atleast) {    //调用的函数、自定义时间、多长时间至少会执行一次
    var timer = null;
    var previous = null;

    return function () {
        var now = +new Date();

        if ( !previous ) previous = now;
        if ( atleast && now - previous > atleast ) {
            fn();
            // 重置上一次开始时间为本次结束时间
            previous = now;
            clearTimeout(timer);
        } else {
            clearTimeout(timer);
            timer = setTimeout(function() {
                fn();
                previous = null;
            }, delay);
        }
    }
};
window.onscroll = throttle(testFn, 500, 1000);

深拷贝

  • 方法一:JSON.parse(JSON.stringify(obj))性能最快
    • 具有循环引用的对象时,报错
    • 当值为函数、undefined、或symbol时,无法拷贝
  • 方法二:递归
// 深度优先
// map是为了解决循环引用问题
function deepCloneDFS(obj, map = new Map()){
  if (obj instanceof RegExp) return new RegExp(obj)
  if (obj instanceof Date) return new Date(obj)

  if (typeof obj !== "object" || obj === null) return obj;

  let newObj=Array.isArray(obj)?[]:{}
  if (map.get(obj)) {
    return obj;
  }
  map.set(obj, newObj);

  for(var attr in obj){
      if (Object.prototype.hasOwnProperty.call(obj, attr)) {
          newObj[attr]=deepCloneDFS(obj[attr], map)
      }
  }
  return newObj
}
// 广度优先
function getEmpty(o){
	if(Object.prototype.toString.call(o) === '[object Object]'){
		return {};
	}
	if(Object.prototype.toString.call(o) === '[object Array]'){
		return [];
	}
	return o;
}
function deepCloneBFS(origin) {
  let queue = [];
	let map = new Map(); // 记录出现过的对象,用于处理环

	let target = getEmpty(origin);
	if(target !== origin){
		queue.push([origin, target]);
		map.set(origin, target);
	}

	while(queue.length){
		let [ori, tar] = queue.shift();
		for(let key in ori){
			// 处理环状
			if(map.get(ori[key])){
				tar[key] = map.get(ori[key]);
				continue;
			}

			tar[key] = getEmpty(ori[key]);
			if(tar[key] !== ori[key]){
				queue.push([ori[key], tar[key]]);
				map.set(ori[key], tar[key]);
			}
		}
	}

	return target;
}

柯里化函数 currying

主要作用

  1. 提前绑定好函数里面的某些参数,达到参数复用的效果,提高了适用性.
  2. 固定易变因素
  3. 延迟计算
function currying(fn, ...args) {
  return args.length < fn.length ? (...args2) => currying(fn, ...args, ...args2) : fn(...args)
}

计算两个数组的交集

需要注意的是当两个数组是[1,1]和[1]的时候,他们的交集是[1]

方法一

const intersect = (nums1, nums2) => {
  const map = {}
  const res = []
  for (let n of nums1) {
    if (map[n]) {
      map[n]++
    } else {
      map[n] = 1
    }
  }
  for (let n of nums2) {
    if (map[n] > 0) {
      res.push(n)
      map[n]--
    }
  }
  return res
}

方法二

const intersect = (nums1, nums2) => {
  return nums1.filter(item => {
    let idx = nums2.indexOf(item)
    if (idx !== -1) {
      nums2.splice(idx, 1)
      return item
    }
  })
}

实现DUFF装置

DUFF是红宝书中提到的一种可以很大限度降低循环次数的方式

var values = [];
// 假设有 50w 条数据
for (var i = 0; i < 500000; i++) {
  values.push(i);
}


var doSomething = (num) => console.log(num);
var count = Math.ceil(values.length / 8); // 计算循环次数
var start = values.length % 8;  // 余数
var j = 0;

do {
  switch (start) {
    case 0: doSomething(j++);
    case 7: doSomething(j++);
    case 6: doSomething(j++);
    case 5: doSomething(j++);
    case 4: doSomething(j++);
    case 3: doSomething(j++);
    case 2: doSomething(j++);
    case 1: doSomething(j++);
  }
  start = 0;
} while (--count > 0);

监听动态脚步创建

function loadscript(url,callback){
  var script=document.createElement('script');
  script.type='text/javascript';
  if(script.readyState){ //ie
    script.onreadystatechange=function(){
      if(script.readyState == 'loaded' || script.readyState == 'complete'){
        script.onreadystatechange = null;
        callback()
      }
    };
  }else{  //其他浏览器
    script.onload=function(){
      callback()
    };
  };
  script.src=url;
  document.getElementsByTagName('head')[0].appendChild(script)
}

new方法的实现

原理:

  1. 我们创建了一个空对象obj
  2. 我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象
  3. 我们将Base函数对象的this指针替换成obj,然后再调用Base函数,于是我们就给obj对象赋值了一个id成员变量,这个成员变量的值是”base”
  4. 如果有返回且返回为对象,则返回,如果没有,则默认返回原对象
function New(fun) {
  var obj  = {}; 
  if (fun.prototype !== null) {
    obj.__proto__ = fun.prototype; 
    //obj = Object.create(fun.prototype)
  }
  var ret = fun.call(obj);  
  if((typeof ret === "object" || typeof ret === "function") && ret !== null){
    return ret
  }else {
    return obj
  }
}

instanceof的实现方式

用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链

function _instanceof(left, right) {
  const prototype = right.protytype
  let left = Object.getPrototypeOf(left)
  while(true) {
    if(left === null) {
      return false
    }
    if (left === prototype) {
      return true
    }
    left = left._proto_
  }
}

call/apply 的实现原理是什么?

call/apply主要的功能就是更改this指向并立即执行;它们的区别但就是传递的参数不同func.call(thisArg, arg1, arg2, ...),func.apply(thisArg, [argsArray])

Function.prototype.call = (content = window) => {
  const fn = Symbol(); // 防止污染属性
  content[fn] = this
  const args = [...arguments].slice(1)
  const result = content[fn](...args)
  delete content[fn]
  return result
}

applycall的实现方式基本类似,仅仅是在处理参数上多做下处理就好了

实现LazyMan 类

LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food

实现方式:

class LazyManClass {
  constructor(name) {
    this.tasks = []
    this.name = name
    console.log(`Hi I am ${this.name}`)
    setTimeout(() => {
      this.next()
    })
  }
  eat(food) {
    const fn = () => {
      console.log(`I am eating ${food}`)
      this.next()
    }
    this.tasks.push(fn.bind(this))
    return this
  }
  sleep(time) {
    const fn = () => {
      console.log(`等待了${time}秒...`);
      setTimeout(_ => {
        this.next()
      }, time * 1000)
    }
    this.tasks.push(fn.bind(this))
    return this
  }
  sleepFirst(time) {
    const fn = () => {
      console.log(`等待了${time}秒...`);
      setTimeout(_ => {
        this.next()
      }, time * 1000)
    }
    this.tasks.unshift(fn.bind(this))
    return this
  }
  next() {
    const fn = this.tasks.shift()
    fn && fn()
    return this
  }
}
const lazyMan = name => {
  return new LazyManClass(name)
}

实现一个 EventEmitter 方法

类似Vue的eventopen in new window方法

class EventEmitter{
  constructor() {
    this.events = {}
  }
  on(name, fn) {
    (this.events[name] || (this.events[name] = [])).push(fn)
    return this
  }
  off(name, fn) {
    if(!this.events[name]) return this
    if (!fn) {
      this.events[name] = null
      return this
    }
    this.events[name] = this.events[name].filter(item => item !== fn)
    return this
  }
  emit(name, ...args) {
    const fn = this.events[name]
    if(!fn) return this
    fn.forEach(item => {
      item.apply(this, args)
    })
    return this
  }
  once(name, fn) {
    const once = function() {
      fn.apply(this, arguments)
      this.off(name, once)
    }
    this.events[name].push(once)
  }
}

'rgb(255, 255, 255)' -> '#FFFFFF'

function rgb2hex(rgb) {
    var rgb = rgb.match(/\d+/g);
    var hex = (n) => {
        return ("0" + Number(n).toString(16)).slice(-2);
        // return Number(n).toString(16).padStart(2, '0')
    }
    return rgb.reduce((acc, cur) => acc + hex, '#').toUpperCase()
}

如何判断一个空对象

//1.
JSON.stringify(data) == "{}"
//2.for…in循环判断
//3.
Object.getOwnPropertyNames(data)
//4.
Object.kes(data)

Object.create 兼容实现

Object._create = (o) => {
  let Fn = function() {}; // 临时的构造函数
  Fn.prototype = o;
  return new Fn;
}

简单版的Promise

class PromiseA {
  constructor (init) {
    this.PromiseStatus = 'pending'
    var resolve = (val) => {
      if (this.resolveCallback) {
        this.PromiseStatus = 'fulfilled'
        this.resolveCallback(val)
      }
    }
    if (init) {
      setTimeout(() => { // 为了支持同步任务
        init(resolve, reject)
      })
    }
  }
  then (onFulfill, onReject) {
    this.resolveCallback = onFulfill
    this.rejectCallback = onReject
    return this
  }
}

模拟实现一个 Promise.finally

不管 Promise 对象最后状态如何,都会执行的操作

Promise.prototype._finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

实现 Promise.race()

有一个promise率先改变状态;则改变Promise状态并执行then方法

Promise.prototype._race = promises => new Promise((resolve, reject) => {
	promises.forEach(promise => {
		promise.then(resolve, reject)
	})
})

实现 Promise.all()

所有promise同步执行完才会执行then方法; 并且只要有一个状态是rejected,则最后结果就是rejected

Promise.prototype._all = function (promises) {
  return new Promise((resolve, reject) => {
    let index = 0;
    let result = [];
    if (promises.length === 0) {
      resolve(result);
    } else {
      setTimeout(() => {
        for (let i = 0; i < promises.length; i++) {
          //promises[i] 可能是普通值
          Promise.resolve(promises[i]).then((data) => {
            result[i] = data;
            if (++index === promises.length) {
              resolve(result);
            }
          }, (err) => {
            reject(err);
            return;
          });
        }
      })
    }
  });
}

实现 Promise.allSettled()

if (!Promise.allSettled) {
  const rejectHandler = reason => ({status: "rejected", reason})
  const resolveHandler = value => ({status: "fulfilled", value})
  Promise.allSettled = promises =>
    Promise.all(
      promises.map((promise) =>
        Promise.resolve(promise) 
          .then(resolveHandler, rejectHandler)
      )
    );
}

实现一个可以控制最大并发数的函数

const sendRequest = function(urls, max = 3, callback) {
  let finished = 0
  const total = urls.length
  const handler = () => {
    if (urls.length) {
      const url = urls.shift()
      fetch(url).then(() => {
        finished++
        handler()
      }).catch(err => {
        throw Error(err)
      })
    }  
    if (finished >= total) {
      callback()
    }
  }
  for(let i = 0; i < max; i++) {
    handler()
  }
}

实现简易的Symbol

参考open in new window

获取页面 FPS

1s是否达到 60 帧,16.5ms左右渲染一次,即可间接地反映浏览器的渲染帧率,所以通过显示当前网页的 FPS 值,判断页面是否卡顿参考open in new window

var lastTime = performance.now();
var frame = 0;
var lastFameTime = performance.now();

var loop = function(time) {
	var now =  performance.now();
	var fs = (now - lastFameTime);
	lastFameTime = now;
  var fps = Math.round(1000/fs);
  console.log(fps)
	frame++;
	if (now >= 1000 + lastTime) {
    var fps = Math.round( 1000 / (( now - lastTime ) / frame));
    console.log(fps,'平均值')
		frame = 0;    
		lastTime = now;    
	};           
	window.requestAnimFrame(loop);   
}
loop()

一个极简的模板引擎

描述:

render("我是{{name}},年龄{{age}}", {
  name: "lucifer",
  age: 17
});

// 结果: 我是姓名,年龄18

实现:

function render(tpl, data) {
  return tpl.replace(/\{\{(.+?)\}\}/g, function($1, $2) {
    // $1 分组为 类似 {{name}}
    // $2 分组为 类似 name
    // 加上面的小括号就是为了方便拿到key而已
    return data[$2];
  });
}

camelCased (驼峰式) 命名与 kebab-case(短横线命名)互转

const hyphenate = function (str) {
  const hyphenateRE = /\B([A-Z])/g
  return str.replace(hyphenateRE, '_$1').toLowerCase()
}
console.log(hyphenate('aCDef')) // a_c_def


const camelize = function (str) {
  const camelizeRE = /_(\w)/g
  return str.replace(camelizeRE, function (_, $1) { return $1 ? $1.toUpperCase() : '' })
}
console.log(camelize('a_c_def')) // ACDef

实现JSON.stringify函数

function JSONStringify(obj) {
  if (
    obj === undefined ||
    obj === null ||
    typeof obj === "string" ||
    typeof obj === "boolean" ||
    typeof obj === "number"
  ) {
    return obj;
  }
  if (typeof obj === "function") {
    return "";
  }
  if (Array.isArray(obj)) {
    let result = [];
    for (let i = 0; i < obj.length; i++) {
      result.push(JSONStringify(obj[i]));
    }
    return "[" + result.join(",") + "]";
  } else {
    let result = [];
    for (let key in obj) {
      result.push(`"${key}":${JSONStringify(obj[key])}`);
    }
    return "{" + result.join(",") + "}";
  }
}

实现repeat方法

String.prototype.repeatString = function (num) {
  return Array(num + 1).join(this);
}

AOP面向切面编程

AOP就是让一个函数在另一个函数之前或者之后执行; 在 js 中的妙用.参考open in new window

  • 防止 window.onload 被二次覆盖.
  • 无侵入的统计代码.
  • 分离表单请求和校验.
  • 给 ajax 请求动态添加参数.
  • 职责链模式.
  • 组合代替继承.
Function.prototype.before = function(func) {
  const _self = this
  return function() {
    if(func.apply(this, arguments) === false){
      return false
    }
    return _self.apply(this, arguments)
  }
}
Function.prototype.after = function(func) {
  const _self = this
  return function() {
    const result = _self.apply(this, arguments)
    if(result === false){
      return false
    }
    func.apply(this, arguments) 
    return result
  }
}

compose组合函数实现

以把处理数据的函数向管道一样连起来,上一个函数的结果作为下一个函数的参数,得到最终的结果:

// reduce
const compose = (...funs) => {
  return (...arg) => {
    return funs.reduce((acc, curFn) => {
      return typeof acc === 'function' ? curFn(acc(...arg)) : curFn(acc)
    })
  }
}
// loadsh
var flow = function (fns) {
  var len = fns.length

  var index = len
  while (index--) {
    if (typeof fns[index] !== 'function') {
      throw new TypeError('Expected a function')
    }
  }

  return function (...args) {
    var index = 0
    var reslut = len ? fns[index].apply(this, args) : args[0]
    while (++index < len) {
      reslut = fns[index].call(this, reslut)
    }
    return reslut
  }
}

实现requestIdleCallback方法来源open in new window

var requestIdleCallback =
  function (cb) {
  if (global.requestIdleCallback) return global.requestIdleCallback(cb)
    var start = Date.now();
    return setTimeout(function () {
      cb({
        didTimeout: false,
        timeRemaining: function () {
          return Math.max(0, 50 - (Date.now() - start));
        }
      });
    }, 1);
  }

var cancelIdleCallback =
  function (id) {
  if (global.cancelIdleCallback) return global.cancelIdleCallback(id)
    return clearTimeout(id);
  }

exports.requestIdleCallback = requestIdleCallback
exports.cancelIdleCallback = cancelIdleCallback

实现 setTimeout 函数

let setTimeout = (fn, timeout, ...args) => {
  // 初始当前时间
  const start = +new Date()
  let timer, now
  const loop = () => {
    timer = window.requestAnimationFrame(loop)
    // 再次运行时获取当前时间
    now = +new Date()
    // 当前运行时间 - 初始当前时间 >= 等待时间 ===>> 跳出
    if (now - start >= timeout) {
      fn.apply(this, args)
      window.cancelAnimationFrame(timer)
    }
  }
  window.requestAnimationFrame(loop)
}

function test(){ 
    console.log("test")
}
let timerID = setTimeout(test, 1000);
小红包免费领
小礼物走一走
Last Updated:
Contributors: slbyml
部分内容来源于网络,如有侵权,请留言或联系994917123@qq.com;访问量:waiting;访客数:waiting