1. /*
  2. * 目标:
  3. * 1. call、 apply、 bind作用及用法
  4. * 2. call、 apply、 bind三个方法有什么区别
  5. * */
  6. console.dir(Function.prototype);
  7. // 1. 事件函数中的 this 是绑定当前事件的元素
  8. // 2. 自执行函数中的 this 指向 window
  9. // 3. 定时器回调函数中的 this 指向 window
  10. // 4. 全局作用域中的 this 是 window
  11. // 5. 方法调用时,看方法名前面有没有点,有点的话,点前面是谁方法中的 this 就是谁,如果没有点,方法中的 this 就是 window
  12. // 6. 箭头函数中的 this 指向声明时所在作用域中的 this
  13. // 7. 构造函数中的 this 指向当前实例
  14. // 8. call / apply / bind 用来修改 this 指向
  15. function sum(a, b) {
  16. console.log(this);
  17. console.log(a, b);
  18. return a + b;
  19. }
  20. var obj = {
  21. id: '0511120117'
  22. };
  23. // sum(1, 2); // window
  24. // 1. call()
  25. // 作用: 修改函数中的 this 指向,并且把修改 this 后的函数执行
  26. // 语法:函数名.call(ctx, 实参1, 实参2.....)
  27. // 参数:ctx 就是用来替换函数中this的对象,从第二个参数起,都是传递给函数的实参
  28. // sum.call(obj, 2, 3); // call 之后,sum 中的 this 就变成了 obj
  29. // 模拟一个 call 方法
  30. ~ function () {
  31. function call(context) {
  32. context = context || window;
  33. let args = [],
  34. result;
  35. for (let i = 1; i < arguments.length; i++) {
  36. args.push(arguments[i]);
  37. }
  38. context.$fn = this;
  39. result = context.$fn(...args);
  40. delete context.$fn;
  41. return result;
  42. }
  43. Function.prototype.call = call;
  44. }();
  45. // 基于 ES6 语法重构
  46. ~function () {
  47. /*生成随机函数名:时间戳的方式*/
  48. function queryRandomName () {
  49. let time = new Date().getTime();
  50. return '$zhufeng' + time;
  51. }
  52. // 模拟 CALL 方法改变函数中的 THIS
  53. function changeThis(context = window,...arg){
  54. let _this = this,
  55. result = null,
  56. ran = queryRandomName();
  57. context[ran] = _this;
  58. result = context[ran](...arg);
  59. delete context[ran];
  60. return result;
  61. };
  62. Function.prototype.changeThis = changeThis;
  63. }();
  64. // 用 call 指定 undefined 和 null 作为 this 无效;
  65. // sum.call(undefined, 3, 4);
  66. // sum.call(null, 1, 3);
  67. // sum.call();
  68. // 2. apply()
  69. // apply 方法和 call 方法作用一样,修改函数中的 this,并且让这个修改 this 之后的函数执行;
  70. // 但是传参方式不同;call 方法是一个一个的传递实参给 sum 的,apply 是把实参都放到一个数组中,数组项是传递给 sum 的实参;
  71. // 模拟一个 apply 方法
  72. ~function(){
  73. /*生成随机函数名:时间戳的方式*/
  74. function queryRandomName () {
  75. let time = new Date().getTime();
  76. return '$zhufeng' + time;
  77. }
  78. function changeThis (context = window, arg = []) {
  79. let _this = this,
  80. result = null,
  81. ran = queryRandomName();
  82. context[ran] = _this;
  83. result = context[ran](...arg);
  84. delete context[ran];
  85. return result;
  86. };
  87. Function.prototype.changeThis = changeThis;
  88. }();
  89. let res = fn.changeThis(obj,[100,200]);
  90. // sum.apply(obj, [11, 12]);
  91. // var ary = [1, 2, 3, 4, 5];
  92. // sum.apply(obj, ary);
  93. // 3. bind() 方法:
  94. // 作用:修改函数中的 this,返回一个修改 this 后的新函数;不会让函数执行。
  95. let sum2 = sum.bind(obj, 1, 2); // 传参的时候和 call 一样,需要一个一个的传
  96. console.log(sum2);
  97. console.log(sum === sum2); // false 因为 sum2 是一个新函数
  98. sum2(); // 修改 this 后,需要自己执行一次这个函数
  99. // call 和 apply 是修改 this 并且让函数执行,call 是一个一个的传参,apply 传递一个数组
  100. // bind 是只修改 this 返回修改 this 后的新函数,并不会让函数执行;

一、模拟call方法:图

内置CALL方法的实现2.png

二、阿里的一道面试题

  1. // 阿里的面试题
  2. function fn1(){console.log(1);}
  3. function fn2(){console.log(2);}
  4. fn1.call(fn2);
  5. fn1.call.call(fn2);
  6. Function.prototype.call(fn1);
  7. Function.prototype.call.call(fn1);

内置CALL方法的实现2.png