js深入之call、apply和bind
一. call和apply
1. 代码完整实现
Function.prototype.mycall = function (context, ...argus) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
const fn = this
let result = null
context = context || window
context.fn = fn
result = context.fn(...argus)
delete context.fn
return result
}
Function.prototype.myapply = function (context, ...argus) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
const fn = this
let result = null
context = context || window
argus = argus && argus[0] || []
context.fn = fn
result = context.fn(...argus)
delete context.fn
return result
}
2. 先来溜溜
- 案例一
class Member {
constructor (options) {
const {name, sex, age} = options
this.name = name
this.sex = sex
this.age = age
}
introduce () {
console.log(`I'm ${this.name}, ${this.age}, ${this.sex}`)
}
}
const member1 = new Member({
name: 'gina',
sex: 'girl',
age: 23
})
const member2 = new Member({
name: 'gun',
sex: 'boy',
age: 24
})
member2.introduce.mycall(member1) // I'm gina, 23, girl
member2.introduce.myapply(member1) // I'm gina, 23, girl
- 案例二
Math.max.myapply(null, [1,2,3,4]) // 4
Math.max.mycall(null, 1,2,3,4) // 4
3. 注意要点
- 开头需要做一个类型判断:
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
- 获取原始函数: 比如执行
Math.max.mycall(null, 1,2,3,4)的时候,mycall函数内部的this指向了Math.max函数,所以我们可以通过const fn = this获取到要执行的函数,然后将该函数绑定到传入的context对象(context.fn = fn),然后再把它删除掉delete context.fn。
总体来说,call和apply的实现还是比较简单的。
二. bind
1. 完整代码实现
Function.prototype.mybind = function (context, ...argus) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
const fn = this
const fBound = function (...argus2) {
return fn.apply(this instanceof fBound ? this : context, [...argus, ...argus2])
}
fBound.prototype = Object.create(this.prototype)
return fBound
}
2. 边溜边说
- 案例一
const foo = {
v: 1
};
function bar() {
return this.v;
}
const bindFoo = bar.mybind(foo);
bindFoo() // 1
bind 函数返回的是一个可执行函数,所以return了一个函数。此刻返回的函数,按正常来说,在执行的时候,this是指向执行处的当前上下文。但该案例中, mybind 需要满足bar在执行中返回值时,this依然是指向 foo,所以我们在mybind返回的函数中需要使用fn.apply来保持上下文和执行mybind的时候一致。
- 案例二
const foo = {
v: 1
};
function bar(name, age) {
console.log(this.v);
console.log(name);
console.log(age);
}
const bindFoo = bar.bind(foo, 'daisy');
bindFoo('18');
// 1
// daisy
// 18
mybind 需要做到可以接受传参,并且将参数给到bar函数,后面再执行bindFoo再传的参数,会接在之前传参的后面。所以mybind源码中使用了[...argus, ...argus2]来进行参数整合。
- 案例三
const value = 2;
const foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
const bindFoo = bar.bind(foo, 'daisy');
const obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin
在执行const obj = new bindFoo('18')这一 new操作的时候,此刻this应该指向当前对象obj。所以mybind在fn.apply的第一个参数,做了这样的判断this instanceof fBound ? this : context。
在const obj = new bindFoo('18')内部执行到this instanceof fBound ? this : context时,此刻this指向obj,fBound其实也就是bindFoo,this instanceof fBound判断了obj是不是继承自bindFoo,也就是进行了构建函数new操作。
- 案例4
function bar() {}
bar.prototype.value = 2
const bindFoo = bar.mybind(null);
bindFoo.prototype.value = 1;
console.log(bar.prototype.value) // 2
mybind 执行后返回的函数fBound修改prototype的时候,不应该影响到fn.prototype,两者应该是独立的。所以源码使用了fBound.prototype = Object.create(this.prototype), 而不是fBound.prototype = this.prototype。
总得来说,bind的实现考虑的点还是比较多的。
参考:
https://github.com/mqyqingfeng/Blog/issues/12
js深入之call、apply和bind的更多相关文章
- js里function的apply vs. bind vs. call
js里除了直接调用obj.func()之外,还提供了另外3种调用方式:apply.bind.call,都在function的原型里.这3种方法的异同在stackoverflow的这个答案里说的最清楚, ...
- js中call、apply、bind那些事
前言 回想起之前的一些面试,几乎每次都会问到一个js中关于call.apply.bind的问题,比如- 怎么利用call.apply来求一个数组中最大或者最小值 如何利用call.apply来做继承 ...
- js中call、apply、bind那些事2
前言 回想起之前的一些面试,几乎每次都会问到一个js中关于call.apply.bind的问题,比如… 怎么利用call.apply来求一个数组中最大或者最小值 如何利用call.apply来做继承 ...
- js中call、apply和bind到底有什么区别?
介绍 在js中,每个函数的原型都指向Function.prototype对象(js基于原型链的继承).因此,每个函数都会有apply,call,和bind方法,这些方法继承于Function. 它们的 ...
- JS中call、apply、bind使用指南,带部分原理。
为什么需要这些?主要是因为this,来看看this干的好事. box.onclick = function(){ function fn(){ alert(this); } fn();}; 我们原本以 ...
- js中call、apply、bind到底有什么区别?bind返回的方法还能修改this指向吗?
壹 ❀ 引 同事最近在看angularjs源码,被源码中各种bind,apply弄的晕头转向:于是他问我,你知道apply,call与bind的区别吗?我说apply与call是函数应用,指定thi ...
- JS中call()和apply()以及bind()的区别
一.方法定义: apply:调用一个对象的一个方法,用另一个对象替换当前对象.例如:B.apply(A, arguments);即A对象应用B对象的方法. call:调用一个对象的一个方法,用另一个对 ...
- JS中call()、apply()、bind()的用法
其实是一个很简单的东西,认真看十分钟就从一脸懵B 到完全 理解! 先看明白下面: 例1 obj.objAge; //17 obj.myFun() //小张年龄undefined 例2 shows( ...
- js中call、apply和bind的区别
在JS中,这三者都是用来改变函数的this对象的指向的,他们有什么样的区别呢.在说区别之前还是先总结一下三者的相似之处:1.都是用来改变函数的this对象的指向的.2.第一个参数都是this要指向的对 ...
- js中call,apply,bind方法的用法
call .apply.和bind 以上这三个方法都是js function函数当中自带的方法,用来改变当前函数this的指向. call()方法 语法格式: fun.call(thisArg[,ar ...
随机推荐
- 有什么很好的软件是用 Qt 编写的?(尘中远)
作者:尘中远链接:http://www.zhihu.com/question/19630324/answer/19365369来源:知乎 http://www.cnblogs.com/grandyan ...
- 三星860 evo 250g 开启AHCI模式读写对比
主板比较老,只支持sata2接口 换用三星860evo后跑分对比
- 各种 MacBook 和 5K iMac 屏幕分辨率对比
苹果全新 12寸超薄 MacBook 比曾经最薄的 MacBook Air 更薄,不过却配备了Retina 显示屏.12寸 Retina MacBook 上的显示屏分辨率为2304*1440,虽然不如 ...
- Qt中使用QSqlDatabase::removeDatabase()的正确方法 good
如果你用过Qt的QSqlDatabase的话,多半会对下面的警告信息感兴趣: QSqlDatabasePrivate::removeDatabase: connection 'qt_sql_defau ...
- abp(net core)+easyui+efcore仓储系统——定义仓储并实现 (四)
abp(net core)+easyui+efcore仓储系统目录 abp(net core)+easyui+efcore仓储系统——ABP总体介绍(一) abp(net core)+easyui+e ...
- 缩放手势 ScaleGestureDetector 源码解析,这一篇就够了
其实在我们日常的编程中,对于缩放手势的使用并不是很经常,这一手势主要是用在图片浏览方面,比如下方例子.但是(敲重点),作为 Android 入门的基础来说,学习 ScaleGestureDetecto ...
- 管理分布式session的四种方式。
应用服务器的高可用架构设计最为理想的是服务无状态,但实际上业务总会有状态的,以session记录用户信息的例子来讲,未登入时,服务器没有记入用户信息的session访问网站都是以游客方式访问的,账号密 ...
- Flask学习之旅--简易留言板
一.写在前面 正所谓“纸上得来终觉浅,方知此事要躬行”,在看文档和视频之余,我觉得还是要动手做点什么东西才能更好地学习吧,毕竟有些东西光看文档真的难以理解,于是就试着使用Flask框架做了一个简易留言 ...
- vue路由传参query和params的区别(详解!)
1.query使用path和name传参都可以,而params只能使用name传参. query传参: 页面: this.$router.push({ path:'/city',name:'City' ...
- Java NIO 学习笔记(四)----文件通道和网络通道
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...