admin管理员组文章数量:1242885
I was reading the function definition of bind, but I can't 100% understand the code as written:
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis || window,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
Specifically, I don't get the purpose of fNOP
, and I don't understand why fBound
's prototype needs to be set. I am also hung up at the fToBind.apply
part (I can't figure out what this represents in this context).
Can someone can explain what is going on here?
I was reading the function definition of bind, but I can't 100% understand the code as written:
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis || window,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
Specifically, I don't get the purpose of fNOP
, and I don't understand why fBound
's prototype needs to be set. I am also hung up at the fToBind.apply
part (I can't figure out what this represents in this context).
Can someone can explain what is going on here?
Share Improve this question edited Oct 8, 2024 at 12:58 dumbass 27.2k4 gold badges36 silver badges73 bronze badges asked Jan 10, 2012 at 19:13 Davis DimitriovDavis Dimitriov 4,2573 gold badges33 silver badges46 bronze badges 6- 4 I need more Vodka "Smirnoff" to answer this question... – NoWar Commented Jan 10, 2012 at 19:55
- About halfway down on stage.learn.jquery./javascript-101/closures, does that shed any insight? – j08691 Commented Jan 10, 2012 at 20:41
-
2
I don't think it will help much, but for reference:
Function.prototype.bind
specification. – Felix Kling Commented Jan 10, 2012 at 20:52 - @Davis It is NOT the definition of bind, it is a partial(!) workaround for older browsers. – user123444555621 Commented Jan 11, 2012 at 19:27
- Keep in mind that MDN is a wiki, and may be erroneous. This is where the code came from: developer.mozilla/index.php?title=en/JavaScript/Reference/… – user123444555621 Commented Jan 11, 2012 at 22:29
4 Answers
Reset to default 5Well, one reason fBound
's prototype needs to be set is so that the result of calling bind
on a function has the same prototype as that function. This is also where fNop
seems to e in--it lets you set fBound
's prototype using new fNop()
without calling the original function which may have side effects.
The call to apply
lets you both set this
in the function and specify additional arguments. Since bind
lets you "curry" arguments to the function, you have to bine both the arguments passed in when the function is bound and the arguments it is called with.
It is to make sure
- (1) the bound function can be used as a constructor, ignoring the binding. (hence the
instanceof
check) - (2) At the same time, you want to make sure that
new g()
inherits fromf
's prototype chain. (hence the.prototype = new fNop
part)
Example:
function f() {
this.foo = 'bar';
}
f.prototype = {
baz: 'yay!'
};
var g = f.bind({});
var o = new g();
console.log(o.foo); // 'bar' - (1)
console.log(o.baz); // 'yay!' - (2)
At the moment you call new g()
, the fBound
function is called as a constuctor with a brand new object object (this
) that is an instance of fNop
.
Edit:
The ECMAScript5 standard defines a plicated algorithm for binding functions. Amongst others, the following assertions must hold true:
var DateJan2042 = Date.bind(null, 2042, 0);
/*1*/ console.assert(Function.prototype.bind.length == 1, 'bind should have a length of 1');
/*2*/ console.assert(typeof DateJan2042 == 'function', 'bind() should return a function');
/*3*/ console.assert(!DateJan2042.hasOwnProperty('prototype'), 'Bound function must not have a prototype');
/*4*/ console.assert(DateJan2042.length == Math.max(Date.length - 2, 0), 'Bound function should have a proper length');
/*5*/ console.assert(typeof DateJan2042() == 'string', 'Function call should return a string');
/*6*/ console.assert({}.toString.call(new DateJan2042()).indexOf('Date') != -1, 'Constructor call should return a new Date object');
/*7*/ console.assert(new DateJan2042() instanceof DateJan2042, 'Instanceof check should pass for constructor\'s return value');
/*8*/ console.assert((new DateJan2042()).getMonth() == 0, 'Constructor should be called with bound arguments');
/*9*/ console.assert((new DateJan2042(1)).getDate() == 1, 'Constructor should take additional arguments');
/*10*/ console.assert(!/^function *\( *[^ )]/.test(Function.prototype.toString.call(DateJan2042)), 'Bound function should have no formal arguments');
Since a properly bound function is not a real Function
object, it's impossible to get it all right using a polyfill (Particularly numbers 2/3, and 4/10), but you can try to implement as much as possible.
The implementation in question tries to solve number 6 and number 7, by hooking into the prototype chain, but that's not enough.
Here is an alternative implementation that works a bit better, but is still not perfect: http://jsfiddle/YR6MJ/
From an earlier ment:
instead of using fNop, why can't you just say
fBound.prototype = this.prototype
?
As far as I can tell, the primary difference is that when the value of this
inside the bound function is an instance of the original function on which bind
was invoked, then the value to bind to -- the first argument originally passed to bind
-- is ignored.
For example, this code:
function Test(blah) {
console.log(this.length, blah);
}
Test.prototype.length = 77;
Test.prototype.fn = Test.bind(['a', 'b', 'c'], "testing");
new Test().fn()
...causes fn
to print:
77 testing
In other words, the value of this
inside fn
is the Test
instance on which it was invoked. Your suggestion would supply the bound array to the apply
inside bind
, so, written that way, the last line of the same code would print:
3 testing
It's not pletely clear to me why this is important, but it does highlight that your suggestion would not produce equivalent results.
// check to see if the native implementation of bind already
// exists in this version of JavaScript. We only define the
// polyfill if it doesn't yet exist.
if (!Function.prototype.bind) {
// creating the bind function for all Function instances by
// assigning it to the `Function.prototype` object. Normally
// you would avoid assigning to builtin prototypes because you
// may cause a conflict with new features, but here this is a
// known feature that is already in the spec that we're adding
// to a JavaScript runtime that is not up to spec, so its ok
Function.prototype.bind = function (oThis) {
// if you attempt to call this function from a non-function object
// for example if you assign this bind function to a normal object
// or use `call`/`apply` to change the context of this function call to
// a non function value (e.g. `Function.prototype.bind.call({})`), we
// throw an error because bind can only work on functions, and we
// require that `this` in this call is a function
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
// bind does two things, it binds a context (`this` value) to a
// function for when its called, and it provides a way to bake in
// some pre-defined arguments that are automatically passed into
// that function when called. Those arguments can be passed into
// the bind call and get picked up here as `aArgs` pulling them
// from `arguments` making sure to lop off the `oThis` value
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this, // this is the function we're binding
fNOP = function () {}, // a constructor used for `new` usage (see below)
// `fBound` is the bound function - the result that bind is going to
// return to represent the current function (`this` or `fToBind`) with
// a new context. The idea behind this function is that it will simply
// take the original function and call it through `apply` with the
// new context specified.
fBound = function () {
// call the original function with a new context using `apply`.
// however if the function is called with `new`, it needs to be called
// with the context of, and return, a new object instance and not the
// bound version of this. In that case, binding gets ignored in favor
// of using the `this` of the new instance rather than the `oThis` binding.
// new object instances inherit from the prototype of their constructors.
// Our `fBound` function is supposed to mimic the original with the
// exception of a change in context. So if new objects are created with
// it, they should behave as though they were created from the original.
// But at the same time, we can't simply carry over the prototype of the
// original into `fBound` because it is a separate function and needs its
// own prototype, just one that also inherits from the original. To
// acmodate this, the `fNOP` function (constructor) above is used as
// an intermediary for creating `fBound`'s prototype while allowing it to
// be unique but also inherit the original. And because that bees part
// of the bound function's prototype chain, it can be used to determine
// whether `this` in `fBound` is an instance created by `new` or not since
// `instanceof` works through a prototype chain lookup.
return fToBind.apply(this instanceof fNOP
? this
: oThis,
// call the function with arguments that include the added
// arguments specified from the original bind call plus
// the arguments this function was called with
aArgs.concat(Array.prototype.slice.call(arguments)));
};
// `fNOP`'s use to provide an intermediary prototype between `fBound` and
// the current function instance mimics `Object.create`. But we're assuming
// if you don't have `bind`, you probably don't have `create` either, so do
// it the old fashioned way with a constructor. This works by setting the
// constructor's prototype to the to-inherit-from constructor's (this)
// prototype. A check is needed to prevent assinging that prototype to null
// if it doesn't exist on this function (Function.prototype is technically
// a valid target for `bind()` because it is a function but one that does not
// have its own prototype).
if (this.prototype) {
fNOP.prototype = this.prototype;
}
// here the inheritance is made. As a new function, `fBound` has no existing
// inheritance chain to worry about, so we can easily replace it with a new
// one - that of a new instance `fNOP`. Since `fNOP`'s prototype is the original
// function's prototype, `fBound` has a prototype which directly inherits from
// that, one level between new instances and the original prototype. So
// `fBound.prototype.__proto__ === this.prototype` and new instances of `fBound`
// created with `new fBound()` will inherit from `fBound.prototype` as well as
// the original function's prototype.
fBound.prototype = new fNOP();
// return the bound version of the function as
// the result of the bind call
return fBound;
};
}
本文标签: I can39t understand how this javascript function worksStack Overflow
版权声明:本文标题:I can't understand how this javascript function works - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1740115205a2226951.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论