1
0
mirror of https://github.com/robertkrimen/otto synced 2025-10-12 20:27:30 +08:00

Add Function.bind

This commit is contained in:
Robert Krimen 2013-03-01 13:08:10 -08:00
parent bef32b9223
commit fef122900f
4 changed files with 83 additions and 0 deletions

View File

@ -71,3 +71,20 @@ func builtinFunction_call(call FunctionCall) Value {
} }
return thisObject.Call(this, []Value{}) return thisObject.Call(this, []Value{})
} }
func builtinFunction_bind(call FunctionCall) Value {
target := call.This
if !target.isCallable() {
panic(newTypeError())
}
targetObject := target._object()
this := call.Argument(0)
argumentList := call.slice(1)
if this.IsUndefined() {
// FIXME Do this elsewhere?
this = toValue(call.runtime.GlobalObject)
}
return toValue(call.runtime.newBoundFunctionObject(targetObject, this, argumentList))
}

View File

@ -69,3 +69,25 @@ func TestFunctionDeclarationInFunction(t *testing.T) {
typeof abc(); typeof abc();
`, "function") `, "function")
} }
func TestFunction_bind(t *testing.T) {
Terst(t)
test := runTest()
test(`
abc = function(){
return "abc";
};
def = abc.bind();
def();
`, "abc")
test(`
abc = function(){
return arguments[1];
};
def = abc.bind(undefined, "abc");
ghi = abc.bind(undefined, "abc", "ghi");
[ def(), def("def"), ghi("def") ];
`, ",def,ghi")
}

View File

@ -184,6 +184,7 @@ func newContext() *_runtime {
}, },
"apply", 2, builtinFunction_apply, "apply", 2, builtinFunction_apply,
"call", 2, builtinFunction_call, "call", 2, builtinFunction_call,
"bind", 1, builtinFunction_bind,
) )
self.Global.Array = self.newGlobalFunction( self.Global.Array = self.newGlobalFunction(

View File

@ -27,6 +27,17 @@ func (runtime *_runtime) newNodeFunctionObject(node *_functionNode, scopeEnviron
return self return self
} }
func (runtime *_runtime) newBoundFunctionObject(target *_object, this Value, argumentList []Value) *_object {
self := runtime.newClassObject("Function")
self._Function = &_functionObject{
Call: newBoundCallFunction(target, this, argumentList),
Construct: defaultConstructFunction,
}
// FIXME
self.stash.set("length", toValue(0), _propertyMode(0))
return self
}
func (self *_object) Call(this Value, argumentList ...interface{}) Value { func (self *_object) Call(this Value, argumentList ...interface{}) Value {
if self._Function == nil { if self._Function == nil {
panic(newTypeError("%v is not a function", toValue(self))) panic(newTypeError("%v is not a function", toValue(self)))
@ -159,6 +170,31 @@ func (self _nodeCallFunction) Source() string {
return "" return ""
} }
type _boundCallFunction struct {
_callFunction_
target *_object
this Value
argumentList []Value
}
func newBoundCallFunction(target *_object, this Value, argumentList []Value) *_boundCallFunction {
self := &_boundCallFunction{
target: target,
this: this,
argumentList: argumentList,
}
return self
}
func (self _boundCallFunction) Dispatch(_ *_functionEnvironment, runtime *_runtime, this Value, argumentList []Value, _ bool) Value {
argumentList = append(self.argumentList, argumentList...)
return runtime.Call(self.target, self.this, argumentList, false)
}
func (self _boundCallFunction) Source() string {
return ""
}
// FunctionCall{} // FunctionCall{}
// FunctionCall is an enscapulation of a JavaScript function call. // FunctionCall is an enscapulation of a JavaScript function call.
@ -177,6 +213,13 @@ func (self FunctionCall) Argument(index int) Value {
return valueOfArrayIndex(self.ArgumentList, index) return valueOfArrayIndex(self.ArgumentList, index)
} }
func (self FunctionCall) slice(index int) []Value {
if index < len(self.ArgumentList) {
return self.ArgumentList[index:]
}
return []Value{}
}
func (self *FunctionCall) thisObject() *_object { func (self *FunctionCall) thisObject() *_object {
if self._thisObject == nil { if self._thisObject == nil {
this := self.runtime.GetValue(self.This) // FIXME Is this right? this := self.runtime.GetValue(self.This) // FIXME Is this right?