The following code (codepen) doesn't work.
"use strict"; //without strict "this" works, but it refers to window object
let person = {
name : "Shimon",
logName : function(){
//console.log("test") //this works
console.log(this.name); //doesn't work
}
};
//person.logName(); //works
(false || person.logName)(); //doesn't work. Uncaught TypeError: Cannot read property 'name' of undefined
I want to understand why.
When I call (false || person.logName)(); I suppose to call person.logName(), and it indeed called.
So why I cannot use this inside this method?
The are 4 rules for the JavaScript engine to determine the this reference.
New Bound: Was the current function called using the new keyword
Explicit Binding: Was the function called using Function#call or Function#apply.
Implicit Binding: Was the function directly on its owning object?
Default: If in strict mode undefined otherwise global
In your case person.logName() falls under the 3rd category so the this reference is set to the value of person.
But in the other case, the line (false || person.logName)() is equivalent to var x = false || person.logName; x();. That is because you are using an expression to get the function and thus the context is lost.
So you either need to use person.logName() or (false || person.logName).call(person)
Here is a more complex example:
var actionMap = {
0: function() {
console.log(this === actionMap ? "actionMap" : "global");
},
1: function() {
console.log(this === actionMap ? "actionMap" : "global");
},
};
function other() {
console.log(this === actionMap ? "actionMap" : "global");
}
actionMap[0](); // works
(actionMap[0])(); // works, brackets get ignored
(actionMap[0] || actionMap[1])(); // context is lost in the expression
(actionMap[2] || actionMap[1])(); // context is lost in the expression
(actionMap[2] || actionMap[1]).call(actionMap); // works, context is called explicitly
var boundOther = other.bind(actionMap);
(boundOther || actionMap[1])(); // function is bound
As you can see in this example, the context is lost even though both functions are owned by the same object. Since the function that gets called is derived by an expression and is not immediately called on its owning object.
This is simply how Javascript semantics behave. Unless you call the method from the object it is attached to this cannot be resolved in the function scope.
Your example of:
(false || person.logName)();
Could be rewritten as:
var func = person.logName;
func();
Here you can visually see that there is an intermediate step where logName() is detached from person. This has a side effect of removing the this context.
There are ways to enforce the this context on "detached" functions. One of them being Function.prototype.bind() which enables the binding of this to any function.
(false || person.logName.bind(person))();
Here a new function is created with person bound as this to achieve the desired behavior. Alternatively you could use Function.prototype.call() to avoid creating a new function.
(false || person.logName).call(person);
This works in your contrived use-case, but it may not fit into your real use-case as you will need to pass different objects as the this parameter when the functions you "detach" are not from the same object.
this works on instantiated classes/functions. In your case you have to treat the object as static like:
let person = {
name : "Shimon",
logName : function(){
console.log(person.name);
}
};
If you want this behavior you can either use classes or prototypal inherintance
class Person {
constructor(name) {
this.name = name;
}
logName() {
console.log(this.name);
}
}
or
function person(name) {
this.name = name;
}
person.prototype.logName = function() {
console.log(this.name);
}
let shimon = new Person("Shimon");
shimon.logName() // >> Shimon
Related
I'm currently reading You don't know JS. In it, there is a section that talks about soft binding technique. Basically it's a variation of binding a function to a particular scope/context.
From the book:
It would be nice if there was a way to provide a different default for default binding (not global or undefined), while still leaving the function able to be manually this bound via implicit binding or explicit binding techniques.
if (!Function.prototype.softBind) {
Function.prototype.softBind = function(obj) {
var fn = this,
curried = [].slice.call( arguments, 1 ),
bound = function bound() {
return fn.apply(
(!this ||
(typeof window !== "undefined" &&
this === window) ||
(typeof global !== "undefined" &&
this === global)
) ? obj : this,
curried.concat.apply( curried, arguments )
);
};
bound.prototype = Object.create( fn.prototype );
return bound;
};
}
Generally I understand what the function does except this part:
bound.prototype = Object.create( fn.prototype );
Why do we have to setup a prototype when using this "soft bind" technique?
Why do we have to setup a prototype when using this "soft bind" technique?
It's more for completeness sake than anything else. If someone tries to create a new instance of the soft-bound function, they'd expect the resulting object to [[Prototype]] link to the same object as the original function's .prototype pointed. So, we make sure to set bound.prototype equal to reference that same object.
The chances of someone wanting to call new on a soft-bound function are low, I'd think, but just to be more safe, it's included nonetheless. The same, btw, is true of the polyfill for the built in bind(..) as listed here:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind#Polyfill
From that page:
Why is new being able to override hard binding useful?
The primary reason for this behavior is to create a function (that can
be used with new for constructing objects) that essentially ignores
the this hard binding but which presets some or all of the function's
arguments. One of the capabilities of bind(..) is that any arguments
passed after the first this binding argument are defaulted as standard
arguments to the underlying function (technically called "partial
application", which is a subset of "currying").
I guess for the softbind to have the same benefits (to provide a constructor that has default arguments passed to it) you have to set the prototype as well or you're missing prototype functions when you use the bound function as a constructor:
//softBind code removed
function Test(arg1,arg2){
this.arg1=arg1;
this.arg2=arg2;
}
Test.prototype.sayArgs=function(){
console.log('args:',this.arg1,this.arg2);
}
//the binding part doesn't do anything so can pass null
var BoundTest = Test.softBind(null, 'arg1');
var bt = new BoundTest('arg2');
bt.sayArgs();//=args:arg1 arg2
If you are going to use it this way though you don't have to add softBind to the Function.prototype, you can use normal bind, either native or the MDN polyfill.
Note that with the polyfill you cannot pass a falsy argument as the first parameter to bind or it'll break.
//Using the MDN bind polyfill you can't pass any falsy
// value like null, undefined,"",0 and false
var BoundTest = Test.bind({}, 'arg1');
var bt = new BoundTest('arg2');
bt.sayArgs();//=args:arg1 arg2;
In the article, the example given is actually broken:
var bar = foo.bind( null, "p1" );
var baz = new bar( "p2" );
Should be:
var bar = foo.bind('anything non falsy' , "p1" );
[update]
//removed if, so it'll set softbind every time
Function.prototype.softBind = function(obj) {
var fn = this,
curried = [].slice.call( arguments, 1 ),
bound = function bound() {
return fn.apply(
(!this ||
(typeof window !== "undefined" &&
this === window) ||
(typeof global !== "undefined" &&
this === global)
) ? obj : this,
curried.concat.apply( curried, arguments )
);
};
bound.prototype = Object.create( fn.prototype );
return bound;
};
function Test(){
//this has the right prototype and has
// the default arguments
console.log('this is:', this,'arguments',arguments);
this.name='test';
}
Test.prototype.something=22
var BoundTest = Test.softBind(null,'arg1','arg2');
var bt = new BoundTest();
//bt is same as new Test('arg1','arg2') would be
console.log('bt is:',bt);
//this.name did not affect window
console.log(window.name);//not test
console.log(bt instanceof Test);//true
When initially invoking a function, the first this within the first function that is called refers to the parent object foo but on a subsequent function called by that first function this refers to the window object?
var foo = (function Obj(){
var t = this;
return {
getThis: getThis,
getSecondThis: getSecondThis
};
function getThis(){
console.log(this);
console.log(t);
getSecondThis()
return false;
}
function getSecondThis(){
console.log(this);
console.log(t);
return false;
}
})();
foo.getThis();
If I change the call from getSecondThis() to this.getSecondThis() then the this within getSecondThis() refers to the parent object foo see the code below
var foo = (function Obj(){
var t = this;
return {
getThis: getThis,
getSecondThis: getSecondThis
};
function getThis(){
console.log(this);
console.log(t);
this.getSecondThis() //line edited
return false;
}
function getSecondThis(){
console.log(this);
console.log(t);
return false;
}
})();
foo.getThis();
The getSecondThis() is within the scope of the parent object foo but window is returned when this is not specified on the second call.
It's just the way JS binds the calling context: JS binds the context (the this reference) ad-hoc. Meaning: depending on how, where and by what means it is invoked, this will reference a different object.
I've explained this in some detail before here, and in the linked answers found on the bottom
Basically, functions are first class objects, meaning that, like any value, you can assign a function to a multitude of variables/properties. Of course, if you assign a function to an object (as a property), that function is often referred to as a method, and you'd expect this to point to the object that owns that method.
However, as Pointy noted in the comments: An object cannot own another object. Objects can be referenced by one or more properties of another object.
JS will kindly set this to refer to the object that owns the function. But if you then assign the object to a variable, it would make no sense to have this point to that same object. Think of situations where you're passing functions as function arguments (callbacks in jQuery and so on). You probably want this to reference the new context (certainly the case in jQ event handlers!).
If no context is provided, JS sadly defaults the this reference to the global object.
You can explicitly bind a function to a given context using the Function.prototype.bind call.
If you want to specify the context for a single call, you can use Function.prototype.call(context, arg1, arg2); or Function.prototype.apply(context, [args]);
Most larger projects (toolkits like jQ for example) solve this issue by taking advantage of closure scoping. Using the module pattern, for example, is a common, and easy way to control the context. I've explained this, too, complete with graphs to illustrate what is going on :)
Some examples/puzzles to make this easier to follow or more fun:
var obj = (function()
{//function scope
'use strict';//this will be null, instead of global, omit this and this can be window
var obj = {},
funcOne = function()
{
console.log(this, obj, this === obj);
funcTwo(this);
obj.funcTwo();
obj.funcTwo(this);
obj.funcTwo(obj);
},
funcTwo = function(that)
{
console.log(this, obj, this === obj, this === that, that === obj);
};
obj.funcOne = funcOne;
obj.funcTwo = funcTwo;
return obj;//is assigned to the outer var
}());
obj.funcOne();
//output:
//- first logs itself twice, then true
//then calls made in funcOne:
funcTwo()
console.log(this, obj, this === obj, this === that, that === obj);
//- this: undefined (omit 'use strict' and it'll be window), obj,
// this === obj => false, this === that => false, that === obj => true
obj.funcTwo();
console.log(this, obj, this === obj, this === that, that === obj);
//logs obj twice, because obj.funcTwo means the context === obj
//this === obj is true (of course)
//that is undefined, so this === that and that === obj are false
//lastly
obj.funcTwo(this);
obj.funcTwo(obj);
You should be able to work that out. You know the context in which funcOne is being executed, and you know what the effects are of invoking funcTwo as a method of obj
Rule of thumb:
I hesitated to write this, because it's far from accurate, but 8/10 cases. Assuming no code has been meddling with contexts through bind, call, and apply, you can work out the context using this trick:
someObject.someMethod();
/\ ||
|===this===|
//another object:
var obj2 = {
borrowed: someObject.someMethod,
myOwn: function()
{
this.borrowed();
}
};
obj2.myOwn();//this === obj2 (as explained above),
\\
\==> this.borrowed === obj2.borrowed
\\
\==> ~= someObject.someMethod.call(obj2);//function is executed in context of obj2
//simple vars
var alias = someObject.someMethod;//assign to var
alias();//no owner to be seen?
||
?<==|
//non-strict mode:
[window.]alias();
/\ implied ||
|| ||
|==<this>===|
//strict mode
alias.call(undefined);//context is undefined
I'm trying to determine wether a function in javascript is a simple, plain (anonymous) function, or a constructor ( a function with a prototype ). So far, I've come up with the following function:
function isPlainFunction(value) {
var own = Object.getOwnPropertyNames(value.prototype),
ctorIndex = own.indexOf('constructor');
if ( ctorIndex !== -1 ) {
own.splice( ctorIndex, 1);
}
if (own.length) {
return false;
}
// walk prototype chain
var proto = Object.getPrototypeOf(value.prototype);
if (proto === Object.prototype) {
return true;
}
return isPlainFunction(proto);
}
I'm only targeting ES5, (node.js) but I am uncertain whether this covers all edge cases, or if there's still something I havent found regarding this subject.
I have (roughly) the following testcases in mind:
assert.ok( isPlainFunction(function(){}) );
var bar = function(){};
bar.prototype = { get one(){ return 1 } };
assert.equal( isPlainFunction(bar), false );
var foo = function(){};
foo.prototype = Object.create( bar );
assert.equal( isPlainFunction(bar), false );
That is, any function that has a prototype or inherited a prototype from one of the not-native types...
If what you are trying to test for is whether or not a function should be used as a constructor, then unfortunately this cannot be accurately determined.
You can invoke the new operator on any function, whether intended to be used that way or not without issues.
If I have this method, for instance:
function doSomethingWithThisObject(someValue) {
this.someVariable = someValue;
}
Which has the following prototype:
doSomethingWithThisObject.prototype = { prototypeVariable : 'I came from prototype' };
I could use it in the following ways:
// Use my function as a constructor:
var obj = new doSomethingWithThisObject('hi there!');
console.log(obj.someVariable); // prints "hi there!"
console.log(obj.prototypeVariable); // prints "I came from prototype"
// Use my function in an object:
var myObject = {
doSomething : doSomethingWithThisObject
};
myObject.doSomething('hi again!');
console.log(myObject.someVariable); // prints "hi again!"
console.log(myObject.prototypeVariable); // prints "undefined"
// Use my function to change global state:
doSomethingWithThisObject('you owe me ice cream!');
console.log(someVariable); // prints "you owe me ice cream!"
console.log(prototypeVariable); // prints "undefined"
Determining whether or not one of those use cases is the correct one is impossible unless the intention is specified somewhere in the code.
Some people suggest to name constructor methods with an uppercase first letter to determine that they should be used as constructors. If you decide for this suggestion with your project's coding guidelines, you could simply check if the function's name begins with an uppercase letter which would denote that the person who wrote the function intends for it to be used as a constructor.
As Ben Barkay has said, any function can become a constructor in JS through the new keyword. Behind the scenes all new is doing is setting the function's context -- you can see this with a simple test:
function test() {
console.log(this)
}
test()
output: Window {top: Window, window: Window…}
new test()
output: test {}
test {}
In JS all you need to make a function a constructor is a new keyword, and all a new does is set the function's this variable. So any function can become a constructor.
Distinguishing an anon function is easier: if it doesn't have a name, it's anonymous:
//anon:
(function() {
console.log("I'm anon")
})()
var anon = function() {
console.log("I, too, am anon")
}
If you programmatically need the function's name, you can get it through function.name
To determine whether a function does not have an extended prototype, e.g. can be assumed to be more then a plain function, the following function would return false:
function isPlainFunction(value) {
if (typeof value !== 'function') {
return false;
}
var own = Object.getOwnPropertyNames(value.prototype);
if ( own.length >= 2 || ( own.indexOf('constructor') < 0 && own.length >= 1 ) ) {
return false;
}
return Object.getPrototypeOf(value.prototype) === Object.prototype;
}
With respect to JS, what's the difference between the two? I know methods are associated with objects, but am confused what's the purpose of functions? How does the syntax of each of them differ?
Also, what's the difference between these 2 syntax'es:
var myFirstFunc = function(param) {
//Do something
};
and
function myFirstFunc(param) {
//Do something
};
Also, I saw somewhere that we need to do something like this before using a function:
obj.myFirstFunc = myFirstFunc;
obj.myFirstFunc("param");
Why is the first line required, and what does it do?
Sorry if these are basic questions, but I'm starting with JS and am confused.
EDIT: For the last bit of code, this is what I'm talking about:
// here we define our method using "this", before we even introduce bob
var setAge = function (newAge) {
this.age = newAge;
};
// now we make bob
var bob = new Object();
bob.age = 30;
// and down here we just use the method we already made
bob.setAge = setAge;
To answer your title question as to what is the difference between a 'function' and a 'method'.
It's semantics and has to do with what you are trying to express.
In JavaScript every function is an object. An object is a collection of key:value pairs. If a value is a primitive (number, string, boolean), or another object, the value is considered a property. If a value is a function, it is called a 'method'.
Within the scope of an object, a function is referred to as a method of that object. It is invoked from the object namespace MyObj.theMethod(). Since we said that a function is an object, a function within a function can be considered a method of that function.
You could say things like “I am going to use the save method of my object.” And "This save method accepts a function as a parameter.” But you generally wouldn't say that a function accepts a method as a parameter.
Btw, the book JavaScript Patterns by Stoyan Stefanov covers your questions in detail, and I highly recommend it if you really want to understand the language. Here's a quote from the book on this subject:
So it could happen that a function A, being an object, has properties and methods, one of which happens to be another function B. Then B can accept a function C as an argument and, when executed, can return another function D.
There is a slight difference -
Method : Method is a function when object is associated with it.
var obj = {
name : "John snow",
work : function someFun(paramA, paramB) {
// some code..
}
Function : When no object is associated with it , it comes to function.
function fun(param1, param2){
// some code...
}
Many answers are saying something along the lines that a method is what a function is called when it is defined on an object.
While this is often true in the way the word is used when people talk about JavaScript or object oriented programming in general (see here), it is worth noting that in ES6 the term method has taken on a very specific meaning (see section 14.3 Method Definitions of the specs).
Method Definitions
A method (in the strict sense) is a function that was defined through the concise method syntax in an object literal or as a class method in a class declaration / expression:
// In object literals:
const obj = {
method() {}
};
// In class declarations:
class MyClass {
method() {}
}
Method Specificities
This answer gives a good overview about the specificities of methods (in the strict sense), namely:
methods get assigned an internal [[HomeObject]] property which allows them to use super.
methods are not created with a prototype property and they don't have an internal [[Construct]] method which means that they cannot be called with new.
the name of a method does not become a binding in the method's scope.
Below are some examples illustrating how methods (in the strict sense) differ from functions defined on objects through function expressions:
Example 1
const obj = {
method() {
super.test; // All good!
},
ordinaryFunction: function ordinaryFunction() {
super.test; // SyntaxError: 'super' keyword unexpected here
}
};
Example 2
const obj = {
method() {},
ordinaryFunction: function ordinaryFunction() {}
};
console.log( obj.ordinaryFunction.hasOwnProperty( 'prototype' ) ); // true
console.log( obj.method.hasOwnProperty( 'prototype' ) ); // false
new obj.ordinaryFunction(); // All good !
new obj.method(); // TypeError: obj.method is not a constructor
Example 3
const obj = {
method() {
console.log( method );
},
ordinaryFunction: function ordinaryFunction() {
console.log( ordinaryFunction );
}
};
obj.ordinaryFunction() // All good!
obj.method() // ReferenceError: method is not defined
A method is a property of an object whose value is a function. Methods are called on objects in the following format: object.method().
//this is an object named developer
const developer = {
name: 'Andrew',
sayHello: function () {
console.log('Hi there!');
},
favoriteLanguage: function (language) {
console.log(`My favorite programming language is ${language}`);
}
};
// favoriteLanguage: and sayHello: and name: all of them are proprieties in the object named developer
now lets say you needed to call favoriteLanguage propriety witch is a function inside the object..
you call it this way
developer.favoriteLanguage('JavaScript');
// My favorite programming language is JavaScript'
so what we name this: developer.favoriteLanguage('JavaScript');
its not a function its not an object? what it is? its a method
Your first line, is creating an object that references a function. You would reference it like this:
myFirstFunc(param);
But you can pass it to another function since it will return the function like so:
function mySecondFunction(func_param){}
mySecondFunction(myFirstFunc);
The second line just creates a function called myFirstFunc which would be referenced like this:
myFirstFunc(param);
And is limited in scope depending on where it is declared, if it is declared outside of any other function it belongs to the global scope. However you can declare a function inside another function. The scope of that function is then limited to the function its declared inside of.
function functionOne(){
function functionTwo(){}; //only accessed via the functionOne scope!
}
Your final examples are creating instances of functions that are then referenced though an object parameter. So this:
function myFirstFunc(param){};
obj.myFirst = myFirstFunc(); //not right!
obj.myFirst = new myFirstFunc(); //right!
obj.myFirst('something here'); //now calling the function
Says that you have an object that references an instance of a function. The key here is that if the function changes the reference you stored in obj.myFirst will not be changed.
While #kevin is basically right there is only functions in JS you can create functions that are much more like methods then functions, take this for example:
function player(){
this.stats = {
health: 0,
mana: 0,
get : function(){
return this;
},
set : function( stats ){
this.health = stats.health;
this.mana = stats.mana;
}
}
You could then call player.stats.get() and it would return to you the value of heath, and mana. So I would consider get and set in this instance to be methods of the player.stats object.
A function executes a list of statements example:
function add() {
var a = 2;
var b = 3;
var c = a + b;
return c;
}
1) A method is a function that is applied to an object example:
var message = "Hello world!";
var x = message.toUpperCase(); // .toUpperCase() is a built in function
2) Creating a method using an object constructor. Once the method belongs to the object you can apply it to that object. example:
function Person(first, last, age, eyecolor) {
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
this.name = function() {return this.firstName + " " + this.lastName;};
}
document.getElementById("demo").innerHTML = person.fullName(); // using the
method
Definition of a method: A method is a property of an object that is a function. Methods are defined the way normal functions are defined, except that they have to be assigned as the property of an object.
var myFirstFunc = function(param) {
//Do something
};
and
function myFirstFunc(param) {
//Do something
};
are (almost) identical. The second is (usually) just shorthand. However, as this jsfiddle (http://jsfiddle.net/cu2Sy/) shows, function myFirstFunc will cause the function to be defined as soon as the enclosing scope is entered, whereas myFirstFunc = function will only create it once execution reaches that line.
As for methods, they have a this argument, which is the current object, so:
var obj = {};
obj.func = function( ) {
// here, "this" is obj
this.test = 2;
}
console.log( obj.test ); // undefined
obj.func( );
console.log( obj.test ); // 2
The exact syntax you showed is because you can also do this:
function abc( ) {
this.test = 2;
}
var obj = {};
obj.func = abc;
obj.func( ); // sets obj.test to 2
but you shouldn't without good reason.
ecma document
4.3.31method :
function that is the value of a property
NOTE When a function is called as a method of an object, the object is
passed to the function as its this value.
It is very clear: when you call a function if it implicitly has a this (to point an object) and if you can't call the function without an object, the function deserves to name as method.
function katana(){
this.isSharp = true;
}
katana();
assert( isSharp === true, "A global object now exists with that name and value." );
var shuriken = {
toss: function(){
this.isSharp = true;
}
};
shuriken.toss();
assert( shuriken.isSharp === true, "When it's an object property, the value is set within the object." );
I am actually not getting what is this code trying to tell?. Can anyone explain me what is Context in JavaScript and what exactly does context represent here in the above code?
Well, the reason this === window in the first example and this === shuriken in the second example all has to do with where those functions are created. Notice that when you define shuirken.toss outside of the object, this points to the window object. And when you call katana with new, this points to the newly created object.
The first call is equivalent to:
katana.call(window); // window is now referenced as this inside the function
In a similar fashion, you could change the call to katana() like this to change the context:
var shuriken = {}
katana.call(shuriken); // shuriken.isSharp === true
The second invocation implicitly has this equivalent function call:
shuriken.toss.call(shuriken);