call、apply和bind的实现
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的实现的更多相关文章
- JS核心系列:浅谈 call apply 与 bind
在JavaScript 中,call.apply 和 bind 是 Function 对象自带的三个方法,这三个方法的主要作用是改变函数中的 this 指向,从而可以达到`接花移木`的效果.本文将对这 ...
- Javascript中call,apply,bind方法的详解与总结
在 javascript之 this 关键字详解 文章中,谈及了如下内容,做一个简单的回顾: 1.this对象的涵义就是指向当前对象中的属性和方法. 2.this指向的可变性.当在全局作用域时,thi ...
- JS中call、apply、bind使用指南,带部分原理。
为什么需要这些?主要是因为this,来看看this干的好事. box.onclick = function(){ function fn(){ alert(this); } fn();}; 我们原本以 ...
- Javascript中call、apply、bind函数
javascript在函数创建的时候除了自己定义的参数外还会自动新增this和arguments两个参数 javascript中函数也是对象,call.apply.bind函数就是函数中的三个函数,这 ...
- 图解call、apply、bind的异同及各种实战应用演示
一.图解call.apply.bind的异同 JavaScript中函数可以通过3种方法改变自己的this指向,它们是call.apply.bind.它们3个非常相似,但是也有区别.下面表格可以很直观 ...
- js里function的apply vs. bind vs. call
js里除了直接调用obj.func()之外,还提供了另外3种调用方式:apply.bind.call,都在function的原型里.这3种方法的异同在stackoverflow的这个答案里说的最清楚, ...
- JS之apply,call,bind区别
为了加深对基础知识的理解,今天再复习下js中的apply,call,bind的区别和用法.整理笔记的过程也是一个再次学习的过程. apply和call js中的调用apply和call方法可以改变某个 ...
- JavaScript中call、apply、bind、slice的使用
1.参考资料 http://www.cnblogs.com/coco1s/p/4833199.html 2.归结如下 apply . call .bind 三者都是用来改变函数的this对象的指向 ...
- JS中的控制函数调用:call(),apply()和bind()
所有的函数都具有call(),apply()和bind()方法.它们可以在执行方法的时候用一个值指向this,并改变面向对象的作用域. apply方法: 以下的两种表达式是等价的: func(arg1 ...
- JS中的call、apply、bind方法
JS中的call.apply.bind方法 一.call()和apply()方法 1.方法定义 call方法: 语法:call([thisObj[,arg1[, arg2[, [,.argN]]] ...
随机推荐
- 数据中心网络技术新贵:VXLAN与园区网络虚拟化
摘要:为了应对传统数据中心网络对服务器虚拟化技术的限制,VXLAN技术应运而生. 1 概述 传统数据中心网络面临的问题 虚拟机规模受设备表项规格限制 在传统二层网络中,交换机通过查询MAC地址表来转发 ...
- B - Play on Words
如果这个图是欧拉路,则每个顶点的出度等于入度.即out[i] = in[i] 如果这个图是半欧拉图,则起点的出度比入度大1,终点的入度比出度大1.其余顶点的出度等于入度.如果满足上述条件,就可以将所有 ...
- POJ 2195 & HDU 1533 Going Home(最小费用最大流)
这就是一道最小费用最大流问题 最大流就体现到每一个'm'都能找到一个'H',但是要在这个基础上面加一个费用,按照题意费用就是(横坐标之差的绝对值加上纵坐标之差的绝对值) 然后最小费用最大流模板就是再用 ...
- Codeforces Round #670 (Div. 2) B. Maximum Product (暴力)
题意:有一长度为\(n\)的序列,求其中任意五个元素乘积的最大值. 题解:先排序,然后乘积能是正数就搞正数,模拟一下就好了. 代码: int t; ll n; ll a[N]; int main() ...
- Codeforces Round #667 (Div. 3) D. Decrease the Sum of Digits (贪心)
题意:给你一个正整数\(n\),每次可以对\(n\)加一,问最少操作多少次是的\(n\)的所有位数之和不大于\(s\). 题解:\(n\)的某个位置上的数进位,意味这后面的位置都可以被更新为\(0\) ...
- 80x86/Pentium微机原理及接口技术-微处理器-学习笔记
80x86/ Pentium微机原理及接口技术 1. 计算机基础... 1 1.1常用术语... 1 1.2计算机中数与编码的表示方法... 1 1.2.1进制表示及进制转换... 1 1.2 ...
- 自己动手实现springboot运行时执行java源码(运行时编译、加载、注册bean、调用)
看来断点.单步调试还不够硬核,根本没多少人看,这次再来个硬核的.依然是由于apaas平台越来越流行了,如果apaas平台选择了java语言作为平台内的业务代码,那么不仅仅面临着IDE外的断点.单步调试 ...
- 超易懂!原来SOLID原则要这么理解!
说到 SOLID 原则,相信有过几年工作经验的朋友都有个大概印象,但就是不知道它具体是什么.甚至有些工作了十几年的朋友,它们对 SOLID 原则的理解也停留在表面.今天我们就来聊聊 SOLID 原则以 ...
- Kubernets二进制安装(8)之部署四层反向代理
四层反向代理集群规划 主机名 角色 IP地址 mfyxw10.mfyxw.com 4层负载均衡(主) 192.168.80.10 mfyxw20.mfyxw.com 4层负载均衡(从) 192.168 ...
- 洛谷p1966 火柴排队 (逆序对变形,目标排序
题目描述 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度. 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为: ∑(ai-bi)^2 其中 ai 表示 ...