Javascript 一个函数,用于访问任何方法链

Javascript 一个函数,用于访问任何方法链,javascript,jquery,Javascript,Jquery,我喜欢Ruby的.tap方法的工作方式。它允许您点击任何方法链,而不破坏该链。我允许您操作一个对象,然后返回该对象,这样方法链就可以正常运行。例如,如果您有foo=“foobar”.upcase.reverse,则可以执行以下操作: "foo = foobar".upcase.tap{|s| print s}.reverse 它将打印上格(但不反转)字符串,并像原始行一样继续反转和赋值 我希望在JS中有一个类似的函数,它只用于一个目的:将对象记录到控制台 我试过这个: Object.proto

我喜欢Ruby的
.tap
方法的工作方式。它允许您点击任何方法链,而不破坏该链。我允许您操作一个对象,然后返回该对象,这样方法链就可以正常运行。例如,如果您有
foo=“foobar”.upcase.reverse
,则可以执行以下操作:

"foo = foobar".upcase.tap{|s| print s}.reverse
它将打印上格(但不反转)字符串,并像原始行一样继续反转和赋值

我希望在JS中有一个类似的函数,它只用于一个目的:将对象记录到控制台

我试过这个:

Object.prototype.foo = function() {
  console.log(this);
  return this;
};
通常,它是有效的(尽管它输出数字对象而不是数字值)

但当我同时使用一些jQuery时,它会中断jQuery并停止页面上所有进一步的代码执行

错误如下:

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); });
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 */
});
  • 未捕获的TypeError:对象foo没有方法“push”
  • 未捕获的TypeError:对象函数(){window.runnerWindow.proxyConsole.log(“foo”);}没有方法“exec”
下面是一个测试用例:(取消注释第一行以查看其中断)

所以我想,把物体的原型弄乱是不安全的


那么,做我想做的事情的正确方法是什么?将当前对象记录到控制台并返回该对象以便链可以继续的函数。对于原语,它应该记录它们的值,而不仅仅是对象。

您正确地理解了如何将方法安全地添加到链中的任何位置,但是您将其添加到Object.prototype是一种侵入性的操作,并且可以很容易地破坏代码。看起来jQuery代码是为您中断的代码

更安全的方法是:

Object.defineProperty(Object.prototype, 'foo', {
  value : function() {  console.log( "foo" );  return this; },
  enumerable : false
});
演示:

最后,一些通用的东西可能是这样的:

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); });
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 */
});
至于你问题的原语部分,这有点棘手。当您对原语调用tap方法时,将创建一个对象包装器,并对其调用tap方法。通过对象包装器的valueOf()方法,原语值仍然可用,因此您可以记录它。棘手的是,您无法知道要调用tap方法的“东西”最初是原语还是对象包装器。假设您永远不想使用对象包装器(这是很合理的),您可以使用下面发布的更好的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);
请注意,在tap函数的第一个版本中,传递给它的函数在
中包含有用的信息,而在第二个版本中,必须将其作为参数传递

如果需要专门的记录器,可以执行以下操作:

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); });
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 */
});

看看这个:嘿@Kostadin,它不适用于任何链,只适用于以“\ux”对象开头的下划线方法链。我知道
tap
函数在JavaScript中是一个很大的缺失部分。对于那些寻找Kotlin的作用域函数的人,我发现它们是可用的。谢谢你的详细回答,@Tibos!为了使答案更完美,您能否提供上一个示例的日志版本?我想要一个快速使用的
1.log();1.轻敲(记录)变量。此外,我认为在定义该属性之前检查该属性是否未定义是合理的。请注意,
1.log
是一个语法错误,您需要
1.0.log
。至于已经定义了属性的场景,这是一个我不想触及的大问题。如果覆盖该属性,则依赖于旧行为的代码将中断;如果不覆盖,则依赖于新行为的代码将中断。只有两个实现都有相同的接口,事情才能继续。谢谢你@Tibos,你太棒了!还有一条注释,
Object.defineProperty
默认情况下将属性定义为不可枚举,因此
enumerable:false
位是多余的。为了清楚起见,我保留它,以表明该属性需要是不可枚举的。