14

现在,当我使用 chrome 开发工具调试主干或木偶时,我最终设置了断点和诸如此类的东西,但是一旦代码暂停,就很难判断我正在使用什么类型的对象,因为 chrome 将所有内容标记为“孩子”。
(我认为因为那是构造函数)

是否有任何简单的方法可以更改此声明或确定我正在使用哪种类型的模型/集合。

这导致我的疯狂程度想要开始做这样的事情:

MyModel = Backbone.Model.Extend({
    // the $$$ puts it at the top of the inspector, the NAME is just for other devs
    $$$NAME = "MyModel",  
    ...
});

我真的不喜欢它,因为它......丑陋,它是一个变量......它只有在我检查和扩展变量时才有帮助......改变 chrome 用来显示它的名称会很棒。

无论如何,有人知道如何更改名称吗?还是您使用其他一些更清洁的约定?

谢谢!

马特

4

4 回答 4

33

背景

看看为什么浏览器使用“child”在控制台/调试器中显示 Backbone 对象的类型是很有趣的。

所有 JavaScript 对象都有一个构造函数属性,即对用于创建对象的函数的引用。浏览器使用构造函数在控制台/调试器中显示对象的“类型”。如果构造函数的 name 属性的值不为空,则将使用它。但是,只有使用命名函数表达式定义的函数才能获得有用的名称属性:

function A() {  }
console.log(A.name); // 'A' 

匿名函数有一个空的 name 属性:

var B = function() {  };
console.log(B.name); // ''

那么,匿名函数会发生什么?Chrome 从函数首次分配的变量或属性的名称推断匿名函数的名称。这里有些例子:

// 1. named function expression - objects will show as “a” in the console
function a() { … }

// 2. anonymous function assigned to variable - objects will show as “b” in the console
var b = function(){ … };

// 3. anonymous function assigned to property of object - objects will show as “container.c” in the debugger
var container = {
    c: function() { … }
};

此处提供了更详细的脚本:http: //jsfiddle.net/danmalcolm/Xa7ma/6/

浏览器似乎从源代码中获得了这个名称——没有一个 JavaScript 功能可以在运行时告诉您函数分配给的第一个变量的名称。其他浏览器支持使用在匿名构造函数上定义的 displayName 属性的约定,但这目前在 Chrome 中不会发生:http ://code.google.com/p/chromium/issues/detail?id=17356 。

回到 Backbone,假设你没有使用自定义构造函数(见下文),你的类型最终会得到一个匿名构造函数,它是在模型、视图、集合和路由使用的Backbone 的扩展函数中创建的,如下所示:

child = function(){ return parent.apply(this, arguments); };

这就是您在控制台/调试器中的 Backbone 对象旁边看到“子”的原因。这是浏览器对对象构造函数的合适名称的最佳猜测。

解决方案

为了给您的对象一个更好的类型名称,您可以在定义 Backbone 类型时通过第一个“protoProps”参数提供一个命名构造函数。只需添加一个包含对“父”构造函数的调用的构造函数属性,如下所示:

var Product = Backbone.Model.extend({
    constructor: function Product() {
        Backbone.Model.prototype.constructor.apply(this, arguments);
    }
});

您的 Product 模型实例现在在调试器中看起来非常好。

对您定义的每个 View、Model、Collection 和 Route 执行此操作有点麻烦。您可以使用猴子补丁 Backbone 的扩展功能来为您完成这项工作。

您首先需要建立定义类型名称的约定。在这里,我们使用一个__name__属性,您指定如下:

var Product = Backbone.Model.extend({
    __name__: 'Product'
    // other props
});

然后,您替换 Model、View、Collection 和 Route 使用的扩展函数来读取此属性并将命名构造函数添加到您的类型。您不需要修改backbone.js 本身,只需将以下内容包含在一个单独的脚本中,该脚本在backbone.js 之后加载。

(function () {

    function createNamedConstructor(name, constructor) {

        var fn = new Function('constructor', 'return function ' + name + '()\n'
            + '{\n'
            + '    // wrapper function created dynamically for "' + name + '" constructor to allow instances to be identified in the debugger\n'
            + '    constructor.apply(this, arguments);\n'
            + '};');
        return fn(constructor);
    }

    var originalExtend = Backbone.View.extend; // Model, Collection, Router and View shared the same extend function
    var nameProp = '__name__';
    var newExtend = function (protoProps, classProps) {
        if (protoProps && protoProps.hasOwnProperty(nameProp)) {
            // TODO - check that name is a valid identifier
            var name = protoProps[nameProp];
            // wrap constructor from protoProps if supplied or 'this' (the function we are extending)
            var constructor = protoProps.hasOwnProperty('constructor') ? protoProps.constructor : this;
            protoProps = _.extend(protoProps, {
                constructor: createNamedConstructor(name, constructor)
            });
        }
        return originalExtend.call(this, protoProps, classProps);
    };

    Backbone.Model.extend = Backbone.Collection.extend = Backbone.Router.extend = Backbone.View.extend = newExtend;
})();
于 2013-02-22T22:07:58.577 回答
4

是的。constructor您可以通过使用命名函数表达式覆盖模型/集合/视图来更改控制台显示名称。toString当模型被强制使用字符串类型时,覆盖以控制控制台输出也可能是有帮助的,例如,+运算符:

App.Model = Backbone.Model.extend({

  //define constructor using a named function expression
  constructor: function Model() {
    Backbone.Model.prototype.constructor.apply(this, arguments);
  },

  //override toString to return something more meaningful
  toString: function() {
    return "Model(" + JSON.stringify(this.attributes) + ")";
  }
});

所以:

var model = new Model({id:1,foo:"bar"})

console.log("state: " + model);
console.log(model);

你会得到:

state: Model({"id":1,"foo":"bar"})
► Model
于 2013-02-14T06:45:31.807 回答
2

考虑使用这个扩展的这个进行调试。

于 2013-06-07T05:39:50.453 回答
2

也尝试添加

"use strict"

在您的应用程序的顶部。它不会在backbone.marionette.js 的第1 行上给您提供堆栈跟踪错误(例如未定义),而是会输出实例并且是更具描述性的信息,例如未找到HistoryView。

于 2014-04-27T03:24:05.737 回答