手写 call、apply 及 bind 函数

call

  1. Function.prototype.Dcall = function (context) {
  2. if (typeof this !== "function") {
  3. throw new TypeError("Error");
  4. }
  5. var context = context || window;
  6. context.say = this;
  7. var args = [];
  8. for (let i = 1; i < arguments.length; i++) {
  9. args.push("arguments[" + i + "]");
  10. }
  11. var result = eval("context.say(" + args + ")");
  12. delete context.say;
  13. return result;
  14. };

apply

  1. Function.prototype.Dapply = function (context, arr) {
  2. if (typeof this !== "function") {
  3. throw new TypeError("Error");
  4. }
  5. var context = context || window;
  6. var result;
  7. context.fn = this;
  8. if (!arr) {
  9. result = context.fn();
  10. } else {
  11. var args = [];
  12. for (let i = 0; i < arr.length; i++) {
  13. args.push("arr[" + i + "]");
  14. }
  15. result = eval("context.fn(" + args + ")");
  16. }
  17. return result;
  18. };

bind

  1. Function.prototype.Dbind = function (context) {
  2. if (typeof this !== "function") {
  3. throw new TypeError("Error");
  4. }
  5. var self = this;
  6. var args = Array.prototype.slice.call(arguments, 1);
  7. return function () {
  8. var bindArgs = Array.prototype.slice.call(arguments);
  9. return self.apply(context, args.concat(bindArgs));
  10. };
  11. };

new

在调用 new 的过程中会发生以上四件事情:

  1. 新生成了一个对象
  2. 链接到原型
  3. 绑定 this
  4. 返回新对象

    实现分析:

  5. 创建一个空对象

  6. 获取构造函数
  7. 设置空对象的原型
  8. 绑定 this 并执行构造函数
  9. 确保返回值为对象 ```javascript Function.prototype.Dnew = function (fn, …args) { var obj = Object.create(fn.prototype); var ret = fn.apply(obj, args); return ret instanceof Object ? ret : obj; };

// 或 Function.prototype.DDnew = function () { var obj = {}; var Constructor = Array.prototype.shift.call(arguments); obj.proto = Constructor.prototype; var result = Constructor.call(obj, arguments); return result instanceof Object ? result : obj; };

  1. <a name="QT1J2"></a>
  2. # instanceof
  3. <a name="wRHDV"></a>
  4. ## 实现原理
  5. 就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
  6. ```javascript
  7. {
  8. function new_instance_of(leftVaule, rightVaule) {
  9. let rightProto = rightVaule.prototype;
  10. leftVaule = leftVaule.__proto__;
  11. while (true) {
  12. if (leftVaule === null) return false;
  13. if (leftVaule === rightProto) return true;
  14. leftVaule = leftVaule.__proto__;
  15. }
  16. }
  17. }
  18. {
  19. function new_instance_of(leftVaule, rightVaule) {
  20. let proto = Object.getPrototypeOf(leftVaule);
  21. while (true) {
  22. if (proto == null) return false;
  23. if (proto === rightVaule.prototype) return true;
  24. proto = Object.getPrototypeOf(proto);
  25. }
  26. }
  27. }

总结:

使用 typeof 来判断基本数据类型是 ok 的,不过需要注意当用 typeof 来判断 null 类型时的问题,如果想要判断一个对象的具体类型可以考虑用 instanceof,但是 instanceof 也可能判断不准确,比如一个数组,他可以被 instanceof 判断为 Object。所以我们要想比较准确的判断对象实例的类型时,可以采取 Object.prototype.toString.call() 方法