How to assign a bound object function to another object property? - javascript

I tried to bind a function from an object to some variable without external calling bind():
var man = {
age: "22",
getAge: function(){
return "My age is "+this.age;
},
test: function(){
return this.getAge.bind(this);
}
}
This works:
var a = man.test();
a();
// "My age is 22"
But when I try to change some things in my code:
var man = {
age: "22",
getAge: function(){
return "My age is "+this.age;
},
test: function(){
return this.getAge.bind(this);
}()//there it's, that do not do "var a = man.test()", but "var a = man.test"
}
JavaScript gives me an Error:
Uncaught TypeError: Cannot read property 'bind' of undefined(…)
What am I doing wrong?

this in your second version is not referring to what you think it is, it's referring to the window and so does not have the property available...
NB: Adding the () to the end calls the anonymous function you created

In your example this is referring to the context the Object literal is written in, not the Object literal.
You can't actually refer to yourself in an Object literal's construction time because even it's identifier hasn't been properly set yet. Instead, separate it into two steps
// 1, set up with a literal
var man = {
age: "22",
getAge: function () {
return "My age is " + this.age;
}
}
// 2, set up things needing references to the object we just made
man.test = man.getAge.bind(man);
By your specific example it looks like you may repeat this pattern many times, are you sure that it wouldn't be better to use a Constructor? This also means you can use inheritance and prototyping
For example, you could have Man set up as inheriting from Human, and also create a Woman later with shared code
// Common category
function Human(age) {
this.age = age;
this.test = this.getAge.bind(this);
}
Human.prototype = Object.create(null);
Human.prototype.getAge = function () {
return 'My age is ' + this.age;
};
// specific category
function Man(age) {
Human.call(this, age);
}
Man.prototype = Object.create(Human.prototype);
Man.prototype.gender = function () {
return 'I am male.';
};
// then
var man = new Man('22'); // as you used a string age
var a = man.test;
a(); // "My age is 22"
Then later
// another specific category
function Woman(age) {
Human.call(this, age);
}
Woman.prototype = Object.create(Human.prototype);
Woman.prototype.gender = function () {
return 'I am female.';
};
// then usage
var woman = new Woman('22'); // as you used a string age
woman.getAge(); // "22", because getAge was common to humans

Related

Confusion over how to use explicit binding methods

Not sure if anyone can help me with this issue but I am aware that one can use the bind, apply and call methods to make the this keyword within a function reference a particular object of your choosing. However, I'm still a little confused because in certain instances when I expected a particular outcome I got another. Here is an example of an object I have:
var obj = {
fullName: "John Doe",
person: {
sayHi: function() {
console.log("This person's name is " + this.fullName)
}
}
}
I am aware that calling the obj.person.sayHi() method will result in the person's name being undefined as the implicit object is person.
I am also aware that you can reference the correct object by calling the method using bind or call like so:
obj.person.sayHi.call(obj) or obj.person.sayHi.bind(obj)()
in order for the person's name to display. However, I also expected that one could attach the bind directly to the function expression inside of the object like so:
var obj = {
fullName: "John Doe",
person: {
sayHi: function() {
console.log("This person's name is " + this.fullName)
}.bind(obj)
}
}
and then call the function like so: obj.person.sayHi() and you would get the same result however in this instance the this keyword refers to the global object. However if I do the following:
let sayHi = function() {
console.log("This person's name is " + this.fullName)
}
sayHi.bind(obj)();
then I get the desired result. Can anyone explain why the second approach is not a valid one? Thanks in advance.
This is invalid:
var obj = {
fullName: "Harry Potter",
person: {
sayHi: function() {
console.log("This person's name is " + this.fullName)
}.bind(obj) // obj is not defined here yet, obj is undefined
}
}
Because obj is undefined when bind is called. obj is indeed defined when the declaration is over, but you have a kind of circular reference in your declaration of obj which most likely will have the value of undefined when bind(obj) is called.
Another way to see this explicitly is to do the following:
var obj = {
fullName: "Harry Potter",
person: {
sayHi: function() {
console.log("This person's name is " + this.fullName)
}.bind(obj) // obj is not defined here yet, obj is undefined
},
ref: obj
}
console.log(obj.ref); // undefined
Hi #Nikos thanks for the response. I think I'm on track now. I just wanted to know one more thing. If I do the following:
function dummy(fn) {return fn();}
var obj = {
fullName: "Harry Potter",
person: {
sayHi: function() {
dummy(function() {
console.log("This person's name is " + this.fullName);
console
}.bind(obj))
}
}
}
Then calling the function obj.person.sayHi() will produce the desired effect. Is this somehow taking the function expression out of the object definition? Sorry I tried posting this as a comment however, I don't know how to get it into the correct formatting

this keyword vs object name when adding method to existing object

function Foo(name, age){
this.name = name;
this.age = age;
this.announce = function(){
alert(this.name + " is " + this.age + " years old");
};
}
var myFoo = new Foo("John", 42);
Lets say I want to add a method to this particular instance of Foo (not to the others).
Should I use this keyword to modify the age property
myFoo.becomeYounger = function(){
this.age--;
};
or should I refer to the object by its name since it already exists?
myFoo.becomeYounger = function(){
myFoo.age--;
};
Which one is better/faster or is there any difference whatsoever?
They both work, but there are some risks about using the object name, look at this:
let user = {
name: "John",
age: 30,
sayHi() {
alert( user.name ); // leads to an error
}
};
let admin = user;
user = null; // overwrite to make things obvious
admin.sayHi(); // Whoops! inside sayHi(), the old name is used! error!
By using this, the code would worked correctly, just take care about this kind of scenarios.
Also if you like to do reusable code, using this fits better:
let user = { name: "John" };
let admin = { name: "Admin" };
function sayHi() {
alert( this.name );
}
// use the same functions in two objects
user.f = sayHi;
admin.f = sayHi;
// these calls have different this
// "this" inside the function is the object "before the dot"
user.f(); // John (this == user)
admin.f(); // Admin (this == admin)
admin['f'](); // Admin (dot or square brackets access the method – doesn't matter)
To learn more, here:
https://javascript.info/object-methods

Why is this the output for javascript?

var name = 'bob';
var someObject = {
name: 'james',
someProperty: {
name: 'sam',
getName: function(){
return this.name;
}
}
}
var testing = someObject.someProperty.getName;
testing();
Is the reason this block of code returns 'bob' because we are just ultimately calling this.name on a global object's name, which is 'bob' or is there a better way to think about this problem? Thanks!
The value of this is determined by how a function is called.
testing() is invoked as window.testing() hence this refers to window and as var name is under the global(scope of window), "bob" is returned.
You can use [.call'(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call) of ES5 to specify this context to get "sam".
The call() method calls a function with a given this value
Use .bind() to have a specified this-reference context in function-body, The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
var name = 'bob';
var someObject = {
name: 'james',
someProperty: {
name: 'sam',
getName: function() {
return this.name;
}
}
}
var testing = someObject.someProperty.getName;
console.log(testing()); //bob
console.log(testing.call(someObject)); //james
console.log(testing.call(someObject.someProperty)); //sam
console.log('------OR--------')
var testing = someObject.someProperty.getName.bind(someObject);
console.log(testing());
var testing = someObject.someProperty.getName.bind(someObject.someProperty);
console.log(testing());
Running the above code in Chrome Dev Console I got the following output:
Input:
var testing = someObject.someProperty.getName;
testing();
Output:
"bob"
But then I changed as:
Input:
var testing = someObject.someProperty.getName();
testing;
Output:
"sam"
Well, in the first scenario, "testing" becomes a function object which will the return the value of global "name" variable. "testing" has nothing to do with "someObject" in this case. Which is similar to:
// global scope
var testing = function() {
return this.name;
}
In the second scenario, "testing" is simply the returned value of "someObject.someProperty.getName()".
When you call testing, the function is being invoked without an object context and the global context is used for the this pointer. this.name on the global context is 'bob' and hence the result. Calling someObject.someProperty.name() would return 'sam'. If you want it to return 'james' you need to call it like this:
someObject.someProperty.getName.call(someObject);
or:
var testing = someObject.someProperty.getName.bind(someObject);
testing();
Just to add on, if you want to use a function to get sam, you can wrap the function call in another function:
var testing = someObject.someProperty.getName;
testing(); // "bob"
function getSam(){
return someObject.someProperty.getName();
}
getSam(); // "sam"
I believe you are partly mixing up this simple object literal with object constuctors. The object literal someObject does not automatically engage this - its pretty much the same as a bunch of variables.
var name = 'bob';
var someObject = {}
someObject.name = 'james'
someObject.someProperty = {};
someObject.someProperty.name = 'sam';
someObject.someProperty.getName = function(){
return this.name; // global context
}
You can approximate instancing, _and thus engage this to in javascript in a few ways:
Using Object.create
Passing the object literal someObject to Object.create() will create a new context for this as you might expect:
var name = 'bob';
var someObject = {
name: 'james',
someProperty: {
name: 'sam',
getName: function(){
return this.name;
}
}
}
var aNewContext = Object.create(someObject);
aNewContext.someProperty.getName();
// "sam"
Using Function constructors
When you use the keyword new with a function, it also creates a new object and assigns it to this. Functions added to the prototype can be called as methods, will also reference this:
function SomePropertyConstructor() {
this.name = 'sam';
}
SomePropertyConstructor.prototype.getName = function(){
return this.name;
}
var name = 'bob';
var someObject = {
name: 'james',
someProperty: new SomePropertyConstructor()
}
someObject.someProperty.getName();
// "sam"
Note - if you call the function on its own, you will need to control the context using bind:
var unbound = someObject.someProperty.getName;
[ unbound(), unbound.bind(someObject.someProperty)()]
//["bob", "sam"]
var name = 'bob';
var someObject = {
name: 'james',
someProperty: {
name: 'sam',
getName: function() {
console.log(this);
return this.name;
}
}
}
var testing = someObject.someProperty.getName;
testing()
Window {external: Object, chrome: Object, customElements: undefined, originalOutline: undefined, someObject: Object…}
When I tried that, noticed how the result of my (this) was printed, and it refers to the global "this". This isn't what we want.
If I wanted to print "sam", I would do this to call it with a way to redefine what "this" means.
testing.bind(someObject.someProperty)(). // returns "sam"
In addition you can also do this:
var name = 'bob';
var someObject = {
name: 'james',
someProperty: {
name: 'sam',
getName: function() {
return this.name;
}.bind(someObject.someProperty)
}
}
var testing = someObject.someProperty.getName;
This will return "sam" when you call it, regardless of the "this" context.
Another way of doing this is to keep your original code, but instead of setting
var testing = someObject.someProperty.getName;
instead change it to:
var testing = someObject.someProperty.getName();
This way now the object refers to the getName() function using the context of your someProperty.

How do I call a public function from within a private function in the JavaScript Module Pattern

How do I call a public function from within a private function in the JavaScript Module Pattern?
For example, in the following code,
var myModule = (function() {
var private1 = function(){
// How to call public1() here?
// this.public1() won't work
}
return {
public1: function(){ /* do something */}
}
})();
This question has been asked twice before, with a different accepted answer for each.
Save a reference to the return object before returning it, and then use that reference to access the public method. See answer.
Save a reference to the public method in the closure, and use that to access the public method. See answer.
While these solutions work, they are unsatisfactory from an OOP point of view. To illustrate what I mean, let's take a concrete implementation of a snowman with each of these solutions and compare them with a simple object literal.
Snowman 1: Save reference to return object
var snowman1 = (function(){
var _sayHello = function(){
console.log("Hello, my name is " + public.name());
};
var public = {
name: function(){ return "Olaf"},
greet: function(){
_sayHello();
}
};
return public;
})()
Snowman 2: Save reference to public function
var snowman2 = (function(){
var _sayHello = function(){
console.log("Hello, my name is " + name());
};
var name = function(){ return "Olaf"};
var public = {
name: name,
greet: function(){
_sayHello();
}
};
return public;
})()
Snowman 3: object literal
var snowman3 = {
name: function(){ return "Olaf"},
greet: function(){
console.log("Hello, my name is " + this.name());
}
}
We can see that the three are identical in functionality and have the exact same public methods.
If we run a test of simple overriding, however
var snowman = // snowman1, snowman2, or snowman3
snowman.name = function(){ return "Frosty";}
snowman.greet(); // Expecting "Hello, my name is Frosty"
// but snowman2 says "Hello, my name is Olaf"
we see that #2 fails.
If we run a test of prototype overriding,
var snowman = {};
snowman.__proto__ = // snowman1, snowman2, or snowman3
snowman.name = function(){ return "Frosty";}
snowman.greet(); // Expecting "Hello, my name is Frosty"
// but #1 and #2 both reply "Hello, my name is Olaf"
we see that both #1 and #2 fail.
This is a really ugly situation. Just because I've chosen to refactor my code in one way or another, the user of the returned object has to look carefully at how I've implemented everything to figure out if he/she can override my object's methods and expect it to work! While opinions differ here, my own opinion is that the correct override behavior is that of the simple object literal.
So, this is the real question:
Is there a way to call a public method from a private one so that the resulting object acts like an object literal with respect to override behavior?
You can use this to get the object your privileged method greet was called on.
Then, you can pass that value to your private method _sayHello, e.g. using call, apply, or as an argument:
var snowman4 = (function() {
var _sayHello = function() {
console.log("Hello, my name is " + this.name);
};
return {
name: "Olaf",
greet: function() {
_sayHello.call(this);
}
};
})();
Now you can do
var snowman = Object.create(snowman4);
snowman.greet(); // "Hello, my name is Olaf"
snowman.name = "Frosty";
snowman.greet(); // "Hello, my name is Frosty"
And also
snowman4.greet(); // "Hello, my name is Olaf"
snowman4.name = "Frosty";
snowman4.greet(); // "Hello, my name is Frosty"
With module pattern, you hide all the innates of an object in local variables/functions, and usually employ those in your public functions. Each time a new object is created with a module pattern, a new set of exposed functions - with their own scoped state - is created as well.
With prototype pattern, you have the same set of methods available for all objects of some type. What changes for these methods is this object - in other words, that's their state. But this is never hidden.
Needless to say, it's tough to mix those. One possible way is extracting the methods used by privates into a prototype of the module's resulting object with Object.create. For example:
var guardian = function() {
var proto = {
greet: function () {
console.log('I am ' + this.name());
},
name: function() {
return 'Groot';
}
};
var public = Object.create(proto);
public.argue = function() {
privateGreeting();
};
var privateGreeting = public.greet.bind(public);
return public;
};
var guardian1 = guardian();
guardian1.argue(); // I am Groot
var guardian2 = guardian();
guardian2.name = function() {
return 'Rocket';
};
guardian2.argue(); // I am Rocket
var guardian3 = guardian();
guardian3.__proto__.name = function() {
return 'Star-Lord';
};
guardian3.argue(); // I am Star-Lord

In JavaScript: Syntax difference between function & method definition within a class

The Object class has both methods and functions meaning they both are accessed through Object.nameOfMethodOrFunction(). The following question What is the difference between a method and a function explains the difference between a method and and a function, but it doesn't explain how to create them within an object. For example, the code below defines the method sayHi. But how do you define a function inside the same object?
var johnDoe =
{
fName : 'John',
lName: 'Doe',
sayHi: function()
{
return 'Hi There';
}
};
The following defines two classes, ClassA and ClassB, with equal functionality but different in nature:
function ClassA(name){
this.name = name;
// Defines method ClassA.say in a particular instance of ClassA
this.say = function(){
return "Hi, I am " + this.name;
}
}
function ClassB(name){
this.name = name;
}
// Defines method ClassB.say in the prototype of ClassB
ClassB.prototype.say = function(){
return "Hi, I am " + this.name;
}
As shown below, they doesn't differ much in usage, and they are both "methods".
var a = new ClassA("Alex");
alert(a.say());
var b = new ClassB("John");
alert(b.say());
So now what you mean for "function", according to the msdn link that you gave as a comment, seems that "function" is just a "static method" like in C# or Java?
// So here is a "static method", or "function"?
ClassA.createWithRandomName = function(){
return new ClassA("RandomName"); // Obviously not random, but just pretend it is.
}
var a2 = ClassA.createWithRandomName(); // Calling a "function"?
alert(a2.say()); // OK here we are still calling a method.
So this is what you have in your question:
var johnDoe =
{
fName : 'John',
lName: 'Doe',
sayHi: function()
{
return 'Hi There';
}
};
OK, this is an Object, but obviously not a class.
Quoting Aaron with "A method is on an object. A function is independent of an object".
Logically a method is useless without a "this" defined.
Consider this example:
var johnDoe =
{
fName: 'John',
lName: 'Doe',
sayHi: function () {
return 'Hi There, my name is ' + this.fName;
}
};
function sayHi2() {
return 'Hi There, my last name is ' + this.lName;
}
//Will print Hi there, my first name is John
alert(johnDoe.sayHi());
//An undefined will be seen since there is no defined "this" in SayHi2.
alert(sayHi2());
//Call it properly now, using the oject johnDoe for the "this"
//Will print Hi there, my last name is Doe.
alert(sayHi2.call(johnDoe));
var johnDoe = {
fName: 'John',
lName: 'Doe',
sayHi: function(){
function message(){ return 'Hi there'; }
return message();
}
};
That's about as good as you're going to get with the object declaration method of creating a 'class' in JavaScript. Just keep in mind that function is only valid within sayHi's scope.
However, if you use a function as a class structure, you have a little more flexibility:
var johnDoe = function(){
this.publicFunction = function(){
};
var privateFunction = function(){
};
};
var jd = new johnDoe();
jd.publicFunction(); // accessible
jd.privateFunction(); // inaccessible
(though both are really considered methods since they have access to the object's scope within).

Categories

Resources