JavaScript实现常用API

一、new运算符

语法
new constructor[([arguments])]

constructor
一个指定对象实例的类型的类或函数。
arguments
一个用于被 constructor 调用的参数列表。

描述

当代码 new Foo(...) 执行时,会发生以下事情:

  1. 一个继承自 Foo.prototype 的新对象被创建。
  2. 使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo*不带任何参数调用的情况。
  3. 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)。

根据上面的条件,我们就可以写代码了

 function myNew(constructor, ...args) {
        //以构造器原型创建对象
        let instance = Object.create(constructor.prototype);
        //执行构造器,并把构造器内部的this指向返回的对象
        let res = constructor.apply(instance, args);
        //如果构造器有返回对象,则使用返回的对象,没有则使用以构造器原型创建对象
        return typeof res === "object" ? res : instance;
      }

测试代码

let mFunc = myNew(Foo, "张三");
      mFunc.work();
      console.log(mFunc.name);

二、call/apply方法

call语法
fun.call(thisArg, arg1, arg2, ...)

thisArg
在 fun 函数运行时指定的 this 值。
if(thisArg == undefined|null) this = window,
if(thisArg == number|boolean|string) this == new Number()|new Boolean()| new String()
arg1, arg2, ...
指定的参数列表。

返回值

使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined

apply语法
func.apply(thisArg, [argsArray])

thisArg
可选的。在 func 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,原始值会被包装。
argsArray
可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 nullundefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。
其实call和apply的区别就是第二个参数,apply第二个参数是一个数组或者类数组。其他的和call一样,性能也一样。所以,自定义功能时,唯一的区别就是传递的参数。
根据上面的条件,我们就可以写代码了

      Function.prototype.MyCall = function(context, ...args) {
        //获取当前this(调用MyCall的函数),自定义的call方法在函数的原型对象上,所以typeof this =='function'
        let mFunc = this;
        //创建变量,作为指定this对象(call的第一个参数context)的属性名
        let fnKey = "fnKey";
        //把当前this(调用MyCall的函数)作为属性值保存到指定this对象(call的第一个参数context)上,属性名为上面定义的fnKey
        context[fnKey] = mFunc;
        //调用指定this对象(call的第一个参数context)上的当前this(调用MyCall的函数)
        let result = context[fnKey](...args);
        //执行结束,删除该属性
        delete context[fnKey];
        return result;
      };
     Function.prototype.MyApply = function(context, args) {
        //获取当前this(调用MyCall的函数),自定义的call方法在函数的原型对象上,所以typeof this =='function'
        let mFunc = this;
        //创建变量,作为指定this对象(call的第一个参数context)的属性名
        let fnKey = "fnKey";
        //把当前this(调用MyCall的函数)作为属性值保存到指定this对象(call的第一个参数context)上,属性名为上面定义的fnKey
        context[fnKey] = mFunc;
        //调用指定this对象(call的第一个参数context)上的当前this(调用MyCall的函数)
        let result = context[fnKey](...args);
        //执行结束,删除该属性
        delete context[fnKey];
        return result;
      };

测试代码

 let obj = {
        name: "张三",
        id: 1002
      };
      function TestFunc(name, id) {
        this.name = name;
        this.id = id;
        return {
          name: this.name,
          id: this.id
        };
      }
      let resCall = TestFunc.MyCall(obj, "abc", 11111);
      console.log(obj);
      console.log(resCall);
      let resApply = TestFunc.MyApply(obj, ["efg", 2222]);
      console.log(obj);
      console.log(resApply);

三、Object.create()方法

语法
Object.create(proto[, propertiesObject])

proto
新创建对象的原型对象
propertiesObject
可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。
返回新的对象,带着指定的原型对象和属性。
这里偷个懒,给新对象添加属性就采用Object.defineProperties(),不熟悉Object.defineProperties()可以点击上面蓝色部分查看,里面也有兼容版本polyfill的实现。
根据上面的条件,我们就可以写代码了

 Object.prototype.myCreate = function(proto, propertiesObject) {
        //判断传入的原型对象
        if (typeof proto !== "object" && typeof proto !== "function") {
          throw new TypeError("Object prototype may only be an Object");
        } else if (proto === null) {
          throw new Error(" Object.create不支持第一个参数为null");
        }
        if (propertiesObject === null) {
          throw new Error(" Object.create第二个参数不能为null");
        }
        //创建一个构造函数
        function F() {}
        //设置构造函数的原型为新创建对象的原型对象
        F.prototype = proto;
        //构造器的原型对象的constructor指向自身,防止原型链错乱
        F.prototype.constructor = F;
        //创建实例对象
        let newObj = new F();
        //加到新创建对象的可枚举属性
        if (typeof propertiesObject != "undefined") {
          //这里偷懒,这个API暂时就不自定义
          Object.defineProperties(newObj, propertiesObject);
        }
        return newObj;
      };

测试代码

     let obj = {};
      let propertiesObj = {
        property1: {
          value: true,
          writable: true
        },
        property2: {
          value: "Hello",
          writable: false
        }
      };
      console.log(Object.create(obj, propertiesObj));
      let myObj = Object.myCreate(obj, propertiesObj);
      console.log(myObj);

四、数组的map方法

语法
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
 // Return element for new_array 
}[, thisArg])

callback
生成新数组元素的函数,使用三个参数:
currentValue
callback 数组中正在处理的当前元素。
index可选
callback 数组中正在处理的当前元素的索引。
array可选
callback map 方法被调用的数组。
thisArg可选
执行 callback 函数时使用的this 值。
返回一个新数组,每个元素都是回调函数的结果。
根据上面的条件,我们就可以写代码了

       Array.prototype.MyMap = function (callBack, context) {
           // 浅拷贝要遍历的数组
           var copyArr = Array.prototype.slice.call(this);
            //有就使用,null或者undefined,设定为全局对象
            context = context || window;
            //调函数的结果
            var resultArr = [];
            for (let index = 0; index < copyArr.length; index++) {
                //是否是自己的属性
                if (!copyArr.hasOwnProperty(index)) continue;
                //把回调结果放入返回的数组中,回调函数按照上面设置为三个参数
                resultArr.push(callBack.call(context, copyArr[index], index, this));
            }
            return resultArr;
        }

测试代码

        let arr = [1, 2, { name: '123' }];
        arr.MyMap(function(item,index,context){
            console.log('item:'+item,"index:"+index);
            console.log(this);
        },{id:123});
结果
        let arr = [1, 2, { name: '123' }];
       let newArr= arr.MyMap(function(item,index,context){
            console.log('item:'+item,"index:"+index);
            console.log(this);
            return {name:item}
        });
        console.log(newArr);
image.png

另外,map方法中,对于每一项是浅拷贝。

        let arr = [1, 2, { name: '123' }];
        arr.map(function (item,index) {
            if (typeof item === 'object') {
                item.id = 4555;
            } else {
                item = index;
            }
        });
  console.log(arr);

image.png

所以,上面采用浅拷贝就行,浅拷贝和深拷贝有疑惑的,可以阅读一下这篇文章

五、数组的reduce方法

语法
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

callback
执行数组中每个值的函数,包含四个参数:
accumulator
累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
currentValue
数组中正在处理的元素。
currentIndex可选
数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则为1。
array可选
调用reduce()的数组
initialValue可选
作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。
返回函数累计处理的结果。
根据上面的条件,我们就可以写代码了

        Array.prototype.myReduce = function (callBack, initialValue) {
            // 浅拷贝要遍历的数组
            let copyArr = Array.prototype.slice.call(this);
            //提供初始值,从第一项开始,没有提供从第二项开始
            let currentIndex = initialValue ? 0 : 1;
            let result = initialValue ? initialValue : copyArr[0];
            for (let index = currentIndex; index < copyArr.length; index++) {
                //调用回调函数,按照上面的设置四个参数
                result = callBack.call(null, result, copyArr[index], index, this);
            }
            return result;
        }

下面是测试代码

      let redarr = [1, 2, 3, 4, 5];
        let result = redarr.reduce(function (accumulator, item) {
            return accumulator + item;
        }, 10);
        console.log(result);
        let mResult = redarr.myReduce(function (accumulator, item) {
            return accumulator + item;
            return
        }, 10);
        console.log(mResult);
结果

bind方法

语法
function.bind(thisArg[, arg1[, arg2[, ...]]])

thisArg
调用绑定函数时作为this参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用bindsetTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。如果bind函数的参数列表为空,执行作用域的this将被视为新函数的thisArg
arg1, arg2, ...
当目标函数被调用时,预先添加到绑定函数的参数列表中的参数。
返回一个原函数的拷贝,并拥有指定的this值和初始参数。
根据上面的条件,我们就可以写代码了

      Function.prototype.myBind = function(context, ...args) {
        //获取当前方法
        let _that = this;
        //创建返回的函数
        let mNewFunc = function(...argsNew) {
          //把参数拼接,并改变原函数的this对象,返回原函数的返回值
          return _that.apply(context || window, [...args, ...argsNew]);
        };
        //保证原函数的原型对象上的属性不丢失
        mNewFunc.prototype = Object.create(_that.prototype);
        return mNewFunc;
      };

测试代码

      var myName = "999";
      let module = {
        myName: "上海"
      };
      function bindFuc(args) {}
      bindFuc.myName = "ABCDE";
      function textFuc(args) {
        console.log("args:", arguments);
        console.log("myName:" + this.myName);
      }
      var boundTextFuc = textFuc.bind(module, "123");
      boundTextFuc("145");
      var boundTextFuc = textFuc.myBind(module, "123");
      boundGetX("145");

instanceof运算符

语法
object instanceof constructor

object
要检测的对象
constructor
某个构造函数
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
根据上面的条件,我们就可以写代码了

      Object.prototype.myInstanceof = function(constructor) {
        //获取要检测的对象
        let object = this;
        //判断constructor是否是对象
        if (constructor == null || typeof constructor !== "function") {
          return new Error("第二个参数必须为构造函数");
        }
        //获取验检测对象的原型
        let objProto = Object.getPrototypeOf(object);
        //循环在检测对象的原型链上查找是否存在constructor.prototype
        while (true) {
          //排除检测对象是Object.prototype的原型的情况
          if (objProto == null) return false;
          if (objProto == constructor.prototype) return true;
          objProto = Object.getPrototypeOf(objProto);
        }
      };

测试代码

      function TestFunc() {}
      let mTestFunc = new TestFunc();
      console.log(mTestFunc.myInstanceof(Number),mTestFunc instanceof Number);
      console.log(mTestFunc.myInstanceof(Function),mTestFunc instanceof Function);
      console.log(mTestFunc.myInstanceof(Object),mTestFunc instanceof Object);
      console.log(Function.myInstanceof(Object),Function instanceof Object);
      console.log(Object.myInstanceof(Function),Object instanceof Function);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容