1 /***************************************实现call************************************************/ 2 //目标:将this指向传入第一个对象,参数不定,且立即执行 3 Function.prototype.myCall = function (obj){ 4 var args = Array.prototype.apply(arguments,[1]); 5 obj.fn = this; 6 obj.fn(...args); 7 delete obj.fn; 8 } 9 10 //我们使用不带apply和bind的写法 11 var foo ={ 12 value:1 13 } 14 function bar(){ 15 console.log(this.value) 16 } 17 bar.call(foo);//1 18 19 /* 20 * 我们现在为了改变this指向,通过上面的例子可以发现, 只需要把当前对象bar放到foo里面就可以改变指向了 21 * 但是这样会增加额外的属性,所以最后我们再删除即可 22 * 因此我们大致的模拟步骤为 23 * 1. foo.fn = bar; //添加属性 24 * 2. foo.fn(); //执行 25 * 3. delete foo.fn(); //删除该属性 26 */ 27 28 //第一版实现this指向 29 Function.prototype.myCall1 = function(context){ 30 context.fn = this; //获取调用myCall的函数,用this 31 context.fn(); 32 delete context.fn; 33 } 34 35 //call还可以传入参数,但是传入参数不确定咋办。因此我们可以利用arguments,从中取值, 36 //取出第二个到最后一个参数,放到一个数组里面 37 //第二版 38 Function.prototype.myCall2 = function(context){ 39 context.fn = this; 40 var args = []; 41 for(let i = 1 , len = arguments.length ; i < len ; i++ ){ 42 args.push('arguments[' + i + ']'); 43 } 44 45 eval('context.fn(' + i + ')'); //把这个参数数组放到要执行的函数的参数里面 46 47 delete context.fn; 48 } 49 50 //还有两个问题,this可以传null,当为null时,视为window 51 //另一个问题是函数是有返回值的 52 //第三版 53 Function.prototype.myCall3 = function(context){ 54 var context = context || window; 55 context.fn = this; 56 57 var args = []; 58 for(let i = 1 , len = arguments.length ; i < len ; i++){ 59 args.push('arguments[' + i + ']'); 60 } 61 62 var result = eval('context.fn(' + i + ')'); 63 64 delete context.fn; 65 return result; 66 } 67 68 //到此就算完了,我们最后优化一下代码,使用ES6的写法 69 //前面的主要是用来学习思想,我们主要记下面的就可以了 70 //最终版 71 Function.prototype.myCall = function(context = window , ...args){ 72 context.fn = this; 73 let result = context.fn(...args); 74 delete context.fn; 75 return result; 76 } 77 78 /***************************************实现apply************************************************/ 79 //apply和call的区别主要就是传参的问题,apply把call里面第二个以后的参数作为一个数组传入 80 //因此同样的思想我们实现一下apply 81 Function.prototype.myApply = function(context = window , arr){ 82 context.fn = this; 83 let result ; 84 if(!arr){ 85 result = context.fn; 86 }else{ 87 result = obj.fn(...arr); 88 } 89 //上面的if语句可以用三目运算符代替 90 //let result = !arr ? context.fn : obj.fn(...arr); 91 92 delete context.fn; 93 return result; 94 } 95 96 /***************************************实现bind************************************************/ 97 /* 同样基于call来实现bind函数,但是bind不是立即调用,所以需要返回一个函数 98 * 但是要注意:bind 函数有个特点,就是在绑定的时候可以传参,返回的函数还可以继续传参 99 * 如下情况: 100 * var person = { 101 name: 'jayChou' 102 }; 103 104 var say = function(p1, p2) { 105 console.log(this.name, p1, p2); 106 } 107 108 var foo = say.myBind(person, 18); 109 foo(20); // jayChou 18 20 110 * 111 * 因此我们只需要把两次传参合在一起,并让结果返回一个函数即可 112 */ 113 114 Function.prototype.myBind = function(context = window , ...arg1){ 115 context.fn = this; 116 return function(...arg2){ 117 let args = [...arg1].concat(...arg2); //concat用于把两个数组合并 118 let result = context.fn(...args); 119 delete context.fn; 120 return result; 121 } 122 }
手动实现call,apply和bind
猜你喜欢
转载自www.cnblogs.com/liuxinfuchen/p/12289169.html
今日推荐
周排行