How do I remove functions from object output?

497 Views Asked by At

I am learning about the constructor pattern.

To exercise what I am learning, I am building an in-memory model called Book inspired by the Mongoose API:

var assert = require("assert");

var Book = (function() {
    var books = [];
    var constructor = function(title, author, genre) {
        assert.ok(title, "title cannot be undefined");
        assert.ok(author, "author cannot be undefined");
        assert.ok(genre, "genre cannot be undefined");
        this.title = title;
        this.author = author;
        this.genre = genre;
        this.save = function() {
            books.push(this);
        };
        this.description = this.title + "is a " + this.genre + " by " + this.author;
    };
    constructor.find = function() {
        return books;
    };
    return constructor;
}());

With this model, I can create Book instances and save them to an in-memory store:

var book = new Book("The Great Gatsby", "F. Scott Fitzgerald", "Novel");
book.save();
var books = Book.find();
console.log(books);

// [ { title: 'The Great Gatsby',
//    author: 'F. Scott Fitzgerald',
//    genre: 'Novel',
//    save: [Function],
//    description: 'The Great Gatsbyis a Novel by F. Scott Fitzgerald' } ]

How do I remove the function property "save" from the output? I only want to show the properties.

I need to know because, I want to send the book to the client using Express and I do not want to convolute the response with functions.

(I come from a C# background and in C#, I would override a function in the System.Object base class called ToString that functions like console.log use internally. I do not know of any equivalent in JavaScript.)

3

There are 3 best solutions below

1
On

Yes, it's possible to override the default toString output:

var Book = (function() {
    var books = [];
    var constructor = function(title, author, genre) {
        assert.ok(title, "title cannot be undefined");
        assert.ok(author, "author cannot be undefined");
        assert.ok(genre, "genre cannot be undefined");
        this.title = title;
        this.author = author;
        this.genre = genre;
        this.save = function() {
            books.push(this);
        };
        this.description = this.title + "is a " + this.genre + " by " + this.author;
    };
    constructor.find = function() {
        return books;
    };
    constructor.prototype.toString = function() {
        return JSON.stringify(this);
    };
    return constructor;
}());
7
On

Short but hacky way:

books = JSON.parse(JSON.stringify(books));
console.log(books);

Note: JSON.stringify happens when you pass objects to the client via express using res.json(book). If all you want is to send the object down to the client then you don't need to do anything but pass your object(s) to res.json([obj|array]).

Encapsulating the behavior on the object itself.

var Book = (function() {
    var books = [];
    var constructor = function(title, author, genre) {
        assert.ok(title, "title cannot be undefined");
        assert.ok(author, "author cannot be undefined");
        assert.ok(genre, "genre cannot be undefined");
        this.title = title;
        this.author = author;
        this.genre = genre;
        this.save = function() {
            books.push(this);
        };
        this.description = this.title + "is a " + this.genre + " by " + this.author;
        this.stripFunctions = function () {
          var item = this;
          Object.keys(item).forEach(function (key) {
            if (item.hasOwnProperty(key) && typeof item[key] === 'function') {
              delete item[key];
            }
          });
          return item;
        };
    };
    constructor.find = function() {
        return books;
    };
    return constructor;
}());

books = books.map(function (book) { return book.stripFunctions(); });
console.log(books);

Note that the above method will remove any functions from your object instance, meaning you won't be able to call them anymore after that.

PS - It's not part of your question, but you should consider adding methods to the prototype for your constructor so that you aren't creating new functions every time you create an instance of Book.

Also, I'll re-iterate that if you send objects down from express using res.json then it calls JSON.stringify for you, and when you stringify an object, functions get stripped off anyway.

0
On

Define the function on the prototype:

var Book = (function() {
    var books = [];
    function Book (title, author, genre) {
        assert.ok(title, "title cannot be undefined");
        assert.ok(author, "author cannot be undefined");
        assert.ok(genre, "genre cannot be undefined");
        this.title = title;
        this.author = author;
        this.genre = genre;
        this.description = this.title + "is a " + this.genre + " by " + this.author;
    }
    Book.prototype.save = function () {
        books.push(this);
    };

    Book.find = function () {
        return books;
    };

    return Book;
}());

If I can help it, I want to encapsulate that behaviour in the Book constructor.

That sounds like an odd requirement, but if you really need that (for whatever reason), you can use Object.defineProperty to define the function without making it enumerable:

var Book = (function() {
    var books = [];
    function Book (title, author, genre) {
        assert.ok(title, "title cannot be undefined");
        assert.ok(author, "author cannot be undefined");
        assert.ok(genre, "genre cannot be undefined");
        this.title = title;
        this.author = author;
        this.genre = genre;
        this.description = this.title + "is a " + this.genre + " by " + this.author;

        Object.defineProperty(this, 'save', {
            value: function () {
                books.push(this);
            }
        });
    }

    Book.find = function () {
        return books;
    };

    return Book;
}());