【问题标题】:Why don't these class implementations have identical behavior?为什么这些类实现没有相同的行为?
【发布时间】:2017-06-27 02:51:49
【问题描述】:

我试图为扩展Function 的类编写概念证明,以演示可以基于另一个函数初始化的函数构造函数,并以下列方式反映thisarguments

class Handle extends Function {
  constructor(functor) {
    super("functor", "slice", `
      "use strict";
      return functor
        .call(this, ...slice.call(arguments, 2));
    `);

    return this.bind(this, functor, Array.prototype.slice)
  }
}

let handle = new Handle(function test() {
  console.log(this instanceof Handle, arguments.length)
})

console.log(handle instanceof Handle, handle.length)

handle(1, 2, 3)

但是,根据我对callapply 的理解,我认为这会产生相同的行为:

class Handle extends Function {
  constructor(functor) {
    super("instance", "call", "slice", `
      "use strict";
      return call(this, instance, ...slice.call(arguments, 3));
    `);

    return Function.bind
      .call(this, functor, this, Function.call, Array.prototype.slice)
  }
}

let handle = new Handle(function test() {
  console.log(this instanceof Handle, arguments.length)
})

console.log(handle instanceof Handle, handle.length)

handle(1, 2, 3)

这会抛出

Uncaught TypeError: call is not a function
  at Function.eval (eval at Handle (js:4), <anonymous>:5:14)

所以call() 函数有问题。我的理解是,如果call() 不是调用表达式的一部分,它的第一个参数将成为被调用的函数,其余参数将成为函数的上下文和参数,有点像Function.call.call(this, instance, ...slice.call(arguments, 3))会做:

class Handle extends Function {
  constructor(functor) {
    super("instance", "call", "slice", `
      "use strict";
      return Function.call.call(this, instance, ...slice.call(arguments, 3));
    `);

    return Function.bind
      .call(this, functor, this, Function.call, Array.prototype.slice)
  }
}

let handle = new Handle(function test() {
  console.log(this instanceof Handle, arguments.length)
})

console.log(handle instanceof Handle, handle.length)

handle(1, 2, 3)

谁能解释我的误解,或者为什么这似乎不是这种情况?

【问题讨论】:

  • 查看here 以更轻松地扩展Function
  • @Bergi 虽然我赞成您的回答,因为它很有趣,但它并没有完全利用继承。除了强制将其用作构造函数之外,您的 ExtensibleFunction 基本上丢弃了实例化的 this,引用您的话,这似乎是在回避问题,而不是以“令人满意”的方式解决它。
  • 不,没有 this 被丢弃,因为当我们不调用 super 时它永远不会被实例化 - 我们不想调用 super 因为这意味着解析和评估每次创建实例时的函数源字符串。
  • @Bergi 哦,这很有趣...我从来没有意识到 super() 导致了实例化,我只是认为 ES6 有一些奇怪的约定,如果扩展类的构造函数必须是第一个是明确的。

标签: javascript function call metaprogramming


【解决方案1】:
Uncaught TypeError: call is not a function

所以 call() 函数有问题。

注意this message is misleading,不是call 不是函数,而是call 试图调用的东西。

我的理解是,如果 call() 不是调用表达式的一部分,它的第一个参数将成为被调用的函数

没有。被调用的函数始终是call 调用的this 上下文,并且当call 未被作为方法调用时,没有任何改变。你需要做call.call(functor, context, ...args)

【讨论】:

  • call.call(functor, context, ...args) 是否调用 functor.call(context, ...args)
  • 那是等价的,是的(但不是它不会在内部访问和调用functor.call方法)
【解决方案2】:

你打电话给Function.call(我认为Function.prototype.call会更正确)没有this。不过,它需要一个,因为this 值是它调用的函数。一个简短的问题示例:

var call = Function.prototype.call;
call();

也许你打算call.call(this, …)

class Handle extends Function {
  constructor(functor) {
    super("instance", "call", "slice", `
      "use strict";
      return call.call(this, instance, ...slice.call(arguments, 3));
    `);

    return Function.bind
      .call(this, functor, this, Function.call, Array.prototype.slice)
  }
}

let handle = new Handle(function test() {
  console.log(this instanceof Handle, arguments.length)
})

console.log(handle instanceof Handle, handle.length)

handle(1, 2, 3)

【讨论】:

  • call.call(...)? 一直是问题吗? facepalm 但是Function.callFunction.prototype.call同样有效,因为Function instanceof Function,所以它会有成员方法call,是它自己的一个实例。
  • @PatrickRoberts:对;我只是认为它具有误导性,例如传递 {}.toString.call 或其他东西。幸运的是,没有必要,因为您可以使用您原来的方法。
  • 我知道你先回答了,而且我确实对这两个答案都投了赞成票,但我更喜欢 Bergi 的解释。它确实很好地说明了这一点,此外,我认为您的回答中的一些观察结果有点自以为是。这并没有使它不正确,我只是认为 Bergi 的回答更适合我的风格,如果这是有道理的话。
猜你喜欢
  • 2020-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多