Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/362.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript “this”在Crockford'中无效有什么原因吗;“咖喱”法?_Javascript_Partial Application - Fatal编程技术网

Javascript “this”在Crockford'中无效有什么原因吗;“咖喱”法?

Javascript “this”在Crockford'中无效有什么原因吗;“咖喱”法?,javascript,partial-application,Javascript,Partial Application,在Douglas Crockford的书“Javascript:The Good Parts”中,他为curry方法提供了代码,该方法接受函数和参数,并返回已添加参数的函数(显然,这并不是真正的,而是一个示例)。下面是代码,我对其进行了修改,使其在没有其他自定义代码的情况下工作: Function.prototype.curry = function(){ var slice = Array.prototype.slice, args = slice.apply(argument

在Douglas Crockford的书“Javascript:The Good Parts”中,他为
curry
方法提供了代码,该方法接受函数和参数,并返回已添加参数的函数(显然,这并不是真正的,而是一个示例)。下面是代码,我对其进行了修改,使其在没有其他自定义代码的情况下工作:

Function.prototype.curry = function(){
  var slice = Array.prototype.slice,
      args = slice.apply(arguments),
      that = this;
  return function() {
    // context set to null, which will cause `this` to refer to the window
    return that.apply(null, args.concat(slice.apply(arguments)));
  };
};
因此,如果您有一个
add
功能:

var add = function(num1, num2) {
  return num1 + num2;
};

add(2, 4);          // returns 6
您可以创建一个已经有一个参数的新函数:

var add1 = add.curry(1);

add1(2);           // returns 3
那很好。但我想知道的是,他为什么要把
这个
设为
?预期的行为是否会是curried方法与原始方法相同,包括相同的
this

我对咖喱的看法如下:

Function.prototype.myCurry = function(){
  var slice = [].slice,
      args = slice.apply(arguments),
      that = this;
  return function() {
    // context set to whatever `this` is when myCurry is called
    return that.apply(this, args.concat(slice.apply(arguments)));
  };
};
示例

var calculator = {
  history: [],
  multiply: function(num1, num2){
    this.history = this.history.concat([num1 + " * " + num2]);
    return num1 * num2;
  },
  back: function(){
    return this.history.pop();
  }
};

var myCalc = Object.create(calculator);
myCalc.multiply(2, 3);         // returns 6
myCalc.back();                 // returns "2 * 3"
如果我想用道格拉斯·克罗克福德的方式来做:

myCalc.multiplyPi = myCalc.multiply.curry(Math.PI);
myCalc.multiplyPi(1);          // TypeError: Cannot call method 'concat' of undefined
如果我按自己的方式做:

myCalc.multiplyPi = myCalc.multiply.myCurry(Math.PI);
myCalc.multiplyPi(1);          // returns 3.141592653589793
myCalc.back();                 // returns "3.141592653589793 * 1"
然而,我觉得如果道格拉斯·克罗克福德按照他的方式去做,他可能有一个很好的理由。我错过了什么

但我想知道的是他为什么把这个设为空

没有真正的理由。可能他想简化,而大多数需要使用或部分应用的函数都不是使用
this
的OOP方法。在功能性更强的样式中,附加到的
history
数组将是函数的另一个参数(甚至可能是一个返回值)

难道预期的行为不会是curry方法与原始方法相同,包括相同的this吗

是的,您的实现更有意义,但是如果一个部分应用的函数使用一个,您可能不会期望它仍然需要在正确的上下文中调用(就像您通过将它重新分配给对象所做的那样)

对于这些,您可以查看部分应用程序的函数对象,包括一个特定的
this
-值。

来自:

thisArg为调用fun提供的值。请注意 可能不是该方法看到的实际值:如果该方法是 函数在非严格模式代码中,null和undefined将被替换 使用全局对象,和基本值将被装箱

因此,如果该方法处于非严格模式,且第一个参数为
null
未定义
该方法内部的
将引用
窗口
。在严格模式下,这是
null
undefined
。我在上添加了一个实例


此外,如果函数根本不引用
,则传入
null
未定义
不会造成任何伤害。这可能就是为什么Crockford在他的例子中使用了
null
,以避免事情过于复杂。

读者注意,你会受到惊吓。

在JavaScript中,当涉及到咖喱、函数、部分应用程序和面向对象时,有很多值得讨论的内容。我会尽量简短地回答这个问题,但还有很多问题需要讨论。因此,我将我的文章分为几个部分,在每一部分的结尾,我都为你们中那些迫不及待想全部阅读的人总结了每一部分


1.咖喱还是不咖喱 让我们谈谈哈斯克尔。在Haskell中,默认情况下每个函数都是curry。例如,我们可以在Haskell中创建一个
add
函数,如下所示:

add :: Int -> Int -> Int
add a b = a + b
($>) :: a -> (a -> b) -> b
a $> f = f a

(>>>) :: (a -> b) -> (b -> c) -> (a -> c)
f >>> g = g . f

oddities = [0..9] $> filter odd >>> length
function bindState(g, f) {
    return function (s) {
        var a = g(s);
        return f(a[0])(a[1]);
    };
}
multiply = (a, b, history) --> [a * b, [a + " * " + b] ++ history]

back = ([top, ...history]) -> [top, history]

myCalc = []

multiplyPi = multiply Math.PI

bindState = (g, f, s) -->
    [a, t] = g s
    (f a) t

test = do
    prod <- bindState multiplyPi 1
    alert prod
    back

alert (test myCalc .0)
注意类型签名
Int->Int->Int
?这意味着
add
接受
Int
并返回类型为
Int->Int
的函数,该函数依次接受
Int
并返回
Int
。这使您可以轻松地部分应用Haskell中的函数:

add2 :: Int -> Int
add2 = add 2
JavaScript中的相同函数看起来很难看:

function add(a) {
    return function (b) {
        return a + b;
    };
}

var add2 = add(2);
这里的问题是JavaScript中的函数在默认情况下不是通用的。你需要用手来做咖喱,这很痛苦。因此,我们使用部分应用程序(aka)代替

第1课:使用咖喱使部分应用函数更容易。但是,它仅在默认情况下使用函数的语言中有效(例如Haskell)。如果您必须手动使用curry函数,那么最好使用部分应用程序


2.函数的结构 Haskell中也存在未剪切函数。它们看起来像“普通”编程语言中的函数:

main = print $ add(2, 3)

add :: (Int, Int) -> Int
add(a, b) = a + b
您可以分别使用Haskell中的and函数将当前形式的函数转换为非当前形式的函数,反之亦然。Haskell中的未传递函数仍然只接受一个参数。然而,该参数是多个值(即a)的乘积

同样,JavaScript中的函数也只接受一个参数(它只是还不知道)。该参数是产品类型。函数中的
参数
值是该产品类型的一种表现形式。JavaScript中的
apply
方法就是一个例子,它接受一个产品类型并对其应用一个函数。例如:

print(add.apply(null, [2, 3]));
var oddities = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].filter(odd).length;

function odd(n) {
    return n % 2 !== 0;
}
filter :: (a -> Bool) -> [a] -> [a]
filterOdd = filter odd
oddities = length . filterOdd $ [0..9]
您能看到JavaScript中的上一行和Haskell中的下一行之间的相似性吗

main = print $ add(2, 3)
如果您不知道分配给
main
,请忽略该分配。这与当前的话题无关。重要的是,Haskell中的元组
(2,3)
与JavaScript中的数组
[2,3]
同构。我们从中学到了什么

JavaScript中的
apply
函数与Haskell中的函数application(或
$
)相同:

($) :: (a -> b) -> a -> b
f $ a = f a
oddities = length . filter odd $ [0..9]
我们获取
a->b
类型的函数,并将其应用于
a
类型的值,以获得
b
类型的值。但是,由于JavaScript中的所有函数在默认情况下都是无载波的,
apply
函数始终将产品类型(即数组)作为其第二个参数。也就是说,type
a
的值实际上是一种产品类型
($>) :: a -> (a -> b) -> b
a $> f = f a

(>>>) :: (a -> b) -> (b -> c) -> (a -> c)
f >>> g = g . f

oddities = [0..9] $> filter odd >>> length
function multiply(a, b, history) {
    return [a * b, [a + " * " + b].concat(history)];
}

function back(history) {
    return [history[0], history.slice(1)];
}
var myCalc = [];
var multiplyPi = multiply.curry(Math.PI);
var test = bindState(multiplyPi.curry(1), function (prod) {
    alert(prod);
    return back;
});
test = do
    prod <- bindState multiplyPi.curry 1
    alert prod
    back
function bindState(g, f) {
    return function (s) {
        var a = g(s);
        return f(a[0])(a[1]);
    };
}
alert(test(myCalc)[0]);
multiply = (a, b, history) --> [a * b, [a + " * " + b] ++ history]

back = ([top, ...history]) -> [top, history]

myCalc = []

multiplyPi = multiply Math.PI

bindState = (g, f, s) -->
    [a, t] = g s
    (f a) t

test = do
    prod <- bindState multiplyPi 1
    alert prod
    back

alert (test myCalc .0)
var multiplyPi = myCalc.multiply.myCurry(Math.PI);
multiplyPi(1);  // TypeError: this.history.concat is not a function