zoukankan      html  css  js  c++  java
  • 理解javascript中call/apply的使用和模拟实现

    call/apply 的作用
    call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数.
    注意:该方法的作用和 apply() 方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
    function sayName(age) {
      // this.name = "person"; (1)
         console.log(this.name,age);
    }
    const student = {
        name: "student"
    };
    sayName.call(student,12); //person 12 (1)
    sayName.call(student,13); //student 13
    // call的作用: call 改变了 this 的指向,指向到 student
    //(1)假如函数内部有相同属性情况,引用函数内部的属性
    总结来说,call总共做了三件事,给对象加了一个函数,给函数传参,然后执行函数。下面我们对call进行模拟
    下面我们先来模拟实现函数调用的this指向和函数的执行,怎么才能让函数的上下文指向调用对象呢,很简单我们直接把函数当作对象里面的一个方法即可,如
    const student = {
      name: "student”,  
      sayName:function(age){
        console.log(this.name,age);
      }
    };
    student.sayName(12); //person 12 (1)
    我们不能让函数一直在对象里面占用内存,所以在调用之后需要删除,这样我们就可以封装一个简化版的call
    // 第一版
    Function.prototype.callSub = function(context) {
      context.fn = this;
      context.fn();
      delete context.fn;
    };
    sayName.callSub(student); //student
    这里我们基本上完成一个可用的call,但是最重要的一步传参还没有完成。我们可以通过Arguments 对象取值,取出第二个到最后一个参数,然后放到一个数组里,传给函数调用。
    // 第二版
    Function.prototype.callSub = function(context) {
      context.fn = this;
      let args = [];
      for (var i = 1; i < arguments.length; i++) { // 需要从第二个参数开始
        args.push(arguments[i]);
      }
      var result = context.fn(...args);
      delete context.fn;
      return result;
    };
    function sayName(age, name) {
      return {
        age: age,
        name: name
      };
    }
    const student = {
       name: "小红"
    };
    console.log(sayName.callSub(student, 12, "小明")); //{age: 12, name: "小明"}
    在参数传值这里我们利用了es6的解构赋值功能实现数组到参数的转化,但是call/apply是在es5中加入的,所以我们不能用es6的语法实现,传参的工作。这里我们利用eval()函数拼接一个可执行的js字符串脚本。
    还有一点需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于non-strict mode,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
    // 最终版
    Function.prototype.callSub = function(context) {
      var context = context || window; // 当context为null 的时候指向window
      context.fn = this;
      let args = [];
      for (var i = 1; i < arguments.length; i++) { // 需要从第二个参数开始
        args.push("arguments[" + i + "]");
      }
      var result = eval("context.fn(" + args + ")");
      delete context.fn;
      return result;
    };
    function sayName(age, name) {
      return {   
        age: age,
        name: name,
        gender:this.gender
      };
    }
    const student = {
        name: "小红",
        gender:"woman"
    };
    console.log(sayName.callSub(student, 12, "小明")); //{age: 12, name: "小明", gender: "woman”}
     
    function sayHello() {
      console.log("hello");
    }
    sayHello.callSub(); // hello
     
    call和apply的区别就是传参的方式不一样
     
    apply的模拟
    // apply接受的参数是一个数组
    Function.prototype.applySub = function(context, arr) {
      var context = context || window;
      context.fn = this;
      var result;
      if (!arr) {
        result = context.fn();
      } 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;
    };
     
    function sayName(age, name) {
      return {
        age: age,
        name: name,
        gender: this.gender
      };
    }
    const student = {
      name: "小红",
      gender: "woman"
    };
    console.log(sayName.applySub(student, [12, "小明"])); //{age: 12, name: "小明",gender:"woman"}
    function sayHello() {
      console.log("hello");
    }
    sayHello.applySub(); // hello
     
  • 相关阅读:
    Word转pdf
    jquery 中json数组的操作 增删改
    Js、Jquery定时执行(一次或者重复多次,取消重复)
    sql server 2008 (不允许保存更改,您所做的更改要求删除并重新创建以下表) 的解决办法
    C#中 ArrayList 的使用
    Jquery正则表达式公式
    C#判断字符串是否存在字母及字符串中字符的替换实例
    纳闷的EF异常:在提供程序连接上启动事务时出错
    C# WinForm获取当前路径汇总
    Entity Framwork 6 编译出错的问题(VS2012)
  • 原文地址:https://www.cnblogs.com/chrissong/p/10493577.html
Copyright © 2011-2022 走看看