利用任何方法链的功能

And*_*aus 5 javascript jquery

我喜欢Ruby的.tap方法是如何工作的.它可以让您轻松进入任何方法链而不会破坏链条.我让你操作一个对象然后返回对象,以便方法链可以正常继续.例如,如果你有foo = "foobar".upcase.reverse,你可以这样做:

"foo = foobar".upcase.tap{|s| print s}.reverse
Run Code Online (Sandbox Code Playgroud)

它将打印upcased(但不反转)字符串,并像原始行一样继续进行反转和赋值.

我想在JS中有一个类似的功能,它可以用于一个目的:将对象记录到控制台.

我试过这个:

Object.prototype.foo = function() {
  console.log(this);
  return this;
};
Run Code Online (Sandbox Code Playgroud)

通常,它可以工作(虽然它输出数字的数字对象而不是数字值).

但是当我使用一些jQuery时,它会破坏jQuery并停止页面上所有进一步的代码执行.

错误是这样的:

  • 未捕获的TypeError:对象foo没有方法'push'
  • 未捕获TypeError:Object function(){window.runnerWindow.proxyConsole.log("foo"); 没有方法'exec'

这是一个测试用例:http://jsbin.com/oFUvehAR/2/edit(取消注释第一行以查看它是否中断).

所以我想混淆对象的原型是不安全的.

然后,做我想要的正确方法是什么?将当前对象记录到控制台并返回对象以便链可以继续的函数.对于基元,它应该记录它们的值而不仅仅是对象.

Tib*_*bos 7

您正确地弄清楚如何在链中的任何位置安全地添加方法,但是将其添加到Object.prototype是侵入性的并且可以轻松地破坏代码.看起来jQuery代码是打破你的.

一种更安全的方式是:

Object.defineProperty(Object.prototype, 'foo', {
  value : function() {  console.log( "foo" );  return this; },
  enumerable : false
});
Run Code Online (Sandbox Code Playgroud)

演示:http://jsbin.com/oFUvehAR/7/edit

最后,泛型可能看起来像这样:

Object.defineProperty(Object.prototype, 'tap', {
  value : function(intercept) {  
    intercept.call(this);  
    return this; 
  },
  enumerable : false
});

// Usage:
var x  = { a:1 };
x.tap(function(){ console.log(this); });
Run Code Online (Sandbox Code Playgroud)

至于你的问题的原语部分,这有点棘手.在基元上调用tap方法时,会创建一个Object包装器并在其上调用tap方法.通过该Object包装器的valueOf()方法,原始值仍然可用,因此您可以记录它.棘手的部分是你无法知道你想要调用tap方法的"东西"最初是原始的还是Object包装器.假设您从不想使用Object包装器(这是非常合理的),您可以使用下面发布的更好的tap方法.

Object.defineProperty(Object.prototype, 'tap', {
  value : function(intercept) {  
    var val = (this instanceof Number || this instanceof String || this instanceof Boolean) ? this.valueOf() : this;
    intercept(val);  
    return val; 
  },
  enumerable : false
});

var log = console.log.bind(console);

var x  = { a : 1 };

x.tap(log);
2.0.tap(log);
Run Code Online (Sandbox Code Playgroud)

请注意,在tap函数的第一个版本中,传递给它的函数具有有用的信息this,在第二个版本中,必须将其作为参数传递.

如果你想要一个专门的记录器,你可以做这样的事情:

Object.defineProperty(Object.prototype, 'log', {
  value : function(){
    return this.tap(console.log.bind(console));
  },
  enumerable : false,
  writable : true /* You want to allow objects to overwrite the log method */
});
Run Code Online (Sandbox Code Playgroud)