Js实现Call、apply、bind、Promise、Object.create、new、reduce基础指南_bind小白攻略

手动实现Call这些API的特点以及具体用法我就不介绍了,相信你如果奔着手动实现的方向来看,那么你一定对它很了解核心: this永远指向最后调用它的对象代码:Function.prototype.myCall = function(context, …args) {context[‘key’] = this;context.key(…args);de

Js实现Call、apply、bind、Promise、Object.create、new、reduce基础指南

手动实现Call

这些API的特点以及具体用法我就不介绍了,相信你如果奔着手动实现的方向来看,那么你一定对它很了解

Js实现Call、apply、bind、Promise、Object.create、new、reduce基础指南_bind小白攻略

核心: this永远指向最后调用它的对象

代码:

Function.prototype.myCall = function(context, ...args) {
    context['key'] = this;
    context.key(...args);
    delete context.key;
}

写到这里,call基本的特点已经实现,其实面试中一般写到这里也就通过了,但是实际上却还差很远。之前我的那篇文章也是如此,只是实现了基本的功能。因为完完全全的实现确实会花费很多时间.
还是简单说一下吧,我们知道call中是可以传入一些基本类型的,并且我们现在的这种实现方式增加了一个显示的key属性,实际上你在调用真正的call的时候去打印this是无法发现这些额外属性的,那么我们就仿照这些特性再去优化,接近一下

Function.prototype.myCall = function(context, ...args) {
    let newContext = context;
    if ([null, undefined].includes(context) ) {
        newContext = window || {};
    }
    switch (typeof context) {
        case 'number': {
            newContext = new Number(context);
            break;
        }
        case 'boolean': {
            newContext = new Boolean(context);
            break;
        }
        case 'string': {
            newContext = new String(context);
            break;
        }
    }

    Object.defineProperty(newContext, 'key', {
        value: this,
        configurable: true,
        enumerable: false,
    });
    newContext.key(...args);
    delete newContext.key;
}

这里就差不多了,后面的我就直接上代码了

实现apply

Function.prototype.myApply = function(context, obj) {
    if (typeof obj !== 'object') {
        throw new TypeError('CreateListFromArrayLike called on non-object')
    }
    let newContext = context;
    if ([null, undefined].includes(context)) {
        newContext = window;
    }
    switch (typeof context) {
        case 'number': {
            newContext = new Number(context);
            break;
        }
        case 'boolean': {
            newContext = new Boolean(context);
            break;
        }
        case 'string': {
            newContext = new String(context);
            break;
        }
    }
    Object.defineProperty(newContext, 'key', {
        value: this,
        configurable: true,
        enumerable: false,
    });
    newContext.key(...obj);
    delete newContext.key;
};

实现bind

Function.prototype.myBind = function(context, ...args) {
    let _this = this;
    let newFun = (...args2) => {
        _this.call(context, ...args, ...args2)
    };
    newFun.prototype = Object.create(_this.prototype)
    return newFun
};

实现Promise

function Promi(executor) {
    let _this = this;
    _this.$$status = 'pending';
    _this.failCallBack = undefined;
    _this.successCallback = undefined;
    _this.error = undefined;
    setTimeout(_ => {
        try {
            executor(_this.onResolve.bind(_this), _this.onReject.bind(_this))
        } catch (e) {
            _this.error = e;
            if (_this.callBackDefer && _this.callBackDefer.fail) {
                _this.callBackDefer.fail(e)
            } else if (_this._catch) {
                _this._catch(e)
            } else {
                throw new Error('un catch')
            }
        }
    })
}

Promi.prototype = {
    constructor: Promi,
    onResolve: function(params) {
        if (this.$$status === 'pending') {
            this.$$status = 'success';
            this.resolve(params)
        }
    },
    resolve: function(params) {
        let _this = this;
        let successCallback = _this.successCallback;
        if (successCallback) {
            _this.defer(successCallback.bind(_this, params));
        }
    },
    defer: function(callBack) {
        let _this = this;
        let result;
        let defer = _this.callBackDefer.success;
        if (_this.$$status === 'fail' && !_this.catchErrorFunc) {
            defer = _this.callBackDefer.fail;
        }
        try {
            result = callBack();
        } catch (e) {
            result = e;
            defer = _this.callBackDefer.fail;
        }
        if (result && result instanceof Promi) {
            result.then(_this.callBackDefer.success, _this.callBackDefer.fail);
            return '';
        }
        defer(result)
    },
    onReject: function(error) {
        if (this.$$status === 'pending') {
            this.$$status = 'fail';
            this.reject(error)
        }
    },
    reject: function(error) {
        let _this = this;
        _this.error = error;
        let failCallBack = _this.failCallBack;
        let _catch = _this._catch;
        if (failCallBack) {
            _this.defer(failCallBack.bind(_this, error));
        } else if (_catch) {
            _catch(error)
        } else {
            setTimeout(_ => { throw new Error('un catch promise') }, 0)
        }
    },
    then: function(success = () => {}, fail) {
        let _this = this;
        let resetFail = e => e;
        if (fail) {
            resetFail = fail;
            _this.catchErrorFunc = true;
        }
        let newPromise = new Promi(_ => {});
        _this.callBackDefer = {
            success: newPromise.onResolve.bind(newPromise),
            fail: newPromise.onReject.bind(newPromise)
        };
        _this.successCallback = success;
        _this.failCallBack = resetFail;
        return newPromise
    },
    catch: function(catchCallBack = () => {}) {
        this._catch = catchCallBack
    }
};   


// 测试代码

task()
    .then(res => {
        console.log('1:' + res)
        return '第一个then'
    })
    .then(res => {
        return new Promi(res => {
            setTimeout(_ => res('第二个then'), 3000)
        })
    }).then(res => {
        console.log(res)
   })
    .then(res => {
        return new Promi((suc, fail) => {
            setTimeout(_ => {
                fail('then失败')
           }, 400)
        })
    })
    .then(res => {
        console.log(iko)
   })
    .then(_ => {}, () => {
       return new Promi(function(res, rej) {
           setTimeout(_ => rej('promise reject'), 3000)
       })
   })
    .then()
    .then()
    .then(_ => {},
        rej => {
            console.log(rej);
            return rej + '处理完成'
        })
    .then(res => {
        console.log(res);
        // 故意出错
        console.log(ppppppp)
    })
    .then(res => {}, rej => {
        console.log(rej);
        // 再次抛错
        console.log(oooooo)
    }).catch(e => {
        console.log(e)
   })

实现Object.create

Object.create = Object.create || function(obj){

    var F = function(){};

    F.prototype = obj;

    return new F();

}  

实现new

function myNew(fun) {
    if (typeof fun !== 'function') throw new TypeError('fun is not a constructor')
    return function() {
        let obj = { '__proto__': fun.prototype }
        fun.call(obj, ...arguments)
        return obj
    }
}

实现reduce

Array.prototype.myReduce = function(fn, orginal = '__orginal') {
    let copy = [...this]
    if (orginal !== '__orginal') {
        copy.unshift(orginal)
    }
    while (copy.length > 1) {
        let prev = copy.shift()
        let next = copy.shift()
        copy.unshift(fn(prev, next))
    }
    return copy[0]
}
海计划公众号
(0)
上一篇 2020/03/24 05:42
下一篇 2020/03/24 05:42

您可能感兴趣的内容

  • B3log黑客派菜鸟教程_程序员和设计师的聚集地

    B3log黑客派菜鸟教程 官方网址:https://hacpai.com/ 简介描述:程序员和设计师的聚集地 B3log 开源社区线上论坛。 黑客派是程序员和设计师的聚集地,汇聚了…

    2020/03/06
  • Node.JS应用最佳实践:日志小白攻略_日志使用帮助

    日志记录是每个开发人员从第一天编写代码时就要做的事情,但很少有人知道它可以产生的价值和最佳实践。在本文中,我们将讨论以下主题:什么是日志,为什么很重要性?记录日志的最佳做法日志的重要部分正确使用日志级别为什么选择 Winston?什么是日志,为什么很重要?日志是反映程序各个方面的事件,如果能够正确编写,那么它就是最简单的故障排除和诊断程序的模式。当你启动 N

    2020/03/26
  • 21世纪教育指南教程_试题,课件,教案,学案、资源

    21世纪教育指南教程 官方网址:https://www.21cnjy.com/ 简介描述:试题,课件,教案,学案、资源 国内排名前列的中小学教育资源网,提供全国中小学各版本教材的课…

    2020/03/11
  • Awwwards小白教程_权威的网站设计评选平台,了解最新网站设计趋势。

    Awwwards使用帮助 官方网址:https://www.awwwards.com/ 简介描述:权威的网站设计评选平台,了解最新网站设计趋势。 Awwwards are the …

    2020/03/06
  • JS 进阶需要掌握的13个概念小白指南_概念小白知识

    1.变量赋值 (值 vs 引用)理解 JS 如何给变量赋值可以帮助我们减少一些不必要的 bug。相反,如果,不理解这一点,可能很容易地编写被无意中更改值的代码。JS 总是按照值来给变量赋值。 这一部分非常重要:当指定的值是 JavaScript 的五种基本类型之一(即 Boolean,null,undefined,String 和 Number)时,分配是实

    2020/03/23
  • 你所要知道的所有关于Angular的变化检测机制入门教程_Angular入门基础

    原文地址:Everything you need to know about change detection in Angular如果想像我一样全面的了解Angular的脏值检测机制,除了浏览源代码之外别无他法,网上可没有太多可用信息。大部分文章都提到,Angular中每个组件都自带一个脏值检测器,但是它们都仅仅停留在脏值检测的策略和案例的使用,并没有做太

    2020/04/05
  • word-wrap属性怎么设置文字自动换行?小白知识_属性入门指南

    可以在需要换行的元素内,将word-wrap属性的值设置为break-word来设置文字自动换行。。下面本篇文章就来给大家介绍一下word-wrap属性,希望对大家有所帮助。word-wrap属性设置或检索当内容超出其容器边界时是否中断单词,可用于将长单词或 URL 地址在容器的边界处进行自动换行。语法:word-wrap: normal|break-wor

    2020/03/20
  • 当你在工作中失去动力时该怎么办?菜鸟教程网_工作基础知识入门

    当你在工作中失去动力时该怎么办?菜鸟教程网 在思考工作的时候,我们大多数人首先会想到,它伴随着危险,我们希望自己可以放弃它。但是,你还记得你第一次开始工作时的美好时光、那种感觉和紧…

    2020/03/19
  • 如何快速的创建一个包含100个元素的数组基础教程_Array基础知识

    ​本文为笔者原创,之后可能随时修改,若需转载请保留此头部,以便读者追本朔源。本文地址 : http://blog.zhukejin.com/archives/328如题, 最初见到的问题是如何创建100个为元素为0 的数组,研究了一系列的方法,包含Es6 新的API ,不得不说, ES6 好强大!如果问一个新手,那么得到的回答极有可能就是 循环,当然循环也无

    2020/04/05
  • 页面加载时,vue生命周期的触发顺序小白帮助_加载零基础入门

    页面加载时,vue生命周期的触发顺序小白帮助 页面加载的时候,vue生命周期的触发顺序是怎样的呢? 那么进入某个路由对应的组件的时候,我们会触发哪些类型的周期呢? 根实例的加载相关…

    2020/03/19
  • 深入理解letter-spacing,word-spacing的对比区别基础入门_css入门教程

    letter-spacing lletter-spacing 属性增加或减少字符间的空白(字符间距)。该属性定义了在文本字符框之间插入多少空间。由于字符字形通常比其字符框要窄,指定长度值时,会调整字母之间通常的间隔。因此,normal 就相当于值为 0。

    thisisatest

    //

    2020/04/06
  • css3的transform:tanslateZ没有效果基础指南_效果使用帮助

    关于css动画tansform:translateZ(100PX)没有效果的记录之前学习cs3动画就学的迷迷糊糊的,这次项目中刚好用到了cs3动画,遇到了一个问题,记录下来,方便后期查阅在使用 tansform属性时,值设为tansform:translateX或tansform:translateY都是可以的,但是设置成tansform:translate

    2020/03/30
  • JavaScript和TypeScript中的符号零基础入门_符号使用帮助

    symbol 是 JavaScript 和 TypeScript 中的原始数据类型,可用于对象属性。与 number 和 string 相比,symbol 具有一些独特的功能,使它脱颖而出。JavaScript 中的符号可以用 Symbol() 工厂函数创建符号:const TITLE = Symbol(‘title’)Symbol 没有构造函数。该参数是可

    2020/03/24
  • Vue 技能进阶:使用设计模式写出优雅的前端代码基础知识教程_插槽小白指南

    本文针对 Vue 中如何控制组件子树之外的东西,探讨了四种解决方案,并展示了每种解决方案的优缺点。希望读者能从中受到启发。问你个问题,以前你可能从来没想过:有没有办法从子组件填充父组件插槽?最近一位同事问我这个问题,答案很简单:可以。但我找出的解决方案可能与你想到的有很大区别。我一开始想到的法子非常糟糕,然后试了好几次才找出来这个问题的最佳方案,起码我觉得够

    2020/03/26
  • JavaScript 类完整指南攻略教程_指南入门教程

    JavaScript 使用原型继承:每个对象都从其原型对象继承属性和方法。在 JavaScript 中不存在 Java 或 Swift 等语言中所使用的作为创建对象 蓝图的传统类,原型继承仅处理对象。原型继承可以模仿经典类的继承。为了将传统类引入 JavaScript,ES2015 标准引入了 class 语法:基于原型继承上的语法糖。本文使你熟悉 Java

    2020/03/22
  • 通过fetch发送 post 请求下载文件基础知识入门_fetch基础入门

    背景最近遇到一个下载的需求,由于 url 参数太长(常用的下载方法 a 标签或者 location.href 的方法都是 get 请求,get 请求参数长度有限制),无法下载,考虑了好几种方案,最终还是觉得通过 ajax 的 POST 方法进行下载,比较容易实现,下面记录实现过程以及遇到的问题。但是由于 AJAX 并不会唤起浏览器的下载窗口, AJAX 设计

    2020/03/23