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
Related
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
I've seen many questions for that context, but I still can't figure out, what exactly my Problem is. (I'm still experimenting with JavaScript, especially with objects)
Code:
function Field(val)
{ var value = val;
this.__defineGetter__("value", function(){ return value; });
this.__defineSetter__("value", function(val){ value = val; if(this.onchange) this.onchange.call(); });
}
function LW_makePlan()
{
/* [...] */
this.Filter1=new Field("");
this.Filter2=new Field("");
this.Filter3=new Field("");
this.init = function()
{
/* [...] */
this.Filter1.onchange=this.getSomething;
}
this.getSomething = function()
{
arg="modus=doWhat";
arg=arg+"&filter1=" + this.Filter1.value;
arg=arg+"&filter2=" + this.Filter2.value;
arg=arg+"&filter3=" + this.Filter3.value;
request_fkt(null, true, arg , this.setSomething);
}
this.setSomething = function(data)
{
alert(data);
}
this.init();
};
What I'm trying:
test = new LW_makePlan();
test.Filter1.value="anything";
test.Filter1 has an "onchange"-property, that is checked in the setter of "Field". if set, the setter will also call the object given within the onchange-property.
this works so far BUT it seems, that this call creates a whole new object-instance ... no not an instance, it is, as if the function "getSomething" is copied as a stand-alone function, because the Code i called, but for example this.Filter1 within the function "getSomething" is undefined ...
Why is this happening and how can I avoid this?
PS: I don't want to use some type of event-handling-Things from 3rd Party codes, I'd like to do it myself with a little help maybe.
EDIT:
Thanks to Steffen Heil, changed to:
var scope=this;
this.Filter1.onchange=function() { scope.getSomething(); };
and it works!
Your call to this.onchange is in Field, so you are calling a function of Field. The assignment this.Filter1.onchanged=this.getSomething kind of copies the method getSomething from LW_makePlan to Field, where it will be called.
So inside of getSomething that is now called onchanged the reference this referes to the Field not the LW_makePlan.
Replace the assignment with this:
var source = this;
this.Filter1.onchange = function() { return source.getSomething(); };
And it will work. Most frameworks have a bindmethod that makes this more readable (hiding the extra variable in a scope).
In reply to the first comment:
You can explicitly call a function like this:
x.call( object, arg1, arg2, ag3 );
x.apply( object, [ arg1, arg2, arg3 ] );
These the are the same and it does not matter what x is. Inside the called function this has the value of object.
x can be:
alert
window.alert
(function(){})
(alert)
(window.alert)
Normal calls to a function are shortcuts:
object.f = g;
object.f( arg1 ) => g.call( object, arg1 );
f( arg1 ) => f.call( window, arg1 );
While window is the global object in a browser; other environments may use another global object.
While the difference between these two shortcuts seems tivial, what about the following?
(object.f)( arg1 )
This is completely valid javascript, as object.f is a function and a function can be invoked using (args1). But:
object.f = g;
(object.f)( arg1 ) => g.call( window, arg1 )
So a.f = b.f; copies a member reference from a to b, but the this context, the code is executon on depends on the way f is called.
a.f(x) == a.f.call(a,x) == (a.f).call(a,x) == b.f.call(a,x) == (b.f).call(a,x)
b.f(x) == b.f.call(b,x) == (b.f).call(b,x) == a.f.call(b,x) == (a.f).call(b,x)
By the way, you can define your own bind very easily:
function bind( object, method ) {
return function() {
return object[ method ].apply( object, arguments );
};
}
Then the original code would become:
this.Filter1.onchange = bind( this, 'getSomething' );
This would match the fix I gave above using "late binding". Most libraries prefer "early binding":
function bind( object, method ) {
return function() {
return method.apply( object, arguments );
};
}
Then the original code would become:
this.Filter1.onchange = bind( this, this.getSomething );
The advantage is better performance, but the main difference is what happens, when getSomething changes after bind was called. The first implementation calls the new value, the second the old value.
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 know that inside the function it is this.
var func = function {
return this.f === arguments.callee;
// => true, if bound to some object
// => false, if is bound to null, because this.f === undefined
}
var f = func; // not bound to anything;
var obj = {};
obj1.f = func; // bound to obj1 if called as obj1.f(), but not bound if called as func()
var bound = f.bind(obj2) // bound to obj2 if called as obj2.f() or as bound()
Edited:
You can't actually call obj2.f() as f doesn't become a property of obj2
edit end.
The question is: how to find the object, that the function is bound to, outside of this function?
I want to achieve this:
function g(f) {
if (typeof(f) !== 'function') throw 'error: f should be function';
if (f.boundto() === obj)
// this code will run if g(obj1.f) was called
doSomething(f);
// ....
if (f.boundto() === obj2)
// this code will run if g(obj2.f) or g(bound) was called
doSomethingElse(f);
}
and partial application without changing the object that the function is bound to:
function partial(f) {
return f.bind(f.boundto(), arguments.slice(1));
}
Consensus:
You can't do it. Takeaway: use bind and this with great care :)
Partial Application
You can do partial application:
// This lets us call the slice method as a function
// on an array-like object.
var slice = Function.prototype.call.bind(Array.prototype.slice);
function partial(f/*, ...args */) {
if (typeof f != 'function')
throw new TypeError('Function expected');
var args = slice(arguments, 1);
return function(/* ...moreArgs */) {
return f.apply(this, args.concat(slice(arguments)));
};
}
What Object is this Function Bound To?
Additionally, there's a pretty straight-forward solution to the first part of your question. Not sure if this is an option for you, but you can pretty easily monkey-patch things in JS. Monkey-patching bind is totally possible.
var _bind = Function.prototype.apply.bind(Function.prototype.bind);
Object.defineProperty(Function.prototype, 'bind', {
value: function(obj) {
var boundFunction = _bind(this, arguments);
boundFunction.boundObject = obj;
return boundFunction;
}
});
Just run that before any other scripts get run, and any script which uses bind, it will automatically add a boundObject property to the function:
function f() { }
var o = { };
var g = f.bind(o);
g.boundObject === o; // true
(Note: I'm assuming you're in an ES5 environment above due to the fact that you're using bind.)
A function in javascript is not technically bound to anything. It may be declared as a property of an object as in:
var obj = {
fn: function() {}
}
obj.fn();
But, if you get the function into it's own variable by itself, it is not bound to any particular object.
For example, if you did this:
var obj = {
fn: function() {}
}
var func = obj.fn;
func();
Then, when you call func() which in turn executes the fn() function, it will have no association with obj at all when it is called. Associating an object with a function is done by the caller of the function. It is not a property of the function.
If one were to use fn.bind(obj), that creates a new function that just internally executes a call to obj.fn(). It doesn't magically add any new capabilities to javascript. In fact, you can see a polyfill for .bind() to see how it works here on MDN.
If you are expecting this to always be a particular object no matter how a function is called, that is not how javascript works. Unless a function is actually a shim that forces an association with a hard-wird object when it's called (what .bind() does), then a function doesn't have a hard-wired association. The association is done by the caller based on how it calls the function. This is different than some other languages. For example, in C++, you can't call a function without having the right object to associate with it at call time. The language simply won't resolve the function call and you get a compile error.
If you are branching on types in javascript, then you are probably not using the object-oriented capabilities of the language properly or to your best advantage.
Instead of binding the function func to the objects, why not try treating func as an object, that can hold obj1 and obj2 as its properties?
For example:
var func = function {
this.object; // Could be obj1 or obj2
return this.f === arguments.callee;
// => true, if this.object is not null
}
var f = func;
f.object = obj1; // or func.object = obj2;
You can also write a function that handles whether or not the object is obj1 or obj2:
function g(f) {
if (typeof(f) !== 'function') throw 'error: f should be function';
if (f.object === obj)
// this code will run if g(f) was called
doSomething(f);
if (f.object === obj2)
// this code will run if g(f) or g(bound) was called
doSomethingElse(f);
}
The reason is that you want to treat obj1 and obj2 as a property of the function f. However, when you bind, you are adding the function as a property of either obj1 or obj2. It's possible to bind the function to multiple objects, so it doesn't make sense to look for THE one object to which you bound the function; because you're adding the function as a subset of the object.
In this case, since you want to treat the object as a subset of the function, it might make sense to add a property into the function that can hold obj1 or obj2.
If you are the one doing the bind, you can add a field to the function to record the this for later testing.
var that = "hello";
var xyz = function () { console.log(this); }.bind(that);
xyz.that = that;
// xyz is callable as in xyz(), plus you can test xyz.that without calling
Reference : http://ejohn.org/blog/simple-class-instantiation/
// makeClass - By John Resig (MIT Licensed)
function makeClass(){
return function(args){
if ( this instanceof arguments.callee ) {
if ( typeof this.init == "function" )
this.init.apply( this, args.callee ? args : arguments );
} else
return new arguments.callee( arguments );
};
}
I was wondering, if there are any ECMAScript 5 compliant way to implement the same functionality. The problem is, accessing arguments.callee is deprecated in strict mode.
As I understand it arguments.callee isn't deprecated in strict mode, in which case you could continue to use it; rather, it has been removed and attempted use will (or is supposed to) throw an exception.
The workaround is to use named anonymous functions, if you'll forgive the oxymoron. Really I should say "named function expressions". An example:
function someFunc(){
return function funcExpressionName(args){
if (this instanceof funcExpressionName) {
// do something
} else
return new funcExpressionName( arguments );
};
}
The name you provide, in my example funcExpressionName is not supposed to be accessible from anywhere except inside the function it applies to, but unfortunately IE has other ideas (as you can see if you Google it).
For the example in your question I'm not sure how to handle the args.callee since I don't know how that is set by the calling function, but the use of arguments.callee would be replaced as per my example.
The above idea given by nnnnnn is quite good. And in order to avoid IE issues I suggest the following solution.
function makeClassStrict() {
var isInternal, instance;
var constructor = function(args) {
// Find out whether constructor was called with 'new' operator.
if (this instanceof constructor) {
// When an 'init' method exists, apply it to the context object.
if (typeof this.init == "function") {
// Ask private flag whether we did the calling ourselves.
this.init.apply( this, isInternal ? args : arguments );
}
} else {
// We have an ordinary function call.
// Set private flag to signal internal instance creation.
isInternal = true;
instance = new constructor(arguments);
isInternal = false;
return instance;
}
};
return constructor;
}
Note how we avoid reference to args.callee in the // do something part by using an internal flag.
John Resig's original code fails with a parameterless constructor.
var Timestamp = makeClass();
Timestamp.prototype.init = function() {
this.value = new Date();
};
// ok
var timestamp = Timestamp();
alert( timestamp.value );
// TypeError: args is undefined
var timestamp = new Timestamp();
alert( timestamp.value );
But it can be repaired using the following line
this.init.apply( this, args && args.callee ? args : arguments );