闭包的确切用途是什么?

时间:2019-01-30 12:21:09

标签: javascript closures

closure在每次面试中都在伤害我。终于我了解了closure。但是我的问题是,以下这些摘要之间有什么区别?我们应该在哪种情况下使用闭包?

关闭:

function add(x) {
    return function(y) {
    return function(z) {
    return x + y + z;
    } 
    };
}

console.log(add(1)(2)(3))

简单可以代替闭合,我们可以将三个参数传递给单个方法。

function add(x,y,z) {
   
    return x + y + z;
}

console.log(add(1,2,3))

那我为什么要使用闭包?

4 个答案:

答案 0 :(得分:1)

此特定示例显示了启用partial application的咖喱函数。对于单个函数调用而言,它似乎没有用,但它还允许您编写

background-image

当然,如果没有使用function add(x) { return function(y) { return x + y; }; } const add2 = add(2); console.log(add2(1)); // 3 console.log(add2(40)); // 42函数,您可能会使用

add

但这是不必要的冗长。


当然,这只强调了一个封闭的用例,有many many more

答案 1 :(得分:1)

在此示例中,输出没有实际差异。但是,让我们变得更简单:

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


console.log(add(1)(2));
function add(a, b) {
  return a+b;
}

console.log(add(1, 2));

为简单起见,该函数采用两个参数。一次,您必须在调用时同时传递它们,另一次,您必须一次传递它们。让我们看看这可能有用:

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

let numbers = [1, 2, 3, 4, 5, 6];

//lets add the same number to all members of the array:
const numberToAdd = 5;

//traditionally
const newArray = []; //make a new array

for (let num of numbers) { //define a loop
  newArray.push(num + numberToAdd); //populate the array
}

console.log(newArray);

//functional
const mappedArray = numbers.map(num => num + numberToAdd); //using .map to handle everything
console.log(mappedArray);

//using the function we have
const betterMappedArray = numbers.map(add(numberToAdd));
console.log(betterMappedArray);

因此,通过.map进行的函数方法更短,更容易,但是可以通过传递函数来进一步改进。在没有add函数的情况下,您仍然要传递一个函数,而您每次要定义一个新的加法函数 ,您只想向数组中添加一些东西。如果要添加可变数量,则没有add的情况下,您必须创建两个新功能,这些功能基本上都可以完成相同的事情

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

let numbers = [1, 2, 3, 4, 5, 6];


console.log("using .map() with new functions");
console.log(
  numbers
    .map(num => num + 5)
    .map(num => num + 10)
);

console.log("using .map() with add()");
  
console.log(
  numbers
    .map(add(5))
    .map(add(10))
);

如您所见,将所有功能并排放置似乎很浪费。即使将签名设为add(a, b),因此它需要两个参数,这意味着您将不得不调用numbers.map(num => add(num, 5)),但这并不能改善任何情况。

请记住,add非常简单-您的函数可能更复杂。仍然,通过简单的示例,让我们重新编写示例以显示它如何有用:

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

const shoppingCart = [
  {name: "item1", price: 1},
  {name: "item2", price: 2},
  {name: "item3", price: 3},
  {name: "item4", price: 4},
  {name: "item5", price: 5},
  {name: "item6", price: 6},
];

const applyHandlingTax = add(5);
const applyShippingTax = add(10);

const numbers = shoppingCart.map(item => item.price); //extract prices from the shopping cart

const finalPrices = numbers
  .map(applyHandlingTax)
  .map(applyShippingTax);

console.log(finalPrices);

这是一个简单的示例,带有夸张的数字,但是它只是用来说明我们在这里可以做些什么。这在功能上与前面的代码段相同,但是正如您所看到的,我们现在有了业务逻辑,几乎没有任何更改。

  • 我们已将applyHandlingTax定义为添加5。我们还定义了applyShippingTax来添加10
  • 这两个都是函数,因此我们可以通过.map将它们直接应用到我们拥有的结果集上,从而减少了我们必须编写的代码量。
  • 由于您正在编写易于阅读的代码,因此业务规则很容易理解-您只需要很少的编程知识就可以了解numbers.map(applyHandlingTax).map(applyShippingTax)正在对每个数字征收运费和手续费。作为开发人员,很明显,您要添加运费和手续费。
  • 如果我们确定add可以正常工作,那么从定义上看也会起作用-无需测试applyShippingTax。实际上,没有什么要测试的-函数的主体没有,我们没有为它编写任何逻辑,我们只为函数使用结果。
  • 我们编写的唯一有意义的代码是用于定义add并从商品中提取价格。即使这样,也可以像使用add一样轻松地提取后者,因此我们可以将其应用于 any 种结果集

function extract(name) {
  return function(item) {
    return item[name];
  }
}

const shoppingCart = [
  {name: "item1", price: 1},
  {name: "item2", price: 2},
  {name: "item3", price: 3},
  {name: "item4", price: 4},
  {name: "item5", price: 5},
  {name: "item6", price: 6},
];

const people = [
  {name: "Alice",  drives: "Audi"},
  {name: "Bob",    drives: "BMW"},
  {name: "Carol",  drives: "Citroen"},
  {name: "David",  drives: "Dodge"},
  {name: "Esther", drives: "EDAG"},
  {name: "Fred",   drives: "Ford"},
];

const prices = shoppingCart.map(extract('price'));
console.log(prices);

const names = people.map(extract('name'));
const cars = people.map(extract('drives'));
console.log(names);
console.log(cars);

即使有一个非常简单的add示例,我们也可以做得很远。 add的编写方式称为currying-该函数返回一次另一个函数,而不是一次获取 X 个参数直到满意为止,需要 X-1

与其他功能配合使用的功能-要么产生它们(像add一样),要么消耗它们,或者两者都称为higher-order functions。如果我们使用这些数据并持续地使用它们,则会进入functional programming的领域。不需要使用完全功能的样式来获取这些好处-正如我们在add中所看到的那样,即使我们正在应用业务规则,您也可以编写一个简单的应用程序而无需使用大量代码。

答案 2 :(得分:0)

'add(1)(2)(3)'和'add(1,2,3)'具有相同的结果。但是前者我可以做很多事情。 让我举一个例子。

function add(x) {
    return function(y) {
    return function(z) {
    return x + y + z;
    } 
    };
}

const WELCOME_POINT = 10;
const VIP_POINT = 20;
function myPointCaculator(currentPoint, predicate) {
  if(typeof predicate === 'number')
    return currentPoint + predicate;
  else if(typeof predicate === 'function') {
    return currentPoint + predicate(WELCOME_POINT);
  } else {
    return currentPoint;
  }
}
  
const eventPoint = add(WELCOME_POINT)(VIP_POINT);
myPointCaculator(WELCOME_POINT, eventPoint);
“ add(1,2,3)”将直接执行,但是“ add(1)(?)(?)”可以作为参数传递给函数。那是不同的。这是关于“咖喱”和“一阶函数”的 javascript中的一阶函数可以使您使用函数式编程进行编码。

答案 3 :(得分:0)

这是一个非常综合且几乎没有用的示例。简而言之,您需要闭包,因为Javascript使用了大量基于回调的代码,并且您希望在这些回调中保留对前一个作用域的可访问性:

function foo(bar) {
    setTimeout(function () {
        alert(bar);
    });
}

这里的关闭机制允许alert(bar)仍然可以访问bar,即使foo已经完成执行并且其所有变量都应该超出范围。这就是 closure 的意思,它关闭作用域并保持可用状态。