当前位置: 首页 > article >正文

js高级-理解call()的原理

js高级-理解call()的原理

 1.call()的定义:

    call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

 重点在于我们可以设置this指向另一个对象,那么这个对象中的数据和方法就可以被访问到

如果想利用a对象中的FN1方法去处理b对象中的数据,就可以使用 FN1.call(b);

var foo = {
    value: 1
};

function bar() {
    console.log(this.value);
}

bar.call(foo); // 1
// 这里如果直接调用bar(); 因为bar()调用时创建函数执行上下文,查找this指向调用它的对象window,所以this.value就是在window中查找value
// 没有定义value,undefined
// 而bar.call(foo);将bar中的this指向为foo对象,则调用时,首先在foo对象中查找是否有value属性,输出为1

2.call(obj)的作用

(1)改变了函数活动对象this的指向,指向新对象foo

(2)之后执行该函数

3.模拟实现call()功能

  首先,call要a对象使用b对象的方法,那么我们直接在a对象中添加一个新属性存储该方法,再调用a中的该方法,因为是a对象调用,所以方法this指向a对象,

得到执行结果后,再删除该属性,即可完成功能

  • 将函数设置为对象属性
  • 执行该函数属性
  • 删除该属性

1:
foo.fn = bar
2:
foo.fn()
3:
delete foo.fn

3.1 第一版的call()

实现了call()的基本功能

//因为是所有函数对象调用的call()方法,所以应该写在函数的原型对象上
根据原型链 fn._proto_ === Function.prototype ,定义call方法

Function.prototype.call = function(context){
    //1.call会接收参数context,为this重新指向的对象
    //2.如何获取调用的函数? 
    // 由bar.call(obj)可知,这是一个函数实例对象bar,调用call方法,由对象调用的函数中this将指向该对象,所以call内部的this指向bar,这就获得了调用的函数
    
    context.fn = this; // 把bar赋给obj.fn属性
    
    context.fn(); // 如果不用context调用,那么fn()中的this会直接指向window
    
    delete context.fn;
    //删除fn属性
}

但是call()本身可以接收多个参数进行处理,不止修改指向的对象,继续修改

3.2 第二版的call()

 问题1: 因为调用call()的函数本身需要接收的参数各有不同,所以实际问题是call()要接收不定个数的参数

 obj对象 + 多个参数 , 我们可以调用call()内部的arguments对象,获得所有接收的参数伪数组,去除第一个后,就是之后调给fn的参数数组

(1)从arguments中取索引为1后的所有数值,然后放到一个数组

// 以上个例子为例,此时的arguments为:
// arguments = {
//      0: foo,
//      1: 'kevin',
//      2: 18,
//      length: 3
// }
// 因为arguments是类数组对象,所以可以用for循环
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
    // arguments是类数组对象,它的length是实时计算出来的,所以遍历之前需要保存长度,要不每次循环又会计算,过滤出第一个传参
    args.push('arguments[' + i + ']');
}

// 执行后 args为 [foo, 'kevin', 18]

(2)将该数组作为形参放到fn()中

调用eval()方法,构造一个js语句执行块,将args参数变量用字符串加法实现

eval('context.fn(' + args +')') 
// 实际执行了 context.fn(args.toString())

args隐式的字符串化了

// 第二版
Function.prototype.call2 = function(context) {
    context.fn = this;
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
    }
    eval('context.fn(' + args +')');
    delete context.fn;
}

3.3 第三版

问题1 当输入为null时this指向window

  只需要做一个默认判断即可,推荐用或们的短路

  var context = context || window; //如果输入为null,空,都默认指向window 
// 这个赋值逻辑在于 有或者无(优先值) || 默认值(默认值)  可实现有取优先值,无取默认值

问题2 函数有返回值return,call()需要输出return的内容

创建一个变量接收 eval()处理后的返回值,最后再添加return返回

  var result = eval('context.fn(' + args +')');

    delete context.fn
    return result;

以上是基于es3实现的call()方法

3.4 基于es6实现的call()方法

因为es6实现了扩展运算符 ...rest  所以无需使用eval()方法执行函数,而是直接将...args添加到context.fn(...args)

复制代码

// ES6 call 实现
Function.prototype.es6Call = function (context) {
  var context = context || window;
  context.fn = this;
  var args = [];
  for (var i = 1, len = arguments.length; i < len; i++) {
    args.push('arguments[' + i + ']');
  }
  const result = context.fn(...args);
  return result;
}


http://www.kler.cn/a/350602.html

相关文章:

  • 通过图形界面展现基于本地知识库构建RAG应用
  • Ubuntu 24.04 LTS linux 文件权限
  • 【爬虫】使用 Scrapy 框架爬取豆瓣电影 Top 250 数据的完整教程
  • postcss插件-实现vw适配
  • 统信V20 1070e X86系统编译安装PostgreSQL-13.11版本以及主从构建
  • 【Go】Go Gorm 详解
  • Java基础15-Java高级
  • Leetcode—1188. 设计有限阻塞队列【中等】(多线程)
  • 从零开始:使用 Flask 或 Django 构建 RESTful API
  • 踩坑日记:线上接口超时问题排查
  • 程序员如何使用AI工具进行设计开发?
  • ES操作指南
  • 【MySQL】滑动窗口函数详解
  • PostgreSQL Windows系统初始化、登录、创建用户及数据库
  • CRMEB标准版Mysql修改sql_mode
  • java的数据类型
  • C++:类和对象1
  • uni-app的界面通讯思维导图
  • 64页精品PPT | 汽车经销商数据应用解决方案
  • 你存在,在我们的攻击画像里
  • Spring Boot:中小型医院网站的敏捷开发
  • 模拟退火算法最常见知识点详解与原理简介控制策略
  • VsCode环境配置C++环境
  • 基于SpringBoot的“社区医院管理服务系统”的设计与实现(源码+数据库+文档+PPT)
  • 高质量SCI论文撰写及投稿丨论文选题、文献调研、实验设计、数据分析、论文结构及语言规范等----AI强大功能
  • 抖音解压视频素材宝库