call方法

基础版, 只能修改指向,不能传参

Function.prototype.myCall = function(context) {
// 获取调用者,这里为bar
context.fn = this;
// 运行函数
context.fn();
// 删除缓存
delete context.fn;
} let foo = {
value: 1
} function bar() {
console.log(this.value);
} bar.myCall(foo);
// 1

eval版本

Function.prototype.myCall = function(context) {
// 获取调用者,这里为sayHi
// 将无调用者的情况转换为有调用者的情况
// 有调用者那么函数内部的this就指向调用者
context.fn = this;
var len = arguments.length;
// 保存传递的参数
var args = Array(len - 1);
for (var i = 1; i < len; i++) {
// 这里只是把获取参数的字符串拼接了,供eval调用
args[i - 1] = 'arguments[' + i + ']';
}
// 不直接调用而用eval是因为参数个数不一定
// 在这里相当于是在执行context.fn(arguments[1],arguments[2]);
eval('context.fn('+ args +')');
delete context.fn;
} let person = {
name: 'Wango'
} function sayHi(age, sex) {
console.log(this.name, age, sex)
} sayHi.myCall(person, 24, 'male');
// Wango 24 male

ES6 版本

Function.prototype.myCall = function(context) {
context.fn = this;
// 将arguments转换为数组并调用slice方法去除第一个参数
// 再使用...将数组打散
context.fn(...Array.from(arguments).slice(1));
delete context.fn;
} let person = {
name: 'Wango'
} function sayHi(age, sex) {
console.log(this.name, age, sex);
} sayHi.myCall(person, 24, 'male');
// Wango 24 male

ES6 升级版

Function.prototype.myCall = function(context, ...args) {
context.fn = this;
// 相比使用arguments,这里只是用了ES6的剩余参数
context.fn(...args);
delete context.fn;
} let person = {
name: 'Wango'
} function sayHi(age, sex) {
console.log(this.name, age, sex);
} sayHi.myCall(person, 24, 'male');
// Wango 24 male

最终版

之前的版本如果传入的对象原本的fn属性或方法会被覆盖,然后被删除;而且传入第一个参数如果不是对象,会报错,所以还需要一些容错处理

// 保存一个全局变量作为默认值
const root = this; Function.prototype.myCall = function(context, ...args) {
if (typeof context === 'object') {
// 如果参数是null,使用全局变量
context = context || root;
} else {
// 参数不是对象的创建一个空对象
context = Object.create(null);
}
// 使用Symbol创建唯一值作为函数名
let fn = Symbol();
context[fn] = this;
context[fn](...args);
delete context[fn];
} let person = {
name: 'Wango',
fn: function() {
console.log(this.name);
}
} function sayHi(age, sex) {
console.log(this.name, age, sex);
} sayHi.myCall(person, 24, 'male');
// Wango 24 male
sayHi.myCall(null, 24, 'male');
// undefined 24 male
sayHi.myCall(123, 24, 'male');
// undefined 24 male
// 原函数不受影响
person.fn();
// Wango

call的实现最核心的部分就是将没有调用者的情况转换为有调用者的情况,函数内部的this自然就指向调用者

apply方法

apply的实现思路和call方法是一样的,只是只接收两个参数,第二个参数为类数组

const root = this;

Function.prototype.myApply = function(context, arg) {
if (typeof context === 'object') {
context = context || root;
} else {
context = Object.create(null);
} const fn = Symbol();
context[fn] = this;
context[fn](...arg);
delete context[fn];
} let person = {
name: 'Wango',
fn: function() {
console.log(this.name);
}
} function sayHi(age, sex) {
console.log(this.name, age, sex);
} sayHi.myApply(person, [24, 'male']);
// Wango 24 male

bind方法

// bind 改变this指向但是不立即执行函数,而是返回一个绑定了this的函数

// bind方法在绑定this指向的同时也可以传递参数
Function.prototype.myBind = function(context, ...innerArgs) {
// 不需要对参数类型进行判断,后边调用call方法时会进行处理
const fn = this;
// 不执行函数,而是返回一个函数等待调用
return function(...finalArgs) {
// 通过已经实现的call方法实现对this指向的改变,并传入参数执行
return fn.call(context, ...innerArgs, ...finalArgs);
}
} const person = {
name: 'Wango'
} function sayHi(age, sex) {
console.log(this.name, age, sex);
} const personSayHi_1 = sayHi.myBind(person, 24, 'male');
personSayHi_1();
// Wango 24 male
const personSayHi_2 = sayHi.myBind(person);
personSayHi_2(24, 'male');
// Wango 24 male

bind方法的核心在于在返回的函数中调用call方法

参考网址:call、apply和bind的实现

call、apply和bind的实现的更多相关文章

  1. JS核心系列:浅谈 call apply 与 bind

    在JavaScript 中,call.apply 和 bind 是 Function 对象自带的三个方法,这三个方法的主要作用是改变函数中的 this 指向,从而可以达到`接花移木`的效果.本文将对这 ...

  2. Javascript中call,apply,bind方法的详解与总结

    在 javascript之 this 关键字详解 文章中,谈及了如下内容,做一个简单的回顾: 1.this对象的涵义就是指向当前对象中的属性和方法. 2.this指向的可变性.当在全局作用域时,thi ...

  3. JS中call、apply、bind使用指南,带部分原理。

    为什么需要这些?主要是因为this,来看看this干的好事. box.onclick = function(){ function fn(){ alert(this); } fn();}; 我们原本以 ...

  4. Javascript中call、apply、bind函数

    javascript在函数创建的时候除了自己定义的参数外还会自动新增this和arguments两个参数 javascript中函数也是对象,call.apply.bind函数就是函数中的三个函数,这 ...

  5. 图解call、apply、bind的异同及各种实战应用演示

    一.图解call.apply.bind的异同 JavaScript中函数可以通过3种方法改变自己的this指向,它们是call.apply.bind.它们3个非常相似,但是也有区别.下面表格可以很直观 ...

  6. js里function的apply vs. bind vs. call

    js里除了直接调用obj.func()之外,还提供了另外3种调用方式:apply.bind.call,都在function的原型里.这3种方法的异同在stackoverflow的这个答案里说的最清楚, ...

  7. JS之apply,call,bind区别

    为了加深对基础知识的理解,今天再复习下js中的apply,call,bind的区别和用法.整理笔记的过程也是一个再次学习的过程. apply和call js中的调用apply和call方法可以改变某个 ...

  8. JavaScript中call、apply、bind、slice的使用

    1.参考资料 http://www.cnblogs.com/coco1s/p/4833199.html   2.归结如下 apply . call .bind 三者都是用来改变函数的this对象的指向 ...

  9. JS中的控制函数调用:call(),apply()和bind()

    所有的函数都具有call(),apply()和bind()方法.它们可以在执行方法的时候用一个值指向this,并改变面向对象的作用域. apply方法: 以下的两种表达式是等价的: func(arg1 ...

  10. JS中的call、apply、bind方法

    JS中的call.apply.bind方法 一.call()和apply()方法 1.方法定义 call方法: 语法:call([thisObj[,arg1[, arg2[,   [,.argN]]] ...

随机推荐

  1. Spark练习之action操作开发

    Spark练习之action操作开发 一.reduce 1.1 Java 1.2 Scala 二.collect 2.1 Java 2.2 Scala 三.count 3.1 Java 3.2 Sca ...

  2. Java排序算法(三)直接插入排序

    一.测试类SortTest import java.util.Arrays; public class SortTest { private static final int L = 20; publ ...

  3. 正点原子DS100拆解全过程-硬件工程师必备

    前言: 之前一篇只针对正点原子DS100手持示波器的使用介绍文章.可作为一个电子工程师,光使用不是我们的风格哈,我们还要拆开看看电路. 开拆 外壳 首先,看下图,DS100不是使用螺母进行固定的,而是 ...

  4. Python爬虫入门教程:豆瓣Top电影爬取

        基本开发环境 Python 3.6 Pycharm 相关模块的使用 requests parsel csv 安装Python并添加到环境变量,pip安装需要的相关模块即可. 爬虫基本思路 一. ...

  5. 动态规划TG.lv(1) (洛谷提高历练地)

    动态规划TG.lv(1) P1005 矩阵取数游戏 分析:每行不超过80个数字,直接区间DP即可,\(dp[i][j]\)表示区间\([i,j]\)之间取数可以得到的答案,每次向右或者向左扩展即可.但 ...

  6. BZOJ1150 [CTSC2007]数据备份Backup 链表+小根堆

    BZOJ1150 [CTSC2007]数据备份Backup 题意: 给定一个长度为\(n\)的数组,要求选\(k\)个数且两两不相邻,问最小值是多少 题解: 做一个小根堆,把所有值放进去,当选择一个值 ...

  7. Educational Codeforces Round 94 (Rated for Div. 2) String Similarity、RPG Protagonist、Binary String Reconstruction、Zigzags 思维

    题目链接:String Similarity 题意: 首先题目定义了两个串的相似(串的构成是0.1),如果两个串存在对于一个下标k,它们的值一样,那么这两个串就相似 然后题目给你一个长度为2n-1的串 ...

  8. Codeforces Round #529 (Div. 3) F. Make It Connected (贪心,最小生成树)

    题意:给你\(n\)个点,每个点都有权值,现在要在这\(n\)个点中连一颗最小树,每两个点连一条边的边权为两个点的点权,现在还另外给了你几条边和边权,求最小权重. 题解:对于刚开始所给的\(n\)个点 ...

  9. tkinter 实现爬虫的UI界面

    使用python的内置模块tkinter编写了爬取51Ape网站(无损音乐的百度云链接)的UI界面 tkinter入门简单, 但同时在编写的过程中因为文档的缺少很不方便. 下面是UI界面模块的编写,由 ...

  10. ssh配置方面小实验②

    4.禁止使用密码登录当我们学会了使用密钥对进行验证后,建议生产环境下将账户密码登录功能关掉配置文件:/etc/ssh/sshd_config选项: PasswordAuthentication no ...