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.
Related
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
I was wondering if it is possible to do the following:
worker.name("John").salary(100)
Basically, this changes the worker's(John's) salary to 100.
Is there a way to define a function in a function?
Can you tell me the method and explain it?
This is often times called chaining. Essentially, each method returns the parent object:
var worker = {
name: function ( name ) {
this._name = name;
return this;
},
salary: function ( salary ) {
this._salary = salary;
return this;
}
};
worker.name("Jonathan").salary("$1");
alert( worker._name );
alert( worker._salary );
You'll note that each method returns the object. This is made very clear in the following screenshot. If we console output the results of calling the name method, we see that the entire worker object is returned:
Create a constructor function like:
var Worker = function(){
};
Worker.prototype.name = function (name){
this.name = name;
return this;
};
Worker.prototype.salary = function (salary){
this.salary = salary;
return this;
}
Now above constructor function can be used as:
var worker = new Worker();
worker.name("John Doe").salary(100);
This is possible:
var worker = {
nameVar: null,
salaryVar: null,
name: function(name) {
this.nameVar = name;
return this;
},
salary: function(salary) {
this.salaryVar = salary;
return this;
}
}
Each method modifies the object, and returns this, which is the object. Then you can call another method, like in your example, without writing the object name explicitly.
Alternatively, you can implement a .clone method, and instead of this, return the clone with a modified property. This would be somewhat similar to the way jQuery works.
I tried execute this code:
var system = new Object();
(function($) {
$.init = function() {
var o = {
message: function(arg) {
return arg.val;
},
alert: this.message({
val: "Hello, world."
})
};
return o.alert;
};
})(system);
alert(system.init());
but, when I execute it I get error message which tells me that this.message is not a function. Obviously, this not refers to object o itself, and I want to know why.
I found few solutions on stackoverflow where this is always inside function body, but why this can not be outside? Thanks.
Change your code to:
var system = new Object();
(function($) {
$.init = function() {
var o = {
message: function(arg) {
return arg.val;
}
};
o.alert = o.message({
val: "Hello, world."
});
return o.alert;
};
})(system);
system.init()
Yields:
"Hello, world."
In your code this would refer to window in that context. If you want to create an object and refer to it as this - use constructor function, i.e.:
var o = new function(){
...
<here: this == o>
...
}
You can use an anonymous constructor function instead of object literal syntax if you want to reference the object during instantiation.
(function($) {
$.init = function() {
// -----vvv---vvv---constructor function
var o = new function() {
this.message = function(arg) {
return arg.val;
},
this.alert = this.message({
val: "Hello, world."
})
};
return o.alert;
};
})(system);
Because the value of this is only defined within a function when the function is invoked, object literal syntax never changes its value.
So instead we create a function, and invoke it using new so that the value of this in the function is a reference to the new object we're building.
And of course it doesn't need to be anonymous, but if you're only going to use it once, there's no need for a name.
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).
Quick and strange question:
I have an object (in this example is small but in the project is larger):
var myObject = {
hello: 1, // easier I think
'hey.ya': 5 // quite impossible but the first option is valid too
}
then I want to pass somehow to a function and use "hello" for example in a closure like this
function x(){
// my closure
return function(){this.init = function(){alert(hello)}, this.heyYa = function(){alert(/* I do not know how to call the other hey.ya variable */)}}
}
var myClass = x(), instance = new myClass(); instance.init();
thanks!
You need to use the myObject
var myObject = {
hello: 1,
'hey.ya': 5
}
function x(obj){
return function(){
this.init = function(){
alert(obj.hello)
},
this.heyYa = function(){
alert(obj['hey.ya'])
}
}
}
var myClass = x(myObject);
var instance = new myClass();
instance.init(); // alerts '1'
instance.heyYa(); // alerts '5'