Asked  7 Months ago    Answers:  5   Viewed   38 times

Has anyone noticed this behavior before? This really threw me off... I would have expected prototyped arrays to be private to each class instance rather than shared between all class instances.

Can someone verify that this is the correct behavior and perhaps explain this behavior in more detail?

Notice the commented code and how it affects the behavior of the script.

<html>
<head>

<script type="text/javascript">

function print_r( title, object ) {

    var output = '';
    for( var key in object ) {

        output += key + ": " + object[ key ] + "n";

    }

    output = title + "nn" + output;

    alert( output );

}

function Sandwich() {

    // Uncomment this to fix the problem
    //this.ingredients = [];

}

Sandwich.prototype = {

    "ingredients" : [],
    "addIngredients" : function( ingArray ) {

        for( var key in ingArray ) {

            this.addIngredient( ingArray[ key ] );

        }

    },
    "addIngredient" : function( thing ) {

        this.ingredients.push( thing );

    }

}

var cheeseburger = new Sandwich();
cheeseburger.addIngredients( [ "burger", "cheese" ] );

var blt = new Sandwich();
blt.addIngredients( [ "bacon", "lettuce", "tomato" ] );

var spicy_chicken_sandwich = new Sandwich();
spicy_chicken_sandwich.addIngredients( [ "spicy chicken pattie", "lettuce", "tomato", "honey dijon mayo", "love" ] );

var onLoad = function() {

    print_r( "Cheeseburger contains:", cheeseburger.ingredients );

};

</script>

</head>
<body onload="onLoad();">
</body>
</html>

Many thanks.

 Answers

79

The prototype of an object is just an object. The prototype properties are shared between all objects that inherit from that object. No copies of the properties are made if you create a new instance of a "class" (classes don't exist anyway in JS), i.e. an object which inherits from the prototype.

It only makes a difference on how you use the these inherited properties:

function Foo() {}

Foo.prototype = {
    array: [],
    func: function() {}
}

a = new Foo();
b = new Foo();

a.array.push('bar');
console.log(b.array); // prints ["bar"]

b.func.bar = 'baz';
console.log(a.func.bar); // prints baz

In all these cases you are always working with the same object.

But if you assign a value to a property of the object, the property will be set/created on the object itself, not its prototype, and hence is not shared:

console.log(a.hasOwnProperty('array')); // prints false
console.log(a.array); // prints ["bar"]
a.array = ['foo'];
console.log(a.hasOwnProperty('array')); // prints true
console.log(a.array); // prints ["foo"]
console.log(b.array); // prints ["bar"]

If you want to create own arrays for each instance, you have to define it in the constructor:

function Foo() {
    this.array = [];
}

because here, this refers to the new object that is generated when you call new Foo().

The rule of thumb is: Instance-specific data should be assigned to the instance inside the constructor, shared data (like methods) should be assigned to the prototype.


You might want to read Details of the object model which describes differences between class-based vs. prototype-based languages and how objects actually work.

Update:

You can access the prototype of an object via Object.getPrototypeOf(obj) (might not work in very old browsers), and Object.getPrototypeOf(a) === Object.getPrototypeOf(b) gives you true. It is the same object, also known as Foo.prototype.

Tuesday, June 1, 2021
 
zhartaunik
answered 7 Months ago
81

There are about a hundred terminology issues here, mostly built around someone (not you) trying to make their idea sound like The Best.

All object oriented languages need to be able to deal with several concepts:

  1. encapsulation of data along with associated operations on the data, variously known as data members and member functions, or as data and methods, among other things.
  2. inheritance, the ability to say that these objects are just like that other set of objects EXCEPT for these changes
  3. polymorphism ("many shapes") in which an object decides for itself what methods are to be run, so that you can depend on the language to route your requests correctly.

Now, as far as comparison:

First thing is the whole "class" vs "prototype" question. The idea originally began in Simula, where with a class-based method each class represented a set of objects that shared the same state space (read "possible values") and the same operations, thereby forming an equivalence class. If you look back at Smalltalk, since you can open a class and add methods, this is effectively the same as what you can do in Javascript.

Later OO languages wanted to be able to use static type checking, so we got the notion of a fixed class set at compile time. In the open-class version, you had more flexibility; in the newer version, you had the ability to check some kinds of correctness at the compiler that would otherwise have required testing.

In a "class-based" language, that copying happens at compile time. In a prototype language, the operations are stored in the prototype data structure, which is copied and modified at run time. Abstractly, though, a class is still the equivalence class of all objects that share the same state space and methods. When you add a method to the prototype, you're effectively making an element of a new equivalence class.

Now, why do that? primarily because it makes for a simple, logical, elegant mechanism at run time. now, to create a new object, or to create a new class, you simply have to perform a deep copy, copying all the data and the prototype data structure. You get inheritance and polymorphism more or less for free then: method lookup always consists of asking a dictionary for a method implementation by name.

The reason that ended up in Javascript/ECMA script is basically that when we were getting started with this 10 years ago, we were dealing with much less powerful computers and much less sophisticated browsers. Choosing the prototype-based method meant the interpreter could be very simple while preserving the desirable properties of object orientation.

Friday, June 4, 2021
 
SuperString
answered 6 Months ago
78

The this value will not work like that, it refers to a value determined by the actual execution context, not to your object literal.

If you declare a function member of your object for example, you could get the desired result:

var closure =  {
  myPic: document.getElementById('pic1'),
  getPicArray: function () {
    return [this.myPic];
  }
};
//...
closure.getPicArray();

Since the this value, inside the getPicArray function, will refer to your closure object.

See this answer to another question, where I explain the behavior of the this keyword.

Edit: In response to your comment, in the example that I've provided, the getPicArray method will generate a new Array object each time it is invoked, and since you are wanting to store the array and make changes to it, I would recommend you something like this, construct your object in two steps:

var closure =  {
  myPic: document.getElementById('pic1')
};
closure.picArray = [closure.myPic];

Then you can modify the closure.picArray member without problems.

Friday, July 23, 2021
 
Zigglzworth
answered 5 Months ago
12

You can simply filter one array's elements by setting the condition based on other array's elements like.

var myFirstObjArray = [{foo: 1, bar: 1}, {foo: 3, bar: 3}, {foo: 4, bar: 5}],
    mySecondObjArray = [{foo: 2}, {foo: 4}, {foo: 5}],
    
    firstArray  = myFirstObjArray.filter(o=> !mySecondObjArray.some(i=> i.foo === o.foo));
    
    secondArray  = mySecondObjArray.filter(o=> !myFirstObjArray.some(i=> i.foo === o.foo));
    
    console.log(firstArray.map(o=> {return {'foo' :  o.foo}}))
    console.log(secondArray.map(o=> {return {'foo' :  o.foo}}))

Ps:

The some() method tests whether at least one element in the array passes the test implemented by the provided function. And I've added a function which just checks if foo property exists in the other array with the same value to be able to filter from the first array.

At the end you can use .map to filter out the desired key value pairs

Hope that makes sense

Read more about .some and filter

Tuesday, August 3, 2021
 
Trav L
answered 4 Months ago
94

ES6 now allows for this:

var obj = {
  [prop]: 'value'
};
Sunday, September 12, 2021
 
Matthew J Morrison
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share