Get a Backbone Model instance's model/class na

2019-04-17 23:13发布

Given an instance of Backbone model, how can I know the "class" (so to speak) of this instance ?

For instance:

class Car extends Backbone.Model

mycar = new Car()

And what I need is:

mycar.modelName # => 'Car'

5条回答
我命由我不由天
2楼-- · 2019-04-17 23:51

As of writing you can get a model's class name by doing the following:

model.constructor.name

This works for me in the Chrome browser.

查看更多
老娘就宠你
3楼-- · 2019-04-18 00:00

I tried Mitch's suggestion but in Chrome, model.constructor.name is always "" for me.

I chose instead to use a convention. I now create a class property using the second param of extend that works as follows:

directory.models.SomeModel = Backbone.Model.extend({ 
    //  usual model instance stuff
}, {
    modelType: "SomeModel"   // class property
});

I found that I can always get what I want like this:

var mt = model.constructor.modelType;
查看更多
手持菜刀,她持情操
4楼-- · 2019-04-18 00:05

This is already resolved in all browsers except for IE, but a polyfill is easy to make. The answer is Function.name:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name

Definitely take advantage of this! In ES6 classes, a constructor's Function.name is the class name, so in ES6:

class Car { /*...*/ }
var c = new Car();
console.log(c.constructor.name); // "Car"

In current ES5, not all libraries take advantage of this. Backbone, for example, does not. :( Backbone constructors are anonymous functions, so

var m = new Backbone.Model();
console.log(m.constructor.name); // "undefined" :(

unfortunately. Like other's have said, if you're using Backbone, you'll have to make your own naming system.

Here's a polyfill for Function.name:

// Polyfill for Function.name on browsers that do not support it (IE):
// See: http://stackoverflow.com/questions/6903762/function-name-not-supported-in-ie
if (!(function f() {}).name) {
    Object.defineProperty(Function.prototype, 'name', {
        get: function() {
            var name = this.toString().match(/^\s*function\s*(\S*)\s*\(/)[1];

            // For better performance only parse once, and then cache the
            // result through a new accessor for repeated access.
            Object.defineProperty(this, 'name', { value: name });

            return name;
        }
    });
}
查看更多
We Are One
5楼-- · 2019-04-18 00:07

It's problematic in general, I think. I was going to suggest some of the options mentioned here ( How do I get the name of an object's type in JavaScript? ) but I was having issues with the first option. You could always extend all your models to have a function that returned a "name" for the model, but I can understand why you might find that less than satisfactory.

查看更多
Explosion°爆炸
6楼-- · 2019-04-18 00:12

You can query the inheritance tree (aka prototype chain) for your object instance like this:

object.__proto__.__proto__.__proto__

You can see what is available. Any type name property will have to be added by you to each view and/or model manually. This is the cleanest way to go. I recommend this. Simply add 'className: "MyCompany.MyProject.MyArea.MyView"' to each of your views and/or models.

If you don't add a className property to your objects, there is a slightly hacky way to still get it (eg for debug output purposes):

Assuming you do something like: "MyCompany.MyProject.MyArea.MyView = Backbone.View.extend({" the view/model class name is in a global namespace var name. From the object instance we cannot reach that global var name. So while not really ideal, the best we can do is traverse the namespace looking for the var:

function findBackboneClassName(ns, object, prefix, depth) {
    prefix = typeof prefix !== 'undefined' ? prefix : "";
    depth = typeof depth !== 'undefined' ? depth : 0;
    if (depth > 5) return null;

    for (i in ns) {
        try { ns[i]; } catch (e) { continue; }
        if (/top|window|document|parent|frames|self|chrome|form|theForm/.test(i)) continue;
        //console.log(">" + prefix + "." + i + " " + typeof(ns[i]));

        if (ns[i] == object.constructor) {
            //console.log("Found:", prefix + "." + i);
            return prefix + "." + i;
        }

        if (typeof (ns[i]) == "object") {
            var r = findBackboneClassName(ns[i], object, prefix + (prefix != "" ? "." : "") + i, depth + 1);
            if (r != null) return r;
        }
    }
    return null;
}
findBackboneClassName(window, myBackboneViewInstance);

Ideally you are using a namespace for your types other than "window". That makes it much cleaner and easier to traverse. You simply replace "window" with the base of your namespace and pass in the desired prefix if you want it prefixed properly. You can also remove a couple lines like the try..catch and the if..continue.

findBackboneClassName(MyCompany.MyProject, myBackboneViewInstance, "MyCompany.MyProject");

The trick of getting class name from objectInstance.constructor does not work for backbone because of the way it does inheritance. All ctors look the same for me: "function () { return parent.apply(this, arguments); }".

查看更多
登录 后发表回答