Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

模拟实现call和apply #30

Open
bosens-China opened this issue Oct 8, 2019 · 0 comments
Open

模拟实现call和apply #30

bosens-China opened this issue Oct 8, 2019 · 0 comments
Labels
深入系列 主要介绍实现的思路

Comments

@bosens-China
Copy link
Owner

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数

上面是 call 的定义,apply 与 call 类似只是第二个参数是一个数组而 call 接收的则是一个参数列表。

看一个例子

var obj = {
  name: "obj"
};
function foo() {
  return this.name;
}
foo.call(obj); //obj

上面通过 call 改变了 this 的指向,下面就是模拟实现

实现

call 和 apply 是 es5 的方法,既然是模拟实现那肯定不能用 es5 方法了,我们先分析一下怎么来指定 this,this 跟动态作用域类似是在执行时确定,那我们在指定 this 的属性上添加一个方法并且执行,那么这个方法的 this 就是指定 this

var obj = {
  name: "obj",
  foo: function foo() {
    return this.name;
  }
};

obj.foo();

上面解决了 this,下面来看下怎么实现参数的传递,call 方法可以传递任意参数列表,我们可以通过arguments来获取,它是一个类数组。

function getName() {
  console.log(arguments); //Arguments(5) [1, 2, 3, 4, 5]
}
function foo() {
  var args = [];
  for (var i = 0; i < arguments.length; i++) {
    args.push("arguments[" + i + "]");
  }
  eval("getName(" + args + ")");
}
foo(1, 2, 3, 4, 5);

OK,这两块已经搞定了,下面就是实现

call

Function.prototype.calls = function(con) {
  con.fn = this;
  // 获取参数
  var args = [];
  for (var i = 1; i < arguments.length; i++) {
    args.push("arguments[" + i + "]");
  }
  var result = eval("con.fn(" + args + ")");
  delete con.fn;
  return result;
};

上面删除属性是为了避免污染,这里的fn可以是任意属性名只要保证不重复就行了,不过仔细观察上面函数还是有问题

  1. call 的 this 可以是 null 或者 undefined
  2. 可以是字符串数字之类的,会转化为包装对象;
Function.prototype.calls = function(con) {
  if (con == null) {
    con = typeof window === "object" ? window : global;
  }
  con = Object(con);
  con.fn = this;
  // 获取参数
  var args = [];
  for (var i = 1; i < arguments.length; i++) {
    args.push("arguments[" + i + "]");
  }
  var result = eval("con.fn(" + args + ")");
  delete con.fn;
  return result;
};

测试一下

function foo() {
  return this.length;
}
console.log(foo.calls("obj")); // 3

撒花,这样就实现了 call

apply

与 call 十分类似,这里就直接贴代码了

Function.prototype.applys = function(con, arr) {
  if (con == null) {
    con = typeof window === "object" ? window : global;
  }
  con = Object(con);
  con.fn = this;
  var result;
  if (typeof arr === "object" && arr.length) {
    var args = [];
    for (var i = 0; i < arr.length; i++) {
      args.push("arr[" + i + "]");
    }
    result = eval("con.fn(" + args + ")");
  } else {
    result = eval("con.fn()");
  }
  delete con.fn;
  return result;
};

参考

  1. https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call
  2. JavaScript深入之call和apply的模拟实现 mqyqingfeng/Blog#11
@bosens-China bosens-China added the JavaScript JavaScript系列的文章 label Oct 8, 2019
@bosens-China bosens-China added 深入系列 主要介绍实现的思路 and removed JavaScript JavaScript系列的文章 labels Dec 18, 2019
@bosens-China bosens-China changed the title 模拟实现call和apply javascript专题系列之模拟实现call和apply Dec 18, 2019
@bosens-China bosens-China changed the title javascript专题系列之模拟实现call和apply 模拟实现call和apply Dec 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
深入系列 主要介绍实现的思路
Projects
None yet
Development

No branches or pull requests

1 participant