那它的this指向于它所应用的:
class Page { constructor(callBack) { this.className = 'Page' // callBack() // 直接执行的话 由于class 内部是严格模式,所以this 实际指向的是 undefined this.MessageCallBack = callBack // 回调函数的this 隐式绑定到class page this.MessageCallBack('发给注册页面的信息') } }既然知道问题了,那我们只要绑定回调函数的this指向为PageA就解决问题了。
回调函数this丢失的解决方案:
bind绑定回调函数的this指向:
这是典型bind的应用场景, 绑定this指向,用做回调函数。
this.pageClass = new Page(this.handleMessage.bind(this)) // 绑定回调函数的this指向PS: 这也是为什么react的render函数在绑定回调函数的时候,也要使用bind绑定一下this的指向,也是因为同样的问题以及原理。
箭头函数绑定this指向
箭头函数的this指向定义的时候外层第一个普通函数的this,在这里指的是class类:PageA
这块内容,可以看下我之前写的博客:
this.pageClass = new Page(() => this.handleMessage()) // 箭头函数绑定this指向 中高级面试题-手写call/apply、bind:在大厂的面试中,手写实现call,apply,bind(特别是bind)一直是比较高频的面试题,在这里我们也一起来实现一下这几个函数。
你能手写实现一个call吗?思路
根据call的规则设置上下文对象,也就是this的指向。
通过设置context的属性,将函数的this指向隐式绑定到context上
通过隐式绑定执行函数并传递参数。
删除临时属性,返回函数执行结果
Function.prototype.myCall = function (context, ...arr) { if (context === null || context === undefined) { // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window) context = window } else { context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象 } context.testFn = this; // 函数的this指向隐式绑定到context上 let result = context.testFn(...arr); // 通过隐式绑定执行函数并传递参数 delete context.testFn; // 删除上下文对象的属性 return result; // 返回函数执行结果 }; 判断函数的上下文对象:很多人判断函数上下文对象,只是简单的以context是否为false来判断,比如:
// 判断函数上下文绑定到`window`不够严谨 context = context ? Object(context) : window; context = context || window;经过测试,以下三种为false的情况,函数的上下文对象都会绑定到window上:
// 网上的其他绑定函数上下文对象的方案: context = context || window; function handle(...params) { this.test = 'handle' console.log('params', this, ...params) // do some thing } handle.elseCall('') // window handle.elseCall(0) // window handle.elseCall(false) // window而call则将函数的上下文对象会绑定到这些原始值的实例对象上:
所以正确的解决方案,应该是像我上面那么做:
// 正确判断函数上下文对象 if (context === null || context === undefined) { // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window) context = window } else { context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象 } 你能手写实现一个apply吗?思路:
传递给函数的参数处理,不太一样,其他部分跟call一样。
apply接受第二个参数为类数组对象, 这里用了JavaScript权威指南中判断是否为类数组对象的方法。
Function.prototype.myApply = function (context) { if (context === null || context === undefined) { context = window // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window) } else { context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象 } // JavaScript权威指南判断是否为类数组对象 function isArrayLike(o) { if (o && // o不是null、undefined等 typeof o === 'object' && // o是对象 isFinite(o.length) && // o.length是有限数值 o.length >= 0 && // o.length为非负值 o.length === Math.floor(o.length) && // o.length是整数 o.length < 4294967296) // o.length < 2^32 return true else return false } context.testFn = this; // 隐式绑定this指向到context上 const args = arguments[1]; // 获取参数数组 let result // 处理传进来的第二个参数 if (args) { // 是否传递第二个参数 if (!Array.isArray(args) && !isArrayLike(args)) { throw new TypeError('myApply 第二个参数不为数组并且不为类数组对象抛出错误'); } else { args = Array.from(args) // 转为数组 result = context.testFn(...args); // 执行函数并展开数组,传递函数参数 } } else { result = context.testFn(); // 执行函数 } delete context.testFn; // 删除上下文对象的属性 return result; // 返回函数执行结果 }; 你能手写实现一个bind吗?划重点: