• 模拟实现call、apply


    1. 知识点补充:

    首先在模拟实现前,先Mark一些我之前不知道的知识:

    a. eval(string)函数:可计算某个字符串,并执行其中的JavaScript代码

    其中,string是必需传入的待计算或待执行的语句,并且必须是原始字符串的形式!

    eval(string)相当于<script> string </script>

    b. 类数组对象(Array-like Object)

    类数组对象是一个对象,比如:arguments、DOM API返回的NodeList对象都属于类数组对象,具有指向对象元素的数组index下标和length属性,但是它们不能使用push/pop/shift/unshift等数组方法!

    但是如何能将类数组转换为真正的数组呢?有如下方法:

    1. Array.prototype.slice.call( arguments )  // 在低版本IE下不支持
    2. [].slice.call( arguments )      // 等同于1
    3. let arr = Array.from( arguments )  // ES6,可将类数组对象和可遍历对象转为真正的数组
    4. let arr = [ ...arguments ]

    以下例为例演示:

    var foo = {
        value: 1
    };
    
    function bar(name, age) {
        console.log(this.value);
       console.log(name)
       console.log(age) } bar.call(foo, 'ning', 20);

    这里可以考虑将bar这个函数作为foo的一个方法,然后在外层执行这个函数,然后再删掉该函数即可!

    2. call的模拟实现

        Function.prototype.call2 = function (context) {
            context.fn = this;  // context是foo,this是bar也就是调用call的那个函数
            context.fn();
            delete context.fn;
        }
    
        // 使用下例来验证call2是否可行
        var foo = {
            value: 1
        }
        function bar() {
            console.log(this.value);
        }
    
        bar.call2(foo);

    content.fn = this;这句首先获取到了调用call的函数,本例这里也就是bar;

    context.fn();即执行bar这个函数;

    delete删掉该函数。

    但是现在的模拟中有几个问题:

    1. 不能传入参数,因此我们将利用arguments,从Arguments对象中从第二个参数(因为第一个参数是this)开始取值,放到一个数组里,再把这个数组放到要执行的函数的参数里
    2. this参数传入null或者undefined时,我们需要将this指向window
    3. 当call2()内传的不是一个对象,而是一个基本数据类型时,如何处理?(在call实现时会自动调用Object()转换)
    4. 函数可以有返回值

    所以我们得到以下call2()代码:

        Function.prototype.call2 = function (context) {
            context = context ? Object(context) : window;  
            context.fn = this;
    
            var arr = [];
            for (var i = 1, len = arguments.length; i < len; i++) {
                arr.push('arguments[' + i + ']');
            }
    
            var result = eval('context.fn(' + arr + ')');
            delete context.fn;
            return result;
        }

    下面我们测试一下:

        var value = 'global';
        var foo = {
            value: 1
        }
        function bar(name, age) {
            console.log(this.value)
            return {
                value: this.value,
                name: name,
                age: age
            }
        }
    
        bar.call2(null)  // global
    
        console.log(bar.call2(foo, 'ning', 20))
        // 1
        // {value: 1, name: "ning", age: 20}

    说明两点:

    1. arr.push('arguments['+ i +']');这句得到的是(2) ["arguments[1]", "arguments[2]"]一个新数组,是我们想要的
    2. eval('context.fn('+ arr +')');这句中arr会自动调用arr.toString()得到一个字符串:arguments[1],arguments[2],然后进行字符串拼接

    下面给出ES6版本的:

        Function.prototype.call2 = function (context) {
            context = context ? Object(context) : window; 
            context.fn = this;
    
            let arr = [...arguments].slice(1);
            let result = context.fn(' + arr + ');
    
            delete context.fn;
            return result;
        }

    3. apply的模拟实现:

        Function.prototype.apply2 = function (context, arr) {
            context = context ? Object(context) : window;
            context.fn = this;
    
            var result = [];
            // 没有arr参数直接执行
            if (!arr) {
                result = context.fn();
                // 有arr参数则将参数拼接后执行
            } else {
                var args = [];
                for (var i = 0; i < arr.length; i++) {
                    args.push('arr[' + i + ']')
                }
                result = eval('context.fn(' + args + ')')
            }
    
            delete context.fn;
            return result;
        }

    下面给出ES6版本的:

        Function.prototype.apply2 = function (context, arr) {
            context = context ? Object(context) : window;
            context.fn = this;
    
            let result = [];
            if (!arr) {
                result = context.fn();
            } else {
                // ...arr的使用
                result = context.fn(...arr)
            }
    
            delete context.fn;
            return result;
        }
  • 相关阅读:
    进销存管理软件,企业ERP如何上线准备,一张图让你明白
    检测行业ERP信息系统(检测行业管理软件)构建,大家猜猜看花了多少银子开发!
    商会协会会员管理系统
    给广大程序员的一封信!尤其是想换行销售的程序员们
    如何利用极致业务基础平台构建一个通用企业ERP之二十多界面显示
    如何利用极致业务基础平台构建一个通用企业ERP之十九过滤器的功能介绍(2)
    快速开发平台如何让开发周期变成原来的十分之一,有图有真相!告别码农的机会!告别公司亏损时代!
    如何利用极致业务基础平台构建一个通用企业ERP之十八如何调用存储过程介绍
    创建新文件(包括上级文件夹),获取外置SD卡的根目录
    c++匿名函数精简写法
  • 原文地址:https://www.cnblogs.com/ningyn0712/p/11761349.html
Copyright © 2020-2023  润新知