JavaScript基础之实现部分原生与有趣的api(一)

24 篇文章 0 订阅
19 篇文章 0 订阅

JS实现原生api

::: tip

ES5实现map()方法

Array.prototype.MyMap = function(fn,context){
    var arr = Array.prototype.slice.call(this); //es5 不需要用...展开
    var mappedArr = [];
    for(var i =0;i<arr.length;i++){
        if(!arr.hasOwnProperty(i))continue;
        mappedArr.push(fn.call(context,arr[i],i,this))
    }
    return mappedArr;
}

ES5实现数组的reduce()方法

Array.prototype.myReduce = function(fn,initialValue){
    var arr = Array.prototype.slice.call(this);
    var res,startIndex;
    res = intitalValue?initialValue:arr[0];
    startIndex = intitalValue?0:1;
    for(var i=0;i<arr.length;i++){
        res = fn.call(null,res,arr[i],i,this);
    }
    return res;
}

ES6实现call/apply

Function.prototype.myCall = function(context = window,..args){
    let func = this;
    let fn = Symbol("fn");
    conext[fn] = func;

    let res = context[fn](...args); //重点代码 利用this指向,相当于context.call(...args)
    delete context[fn];
    return res;
}

实现generator


// 使用 * 表示这是一个 Generator 函数
// 内部可以通过 yield 暂停代码
// 通过调用 next 恢复执行
function* test() {
  let a = 1 + 2;
  yield 2;
  yield 3;
}
let b = test();
console.log(b.next()); // >  { value: 2, done: false }
console.log(b.next()); // >  { value: 3, done: false }
console.log(b.next()); // >  { value: undefined, done: true }

// cb 也就是编译过的 test 函数
function generator(cb) {
  return (function() {
    var object = {
      next: 0,
      stop: function() {}
    };

    return {
      next: function() {
        var ret = cb(object);
        if (ret === undefined) return { value: undefined, done: true };
        return {
          value: ret,
          done: false
        };
      }
    };
  })();
}
// 如果你使用 babel 编译后可以发现 test 函数变成了这样
function test() {
  var a;
  return generator(function(_context) {
    while (1) {
      switch ((_context.prev = _context.next)) {
        // 可以发现通过 yield 将代码分割成几块
        // 每次执行 next 函数就执行一块代码
        // 并且表明下次需要执行哪块代码
        case 0:
          a = 1 + 2;
          _context.next = 4;
          return 2;
        case 4:
          _context.next = 6;
          return 3;
		// 执行完毕
        case 6:
        case "end":
          return _context.stop();
      }
    }
  });
}

实现Object.create方法

function create(proto){
    function F(){};
    F.prototype = proto;
    return new F();
}

实现Bind方法

核心要点:
1、对于普通函数,绑定this指向
2、对于构造函数,要保证原函数的原型对象上的属性不能丢失

Function.prototype.bind = function(context,...args){
    let self = this; // this表示的调用bind函数
    let fBound = function(){
        //this instanceof fBound为true表示构造函数的情况。如new func.bind(obj)
        return self.apply(this instanceof fBound?this:context||window,args);
    }

    fBound.prototype = Object.create(this.prototype); //保证原函数的原型对象上的属性不丢失
    return fBound;
}


Function.prototype.bind=function(context,...args){
    if(typeof this!=='function'){
        throw new Error('not a function');
    }
    let fn = this;
    let resFn = function(...args2){
        return fn.apply(this instanceof resFn?this:context,args.contact(args2));
    };
    const DumpFunction = function DumpFunction(){};
    DumpFunction.prototype = this.prototype;
    resFn.prototype = new DumpFunction();
    return resFn;
}

Function.prototype.myFind=function(context){
    if(typeof this!=='function'){
        throw new TypeError('Error');
    }
    var _this = this;
    var args = [...arguments].slice(1)
    //return a function 
    return function F(){
        //due to return a function ,use new F() 
        if(this instanceof F){
            return new _this(...args,...arguments)
        }
        return _this.apply(context,args.contact(...arguments))
    }
}

实现Call

Function.prototype.call = function(context){
    var context = context||window;
    //add property to context
    // getValue.call(a, 'yck', '24') => a.fn = getValue
    context.fn = this;
    //将context后面的参数取出来
    var args = [...arguments].slice(1);
    /// getValue.call(a, 'yck', '24') => a.fn('yck',24)
    var result = context.fn(...args)
    //删除fn
    delete context.fn;
    return result;
}

实现apply

Function.prototype.apply = function(context){
    var context = context||window;
    context.fn = this;
    var result 
    //判断是否存储第二个参数
    //如果存在,就将第二个参数展开
    if(arguments[1]){
        result = context.fn(...arguments[1])
    }else{
        result = context.fn();
    }

    delete context.fn;
    return result;
}

实现new关键字

核心要点:

1.创建一个全新的对象,这个对象的__proto__要指向构造函数的原型对象
2.执行构造函数
3.返回值为object类型则作为new方法的返回值返回,否则返回上述全新对象

function myNew(fn,...args){
    let instance = Object.create(fn.prototype);
    let res = fn.apply(instance,args);
    return typeof res === 'object'?res:instance;
}

实现instanceof的作用

核心要点:原型链的向上查找

function myInstanceOf(left,rigth){
    let proto = Object.getPrototype(left);
    while(true){
        if(proto==null) return false;
        if(proto==right.prototype) return true;
        proto = Object.getPrototype(proto);
    }
}

function instance_of(L:Object,R:any){
    let protoChain = Object.getOwnProperty(L);
    const LPrototype = R.prototype;
    //最坏的情况递归查到Object.prototype === null
    while(protoChain){
        //两个对象指向同一个内存地址,则为同一个对象
        if(protoChain === LPrototype){
            return true;
        }
        protoChain = Object.getPrototypeOf(protoChain);
    }
    //找到终点还没有找到,则无
    return false;
}

实现单例模式

核心要点:使用闭包和proxy拦截

function proxy(fun){
    let instance;
    let handler = {
        constructor(target,args){
            if(!instance){
                instance = Reflect.constructor(fun,args);
            }
            return instance;
        }
    }
    return new Proxy(func,handler);
}

实现防抖效果

核心要点:如果在定时器的时间范围内再次触发,则重新计时

const debounce = (fn,delay)=>{
    let timer = null;
    return (...args) =>{
        clearTimeout(timer);
        timer = setTimeout(()=>{
            fn.apply(this,args);
        },delay);
    };
};

实现节流功能

核心要点:如果在定时器时间的范围内再次触发,则不予理睬,等当前定时器完成,才能启动下一个定时器

const throttle = (fn,delay=500)=>{
    let flag = true;
    return (...args)=>{
        if(!flag) return;
        flag = false;
        setTimeout(()=>{
            fn.apply(this,args);
            flag =true;
        },delay);
    };
};

JS数组扁平化(flat)方法总结

需求:多维数组=>一维数组

let ary = [1, [2, [3, [4, 5]]], 6];
let str = JSON.stringify(ary);

//第0中处理:直接的调用
arr_flat = arr.flat(Infinity);

//第一种处理
ary = str.replace(/(\[|\])/g, '').split(',');
// (/(\[|\])/g)

//第三种处理:递归处理
let result = [];
let fn = function(ary){
    for(let i=0;i<ary.length;i++){
        let item = ary[i];
        if(Array.isArray(ary[i])){
            fn(item);
        }else{
            result.push(item);
        }
    }
}

//第四种处理:用 reduce 实现数组的 flat 方法
function flatten(ary){
    return ary.reduce((pre,cur)=>
    {
        return pre.contact(Array.isArray(cur)?flatten(cur):cur);
    },[]);
}

let ary = [1, 2, [3, 4], [5, [6, 7]]]
console.log(flatten(ary))

//第五种处理:扩展运算符
while(ary.some(Array.isArray)){
    ary = [].contact(...ary);
}

// 多维数组扁平化
var arr = [1,2,[3,4,5,[6,7,8,[9,10]]]];

function streamroller(arr){
    var res = [];
    for(var i=0;i<arr.length;i++){
        if(Array.isArray(arr[i])){
            res.push.apply(res,steamroller(arr[i]));
        }else{
            res.push(arr[i]);
        }
    }
    return res;
}

console.log(steamroller(arr));

//apply+some
// 利用arr.some判断当数组中还有数组的话,递归调用steamroller2扁平函数(利用apply扁平), 用concat连接,最终返回arr;
function steamroller2(arr){
    while(arr.some(item=>Array.isArray(item))){
        arr = [].concat.apply([],arr);
    }
    return arr;
}
console.log(steamroller2(arr));

//reduce方法
// 当数组中还有数组的话,递归调用steamroller3扁平函数(利用reduce扁平), 用concat连接,最终返回arr.reduce的返回值;
function steamroller3(arr){
    return arr.reduce((pre,next)=>{
        return pre.concat(Array.isArray(next)?steamroller3(next):next);
    },[])
}

//es6
// 利用arr.some判断当数组中还有数组的话,递归调用flatten扁平函数(利用es6展开运算符扁平), 用concat连接,最终返回arr;
function steamroller4(arr){
    while(arr.some(item=>Array.isArray(item))){
        arr=[].concat(...arr);
    }
    return arr;
}
console.log(steamroller4(arr))

数组去重排序

方法一:
双层循环,外层循环元素,内层循环时比较
如果有相同的值则跳过,不相同则push进数组

Array.prototype.distinct = function(){
    var arr = this;
    result = [],i,j,len=arr.length;
    for(i=0;i<len;i++){
        for(j=i+1;j<len;j++){
            if(arr[i]==arr[j]){
                j=++i;
            }
        }
    result.push(arr[i]);
    }
    return result;
}

var array = [1,2,3,4,5,5,4,1,5,2];
console.log(array.distinct());

ES6

[...new Set(arr)].sort()

hash数组去重

    var arr=[1,8,5,6,4,2,3,8,6,7,5,3];
    var n={},r=[];  //n为hash表,r为临时数组
        for(var i=0;i<arr.length;i++){
          if(!n[arr[i]]){ //如果hash表中没有当前项
             n[arr[i]]=true;   //把当前项存入hash表
             r.push(arr[i]);   //把当前项
             r.sort();
             r.severse();
           }
         }
        console.log(r);

利用新数组indexof查找

       var arr=[1,8,5,6,4,2,3,8,6,7,5,3];
       var res=[];
       for(var i=0;i<arr.length;i++){
         if(res.indexOf(arr[i])==-1){
             res.push(arr[i]);
             res.sort();
         }
      }
     console.log(res);

reduce模拟map

// 别忘了map的第二个参数
Array.prototype._map = function(fn, context = window) {
  if (typeof fn !== 'function') {
    throw new TypeError(`${typeof fn} is not a function`);
  }
  return this.reduce((prev, val, index, arr) => {
    return prev.concat(fn.call(context, val, index, arr));
  }, []);
};

:::

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值