Debugging Javascript (Backbone and Marionette)

Debugging Javascript (Backbone and Marionette)

本文关键字:Marionette and Backbone Javascript Debugging      更新时间:2023-09-26

现在,当我使用chrome dev工具调试主干或提线木偶时,我最终会设置断点等等,但一旦代码暂停,就很难判断我使用的是什么类型的对象,因为chrome将所有对象都标记为"子对象"
(我认为是因为这是构造函数)

有什么简单的方法可以更改这个声明或确定我使用的模型/集合的类型吗。

这让我非常疯狂,我想开始做这样的事情:

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

我真的不喜欢它,因为它。。。丑陋,这是一个变量。。。只有当我检查并展开变量时,它才有帮助。。。如果能更改chrome用来显示它的名称,那就太好了。

无论如何,有人知道如何更改名称吗?或者你用其他更清洁的约定?

谢谢!

Matt

背景

有趣的是,浏览器为什么使用"子"来显示控制台/调试器中的主干对象类型。

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

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

匿名函数的名称属性为空:

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,假设您没有使用自定义构造函数(见下文),您的类型将以匿名构造函数结束,该函数是在Model、View、Collection和Route使用的Backbone扩展函数中创建的,如下所示:

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

这就是为什么您在控制台/调试器中的主干对象旁边会看到"子"。这是浏览器对对象构造函数的合适名称的最佳猜测。

解决方案

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

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

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

对您定义的每个视图、模型、集合和路线执行此操作有点麻烦。您可以使用猴子补丁Backbone的扩展功能为您完成工作。

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

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

然后替换Model、View、Collection和Route用来读取此属性的扩展函数,并将命名构造函数添加到类型中。您不需要修改backline.js本身,只需在backline.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;
})();

是。您可以通过使用命名函数表达式覆盖模型/集合/视图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

请考虑使用此扩展的库进行调试。

同时尝试添加

"use strict"

位于应用程序顶部。它不会像backbone.marionette.js的第1行上的undefined那样向您提供堆栈跟踪错误,而是会输出实例,并提供更具描述性的信息,比如找不到HistoryView。