从底层理解this是什么( 三 )

4.通过构造函数中设置咱们现在再来看一下通过new调用构造函数到底做了什么:
function polyNew(source, ...arg) {// 创建一个空的简单JavaScript对象(即{})let newObj = {};// 链接该对象(即设置该对象的构造函数)到另一个对象Object.setPrototypeOf(newObj, source.prototype);// 将步骤1新创建的对象作为this的上下文 ;const resp = source.apply(newObj, arg);// 判断该函数返回值是否是对象if (Object.prototype.toString.call(resp) === "[object Object]") {// 如果该函数没有返回对象 , 则返回this 。return resp} else {// 如果该函数返回对象 , 那用返回的这个对象作为返回值 。return newObj}}显然我们看到 source.apply(newObj, arg), 所以构造函数其实也改变了this指向 , 将this指向从原函数换到了新构造出来的函数 。
解疑填坑

  • this存放在哪里?this存放在每个执行上下文中
  • this是如何出现 , 又是如何消失的?this随着执行上下文出现 , 当执行上下文被回收后 , 也随之消失
  • this有什么作用?全局执行上下文中:this指向了 window 对象 , 方便我们来调用全局 window 对象 。 函数执行上下文中:this指向了调用该函数的对象 , 减少的参数的传递 , 原来如果需要在函数内部操作被调用对象 , 当然还需要将对象作为参数传递进去 , 而有了 this, 就不需要了 , 直接拿 this 就能操作被调用对象的属性 。
call、apply、bind为何物上文用了多次call , 想必大家已经明白call做了什么了:
MDN:call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数 。
举个例子:
let bar = {myName : "dellyoung",}function foo(){console.log(this.myName)}foo.call(bar) // 打印出 dellyoung也就是说:调用foo函数的时候 , 通过使用call() , 并且传入bar , 使得foo函数内部的this指向了bar
实现call咱们就根据这个结论来实现一下call:
Function.prototype.dellCall = function (context = window,...param) {// 判断是函数才能调用call方法if (typeof this !== 'function') {return new TypeError("类型错误");}// 将this也就是被调用的函数 , 通过赋值给传入的对象 , 来达到将被调用的函数添加到传入的对象上的目的context.fun = this;// 用传入的对象来调用需要被调用的函数 , 并保留返回结果const resp = context.fun(...param);// 删除传入对象上被添加的函数 , 防止内存泄漏Reflect.deleteProperty(context, 'fun');// 返回结果return resp;};其实核心很简单 , 咱们分析一下:
  • 将被调用的函数作为一个属性添加到传入的对象上
  • 从而可以实现在传入的对象上 , 调用需要被调用的函数
  • 咱们分析完发现核心原理还是:谁调用函数 , 函数的this指向谁
万变不离其宗: 谁调用函数 , 函数的this指向谁。 这句话其实可以帮助我们理解绝大部分this的问题了
实现applyapply其实和call差不多 , 只不过传递参数的方式不同:
foo.call(obj,[param1,param2,...,paramN]) // 参数是数组 , 传入一个数组作为参数foo.apply(obj,param1,param2,...,paramN) // 参数非数组 , 可以传一串参数咱们对上面的call稍微改一下就是apply了:
Function.prototype.dellApply = function (context = window, param = []) {// 判断是函数才能调用call方法if (typeof this !== 'function') {return new TypeError("类型错误");}// 将被调用的函数作为一个属性添加到传入的对象上context.fun = this;// 在传入的对象上 , 调用需要被调用的函数const resp = context.fun(...param);// 删除传入对象上被添加的函数 , 防止内存泄漏Reflect.deleteProperty(context, 'fun');// 返回结果return resp;}


推荐阅读