I run the following in node: why does the first console.log show:
greet: [Function],
and the second console.log show:
greet: [Function: greet]
Is there any difference? (note, console.log in chrome doesn't show the difference)
function deepCopy(obj){
/* an object has key value pairs
check if val is object
if true deepCopy(val)
else return value */
const keys = Object.keys(obj); // gets keys of object
const newObject = {}
for(index in keys){
if(typeof(obj[keys[index]]) === 'object') {
newObject[keys[index]] = deepCopy(obj[keys[index]])
}else{
newObject[keys[index]] = obj[keys[index]]
}
}
return newObject
}
o={
name:'David',
teach:true,
obj:{
greet:function(){
console.log('hi!')},
last:'cross'
}
}
o2 = deepCopy(o)
o.obj.greet=function(){
console.log('bye')}
o.name='george'
console.log(o)
console.log(o2)
o.obj.greet()
o2.obj.greet()
When you declare the method { great:function(){} }, it will have a .name and will be print out by console.log. Even you pass it around (assign to o2 via deepCopy), it will keep it name.
When you create an anonymous function function(){}, it's .name is empty. which you assign it to o.great = function(){}.
I create a simplify version to explain op question to test.
const a = { great: function(){} }; // in object declaration, method great as name "great" (x)
const b = {};
b.great = function(){}; // b.great is point to an anonymous function with no name (y)
// When console.log a function, if function has name, it will show out
console.log("a.great", a.great);
console.log("b.great", b.great);
console.log("a.great.name", JSON.stringify(a.great.name)); // "great"
console.log("b.great.name", JSON.stringify(b.great.name)); // "";
b.great = a.great; // simliar when you deep copy o2 from o
// in this case, b.great is point to the method great (x)
// so when you log out the name, it will print out
console.log("b.great.name", JSON.stringify(b.great.name)); // "great"
a.great = function(){}; // similar when o.great get re-assign
// a.great got re-assign to an anonymous function with no name (z)
// so when you log out the name, it will print out ""
console.log("a.great.name", JSON.stringify(a.great.name)); // ""
// Finally:
console.log(a.great);
console.log(b.great);
Related
It seems to me that using getters and setters for an object inside a class has no point to it. As I understand it, get/set is useful because it prevents someone outside the class changing something that shouldn't be changed or changing it to something it shouldn't be. However it seems pointless for objects. For example, I have a person with an address, I want to prevent editing the address, you can only view it:
class Person{
constructor(name, address){
this._name = name;
this._address = address;
}
get address(){
return this._address;
}
}
let bob = new Person("bob", {
street: "123 Main Street",
city: "Los Angelos",
state: "California"
});
But then you can still edit it like this:
let address = bob.address;
address.state = "New York";
To prevent this, I would think that you have to return a copy of the object instead of the reference. However, as far as i know, there is no standard way to deep clone an object. So you either have to shallow clone it, which seems not ideal if you have lots of nested references, or just return the reference to the object, which can be edited.
Am I missing something here?
Consider this class.
class Test {
constructor(val) {
this._foo = val;
}
set foo(val) {
throw new Error("It's not possible to change the foo property!")
}
set boo(val) {
console.log("setting _boo")
this._boo = val;
}
get foo() {
console.log("getting _foo");
return this._foo;
}
}
try {
let test = new Test('foooooo');
test.boo = "booooo";
console.log(`foo: ${test.foo}`);
test.foo = "bar";
} catch (e) {
console.log(e);
}
With "Setter" it's possible to control initializing properties.
You can see that it's possible to change the value of "boo" property but any attempt to change the value of the "foo" will throw an exception.
With "Getter" it's possible to control retrieving the value of properties.
You can see that it's possible to retrieve the value of "foo" property but not "boo" and its value is private.
PS:
Here are some examples to better understand JS behavior with objects and arrays:
//This works fine:
//Object
const obj = {};
obj.foo = 'bar';
console.log(obj); // {foo : 'bar'}
obj.foo = 'bar2';
console.log(obj); // {foo : 'bar2'}
//---------------------------------------
//Array:
const arr = [];
arr.push('foo');
console.log(arr); // ['foo']
arr.unshift("foo2");
console.log(arr); // ['foo2', 'foo']
arr.pop();
console.log(arr); // ['foo2']
//===========================================
//but these won't work:
const obj = {};
obj = {foo: 'bar'}; // error - re-assigning
const arr = ['foo'];
const arr = ['bar']; // error - re-declaring
const foo = 'bar';
foo = 'bar2'; // error - can not re-assign
var foo = 'bar3'; // error - already declared
function foo() {}; // error - already declared
New Example:
class A {
constructor(val) {
this._foo = val;
}
set foo(val) {
throw new Error("It's not possible to change the foo property!")
}
get foo() {
return this._foo;
}
}
class B {
constructor(val) {
this._obj = new A(val);
}
get Obj() {
return this._obj;
}
}
let b = new B('Test');
b.Obj.foo = 'new value';
console.log(b.Obj.foo);
In this manner, it's not possible to change the values โโof the internal object.
I was playing around with objects and constructors and stuff like that, and I was wondering if there was a way to bind a value to a variable based on how it was originally defined. I have the following code:
typescript
let cr = "create",
ap = "apply",
$this = {
set: (prop, value) => {
this[prop] = value;
}
};
function creator() {
this.$ = (array: Object[]) => {
array.forEach((kp: Object) => {
let key = Object.keys(kp)[0];
let val = kp[Object.keys(kp)];
$this[key] = val;
creator.create(key, { value: val });
});
};
this.apply = (...objects: Object[]) => {
objects.forEach((obj: Object) => {
creator.call(obj);
});
};
}
function create(obj) {
function createValues(arr) {
let instance: Object = new obj();
let vals: any[] = [];
arr.forEach(name => {
vals.push(instance[name]);
});
return vals;
}
let names: string[] = Object.getOwnPropertyNames(new obj());
let values: string[] = createValues(names);
return combineArrays(names, values);
}
function combineArrays(arr1, arr2): { $?: any } { // the question mark removes an IDE error
let newObj: Object = {};
arr1.forEach(prop => {
newObj[prop] = arr2[arr1.indexOf(prop)];
});
return newObj;
}
Object.prototype.create = function(prop, options) {
return Object.defineProperty(this, prop, options);
};
create(creator).$([
{ hi: "hi" },
{ bye: $this["hi"] } // this is the important stuff
]);
I was wondering if there is a way, inside the set function of the $this variable, to detect how it is being set and therefore determine if that value has changed and so it's value should to, if that makes any sense? Let's say you had this:
let $this = {
set: function(prop, value) {
this[prop] = value;
}
}
let name = 'Ezra';
$this['name'] = name;
// then :
name = 'Bob';
// change $this.name too, so then:
console.log($this.name);
// >> 'Bob'
I believe this is called Data-Binding but I am unsure how to do it without creating endless numbers of proxies.
What you're describing is not really "data-binding" but pass-by-reference. In your example you expect an update to name to be reflected in $this['name']. That would only be possible if you were passing a reference (or a pointer) to the variable.
However, in this case the variable is a string, and strings in JavaScript are immutable:
no string methods change the string they operate on, they all return new strings. The reason is that strings are immutable โ they cannot change, we can only ever make new strings.
So, going step-by-step through your example:
This creates a new string named 'Ezra', and assigns a variable called name to reference that string.
let name = 'Ezra';
This creates (or sets) a property in $this called 'name' that references the string in name (which is 'Ezra').
$this['name'] = name;
This creates a new string called 'Bob' and stores it in the variable called name. The variable already exists. This does not mutate the value that was previously held in there. Instead, name is being updated to point to a new reference.
// then :
name = 'Bob';
However, if you were to pass an object, you'll notice that you can actually mutate it. This is because objects are passed-by-reference and you can mutate the value at that reference.
For example:
// Create a new value that stores an object, with a property 'firstName'
let name = { firstName: 'Ezra' };
// Assign myObject to $this['name']. Now $this['name'] and name both point to the same reference.
$this['name'] = name;
// Change the value in the object pointed-to by name
name.firstName = 'Bob'
console.log($this['name'].firstName); // <- This will output 'Bob'
var obj = {};
console.log(obj.constructor === Object); // true
console.log(typeof obj.constructor); // function
obj['foo'] = 'bar';
obj['constructor'] = 'String';
console.log(obj.constructor === Object); // false
console.log(typeof obj.constructor); // string
I want to mention a case in this example: In the obj object, I've added a new property name constructor with value String. And the type of value is string.
So: 'string' !== 'function'.
Since I override it as the second, I cannot use it like a function as the first.
That also means: some js developers (almost) don't want to declare a property which the name is constructor in an object. If I try to doing that, the default constructor would be overridden.
Same to another case:
var array = [];
console.log(typeof array.forEach); // function
array['forEach'] = 'String';
console.log(typeof array.forEach); // string
Why doesn't js accept multiple keys with same name but difference value types?
What I want to achieve:
var action = {
isDone: false,
isDone: function (flag) {
this.isDone = flag
}
};
action.isDone(progressing is done);
if (action.isDone) {
// done...
}
// 'boolean' !== 'function'
My questions:
1/. How to define new property to an object with same key? (not duplicate with another topics because same key but differnce value types);
2/. Is it the best way to prevent to override an object property? (Or readonly as the title)
var obj = {};
Object.defineProperty(obj, 'constructor', {
get: function () {
return function () {
// default constructor here...
}
},
set: function (newValue) {
// do nothing here...
}
})
var obj = {
};
// being explicit
Object.defineProperty(obj, 'testFunction', {
writable: false,
value: function(){
console.log("Print read only Function");
return;
}
});
console.log(obj.testFunction.toString());
obj.testFunction();
obj.testFunction= function(){
console.log("Override Function");
}
console.log(obj.testFunction.toString());
obj.testFunction();
How to define new property to an object with same key? (not duplicate with another topics because same key but differnce value types);
You can't
Is it the best way to prevent to override an object property?
Sure, but you're still probably using it wrong. And whatever code you write around it is probably bad JavaScript.
Whatever language you're coming from, I suggest do not try to implement other idioms in JavaScript. JavaScript is its own thing. Learn how to write idiomatic JavaScript.
I have this small piece of code, that adds implement function to Object (I know, not a good practice). It copies properties of argument object (superobject) into the object, on which you call the method.
Object.prototype.implement = function(superobject) {
var props = {};
for (var key in superobject){
if (superobject.hasOwnProperty(key) && !this.hasOwnProperty(key))
props[key] = {
enumerable: true,
configurable: true,
get: this.implement.__get__(superobject, key),
set: this.implement.__set__(key)
};
}
Object.defineProperties(this, props);
return this;
};
Object.prototype.implement.__get__ = function(superobject, key){
return function(){return superobject[key];};
};
Object.prototype.implement.__set__ = function(key){
return function(value){
Object.defineProperty(this, key, {
value: value,
writable: true,
configurable: true,
enumerable: true
});
};
};
By copying, it actually creates own property, but the value is reference to the property of the superobject. This is done by the property "getter". This example shows a simple usage:
var obj1 = {
a:10
}
var obj2 = {
b:20
}
obj1.implement(obj2) // "implement" properties of obj2 to obj1
console.log(obj1.a) // -> 10
console.log(obj1.b) // -> 20
obj2.b = 21
console.log(obj1.a) // -> 10
console.log(obj1.b) // -> 21 -> it apperas to be reference to property b in obj2
obj1.hasOwnProperty("a") // -> true
obj1.hasOwnProperty("b") // -> true
obj1.b = 30;
console.log(obj1.b) // -> 30 //since obj1 has now "truly own" property b, it uses value of that instead of obj2's.
console.log(obj2.b) // -> 20
My question is this. After I call implement method on obj1, is obj1.b truly a reference? I mean - does obj1.b and obj2.b point to the same part of memory? Does the value b:20 exist only in one place in memory?
Is there maybe a good simple tool to see javascript's memory usage to confirm this?
I want to create a map of functions to its argument. This map will be updated dynamically. Finally all the functions will be called with their corresponding arguments.
function foo1(x) {
//do something with x
}
function foo2(x) {
//do something else with x
}
var map = {};
map[foo1] = [1,2,3]; //this array is updated dynamically in my code
map[foo2] = [4,5,6];
// I want to call foo1 and foo2 with their [1,2,3] and [4,5,6] arguments respectively.
I tried 2 approaches :
Converted foo1 to string (using toString() method) as the key for the map. Then later I get back the function from this string using Function constructor. But I am afraid if this will hit the performance.
// This works. But concerned about the performance
map[foo1.toString()] = [1,2,3];
for(i in map){
var fn = Function( 'return '+ i)();
fn(map[i]);
}
Store objects that wrap up function and their respective arguments like:
{ fn : foo1 , args : [1,2,3] }
{ fn : foo2 , args : [4,5,6] }
Here I store the references to functions instead of the entire function definition. But I have to traverse through the entire array to add more arguments.
Is there any better approach to maintain this map? What are the drawbacks in the above mentioned approaches?
UPDATE
Answer to the question "in what situation I will need this" :
I am maintaining a map from arguments to functions. I update it dynamically.
Later in my code I want to create a reverse map and call the functions with all its arguments.
For eg :
1 -> foo1
2 -> foo2
3 -> foo1,foo2
4 -> foo1
... and so on.
Then I want to create a reverse map like this :
foo1 -> [1,3,4...]
foo2 -> [2,3,...]
And finally call :
foo1( [1,3,4...])
foo2( [2,3,...])
Objects in JavaScript can only have strings as keys, so using map[foo1] is practically identical to map[foo1.toString()]. These both have problems that you haven't noticed: they discard closed-over variables, e.g.:
function makeCounter() {
var counter = 0;
return function() { return ++counter; }
}
If I have
var myCounter = makeCounter();
then myCounter.toString() will be function() { return ++counter; }, and trying to reconstitute that with the Function constructor will result in having the wrong counter reference.
Really, the best option might be to use the function's name as the property and as a value, use an object like you suggested:
var map = {};
map['foo1'] = { fn: foo1, args: [1, 2, 3] };
Then, if you want to add more arguments later, it's pretty obvious:
map['foo1'].args.push(4);
And to call them all, you might use something like this:
for(var functionName in map) {
if(!Object.prototype.hasOwnProperty.call(map, functionName)) {
continue;
}
map[functionName].fn.apply(null, map[functionName].args);
}
Until there's a native cross-browser solution for having objects as keys, you could always implement your own solution. Here's an example of what you could do. In the code below, the ObjectMap will store a generated key as a property of the object that needs to serve as a key. The property name that is used to store the key on the object is randomized to reduce possible conflicts. The map implementation can then use this property's value to retrieve the key on the object and then retrieve it's associated value.
JSPERF: http://jsperf.com/object-map
function ObjectMap() {
this.key = 0;
//you should implement a better unique id algorithm
this.mapId = '_' + Math.floor(Math.random() * 10000);
this.data = {};
}
ObjectMap.prototype = {
set: function (object, value) {
var key = ++this.key;
if (object[this.mapId]) {
return;
}
object[this.mapId] = key;
this.data[key] = value;
},
get: function (object) {
var key = object[this.mapId];
return key? this.data[key] : null;
},
remove: function (object) {
var key = object[this.mapId];
if (!key) {
return;
}
delete this.data[key];
delete object[key];
}
};
function a() {}
var map = new ObjectMap();
map.set(a, 'test');
console.log(map.get(a)); //test
In order to use objects (or functions) as keys you'll need to use Harmony (EcmaScript 6) WeakMap or Map. They're both currently experimental and both are available in Firefox. I believe WeakMap might also be available in Chrome (with the proper flag settings?).
If your platform supports WeakMap, and you choose to incorporate them, then their usage is quite straightforward:
var myWeakMap=new WeakMap();
myWeakMap.get(key [, defaultValue]);
myWeakMap.set(key, value);
myWeakMap.has(key);
myWeakMap.delete(key);
myWeakMap.clear();
More information (note the MDN references appear to be unlisted):
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/WeakMap
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Map
http://wiki.ecmascript.org/doku.php?id=harmony:weak_maps
Also: Alternatively you can use an array of functions, then use indexOf to get the index of the function, then access the parameters in an another array with that index.
function a(){}
function b(){}
var x=[a,b].indexOf(b); //x=1
Credits to Dagg Nabbit for suggesting this in the comments under my question.
"Don't forget functions can have properties. You could always store the
functions in an array, and attach their index in the array to the
function as a propery, and look them up that way." - Dagg Nabbit
Consider the following map of args-to-callback arguments :
map :
1 -> foo1
2 -> foo1,foo2
3 -> foo2
The objective is to construct a callback-to-args map (reverse map) like this :
callbackMap:
foo1 -> [1,2]
foo2 -> [2,3]
Approach :
var allArgsPossible = [1,2,3]
// contains the list of callbacks to be called
var callbackArray = [];
//maps the callback index in callbackArray to the callback's arguments
//callbackMap[index] = args means callbackArray[index] will be called with parameter "args"
var callbackMap = {};
for( i in allArgsPossible)
{
var item = allArgsPossible[i];
var callbacks = map[ item ];
for(j in callbacks)
{
var callback = callbacks[j];
if(callback.index == undefined)
{
var index = callbackArray.length;
// adding a new property "index" to the callback
callback.index = index;
callbackMap[index] = [item];
//create a new entry in callbackArray
callbackArray.push(callback);
}
else
{
callbackMap[callback.index].push(item);
}
}
}
console.log(JSON.stringify(callbackMap));
for( i in callbackArray)
{
var callback = callbackArray[i];
//get arguments from our reverse map
var args = callbackMap[callback.index];
// Bingo !
callback(args);
}
You can get the whole picture here : http://jsfiddle.net/kyvUA/2/
One point to note here is that the callback function may already have an "index" property for some other purpose. If that is a concern, you can generate a random string and store this property on the callback with the index as the value. ( as suggested by #plalx )
Cheers !