如何手写call、apply、bind?

callFunction.prototype.call(this, arg1, arg2, …..)可以改变this,并且传入参数,立刻执行,返回函数返回值手写callFunction.prototype.myCall = function(context = window, …args) { context = context || window; // 参数默认值并不会排除null,所以重新赋值 context.fn = this; // this是调用call的函数 const result = context.fn(…args); delete context.fn; // 执行后删除新增属性 return result;}applyFunction.prototype.apply(this, [arg1, arg2, …..])可以改变this,并且传入参数,与call不同的是,传入的参数是数组或类数组,立刻执行,返回函数返回值手写apply:Function.prototype.myApply = function(context = window, args = []) { context = context || window; // 参数默认值并不会排除null,所以重新赋值 context.fn = this; // this是调用call的函数 const result = context.fn(…args); delete context.fn; return result;}bindFunction.prototype.bind(this, arg1, arg2, …)可以绑定this,并且传入参数,方式与call相同,但是不会执行,返回已绑定this的新函数手写bind:Function.prototype.myBind = function(context, …args) { const _this = this; return function Bind(…newArgs) { // 考虑是否此函数被继承 if (this instanceof Bind) { return _this.myApply(this, […args, …newArgs]) } return _this.myApply(context, […args, …newArgs]) }} ...

April 11, 2019 · 1 min · jiezi

react中bind函数传参问题

以input事件为例:html代码:<Input placeholder=“电子邮箱” value={this.state.addEmail} onChange={this.addInputChange} onBlur={this.checkEmail.bind(this,‘flagAddEmail’)} />注意最后一行,bind的参数为(this,‘你要传的参数’)js代码如何接收参数:checkEmail(name,evt) { console.log(name); //flagAddEmail console.log(evt.target.value); //input中输入的值}

March 28, 2019 · 1 min · jiezi

手写call、apply、bind及相关面试题解析

它们有什么不同?怎么用?call 接收多个参数,第一个为函数上下文也就是this,后边参数为函数本身的参数。 let obj = { name: “一个” } function allName(firstName, lastName) { console.log(this) console.log(我的全名是“${firstName}${this.name}${lastName}”) } // 很明显此时allName函数是没有name属性的 allName(‘我是’, ‘前端’) //我的全名是“我是前端” this指向window allName.call(obj, ‘我是’, ‘前端’) //我的全名是“我是一个前端” this指向objapplyapply接收两个参数,第一个参数为函数上下文this,第二个参数为函数参数只不过是通过一个数组的形式传入的。allName.apply(obj, [‘我是’, ‘前端’])//我的全名是“我是一个前端” this指向objbindbind 接收多个参数,第一个是bind返回值返回值是一个函数上下文的this,不会立即执行。 let obj = { name: “一个” } function allName(firstName, lastName, flag) { console.log(this) console.log(我的全名是"${firstName}${this.name}${lastName}"我的座右铭是"${flag}") } allName.bind(obj) //不会执行 let fn = allName.bind(obj) fn(‘我是’, ‘前端’, ‘好好学习天天向上’) // 也可以这样用,参数可以分开传。bind后的函数参数默认排列在原函数参数后边 fn = allName.bind(obj, “你是”) fn(‘前端’, ‘好好学习天天向上’)接下来搓搓手实现call、apply和bind实现call let Person = { name: ‘Tom’, say() { console.log(this) console.log(我叫${this.name}) } } // 先看代码执行效果 Person.say() //我叫Tom Person1 = { name: ‘Tom1’ } // 我们尝试用原生方法call来实现this指向Person1 Person.say.call(Person1) //我叫Tom1通过第一次打印执行和第二次打印执行我发现,如果Person1有say方法那么Person1直接执行Person1.say() 结果就是我是Tom1,是的call就是这么实现的。再看代码 Function.prototype.MyCall = function(context) { //context就是demo中的Person1 // 必须此时调用MyCall的函数是say方法,那么我们只需要在context上扩展一个say方法指向调用MyCall的say方法这样this console.log(this) context.say = this //Mycall里边的this就是我们虚拟的say方法 context.say() } // 测试 Person.say.MyCall(Person1)//我叫Tom1perfect!爆棚的满足感!不过拿脚趾头想想也不会这么简单,继续完善我们自己找茬1、call支持多个参数,有可能一个也不没有2、考虑多参数时要把参数传给扩展方法。3、给上下文定义的函数要保持唯一不能是say4、扩展完我们需要吧自定义函数删除接下来针对找茬问题一一解决 let Person = { name: ‘Tom’, say() { console.log(this) console.log(我叫${this.name}) } } Person1 = { name: ‘Tom1’ } //如果没有参数 Person.say.call()没有指定this,this指向window我们也要这样 Function.prototype.MyCall = function(context) { // 如果没有参数我们参考call的处理方式 context = context || window //context就是demo中的Person1 // 必须此时调用MyCall的函数是say方法,那么我们只需要在context上扩展一个say方法指向调用MyCall的say方法这样this context.say = this //Mycall里边的this就是我们虚拟的say方法 context.say() } Person.say.MyCall()没毛病!继续解决// 找茬2:我们默认定义context.say = this fn如果已经被占用 嘎嘎 sb了。 不怕 搞定它 // say需要是一个唯一值 是不是突然想到es6的新类型 Symbol fn = Symbol() 不过我们装逼不嫌事大 都说自己实现了 function mySymbol(obj) { // 不要问我为什么这么写,我也不知道就感觉这样nb let unique = (Math.random() + new Date().getTime()).toString(32).slice(0, 8) // 牛逼也要严谨 if (obj.hasOwnProperty(unique)) { return mySymbol(obj) //递归调用 } else { return unique } }//接下来我们一并把多参数和执行完删除自定义方法删除掉一块搞定 Function.prototype.myCall1 = function(context) { // 如果没有传或传的值为空对象 context指向window context = context || window let fn = mySymbol(context) context.fn = this //给context添加一个方法 指向this // 处理参数 去除第一个参数this 其它传入fn函数 let arg = […arguments].slice(1) //[…xxx]把类数组变成数组,arguments为啥不是数组自行搜索 slice返回一个新数组 context.fn(…arg) //执行fn delete context.fn //删除方法 } let Person = { name: ‘Tom’, say(age) { console.log(this) console.log(我叫${this.name}我今年${age}) } } Person1 = { name: ‘Tom1’ } Person.say.call(Person1,18)//我叫Tom1我今年18测试结果相当完美!实现apply接下来apply就简单多了,只有多参数时第二个参数是数组,就不一步步细说了。 Function.prototype.myApply = function(context) { // 如果没有传或传的值为空对象 context指向window if (typeof context === “undefined” || context === null) { context = window } let fn = mySymbol(context) context.fn = this //给context添加一个方法 指向this // 处理参数 去除第一个参数this 其它传入fn函数 let arg = […arguments].slice(1) //[…xxx]把类数组变成数组,arguments为啥不是数组自行搜索 slice返回一个新数组 context.fn(arg) //执行fn delete context.fn //删除方法 }实现bind这个和call、apply区别还是很大的,容我去抽根烟回来收拾它还是老套路先分析bind都能干些什么,有什么特点 1、函数调用,改变this 2、返回一个绑定this的函数 3、接收多个参数 4、支持柯里化形式传参 fn(1)(2) Function.prototype.bind = function(context) { //返回一个绑定this的函数,我们需要在此保存this let self = this // 可以支持柯里化传参,保存参数 let arg = […arguments].slice(1) // 返回一个函数 return function() { //同样因为支持柯里化形式传参我们需要再次获取存储参数 let newArg = […arguments] console.log(newArg) // 返回函数绑定this,传入两次保存的参数 //考虑返回函数有返回值做了return return self.apply(context, arg.concat(newArg)) } } // 搞定测试 let fn = Person.say.bind(Person1) fn() fn(18)是的,完美,实现了绑定this,返回函数,不立即执行,可以柯里化形式传参。简版的实现就算完成了欢迎吐槽or点赞! ...

January 18, 2019 · 2 min · jiezi