# 编程概念

* closure 闭包：一个子函数，引用父函数的变量，当父函数执行完成，子函数依然能访问这些变量。
* argument drilling: 指的是在开发的时候，函数在调用链的时候，有很多参数要传递，比如A调用B，传了一些参数，B调用C的时候，又要传这个参数。有几个办法可以解决这个问题
  * 对象传参：将相关参数封装到一个对象里
  * 依赖注入：用依赖注入来管理和传递函数所需要的依赖项
  * 使用全局变量虽然也能解决，但可能会引入其他问题，比如命名冲突，可维护性差，难以测试等，不推荐使用。
  * 使用 closure 闭包在一定程度上也能解决参数的问题，一个示例如下
    * 比如一个计算折扣的程序，原始写法如下

```javascript
async function getUserDiscounts() {
    return {
        P001: 5,
        P002: 10
    }
}

async function getCategoryDiscounts() {
    return {
        electronics: 15,
        clothing: 5
    }
}

function proceeProducts(products, userDiscounts, categoryDiscounts) {
    return products
    .map((product) => {
        const userDiscount = userDiscounts[product.id] || 0;
        const categoryDiscount = categoryDiscounts[product.category] || 0;
        const totalDiscount = userDiscount + categoryDiscount;
        const finalPrice = product.price - product.price * (totalDiscount / 100);
        return {
            ...product, 
            finalPrice: finalPrice.toFixed(2)
        };
    })
    .filter((product) => product.finalPrice <= 80)
    .map((product) => product.id)
}

async function main() {
    const products = [
        {id: "P001", category: "electronics", price: 100},
        {id: "P002", category: "clothing", price: 50},
        {id: "P003", category: "electronics", price: 100},
        {id: "P004", category: "clothing", price: 80}
    ]
    const userDiscounts = await getUserDiscounts();
    const categoryDiscounts = await getCategoryDiscounts();

    const processedProducts = proceeProducts(
        products,
        userDiscounts,
        categoryDiscounts
    );

    console.log(
        processedProducts.reduce(
            (obj, key) => {obj[key] = key; return obj;}, {}
        )
    );
}

main()
```

此时折扣的逻辑在多个地方都会用到，此时我们可以创建一个createDiscountCalculator的函数专门计算折扣，这个函数需要接收 userDiscounts 和 categoryDiscounts 作为输入，在专门处理某一个商品时，然后再通过这个 discountCalculator 查一下商品折扣，具体改法是：

```javascript
async function getUserDiscounts() {
    return {
        P001: 5,
        P002: 10
    }
}

async function getCategoryDiscounts() {
    return {
        electronics: 15,
        clothing: 5
    }
}

function createDiscountCalculator(userDiscounts, categoryDiscounts) {
    return function (product){
    const userDiscount = userDiscounts[product.id] || 0;
    const categoryDiscount = categoryDiscounts[product.category] || 0;
    const totalDiscount = userDiscount + categoryDiscount;
    const finalPrice = product.price - product.price * (totalDiscount / 100);

    return  finalPrice.toFixed(2);
    } 
}

function proceeProducts(products, discountCalculator) {
    return products
    .map((product) => {
        const finalPrice = discountCalculator(product);
        return {
            ...product, 
            finalPrice: finalPrice
        };
    })
    .filter((product) => product.finalPrice <= 80)
    .map((product) => product.id)
}

async function main() {
    const products = [
        {id: "P001", category: "electronics", price: 100},
        {id: "P002", category: "clothing", price: 50},
        {id: "P003", category: "electronics", price: 100},
        {id: "P004", category: "clothing", price: 80}
    ]
    const userDiscounts = await getUserDiscounts();
    const categoryDiscounts = await getCategoryDiscounts();
    const discountCalculator = createDiscountCalculator(
        userDiscounts,
        categoryDiscounts
    );

    const processedProducts = proceeProducts(
        products,
        discountCalculator
    );

    console.log(
        processedProducts
    );

    console.log(
        processedProducts.reduce(
            (obj, key) => {obj[key] = key; return obj;}, {}
        )
    );
}

main()
```

下面是一个python 通过闭包的一个实现

```python
from decimal import Decimal, getcontext  
def user_discount():  
    return {  
        "P001": 5,  
        "P002": 10  
    }  
  
  
def category_discount():  
    return {  
        "electronics": 15,  
        "clothing": 5  
    }  
  
  
def discount_calculator(user_discount, category_discount):  
    def discount(product):  
        user_discount_percentage = user_discount.get(product["id"], 0)  
        category_discount_percentage = category_discount.get(product["category"], 0)  
        discount_percentage = user_discount_percentage + category_discount_percentage  
        discount_amount = product["price"] * Decimal(discount_percentage / 100)  
        discounted_price = product["price"] - discount_amount  
        return discounted_price  
    return discount  
  
  
def proceed_products(products, discount_calculator):  
    discounted_product = [{**product, 'finalPrice': discount_calculator(product)} for product in products]  
  
    filtered_products = [  
        product["id"] for product in discounted_product if product['finalPrice'] < 80  
    ]  
    return filtered_products  
  
if __name__ == '__main__':  
    products = [  
        {  
            "id": "P001",  
            "category": "electronics",  
            "price": 100  
        },  
        {  
            "id": "P002",  
            "category": "clothing",  
            "price": 50  
        },  
        {  
            "id": "P003",  
            "category": "electronics",  
            "price": 100  
        },  
        {  
            "id": "P004",  
            "category": "clothing",  
            "price": 80  
        }  
    ]  
    user_discount = user_discount()  
    category_discount = category_discount()  
    discount_calculator = discount_calculator(user_discount, category_discount)  
    discount_products = proceed_products(products, discount_calculator)  
    print(discount_products)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gitbook.aiaod.com/programming/she-ji-mo-shi/bian-cheng-gai-nian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
