Debugging Javascript (Backbone and Marionette)

后端 未结 4 1647
余生分开走
余生分开走 2021-02-01 08:24

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 typ

相关标签:
4条回答
  • 2021-02-01 08:27

    Consider using this library of this extension for debugging.

    0 讨论(0)
  • 2021-02-01 08:39

    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.

    0 讨论(0)
  • 2021-02-01 08:48

    Background

    It is interesting to look at why the browser uses “child” to display the type of Backbone objects in the console / debugger.

    All JavaScript objects have a constructor property, a reference to the function used to create the object. 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. However, only functions defined using named function expressions get a useful name property:

    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. 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/

    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. 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.

    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:

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

    This is why you see “child” next to your Backbone objects in the console / debugger. 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. 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.

    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:

    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. You don’t need to modify backbone.js itself, just include the following in a separate script that is loaded after 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;
    })();
    
    0 讨论(0)
  • 2021-02-01 08:51

    Yes. You can change the console display name by overriding a model/collection/view constructor using a named function expression. 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:

    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
    
    0 讨论(0)
提交回复
热议问题