简体   繁体   中英

How to add a getter to an object

This probably has a prior answer, but I haven't been able to find it.

I have some code that gets data from a database and returns an entity such as:

   var customer1 = {first: "Greg", last: "Gum"};

How do I add a getter to this object so that I can call a FullName getter:

   var theFullName = customer1.fullName;

What I don't understand is the difference between adding a getter to an object, and adding a getter to a "class" so that it becomes available to all future objects of that type. The thing is that the code that creates the object is a black box, so I don't really have access to that code. I don't know the best way to resolve this.

If you just want to define the property on the object literal that has been returned, simply use Object.defineProperty :

var cdata = {first: 'Foo', last: 'Bar'}
Object.defineProperty(cdata, 'fullName', {
  get: function() { return this.first + ' ' + this.last; }
});

console.log(cdata.fullName); // Foo Bar

However, if you want to create a new object from the returned literal, one method would be:

function Customer(data) {
  var k;
  for (k of Object.keys(data)) {
    Object.defineProperty(this, k, {
      value: data[k]
    });
  }

  Object.defineProperty(this, 'fullName', {
    get: function() { return this.first + ' ' + this.last; }
  });
}

var cdata = {first: 'Foo', last: 'Bar'};
var customer = new Customer(cdata);

console.log(customer.fullName); // Foo Bar

But a more memory efficient method is:

function Customer(data) {
  var k;
  for (k of Object.keys(data)) {
    Object.defineProperty(this, k, {
      value: data[k]
    });
  }
}

Customer.prototype = {
  get fullName() {
    return this.first + ' ' + this.last;
  }
};

var cdata = {first: 'Foo', last: 'Bar'};
var customer = new Customer(cdata);

console.log(customer.fullName); // Foo Bar

The first Customer definition adds an instance property fullName which will consume memory for each instance of Customer that is created. The second definition defines the fullName property as a prototype property, thus saving a small amount of memory.

EDIT: You are looking for this:

var customer1 = {first: "Greg", last: "Gum"};
Object.defineProperty(customer1, "fullName", {
  get: function() { return this.first + ' ' + this.last; }
});

var theFullName = customer1.fullName;

A more general aproach:

function addGetter(obj, name, func) {
    Object.defineProperty(obj, name, {
      get: func
    });
}

var customer1 = {first: "Greg", last: "Gum"};
addGetter(customer1, "fullName", function() { return this.first + ' ' + this.last; } )

var theFullName = customer1.fullName;

You could even protoype Object to do sth like customer1.getter("fullName", function() { return this.first + ' ' + this.last; } )


OLD: I have a nice way of doing it with TypeScript, it compiles to this JavaScript:

var Customer = (function () {
    function Customer(first, last) {
        this.first = first;
        this.last = last;
    }
    Object.defineProperty(Customer.prototype, "fullName", {
        get: function () { return this.first + " " + this.last; },
        enumerable: true,
        configurable: true
    });
    return Customer;
})();
var customer1 = new Customer("Greg", "Gum");
var theFullName = customer1.fullName;

However the TypeScript looks way nicer:

class Customer {
    first: string
    last: string

    constructor(first: string, last: string) {
        this.first = first
        this.last = last        
    }

    get fullName() { return this.first + " " + this.last }
}

var customer1 = new Customer("Greg", "Gum")
var theFullName = customer1.fullName

You can play with it here

One way you can solve this is to create your own wrapper class Customer that wraps the received object and adds the getter.

Furthermore, you want to add the getter on the prototype of your Costumer class so that it is only defined once and available for all future instances. Something like:

function Customer(c) {
    this.firstName = c.first;
    this.lastName = c.last;
}

Customer.prototype.getFullName = function() {
    return this.firstName + " " + this.lastName;
}

And you will use it like:

var c = getCustomerFromBlackBox();
var customer = new Customer(c);

If you are using ES6, then you can take advantage of its more robust syntax:

class Customer {

  constructor(c) {
    this.firstName = c.first;
    this.lastName = c.last;
  }

  fullName() { 
    return this.firstName + " " + this.lastName;
  }
}

Based on your comment that you want to edit the received object itself, well then you can do just that:

var c = getCustomerFromBlackBox();

if(typeof c === 'object' && c !== null) {     // make sure c is an object
    c.getFullName = function() {              // add a getter
        return this.first + " " + this.last;
    }
}

Do note that this will add the getter code to every object you receive and thus will take more memory.

Also, since you don't know how this object is created, you might want to add checks to make sure first and last strings to actually exist and perhaps add specific values in case they don't.


EDIT :

Since you said in your comment that you know that your first and last names will never change, and that you want a property rather than a getter function, you can literally just add this property each time you read a customer:

var c = getCustomerFromBlackBox();
c.fullName = c.first + " " + c.last; // Can also add same checks to make sure c is an object and c.first and c.last are strings

Again, this is added to every object and has a similar memory overhead as adding a direct getter function (and not on the prototype)


A good approach that I missed is using Object.defineProperty as shown in James Sumner's answer .

Object.defineProperty(c, 'fullName', {
  get: function() { return this.first + " " + this.last; }
});

This approach adds the property directly on the object (not the prototype) and additionally makes the property immutable meaning you cannot accidentally delete it.

You have to create a constructor to use prototypal inheritance in JavaScript.

Eg like below. Check JS Fiddle .

var customer1Data = {first: "Greg", last: "Gum"};

var Customer = function(customerData ) {
    this.first = customer1Data.first;
    this.last = customer1Data.last;
}

Customer.prototype.fullName = function() {
   return this.first + ' '+this.last;
}

var customer1 = new Customer(customer1Data );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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