call、apply、bind 清疚 2023-07-06 14:49 108阅读 0赞 ### call、apply ### * 假如有一个命名函数fn,有三种写法,都可以执行函数;但是call、apply可以改变this的指向。但如果函数中没有this,call、apply就没有任何意义了,和普通的执行函数概念一样。 * call、apply的第一个参数都是this的重定向指向;不同的是,call可以有多个参数,apply只有两个参数,第二个参数是一个数组。 * 如下: fn(value1,value2,…); fn.call(obj,value1,value2,…); fn.apply(obj,\[vlaue1,value2,…\]); * 如果使用call或apply,第一个参数是null,就意味着将函数中this重定向到window(管你之前指向谁)。 1、一般情况下的call、apply应用场景: function fn(_a,_b){ this.a=_a; this.b=_b; // console.log(this); //this--->window } fn(); //没给参数,给了参数就能打印出所给参数值 console.log(a,b); //undefined undefined var obj = { }; fn.call(obj, 30, 50);//call在执行函数时,函数的参数从第二位开始依次写入obj console.log(obj.a,obj.b);//30 50 fn.apply(obj, [300, 500]);//apply只有两个参数,第一个是函数中this的指向,第二个是一个数组,这个数组就是函数的参数 console.log(obj.a,obj.b);//300 500 ![在这里插入图片描述][20200220185237628.png] 2、场景:对象中的函数,call、apply的应用 : var obj = { fn: function (_a, _b) { this.a = _a; this.b = _b; } } obj.fn(3,5); //this--->obj; console.log(obj); // {a: 3, b: 5, fn: ƒ} var obj1={ }; obj.fn.call(obj1,3,5); obj.fn.apply(obj1,[3,5]); console.log(obj,obj1);//{a: 3, b: 5, fn: ƒ} {a: 3, b: 5} obj.fn.call(null,3,5); //把this重新定向给window了 // 等同于 obj.fn.call(window,3,5); console.log(obj,a,b); //{fn: ƒ} 3 5 ![在这里插入图片描述][20200220190412405.png] ![在这里插入图片描述][20200220190008513.png] * 示例一:下面贴个apply第一个参数为null的巧妙用法(第二个参数,允许作为一个数组传入值) 小知识:Math.max没有this,null,传入以后并没有任何作用,所以用apply目的是传参时传入的是数组。 var Math={ max:function(){ if(arguments.length===0)return; var max=arguments[0]; if(arguments.length===1)return max; for(var i=1;i<arguments.length;i++){ max=max>arguments[i]?max:arguments[i]; } return max; } } var arr=[4,3,7,0]; var max=Math.max.apply(null,arr);//apply可以将参数以数组的形式传入 var max1=Math.max.call(null,14,3,7,0);//call就不行了,要写好多参数 console.log(max,max1); 结果显而易见:7 14 * 示例二:为了了解Array.prototype.slice.call()的使用,然后把slice方法重构一下。 (1)先看一下怎么用的。 一般slice的使用方法:var array=[1,2,2]; array.slice(); 现在想不创建数组,就能使用slice方法的话,可以从原型链里面拿。 var divs=document.getElementsByTagName("div"); var arr=Array.prototype.slice.call(divs); // 等同于 var arr=[].slice.call(divs); console.log(divs,arr); // 这种写法是用了Array原型的概念,arr的原型链下有slice方法,然后用call把slice里的this重定向到divs,就可以复制出divs里的内容给arr。 ![在这里插入图片描述][20200220201130143.png] (2)slice重构 class Array1 { constructor() { // 构造函数 } // 属性和方法 动态属性,实例属性,该类的原型属性,实例化的原型链属性 // 动态属性 其实相对static 静态来说的 // 实例属性,该属性是通过new 构造函数来实例化对象以后调用的 // 该类的原型属性 站在类的立场上,类.prototype.属性 ES5中的方式 // 实例化的原型链属性 站在实例对象的的立场上,ES5中,对象中原型链上的方法和属性 slice(start, end) { // ES6中实例方法中的this就是该类实例化的对象 console.log(this); //目前传入的对象是divs start = Number(start); end = Number(end); if (isNaN(start)) start = 0; if (isNaN(end)) end = this.length; if (start < 0) start = this.length + start; if (end < 0) end = this.length + end; var arr = []; for (var i = start; i < end; i++) { arr.push(this[i]); } return arr;//返回数组 } } var divs=document.getElementsByTagName("div"); var arr=Array1.prototype.slice.call(divs); console.log(arr); // 这里其实就是简单模拟一下Array.prototype.slice.call()的使用机制,arr的原型依然是Array.prototype,因为毕竟返回的是数组。 ![在这里插入图片描述][20200220203051317.png] > 总结: > call 执行函数,将该函数中this指向call的第一个参数,如果该函数有参数,那么call的第二个参数开始一 一带入所有参数。 > apply 执行函数,将该函数中this指向call的第一个参数,如果该函数有参数,那么apply的第二个参数是这个函数的所有参数组成数组。 ### bind ### #### prototype 原型 #### 先来简单介绍一下原型的概念: * 原型是构造函数(类)的叫法,只有构造函数(类)才有prototype,原型链是针对对象的叫法,只有对象才有\_\_proto\_\_。 * 对象的原型链和构造函数(类)的原型是同一个引用对象。对象的原型链就是构造函数(类)的原型。 * ES5中没有类,借用原型的概念实现类(构造函数-ES5;类-ES6;ES5是用首字母大写的命名函数,也就是构造函数来模拟类)。 (1)ES6中的类 class Box { constructor() { } play() { console.log(this); // this--->实例对象 } } var b = new Box(); console.log(b.__proto__.play() === Box.prototype.play()); ![在这里插入图片描述][20200221075506797.png] (2)ES5中的类 function Box() { } Box.a = 3;//静态属性 Box.once = function () { // 静态方法 } Box.prototype.b = 4;//实例属性 Box.prototype.play = function () { // 实例方法 console.log(this); } var box=new Box(); console.log(box.__proto__.play() === Box.prototype.play()); ![在这里插入图片描述][2020022107584589.png] #### bind #### * 当需要在回调函数中重新执行回调函数中的this,就需要是用bind来指向对象。 * bind返回一个绑定obj的新函数。 function fn1(fn){ fn(3); // fn.call(obj,789); } function fn2(_a){ this.a=_a; } // fn1(fn2); //可以使用call在函数fn1中改变fn的this指向并传入参数,但是如果不用这种方式呢? // bind就出现了。 var obj={ }; fn1(fn2.bind(obj));// 把fn2函数中的this指向obj,并且返回这个被指向this后新的函数 console.log(obj); var fns=fn2.bind(obj);// bind返回一个绑定obj的新函数 console.log(fns===fn2);// 这里创建了一个新的函数 ![在这里插入图片描述][20200221083526962.png] * 举个小栗子: var b=900; var obj={ b:2, a:function(){ // 回调函数 setTimeout(function(){ console.log(this.b); //这个this指向window },1000); setTimeout((function(){ console.log(this.b); //这个this不再指向window,而是指向obj }).bind(this),100); // 所以就能实现不使用箭头函数也可以把this指向obj // 事件函数 // 比如事件,但删不掉,因为得到的是新函数 // 可以先存储绑定的函数,删除时也是删除这个存储的 this.bindHandler=this.clickHandler.bind(this); document.addEventListener("click",this.bindHandler); }, clickHandler:function(e){ console.log(this.b); document.removeEventListener("click",this.bindHandler); } } obj.a(); ![在这里插入图片描述][20200221082810586.png] [Promise封装][Promise],就大量用到了this、bind。 > 总结:call、apply会直接执行,但bind是创建一个新的东西,不会立即执行。 [20200220185237628.png]: https://img-blog.csdnimg.cn/20200220185237628.png [20200220190412405.png]: https://img-blog.csdnimg.cn/20200220190412405.png [20200220190008513.png]: https://img-blog.csdnimg.cn/20200220190008513.png [20200220201130143.png]: https://img-blog.csdnimg.cn/20200220201130143.png [20200220203051317.png]: https://img-blog.csdnimg.cn/20200220203051317.png [20200221075506797.png]: https://img-blog.csdnimg.cn/20200221075506797.png [2020022107584589.png]: https://img-blog.csdnimg.cn/2020022107584589.png [20200221083526962.png]: https://img-blog.csdnimg.cn/20200221083526962.png [20200221082810586.png]: https://img-blog.csdnimg.cn/20200221082810586.png [Promise]: https://blog.csdn.net/weixin_43297321/article/details/104422348
还没有评论,来说两句吧...