简体   繁体   English

调试Javascript(Backbone和Marionette)

[英]Debugging Javascript (Backbone and Marionette)

Right now, while I am debugging backbone or marionette using the chrome dev tools, I end up setting break points and whatnot, but once the code pauses, its hard to tell what type of objects i'm working with because chrome labels everything a "child". 现在,当我使用chrome dev工具调试主干或牵线木偶时,我最终设置了断点和诸如此类的东西,但是一旦代码暂停,我很难分辨出我正在使用什么类型的对象,因为chrome标记了所有内容“儿童”。
(I think because that's the constructor function) (我想因为那是构造函数)

Is there any easy way to either change this declaration or determine which type of model/collection i'm using. 是否有任何简单的方法来更改此声明或确定我正在使用的模型/集合类型。

The amount craziness this causes in me wants to start doing something like this: 这引起的疯狂欲望想要开始做这样的事情:

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

I don't really like it, because its... ugly, its a variable... and it only helps when I inspect and expand the variable... it would be great to change the name chrome uses to display it. 我真的不喜欢它,因为它......丑陋,它是一个变量......它只有在我检查和扩展变量时才有用......更改名称chrome用来显示它会很棒。

In any case, does anyone know how to change the name? 无论如何,有谁知道如何更改名称? or have some other cleaner convention you use? 或者你使用其他一些清洁惯例?

Thanks! 谢谢!

Matt 马特

Background 背景

It is interesting to look at why the browser uses “child” to display the type of Backbone objects in the console / debugger. 有趣的是,为什么浏览器使用“child”来显示控制台/调试器中的Backbone对象的类型。

All JavaScript objects have a constructor property, a reference to the function used to create the object. 所有JavaScript对象都有一个构造函数属性,即对用于创建对象的函数的引用。 The constructor is used by the browser to display the object's “type” in the console / debugger. 浏览器使用构造函数在控制台/调试器中显示对象的“类型”。 The value of the constructor function's name property will be used if it is not empty. 如果构造函数的name属性不为空,则将使用它的值。 However, only functions defined using named function expressions get a useful name property: 但是,只有使用命名函数表达式定义的函数才能获得有用的name属性:

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

Anonymous functions have an empty name property: 匿名函数具有空名称属性:

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

So, what happens with anonymous functions? 那么,匿名函数会发生什么? Chrome infers the name of anonymous functions from the name of the variable or property to which the function was first assigned. Chrome从首次分配函数的变量或属性的名称中推断出匿名函数的名称。 Here are some examples: 这里有些例子:

// 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() { … }
};

A more detailed script is available here: http://jsfiddle.net/danmalcolm/Xa7ma/6/ 这里有一个更详细的脚本: http//jsfiddle.net/danmalcolm/Xa7ma/6/

The browser appears to get this name from the source code - there isn't a JavaScript feature that can tell you at runtime the name of the first variable that a function was assigned to. 浏览器似乎从源代码中获取此名称 - 没有JavaScript功能可以在运行时告诉您功能分配给的第一个变量的名称。 Other browsers support a convention where a displayName property defined on anonymous constructor functions is used, but this doesn't currently happen in Chrome: http://code.google.com/p/chromium/issues/detail?id=17356 . 其他浏览器支持使用匿名构造函数定义的displayName属性的约定,但目前在Chrome中不会发生这种情况: http//code.google.com/p/chromium/issues/detail?id = 17356

Returning to Backbone, assuming you're not using a custom constructor (see below), your type will end up with an anonymous constructor function, created in Backbone's extend function used by Model, View, Collection and Route as follows: 回到Backbone,假设你没有使用自定义构造函数(见下文),你的类型将最终得到一个匿名构造函数,在Backbone的模型,视图,集合和路由使用的扩展函数中创建,如下所示:

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

This is why you see “child” next to your Backbone objects in the console / debugger. 这就是您在控制台/调试器中看到Backbone对象旁边的“child”的原因。 It is the browser's best guess at a suitable name for your object's constructor. 这是浏览器对对象构造函数的合适名称的最佳猜测。

Solutions 解决方案

To give your objects a better type name, you can supply a named constructor via the first “protoProps” argument when you define your Backbone types. 为了给对象提供更好的类型名称,在定义Backbone类型时,可以通过第一个“protoProps”参数提供命名构造函数。 Just add a constructor property that wraps a call to the “parent” constructor as follows: 只需添加一个构造函数属性,该属性包含对“父”构造函数的调用,如下所示:

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

Your Product model instances will now look really nice in the debugger. 您的产品模型实例现在在调试器中看起来非常好。

It is a bit cumbersome to do this for every View, Model, Collection and Route that you define. 对于您定义的每个视图,模型,集合和路由,执行此操作有点麻烦。 You can monkey patch Backbone's extend function to do the work for you. 您可以通过猴子补丁Backbone的扩展功能为您完成工作。

You first need to establish a convention for defining the names of your types. 首先需要建立一个约定来定义类型的名称。 Here we're using a __name__ property, which you specify as follows: 这里我们使用__name__属性,您可以按如下方式指定:

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

You then replace the extend function used by Model, View, Collection and Route to read this property and add a named constructor to your type. 然后,您可以替换Model,View,Collection和Route使用的extend函数来读取此属性,并为您的类型添加命名构造函数。 You don't need to modify backbone.js itself, just include the following in a separate script that is loaded after backbone.js. 您不需要修改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;
})();

Yes. 是。 You can change the console display name by overriding a model/collection/view constructor using a named function expression. 您可以通过使用命名函数表达式覆盖模型/集合/视图constructor函数来更改控制台显示名称。 It may also be helpul to override toString to control the console output when the model is forced to string type with, say, the + operator: 当模型被强制为字符串类型,例如+运算符时,覆盖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) + ")";
  }
});

So with: 所以:

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

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

You'll get: 你会得到:

state: Model({"id":1,"foo":"bar"})
► Model

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

Also try adding 也尝试添加

"use strict"

at the top of your application. 在您的应用程序的顶部。 Instead of giving you stack trace errors like undefined on line 1 of backbone.marionette.js it will output the instance and be more descriptive information like HistoryView was not found. 而不是像backbone.marionette.js的第1行那样给出堆栈跟踪错误,例如未定义,它将输出实例,并且找不到像HistoryView这样的更具描述性的信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM