手写JS的new操作符的功能、手写实现JS中Object类的call函数、apply函数的功能

探究JavaScript中new对象的过程,call()、apply()方法的实现原理

    function Person(name, age) {
        this.name = name;
        this.age = age;
        console.log("Person...");
    }

    // let p = new Person("张三", 18);
    // console.log(p);

    /**
     * 【目标1:模拟js中new运算符的功能。传入一个构造函数和可选的构造参数,返回对应的构造实例】
     * js new运算符内部原理:1.创建一个空对象;2.修改构造函数内部的this指向为空对象;3.执行构造函数的代码;4.返回创建的对象
     * (当构造函数内部显式返回基本类型或null时等价于不返回,new操作默认会返回this)
     */
    function MyNew(constructor, ...args) {
        if (!constructor || !constructor.hasOwnProperty("prototype")) {
            throw new TypeError("constructor is not a Constructor");
        }
        let obj = Object.create(constructor.prototype); // 创建空对象{}并绑定对象的原型为参数对象constructor.prototype
        let res = constructor.apply(obj, args);
        if (res != null && (typeof res == "object" || typeof res == "function")) { // typeof null = 'object'
            return res;
        }

        return obj;
    }

    // console.log(MyNew("string类型", '字符串类型', 3)); // Uncaught TypeError: constructor is not a Constructor
    console.log(MyNew(Person, "李四", 21)); // Person...   Person {name: '李四', age: 21}
    console.log(MyNew(Person, "李四")); // Person...   Person {name: '李四', age: undefined}
    console.log(MyNew(function(){console.log('xxx')})); // xxx   {}

    /**********************************************************************/
    /**
     * 【目标2:模拟js中Object类的call方法】
     * call方法原理:1.修改调用者函数内部的this指向为call的第一个参数,默认为window;2.执行函数并返回结果。(参数为可变参数)
     */
    Function.prototype.myCall = function (context) {
        context = context ? Object(context) : window; // 如果调用时没有传参数,则指定执行上下文为window,否则按需转换为Object引用类型
        context._tempFunc = this; // 临时绑定调用者函数
        let args = []; // 收集调用者函数的参数,arguments为伪数组
        for (let i = 1; i < arguments.length; i++) {
            args.push(arguments[i]);
        }
        // let res = this(...args); // (错误写法)独立函数调用,内部this指向window,而非context(不合要求,因为call函数显式绑定this)
        let res = context._tempFunc(...args); // 隐式绑定调用者函数内部的this为context
        delete context._tempFunc; // 释放临时函数

        return res;
    };

    function testFunc(n1, n2) {
        console.log(this);
        return n1 + n2;
    }
    console.log(testFunc.myCall()); // window  NaN
    console.log(testFunc.myCall("hello", 10, 20));  // String {'hello'}   30
    console.log(testFunc.myCall({ a: 'value a' }, 20, 30)); // {a: 'value a'}   50

    /**********************************************************************/
    /**
     * 【目标3:模拟js中Object类的apply方法】
     * apply方法原理:1.修改调用者函数内部的this指向为apply的第一个参数,默认为window;2.执行函数并返回结果。(参数为数组)
     */
    Function.prototype.myApply = function (context, args) {
        context = context ? Object(context) : window; // 如果调用时没有传参数,则指定执行上下文为window,否则按需转换为Object引用类型
        context._tempFunc = this; // 临时绑定调用者函数
        // let res = this(args); // (错误写法)独立函数调用,内部this指向window,而非context(不合要求,因为apply函数显式绑定this)
        let res = context._tempFunc(args); // 隐式绑定调用者函数内部的this为context
        delete context._tempFunc; // 释放临时函数

        return res;
    };

    function testFunc(n1, n2) {
        console.log(this);
        return n1 + n2;
    }
    console.log(testFunc.myApply()); // window  NaN
    console.log(testFunc.myApply("hello", [10, 20]));  // String {'hello'}   30
    console.log(testFunc.myApply({ a: 'value a' }, [20, 30])); // {a: 'value a'}   50
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值