I am currently transforming from Java to Javascript, and it's a bit hard for me to figure out how to extend objects the way I want it to do.
I've seen several people on the internet use a method called extend on object. The code will look like this:
var Person = {
name : 'Blank',
age : 22
}
var Robot = Person.extend({
name : 'Robo',
age : 4
)}
var robot = new Robot();
alert(robot.name); //Should return 'Robo'
Does anyone know how to make this work? I've heard that you need to write
Object.prototype.extend = function(...);
But I don't know how to make this system work. If it is not possible, please show me another alternative that extends an object.
This question is related to
javascript
function
object
prototype
extends
People who are still struggling for the simple and best approach, you can use Spread Syntax
for extending object.
var person1 = {_x000D_
name: "Blank",_x000D_
age: 22_x000D_
};_x000D_
_x000D_
var person2 = {_x000D_
name: "Robo",_x000D_
age: 4,_x000D_
height: '6 feet'_x000D_
};_x000D_
// spread syntax_x000D_
let newObj = { ...person1, ...person2 };_x000D_
console.log(newObj.height);
_x000D_
Note: Remember that, the property is farthest to the right will have the priority. In this example, person2
is at right side, so newObj
will have name Robo in it.
You want to 'inherit' from Person's prototype object:
var Person = function (name) {
this.name = name;
this.type = 'human';
};
Person.prototype.info = function () {
console.log("Name:", this.name, "Type:", this.type);
};
var Robot = function (name) {
Person.apply(this, arguments);
this.type = 'robot';
};
Robot.prototype = Person.prototype; // Set prototype to Person's
Robot.prototype.constructor = Robot; // Set constructor back to Robot
person = new Person("Bob");
robot = new Robot("Boutros");
person.info();
// Name: Bob Type: human
robot.info();
// Name: Boutros Type: robot
You might want to consider using helper library like underscore.js, which has it's own implementation of extend()
.
And it's also a good way to learn by looking at it's source code. The annotated source code page is quite useful.
This will make extend your properties create a new Object with the object parameter prototypes without altering the passed object.
function extend(object) {
if (object === null)
throw TypeError;
if (typeof object !== "object" && typeof object !== "function")
throw TypeError;
if (Object.create)
return Object.create(object);
function f() {}
;
f.prototype = p;
return new f();
}
But if you want to extend your Object without modifying it parameters, you can add extendProperty to your object.
var Person{
//some code
extend: extendProperty
}
//Enforce type checking an Error report as you wish
function extendProperty(object) {
if ((object !== null && (typeof object === "object" || typeof object === "function"))){
for (var prop in object) {
if (object.hasOwnProperty(prop))
this[prop] = object[prop];
}
}else{
throw TypeError; //Not an object
}
}
In the majority of project there are some implementation of object extending: underscore, jquery, lodash: extend.
There is also pure javascript implementation, that is a part of ECMAscript 6: Object.assign: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
You can simply do it by using:
Object.prototype.extend = function(object) {
// loop through object
for (var i in object) {
// check if the extended object has that property
if (object.hasOwnProperty(i)) {
// mow check if the child is also and object so we go through it recursively
if (typeof this[i] == "object" && this.hasOwnProperty(i) && this[i] != null) {
this[i].extend(object[i]);
} else {
this[i] = object[i];
}
}
}
return this;
};
update: I checked for
this[i] != null
sincenull
is an object
Then use it like:
var options = {
foo: 'bar',
baz: 'dar'
}
var defaults = {
foo: false,
baz: 'car',
nat: 0
}
defaults.extend(options);
This well result in:
// defaults will now be
{
foo: 'bar',
baz: 'dar',
nat: 0
}
*This example is updated for ES6 classes and TypeScript.
First off, and factually, Javascript is a prototypal language, not class-based. Its true nature is expressed in the prototypial form below, which you may come to see that is very simple, prose-like, yet powerful.
const Person = {
name: 'Anonymous', // person has a name
greet: function() { console.log(`Hi, I am ${this.name}.`}
}
const jack = Object.create(Person) // jack is a person
jack.name = 'Jack' // and has a name 'Jack'
jack.greet() // outputs "Hi, I am Jack."
This absolves the sometimes convoluted constructor pattern. A new object inherits from the old one, but is able to have its own properties. If we attempt to obtain a member from the new object (#greet()
) which the new object jack
lacks, the old object Person
will supply the member.
You don't need constructors, no new
instantiation (read why you shouldn't use new
), no super
, no self-made __construct
. You simply create Objects and then extend or morph them.
This pattern also offers immutability (partial or full), and getters/setters.
The TypeScript equivalent looks the same:
interface Person {
name: string,
greet: Function
}
const Person = {
name: 'Anonymous',
greet: function(): void { console.log(`Hi, I am ${this.name}.` }
}
const jack: Person = Object.create(Person)
jack.name = 'Jack'
jack.greet()
Person
protoypeconst Person = {
//attributes
firstName : 'Anonymous',
lastName: 'Anonymous',
birthYear : 0,
type : 'human',
//methods
name() { return this.firstName + ' ' + this.lastName },
greet() {
console.log('Hi, my name is ' + this.name() + ' and I am a ' + this.type + '.' )
},
age() {
// age is a function of birth time.
}
}
const person = Object.create(Person). // that's it!
Clean and clear. It's simplicity does not compromise features. Read on.
Person
Note: The correct terms are
prototypes
, and theirdescendants/copies
. There are noclasses
, and no need forinstances
.
const Skywalker = Object.create(Person)
Skywalker.lastName = 'Skywalker'
const anakin = Object.create(Skywalker)
anakin.firstName = 'Anakin'
anakin.birthYear = '442 BBY'
anakin.gender = 'male' // you can attach new properties.
anakin.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'
Person.isPrototypeOf(Skywalker) // outputs true
Person.isPrototypeOf(anakin) // outputs true
Skywalker.isPrototypeOf(anakin) // outputs true
If you feel less safe throwing away the constructors in-lieu of direct assignments, fair point. One common way is to attach a #create
method:
Skywalker.create = function(firstName, gender, birthYear) {
let skywalker = Object.create(Skywalker)
Object.assign(skywalker, {
firstName,
birthYear,
gender,
lastName: 'Skywalker',
type: 'human'
})
return skywalker
}
const anakin = Skywalker.create('Anakin', 'male', '442 BBY')
Person
prototype to Robot
When you branch the Robot
descendant from Person
prototype, you do not affect Skywalker
and anakin
:
// create a `Robot` prototype by extending the `Person` prototype:
const Robot = Object.create(Person)
Robot.type = 'robot'
Attach methods unique to Robot
Robot.machineGreet = function() {
/*some function to convert strings to binary */
}
// Mutating the `Robot` object doesn't affect `Person` prototype and its descendants
anakin.machineGreet() // error
Person.isPrototypeOf(Robot) // outputs true
Robot.isPrototypeOf(Skywalker) // outputs false
In TypeScript you would also need to extend the Person
interface:
interface Robot extends Person {
machineGreet: Function
}
const Robot: Robot = Object.create(Person)
Robot.machineGreet = function(): void { console.log(101010) }
const darthVader = Object.create(anakin)
// for brevity, property assignments are skipped because you get the point by now.
Object.assign(darthVader, Robot)
Darth Vader gets the methods of Robot
:
darthVader.greet() // inherited from `Person`, outputs "Hi, my name is Darth Vader..."
darthVader.machineGreet() // inherited from `Robot`, outputs 001010011010...
Along with other odd things:
console.log(darthVader.type) // outputs robot.
Robot.isPrototypeOf(darthVader) // returns false.
Person.isPrototypeOf(darthVader) // returns true.
Which elegantly reflects the "real-life" subjectivity:
"He's more machine now than man, twisted and evil." - Obi-Wan Kenobi
"I know there is good in you." - Luke Skywalker
function Person (firstName, lastName, birthYear, type) {
this.firstName = firstName
this.lastName = lastName
this.birthYear = birthYear
this.type = type
}
// attaching methods
Person.prototype.name = function() { return firstName + ' ' + lastName }
Person.prototype.greet = function() { ... }
Person.prototype.age = function() { ... }
function Skywalker(firstName, birthYear) {
Person.apply(this, [firstName, 'Skywalker', birthYear, 'human'])
}
// confusing re-pointing...
Skywalker.prototype = Person.prototype
Skywalker.prototype.constructor = Skywalker
const anakin = new Skywalker('Anakin', '442 BBY')
// #isPrototypeOf won't work
Person.isPrototypeOf(anakin) // returns false
Skywalker.isPrototypeOf(anakin) // returns false
If you want to increase code readability, you have to go for ES6 classes, which has increased in adoptation and browser compatibility (or a non-concern with babel):
class Person {
constructor(firstName, lastName, birthYear, type) {
this.firstName = firstName
this.lastName = lastName
this.birthYear = birthYear
this.type = type
}
name() { return this.firstName + ' ' + this.lastName }
greet() { console.log('Hi, my name is ' + this.name() + ' and I am a ' + this.type + '.' ) }
}
class Skywalker extends Person {
constructor(firstName, birthYear) {
super(firstName, 'Skywalker', birthYear, 'human')
}
}
const anakin = new Skywalker('Anakin', '442 BBY')
// prototype chain inheritance checking is partially fixed.
Person.isPrototypeOf(anakin) // returns false!
Skywalker.isPrototypeOf(anakin) // returns true
Admittedly, some of these problems are eradicated by the ES6 classes. When I wrote my answer five years ago, ES6 classes were budding, and has now matured for use.
But underneath the hood of ES6 classes is the obscured true prototypial nature of Javascript. So I was naturally disappointed of its implementation.
Nonetheless, that's not to say ES6 classes are bad. It provides a lot of new features and standardised a manner that is reasonably readable. Though it really should have not used the operator class
and new
to confuse the whole issue.
Writability, Configurability and Free Getters and Setters!
For free getters and setters, or extra configuration, you can use Object.create()'s second argument a.k.a propertiesObject. It is also available in #Object.defineProperty, and #Object.defineProperties.
To illustrate its usefulness, suppose we want all Robot
to be strictly made of metal (via writable: false
), and standardise powerConsumption
values (via getters and setters).
const Robot = Object.create(Person, {
// define your property attributes
madeOf: {
value: "metal",
writable: false,
configurable: false,
enumerable: true
},
// getters and setters
powerConsumption: {
get() { return this._powerConsumption },
set(value) {
if (value.indexOf('MWh')) return this._powerConsumption = value.replace('M', ',000k')
this._powerConsumption = value
throw new Error('Power consumption format not recognised.')
}
}
})
const newRobot = Object.create(Robot)
newRobot.powerConsumption = '5MWh'
console.log(newRobot.powerConsumption) // outputs 5,000kWh
And all prototypes of Robot
cannot be madeOf
something else:
const polymerRobot = Object.create(Robot)
polymerRobot.madeOf = 'polymer'
console.log(polymerRobot.madeOf) // outputs 'metal'
Per @osahyoun answer, I find the following as a better and efficient way to 'inherit' from Person's prototype object:
function Person(name){
this.name = name;
this.type = 'human';
}
Person.prototype.info = function(){
console.log("Name:", this.name, "Type:", this.type);
}
function Robot(name){
Person.call(this, name)
this.type = 'robot';
}
// Set Robot's prototype to Person's prototype by
// creating a new object that inherits from Person.prototype,
// and assigning it to Robot.prototype
Robot.prototype = Object.create(Person.prototype);
// Set constructor back to Robot
Robot.prototype.constructor = Robot;
Create new instances:
var person = new Person("Bob");
var robot = new Robot("Boutros");
person.info(); // Name: Bob Type: human
robot.info(); // Name: Boutros Type: robot
Now, by using Object.create:
Person.prototype.constructor !== Robot
Check also the MDN documentation.
PLEASE ADD REASON FOR DOWNVOTE
No need to use any external library to extend
In JavaScript, everything is an object (except for the three primitive datatypes, and even they are automatically wrapped with objects when needed). Furthermore, all objects are mutable.
Class Person in JavaScript
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
getName: function() {
return this.name;
},
getAge: function() {
return this.age;
}
}
/* Instantiate the class. */
var alice = new Person('Alice', 93);
var bill = new Person('Bill', 30);
Modify a specific instance/object.
alice.displayGreeting = function()
{
alert(this.getGreeting());
}
Modify the class
Person.prototype.getGreeting = function()
{
return 'Hi ' + this.getName() + '!';
};
Or simply say : extend JSON and OBJECT both are same
var k = {
name : 'jack',
age : 30
}
k.gender = 'male'; /*object or json k got extended with new property gender*/
thanks to ross harmes , dustin diaz
If you haven't yet figured out a way, use the associative property of JavaScript objects to add an extend function to the Object.prototype
as shown below.
Object.prototype.extend = function(obj) {
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
this[i] = obj[i];
}
}
};
You can then use this function as shown below.
var o = { member: "some member" };
var x = { extension: "some extension" };
o.extend(x);
Function.prototype.extends=function(ParentClass) {
this.prototype = new ParentClass();
this.prototype.constructor = this;
}
Then:
function Person() {
this.name = "anonym"
this.skills = ["abc"];
}
Person.prototype.profile = function() {
return this.skills.length // 1
};
function Student() {} //well extends fom Person Class
Student.extends(Person)
var s1 = new Student();
s1.skills.push("")
s1.profile() // 2
Please, Ignore my answer of 2015 since Javascript is now supports extends
keyword since ES6 (Ecmasctipt6 )
class Person {
constructor() {
this.name = "anonym"
this.skills = ["abc"];
}
profile() {
return this.skills.length // 1
}
}
Person.MAX_SKILLS = 10;
class Student extends Person {
} //well extends from Person Class
//-----------------
var s1 = new Student();
s1.skills.push("")
s1.profile() // 2
class Person {
static MAX_SKILLS = 10;
name = "anonym"
skills = ["abc"];
profile() {
return this.skills.length // 1
}
}
class Student extends Person {
} //well extends from Person Class
//-----------------
var s1 = new Student();
s1.skills.push("")
s1.profile() // 2
Mozilla 'announces' object extending from ECMAScript 6.0:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends
NOTE: This is an experimental technology, part of the ECMAScript 6 (Harmony) proposal.
class Square extends Polygon {
constructor(length) {
// Here, it calls the parent class' constructor with lengths
// provided for the Polygon's width and height
super(length, length);
// Note: In derived classes, super() must be called before you
// can use 'this'. Leaving this out will cause a reference error.
this.name = 'Square';
}
get area() {
return this.height * this.width;
}
set area(value) {
this.area = value; }
}
This technology is available in Gecko (Google Chrome / Firefox) - 03/2015 nightly builds.
Javascript uses a mechanism which is called prototypal inheritance. Prototypal inheritance is used when looking up a property on an object. When we are extending properties in javascript we are inheriting these properties from an actual object. It works in the following manner:
myObj.foo
or myObj['foo']
) the JS engine will first look for that property on the object itselfWhen we want to extend from a object in javascript we can simply link this object in the prototype chain. There are numerous ways to achieve this, I will describe 2 commonly used methods.
1. Object.create()
Object.create()
is a function that takes an object as an argument and creates a new object. The object which was passed as an argument will be the prototype of the newly create object. For example:
// prototype of the dog_x000D_
const dogPrototype = {_x000D_
woof: function () { console.log('woof'); }_x000D_
}_x000D_
_x000D_
// create 2 dog objects, pass prototype as an argument_x000D_
const fluffy = Object.create(dogPrototype);_x000D_
const notFluffy = Object.create(dogPrototype);_x000D_
_x000D_
// both newly created object inherit the woof _x000D_
// function from the dogPrototype_x000D_
fluffy.woof();_x000D_
notFluffy.woof();
_x000D_
2. Explicitly setting the prototype property
When creating objects using constructor functions, we can set add properties to its prototype object property. Objects which are created form a constructor function when using the new
keyword, have their prototype set to the prototype of the constructor function. For example:
// Constructor function object_x000D_
function Dog (name) {_x000D_
name = this.name;_x000D_
}_x000D_
_x000D_
// Functions are just objects_x000D_
// All functions have a prototype property_x000D_
// When a function is used as a constructor (with the new keyword)_x000D_
// The newly created object will have the consturctor function's_x000D_
// prototype as its prototype property_x000D_
Dog.prototype.woof = function () {_x000D_
console.log('woof');_x000D_
}_x000D_
_x000D_
// create a new dog instance_x000D_
const fluffy = new Dog('fluffyGoodBoyyyyy');_x000D_
// fluffy inherits the woof method_x000D_
fluffy.woof();_x000D_
_x000D_
// can check the prototype in the following manner_x000D_
console.log(Object.getPrototypeOf(fluffy));
_x000D_
And another year later, I can tell you there is another nice answer.
If you don't like the way prototyping works in order to extend on objects/classes, take alook at this: https://github.com/haroldiedema/joii
Quick example code of possibilities (and many more):
var Person = Class({
username: 'John',
role: 'Employee',
__construct: function(name, role) {
this.username = name;
this.role = role;
},
getNameAndRole: function() {
return this.username + ' - ' + this.role;
}
});
var Manager = Class({ extends: Person }, {
__construct: function(name)
{
this.super('__construct', name, 'Manager');
}
});
var m = new Manager('John');
console.log(m.getNameAndRole()); // Prints: "John - Manager"
In ES6, you may use spread operator like
var mergedObj = { ...Obj1, ...Obj2 };
Note that Object.assign() triggers setters whereas spread syntax doesn't.
For more info see link, MDN -Spread Syntax
Old Answer :
In ES6, there is Object.assign
for copying property values. Use {}
as first param if you don't want to modify the target object (the first param passed).
var mergedObj = Object.assign({}, Obj1, Obj2);
For more details see link, MDN - Object.assign()
In case if you need is a Polyfill for ES5, the link offers it too. :)
Source: Stackoverflow.com