function.apply does not register the first argument (index 0) in my array.
I have tried the rest/spread syntax, and
the code below seems to work fine:
let sayName = function (object, ...languages) {
console.log(`My name is ${this.name} and I speak ${languages.join(', ')}`);
};
let stacy = {
name: 'Stacy',
age: 34
};
let languages = ['Javascript', 'CSS', 'German','English', 'Spanish'];
sayName(stacy, ...languages);
But the code below does not work, with explicit binding function.apply:
let languages1 = ['Javascript', 'CSS', 'German','English', 'Spanish'];
sayName.apply(stacy, languages1)
The result always leaves out the 0 index from the array, i.e. "Javascript" is not included in the output.
What am I doing wrong?
Your function seems to expect an object parameter, but that is incorrect seeing how you call it with apply. When you call apply or call, the first argument to that method is used to set the this value for your function's execution context. It does not get passed as argument.
As your function references this, it is important that you set this correctly, and so the use of apply or call is the good choice.
So change:
let sayName = function (object, ...languages) {
to:
let sayName = function (...languages) {
Note that you didn't use object anyway in your function's code.
In the version where you don't call apply, but call the function directly, you do:
sayName(stacy, ...languages);
...but here the this object will not be set correctly. You should have used call to ensure that this will refer to stacy:
sayName.call(stacy, ...languages);
Check out the documentation of apply:
Syntax
function.apply(thisArg, [argsArray])
Parameters
thisArg
Optional. The value of this provided for the call to func. [...]
argsArray
Optional. An array-like object, specifying the arguments with which func should be called. [...]
Object oriented programming, a better way
Now, taking a step back you should really have defined sayName as a prototype method, and construct stacy like so:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayName(...languages) {
console.log(`My name is ${this.name} and I speak ${languages.join(', ')}`);
}
}
let stacy = new Person('Stacy', 34);
let languages = ['Javascript', 'CSS', 'German','English', 'Spanish'];
stacy.sayName(...languages);
... and why not actually store the languages a person speaks as a property as well, and remove the console.log from the method, as it should be the caller who decides to where the output should be made (maybe to a file or to a HTML element):
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
this.languages = [];
}
speaks(...languages) {
this.languages = languages;
}
introduction() {
return `My name is ${this.name} and I speak ${this.languages.join(', ')}`;
}
}
let stacy = new Person('Stacy', 34);
stacy.speaks('Javascript', 'CSS', 'German','English', 'Spanish');
console.log(stacy.introduction());
Related
In the example below I have two console.log's, and I'm getting different answers each time (despite my expecting to get the same answer).
class Dog {
constructor(name, gender) {
this.name = name;
this.gender = gender;
}
toString() {
return this.name;
}
}
let maxi = new Dog('john', 'male');
console.log(maxi) // Dog { name: 'john', gender: 'male' }
console.log(`${maxi}`); // john
In the first example, it seems like I'm logging the object's type and its properties. In the second example, however, it seems like my custom toString() method is being used to convert the object into a string.
Why the difference? And is there a place in the documentation that explains why string literals use the toString() method to convert objects into a string whereas console.log() doesn't. In addition, where does console.log() even pull this string representation of the object?
Thanks!
If you log objects in the last versions of chrome or firefox what you get logged is a reference to the object, and probably not the 'value' at the moment when you call the console.log(). but it's the value of the object at that time.
Reference : Mozilla
So what does fix this problem, convert your object into a string(your log 2), for example:
class Dog {
constructor(name, gender) {
this.name = name;
this.gender = gender;
}
toString() {
return this.name;
}
}
let maxi = new Dog('john', 'male');
console.log('Dog: ' + maxi) // Dog { name: 'john', gender: 'male' }
console.log(`${maxi}`); // {"name":"john","gender":"male"}
this works because I use the + operator instead of the , operator, so he will stick it into the string instead of making a reference to your object.
Summary
A variable does not store the value of the object, but a reference (the address in memory) for the value. So your copies that reference instead of the object.
As I think your requirement is to print object in string form so I have this solution below.
Oh I used arrow function here😅😅. you can use normal es5 function.
Thank you.
class Dog {
constructor(name, gender) {
this.name = name;
this.gender = gender;
}
toString() {
return JSON.stringify(this);
}
}
let maxi = new Dog('john', 'male');
console.log(`${maxi}`); // {"name":"john","gender":"male"}
I think that Console.log uses internaly a method toJSON in order to transport an object in to string. So you must write your own toJSON...
class Dog {
constructor(name, gender) {
this.name = name;
this.gender = gender;
}
toJSON() {
return this.name;
}
}
let maxi = new Dog('john', 'male');
console.log(maxi) // Dog { name: 'john', gender: 'male' }
console.log(`${maxi}`); // john
This really don't work... as Bergi tell me and he was right. I tried the code without remove the original: toString() { ... } and adding the toJSON() {...} and '>>>' like this: console.log('>>>'+maxi) and take output //>>>john. So, i thought that the toJSON() give the //>>>john, but it was the toString(). Thank you Bergi.
I wanted to experiment a bit with the Proxy object, and got some unexpected results, as follows:
Test script
function Person(first, last, age) {
this.first = first;
this.last = last;
this.age = age;
}
Person.prototype.greeting = function () {
return `Hello my name is ${this.first} and I am ${this.age} years old`;
};
So in tracking how the prototype object is being modified, I added this wrapper:
let validator = {
set: function(target, key, value) {
console.log(`The property ${key} has been updated with ${value}`);
target[key] = value;
return true;
}
};
Person.prototype = new Proxy(Person.prototype, validator);
let george = new Person('George', 'Clooney', 55);
Person.prototype.farewell = function () {
return `Hello my name is ${this.first} and I will see you later`;
};
What I expected
The property: "farewell" has been updated with: "function () {
return `Hello my name is ${this.first} and I will see you later`;
}"
and nothing else.
And of course, each time I added or removed something from the prototype, i.e Person.prototype or instance.constructor.prototype I expected to see the console.log() message.
However I did not expect to see anything when setting something on the instance, like:
george.someProp = 'another value'; // did NOT expect to see the console.log()
Output
The property: "first" has been updated with: "george"
The property: "last" has been updated with: "clooney"
The property: "age" has been updated with: "55"
The property: "farewell" has been updated with: "function () {
return `Hello my name is ${this.first} and I will see you later`;
}"
Person.prototype
Proxy {greeting: ƒ, first: "George", last: "Clooney", age: 55, farewell: ƒ, constructor: ƒ}
It set all the properties on the prototype and nothing on the instance, and each time I set something on the instance it sets it straight on the prototype.
Evidently this is not the default behaviour, as if I remove that Proxy, every property set with this will be set on the instance itself and the prototype will start up empty (or in our case with just the greeting function).
What am I missing here ? A point in the right direction would be appreciated.
What you are missing is the fact that when you have a Proxy object in the prototype chain, the set handler will be called when you modify the child object.
In your example, when you set a property on the new instance, the set trap will be executed, the target will be the wrapped Person.prototype object, but there's a fourth argument, the receiver. This argument points to the object that the property has been accessed on.
To properly do the property assignment, you can use the Reflect.set API to set it:
Reflect.set(target, key, value, receiver);
That's why the Reflect API matches the proxy traps arguments.
So, in your example, we could use the Reflect API and you will see that Person.prototype doesn't get "polluted".
function Person(first, last, age) {
this.first = first;
this.last = last;
this.age = age;
}
Person.prototype.greeting = function () {
return `Hello my name is ${this.first} and I am ${this.age} years old`;
};
const validator = {
set: function(target, key, value, receiver) {
console.log(`The property ${key} has been updated with ${value}`);
Reflect.set(target, key, value, receiver)
return true;
}
};
Person.prototype = new Proxy(Person.prototype, validator);
const george = new Person('George', 'Clooney', 55);
Person.prototype.farewell = function () {
return `Hello my name is ${this.first} and I will see you later`;
};
console.log(george.hasOwnProperty('first')); // true
console.log(Person.prototype.hasOwnProperty('first')); // false
Hi this is from a challenge I was working on. Is there any way i can add the introduce method to the personStore object without using the keyword this. Any insight is greatly appreciated.
Using Object.create
Challenge 1/3
Inside personStore object, create a property greet where the value is a function that logs "hello".
Challenge 2/3
Create a function personFromPersonStore that takes as input a name and an age. > When called, the function will create person objects using the Object.create method on the personStore object.
Challenge 3/3
Without editing the code you've already written, add an introduce method to the personStore object that logs "Hi, my name is [name]".
Side Curiosity
As a side note, was curious if there was a way to add the introduce method to the person object that sits inside of the personFromPersonStore function.
my solution:
var personStore = {
// add code here
greet: function (){
console.log('Hello');
}
};
function personFromPersonStore(name, age) {
var person = Object.create(personStore);
person.name = name;
person.age = age;
person.greet = personStore.greet;
return person;
};
personStore.introduce = function () {
console.log('Hi, my name is ' + this.name)
}
//Challenge 3 Tester
sandra.introduce(); // -> Logs 'Hi, my name is Sandra
You can, but using this is a lot simpler.
This code passes the name property as an argument, but as the property is already accessible to the introduce function as an internal property via this, it is a bit wasteful.
var personStore = {
// add code here
greet: function (){
console.log('Hello');
}
};
function personFromPersonStore(name, age) {
var person = Object.create(personStore);
person.name = name;
person.age = age;
person.greet = personStore.greet;
return person;
};
personStore.introduce = function (nm) {
console.log('Hi, my name is ' + nm)
}
person1=personFromPersonStore('Fred',21);
person1.introduce(person1.name);
You can write it like this:
personFromPersonStore("whatevername","whateverage").name
instead of this.
Say you have a class Person. Its definition is the following:
function Person(name, age) {
this.name = name;
this.age = age;
}
Now say you have an array of Persons (or people) and you want to find the oldest one. In C#, you would do this via extension methods:
Person Oldest(this IEnumerable<Person> people) =>
people.OrderByDescending(p => p.Age).First();
// Usage:
var elder = people.Oldest();
What's the equivalent in JavaScript? So far, this is all I was able to come up with:
Array.prototype.oldest = function() {
return this.slice().sort(function(left, right) {
return left.age - right.age;
}).pop();
};
This works well and has the same syntax as C#, but has a few drawbacks:
1) It modifies the prototype of a builtin, which could potentially lead to conflicts with other libraries that do so.
2) It is not type-safe: it's easy to accidentally call on an array that doesn't contain people, which could lead to a runtime error.
Alternatively, you could define it like this:
function oldest(people) {
// ...
}
But then you would have to call it like oldest(people) instead of people.oldest(), which would hamper readability.
Is there any type-safe, syntactically "nice" way to do this in JavaScript that I'm missing?
Because js isn't type safe, you're better off implementing that method as oldest(people) ...
Just think of it as a decorator pattern - a legit approach to projecting functionality into an unknown set of related entities.
Then - you can do type-checking within your function.
You could namespace it to make the syntax more "obvious" if that's your concern I guess:
personList(persons).getOldest()
As one objective-c-ish example.
Then, you can add your type-specific checking to the creator for personList:
function personList(persons) {
//iterate over persons and make sure
//it's valid. return undefined or {} if not.
return {
getOldest: function() {
// insert clever code here ...
persons.slice().dice().puree();
},
getYoungest: function() {
//even more clever code here ...
}
}
}
With that approach, the code
PersonList(cats).getOldest()
With throw a 'getOldest undefined' error -- which makes it easy to spot the problem when you have 11,000,000 lines of js code to sort through.
It has the added advantage of making your c# inner-child feel more at home (with a js file called "ArrayExtensions.Persons.js" in your project folder). Feels like "chicken soup for the c# soul," right?
You can try different approaches:
If you only have one array of Persons in your program, you can add the method as an own property of the array.
var people = [person1, person2];
people.oldest = function() {
// Find the oldest person in this array
};
// ... later
people.oldest();
You can define your method as an static property of Person, and pass the array of Persons as an argument:
Person.oldest = function(people) {
// Find the oldest person in an array
};
var people = [person1, person2];
Person.oldest(people);
Extend Array (requires ES6)
class People extends Array {
oldest() {
// Find the oldest person in a People collection
}
}
var people = new People();
people.push(person1, person2);
people.oldest();
Another approach with a prototype of sorting. Person.prototype.ageCompare checks if the parameter is instance of Person and throws an error if not.
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.ageCompare = function (b) {
if (!(b instanceof Person)) {
throw 'b is no instance of Person!';
}
return this.age - b.age;
};
var peoples = [new Person('Tim', 31), new Person('Jane', 32), new Person('Frank', 28), new Person('Sue', 31), new Person('Baby', 2)];
peoples.sort(function (a, b) { return a.ageCompare(b); });
document.write('<pre>' + JSON.stringify(peoples, 0, 4) + '</pre>');
I am experimenting imitating OOP like behavior in JS. I am trying to have (private) variables: id and name in function Person. To this function I am passing arguments which are used to initialize (private) variables. Then I am returning object having getter and setter for name and only a getter for id, thus effectively making id read-only.
So id can be set only through constructor whereas name can be set and get anytime.
This is the code:
var Person = function (_id,_nm) {
var id, name;
this.id = _id;
this.name = _nm;
return {
setName: function (nm) {
name = nm;
},
getName: function () {
return name;
},
getId: function () {
return id;
},
print: function () {
document.writeln("Id: "+id+"<br />Name: "+name);
}
}
}
var person = new Person(123, "Mahesh");
person.print();
However when new Person(123,"Mahesh") executes, I dont understand it is actually setting id and name or not, since while debugging I can see values set appropriately when hovered over them but Locals panel does not show them initialized:
Or either while in print() is is not referring to the desired id and name variables:
Whats wrong here?
Working fiddle
The reason is because you are using public members for the Person.prototype. You don't need to add this references to these two. So delete:
this.id = _id;
this.name = _nm;
and simply use:
var id = _id,
name = _nm;
Now everything will work fine. The whole idea is to use var, and not this, otherwise a closure will not be created. Now you will not be able to access name and id directly, instead you will have to use setName(), getName(), setId(), getId() etc.
The two members, id and name, will now become closures as you want them to be.
Update
If you used this.id, then it wouldn't have been private and you could just do var p = new Person(1, "Mahesha"); and access p.name or p.id directly. They are supposed to be private so this is not what you want.
With the closure pattern, p.name and p.id are undefined and can only be accessed through p.getName(); and p.getId();. Read on how closures work. The idea is that because you are using that var name, a closure will be created to remember it's value.
Your getName and setName are using that closure to access the name property. There is no this.name, there is a value remembered through a higher - order closure.
this.id and var id are not the same. this.id is a property of the object. var id belongs to the local scope.
Either use new or return a value. Not both.
The problem is that you're creating a new instance of Person using the new keyword, but your constructor function is returning another object instead.
When you return something from a constructor function it returns that value, and not the instance of the function.
You see when you execute new Person(123, "Mahesh") a new instance of Person is created. This is accessible within the constructor function as this.
If you don't return anything from your constructor then JavaScript automatically returns this. However you're explicitly returning another object.
No wonder var person doesn't have id and name properties (which you only defined on this).
In addition print doesn't display the id and name because although you declared them (var id, name) you didn't give them any values. Hence they are undefined.
This is how I would rewrite your Person constructor:
function Person(id, name) {
this.getId = function () {
return id;
};
this.getName = function () {
return name;
};
this.setName = function (new_name) {
name = new_name;
};
this.print = function () {
document.writeln("Id: " + id + "<br/>Name: " + name);
};
}
I didn't set the id and name properties on this because it makes no sense to include them.
You've mixed up using locally scoped ("private") variables for _id and _nm and "public" instance properties (this.id and this.nm).
In this case you need the former, but you created both and only initialised the latter.
Note that since id is read-only you don't really need a separate local variable at all, you can just use the lexically scoped first parameter to the constructor:
var Person = function (id, _nm) {
var name = _nm;
...
};
Let me try to explain using the following:
// Scope 1
var Person = function (_id,_nm) {
// Scope 2
var id, name;
this.id = _id;
this.name = _nm;
return {
// Scope 3
setName: function (nm) {
name = nm;
},
getName: function () {
return name;
},
getId: function () {
return id;
},
print: function () {
$("#output").append("<p>Id: "+id+"; Name: "+name + "<p/>");
}
}
}
The print method will return undefined because you are referring to the var id, name; that is never set in your code. You set the _id and _name to the property id and name but you fail to return the object that you just created. Instead, you return a dictionary, that references the name and id variable you created in Scope 2 that you never set, hence the undefined output in the print() call.
Here is what you should have done:
var NewPerson = function (_id,_nm) {
var self = this;
this.id = _id;
this.name = _nm;
this.print = function () {
$("#output").append("<p>New Person Id: "+this.id+"; Name: "+ this.name + "<p/>");
};
return this;
};
var nperson = new NewPerson(456, "Marcos");
nperson.print();
This will output:
New Person Id: 456; Name: Marcos
In essence, new is creating a new object represented by this, and you must return this to have a reference to the object created. In fact, if you do not use the new before creating an object, you end up referencing a global instance of this. Try the following:
var nperson = new NewPerson(456, "Marcos");
this.name = "Hello";
nperson.print();
var nperson1 = NewPerson(456, "Marcos");
this.name = "Hello";
nperson1.print();
You will see that the first output will be as expected:
New Person Id: 456; Name: Marcos
But the second will pick up the change made to this.name:
New Person Id: 456; Name: Hello
Here is the JSFiddle.