1.实现 call apply

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数语法:function.call(thisArg, arg1, arg2, …)

  1. //实现apply只要把下一行中的...args换成args即可
  2. Function.prototype.myCall = function (context, ...args) {
  3. if (typeof this !== "function") {
  4. throw new Error("must be invoked with function");
  5. }
  6. let fn = Symbol("fn"); // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
  7. let target = context || window; // 若没有传入this, 默认绑定window对象
  8. target[fn] = this; // this指向调用call的对象,即我们要改变this指向的函数
  9. let result = target[fn](...args); //重点代码,利用this指向,相当于context.caller(...args)
  10. delete target[fn];
  11. return result;
  12. };
  13. //测试
  14. //变更函数调用者示例
  15. function foo(num, string) {
  16. console.log(this.name, num, string);
  17. }
  18. // 测试
  19. const obj = {
  20. name: "托马斯",
  21. };
  22. foo.myCall(obj, 1, "2"); // 输出'托马斯' 1 '2'

2.实现 bind

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
语法: function.bind(thisArg, arg1, arg2, …)

核心要点:

1.对于普通函数,绑定 this 指向

2.对于构造函数,要保证原函数的原型对象上的属性不能丢失

  • bind()除了 this 还接收其他参数,bind()返回的函数也接收参数,这两部分的参数都要传给返回的函数
  • new 会改变 this 指向:如果 bind 绑定后的函数被 new 了,那么 this 指向会发生改变,指向当前函数的实例
  • 需要保留原函数在原型链上的属性和方法
  1. Function.prototype.bind = function (context, ...args) {
  2. let self = this; //谨记this表示调用bind的函数
  3. if (typeof self !== "function") {
  4. throw new Error("must be invoked with function");
  5. }
  6. let fBound = function () {
  7. //this instanceof fBound为true表示构造函数的情况。如new func.bind(obj)
  8. return self.apply(
  9. self instanceof fBound ? self : context || window,
  10. args.concat(Array.prototype.slice.call(arguments))
  11. );
  12. };
  13. fBound.prototype = Object.create(self.prototype); //保证原函数的原型对象上的属性不丢失
  14. return fBound;
  15. };
  16. ////测试
  17. const obj = { name: "托马斯" };
  18. function foo() {
  19. console.log(this.name);
  20. console.log(arguments);
  21. }
  22. foo.myBind(obj, "a", "b", "c")(); //托马斯 ['a', 'b', 'c']

3.实现 new 关键字

核心要点:

  1. 创建一个全新的对象
  2. 这个对象的proto要指向构造函数的原型对象
  3. 执行构造函数
  4. 构造函数返回值为 object 类型则作为 new 方法的返回值返回,否则返回上述全新对象
  1. function myNew(fn, ...args) {
  2. // 把obj挂原型链上
  3. let obj = Object.create(fn.prototype);
  4. // 执行构造方法, 并为其绑定新this, 这一步是为了让构造方法能进行this.name = name之类的操作
  5. let res = fn.apply(obj, args);
  6. return typeof res === "object" ? res : obj;
  7. }
  8. // new 创建一个对象,关联原型,执行构造函数,依据执行结果被返回不同的值
  9. function newFactory() {
  10. const object = new Object();
  11. const constructor = Array.prototype.shift.apply(arguments);
  12. object.__proto__ = constructor.prototype;
  13. const res = constructor.apply(object, arguments);
  14. return typeof res === "object" ? res : object;
  15. }