Open zhuibo66 opened 3 years ago
都是为了改变函数调用者的this的指向
第一个参数都是指向个对象(MDN说:传递的任何原始值都将转换为object)
call和bind剩余参数,是个列表,如:arg1,arg2
apply,是个数组,如:[arg1,arg2]
call和apply返回值:undefined
bind返回值:是个新的函数
call和apply执行阶段是:立即执行
bind执行阶段是:因为bind返回的是个函数,所以不会立即执行,所以啥时候执行,取决于coder。
原生定义:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
Function.prototype.myCall = function (thisArg, ...args) { //1、如果调用者不是一个可调用的函数,则抛出TypeError if (typeof this !== "function") { throw new TypeError(`${this} must be a function`); } //2、如果绑定对象是null或者undefined,那么则将其绑定到全局对象上去(浏览器环境下全局对象是window) if (thisArg === null || thisArg === undefined) { thisArg = window; //此处为全局对象,浏览器环境下为window } else { thisArg = Object(thisArg); // 如果是普通类型则将其转换为对象包裹 } const fn = Symbol("唯一标识"); thisArg[fn] = this; const result = thisArg[fn](...args); delete thisArg[fn]; // 此处若不删除属性,thisArg则会出现Symbol属性,破坏了原来内容 return result; };
原生定义:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
//判断是不是类数组 function isArrayLike(val) { //val.length >= 0 排除了负数 以及NaN if (typeof val.length === "number" && val.length >= 0) { if (!isFinite(val.length)) { throw new RangeError("Invalid array length"); } return true; } return false; } Function.prototype.myApply = function (thisArg, argArray) { //1、如果调用者不是一个可调用的函数,则抛出TypeError if (typeof this !== "function") { throw new TypeError(`${this} must be a function`); } //2、如果绑定对象是null或者undefined,那么则将其绑定到全局对象上去(浏览器环境下全局对象是window) if (thisArg === null || thisArg === undefined) { thisArg = window; //此处为全局对象,浏览器环境下为window } else { thisArg = Object(thisArg); // 如果是普通类型则将其转换为对象包裹 } //3、判断argArray是否是null或者undefined,(3,4,5这几步,我感觉只是为了代码容错性更高点,因为原生有) if (argArray === null || argArray === undefined) { argArray = []; } //4、判断argArray是否是个对象 if (!(argArray instanceof Object)) { throw new TypeError("CreateListFromArrayLike called on non-object"); } // 5、判断argArray是否是类数组对象 const argList = []; if (isArrayLike(argArray)) { // argArrar为null或undefined已经转换为空数组 //有可能argArray.length 不是整数 for (let i = 0; i < Math.floor(argArray.length); i++) { argList[i] = argArray[i]; } } const fn = Symbol("唯一标识"); thisArg[fn] = this; const result = thisArg[fn](...args); delete thisArg[fn]; // 此处若不删除属性,thisArg则会出现Symbol属性,破坏了原来内容 return result; };
原生定义:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
//基础版 Function.prototype.myBind = function (thisArg, ...args1) { //1、如果调用者不是一个可调用的函数,则抛出TypeError if (typeof this !== "function") { throw new TypeError(`${this} must be a function`); } const self = this; //2、返回一个新函数 return function (...args2) { return self.apply( thisArg, args1.concat(args2) // 同时,返回的新函数也可以接受参数,所以把两个参数合并 ); }; }; //实现的比较完整版(我理解就是代码的容错性强),因为不是很理解,所以先摘抄过来先 Function.prototype.myBind = function (thisArg, ...args1) { //1、如果调用者不是一个可调用的函数,则抛出TypeError if (typeof this !== "function") { throw new TypeError(`${this} must be a function`); } const self = this; const fNOP = function () {}; const fBound = function (...args2) { // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值 // 以上面的是 demo 为例,如果改成 `this instanceof fBound ? null : context`,实例只是一个空对象,将 null 改成 this ,实例会具有 habit 属性 // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context return self.apply( this instanceof fNOP ? this : thisArg, args1.concat(args2) // 同时,返回的新函数也可以接受参数,所以把两个参数合并 ); }; // 因为修改 fBound.prototype 的时候,也会直接修改绑定函数的 prototype。这个时候,我们可以通过一个空函数来进行中转 // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值 fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; };
学习手写call,apply JavaScript深入之bind的模拟实现
相同点
都是为了改变函数调用者的this的指向
第一个参数都是指向个对象(MDN说:传递的任何原始值都将转换为object)
不同点
剩余参数(第二个参数起)的传参方式不同
call和bind剩余参数,是个列表,如:arg1,arg2
apply,是个数组,如:[arg1,arg2]
返回值不同
call和apply返回值:undefined
bind返回值:是个新的函数
执行阶段不同
call和apply执行阶段是:立即执行
bind执行阶段是:因为bind返回的是个函数,所以不会立即执行,所以啥时候执行,取决于coder。
手写call
原生定义:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
手写apply
原生定义:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
手写bind
原生定义:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
学习手写call,apply JavaScript深入之bind的模拟实现