Javascript - ฟังก์ชั่น Pure vs Impure


12

ฉันได้ผ่านความหมายของทั้งสองชอบ:

ฟังก์ชั่นที่บริสุทธิ์เป็นคนที่ไม่ได้พยายามที่จะเปลี่ยนอินพุตของพวกเขาและมักจะส่งกลับผลลัพธ์เดียวกันสำหรับอินพุทเดียวกัน

ตัวอย่าง

function sum(a, b) {
  return a + b;
}

และฟังก์ชั่น Impure เป็นสิ่งที่เปลี่ยนแปลงอินพุตของตัวเอง

ตัวอย่าง

function withdraw(account, amount) {
  account.total -= amount;
}

ความหมายและโค้ดนำมาจาก ReactJs เอกสารอย่างเป็นทางการ

ตอนนี้ใครบางคนสามารถบอกฉันว่าฉันจะทำผิดพลาดในReact / Reduxเพื่อใช้ฟังก์ชั่นที่ไม่บริสุทธิ์ที่จำเป็นต้องใช้ฟังก์ชั่นบริสุทธิ์ได้อย่างไร


4
ฟังก์ชั่นที่ไม่บริสุทธิ์มีผลข้างเคียง บางอย่างเช่นwindow.getElementByIdฯลฯ ดังนั้นการเรียกใช้ฟังก์ชันเดียวกันด้วยพารามิเตอร์เดียวกันอาจมีผลลัพธ์ที่แตกต่างกันขึ้นอยู่กับผลข้างเคียง ซึ่งการเปลี่ยนตำแหน่งจะล้มเหลว
Dominik

คำตอบ:


10

ตอบสนองและReduxทั้งสองต้องการฟังก์ชั่นที่บริสุทธิ์ควบคู่ไปกับการไม่สามารถใช้งานได้ในการคาดการณ์

หากคุณไม่ปฏิบัติตามสองสิ่งนี้แอปของคุณจะมีข้อบกพร่องซึ่งเป็นเรื่องที่พบได้บ่อยที่สุดReact/Reduxไม่สามารถติดตามการเปลี่ยนแปลงและไม่สามารถแสดงซ้ำได้เมื่อstate/propการเปลี่ยนแปลงของคุณ

ในแง่ของ React ให้พิจารณาตัวอย่างต่อไปนี้:

let state = {
    add: 0,
}

function render() {
    //...
}
//pure function
function effects(state,action) {
//following immutability while updating state, not directly mutating the state.
    if(action == 'addTen') {
        return {...state, add: state.add + 10} 
    }
    return state;
}

function shouldUpdate(s) {
    if(s === state){
        return false
    }
    return true
}

state = effects(state, 'addTen')if(shouldUpdate(state)) {
    render();
}

สถานะถูกจัดเก็บโดยวัตถุสถานะซึ่งได้เพิ่มคุณสมบัติเท่านั้น แอพนี้แสดงผลคุณสมบัติของแอป ไม่ควรแสดงสถานะเสมอเมื่อมีสิ่งใดเกิดขึ้น แต่ควรตรวจสอบว่ามีการเปลี่ยนแปลงเกิดขึ้นในวัตถุสถานะหรือไม่

เช่นนั้นเรามีฟังก์ชั่นเอฟเฟกต์pure functionซึ่งเราใช้เพื่อส่งผลกระทบต่อสถานะของเรา คุณเห็นว่ามันคืนสถานะใหม่เมื่อรัฐจะมีการเปลี่ยนแปลงและส่งกลับสถานะเดียวกันเมื่อไม่จำเป็นต้องปรับเปลี่ยน

นอกจากนี้เรายังมีshouldUpdateฟังก์ชันที่ตรวจสอบการใช้ตัวดำเนินการ === ว่าสถานะเก่าและสถานะใหม่เหมือนกันหรือไม่

ในการทำผิดพลาดในแง่ของการตอบโต้คุณสามารถทำสิ่งต่อไปนี้ได้:

function effects(state,action) {

  doRandom(); // effects should only be called for updating state.
             // Doing any other stuff here would make effects impure.

    if(action == 'addTen') {
        return {...state, add: state.add + 10}
    }
    return state;
}

คุณสามารถทำผิดพลาดได้โดยตั้งค่าสถานะโดยตรงและไม่ใช้effectsฟังก์ชัน

function doMistake(newValue) {
    this.state = newValue
}

ไม่ควรทำข้างต้นและeffectsควรใช้เฉพาะฟังก์ชันเพื่ออัปเดตสถานะ

ในแง่ของการตอบสนองที่เราเรียกว่าเป็นeffectssetState

สำหรับ Redux:

  1. combineReducersยูทิลิตี้ของ Redux ตรวจสอบการเปลี่ยนแปลงการอ้างอิง
  2. connectวิธีการของ React-Redux จะสร้างส่วนประกอบที่ตรวจสอบการเปลี่ยนแปลงการอ้างอิงสำหรับทั้งสถานะรูทและค่าส่งคืนจากmapStateฟังก์ชั่นเพื่อดูว่าองค์ประกอบที่ถูกห่อจำเป็นต้องแสดงอีกครั้งหรือไม่
  3. การดีบักเวลาเดินทางจำเป็นต้องใช้ตัวลดที่pure functionsไม่มีผลข้างเคียงเพื่อให้คุณสามารถข้ามระหว่างสถานะต่าง ๆ ได้อย่างถูกต้อง

คุณสามารถละเมิดสามข้อข้างต้นได้อย่างง่ายดายโดยใช้ฟังก์ชั่นไม่บริสุทธิ์เป็นตัวลด

ต่อไปนี้ถูกนำมาโดยตรงจากเอกสาร redux:

Array.prototype.reduce(reducer, ?initialValue)มันเรียกว่าลดเพราะมันเป็นชนิดของฟังก์ชั่นที่คุณจะส่งผ่านไปยัง
มันสำคัญมากที่ตัวลดยังคงบริสุทธิ์อยู่ สิ่งที่คุณไม่ควรทำในอุปกรณ์ลดแรงดันไฟฟ้า:

Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().

รับอาร์กิวเมนต์เดียวกันควรคำนวณสถานะถัดไปและส่งคืน ไม่แปลกใจ. ไม่มีผลข้างเคียง ไม่มีการเรียก API ไม่มีการกลายพันธุ์ เพียงแค่คำนวณ


7

กล่าวง่ายๆว่ารัฐไม่สามารถกลายพันธุ์ได้ ควรส่งคืนอินสแตนซ์ใหม่ของรัฐทุกครั้งที่มีการเปลี่ยนแปลง

รหัสนี้ไม่ถูกต้อง:

const initialStates = {    
  items: ['item1']
}

export const ItemMaster = (state = initialStates, action) => {    
  switch (action.type) {
    case TYPES.ADD_ITEM:            
    {
        state.items.push(action.item)
        return state
    }
    default:
      return state
  }
}

รหัสนี้เมื่อเขียนเป็นฟังก์ชั่นแท้ด้านล่างนี้จะส่งกลับตัวอย่างใหม่ของอาร์เรย์มันไม่ได้ปรับเปลี่ยนอาร์เรย์ที่แท้จริงของตัวเอง นี่คือเหตุผลที่คุณควรใช้ไลบรารีเช่นimmerเพื่อจัดการกับความไม่เปลี่ยนแปลง

const initialStates = { 
  items: ['item1']
}

export const ItemMaster = (state = initialStates, action) => {    
  switch (action.type) {
    case TYPES.ADD_ITEM:            
    {

        state = {...state,items:state.items.concat(action.item)}
        return state
    }
    default:
      return state
  }
}

5

คุณสามารถทำให้ฟังก์ชั่นบริสุทธิ์ไม่บริสุทธิ์ได้โดยการเพิ่มการเรียก API หรือการเขียนโค้ดที่ทำให้เกิดผลข้างเคียง

ฟังก์ชั่นที่บริสุทธิ์ควรอยู่ในจุดและอธิบายตนเองเสมอและไม่ควรให้คุณอ้างถึงฟังก์ชั่นอื่น ๆ 3 หรือ 4 ตัวเพื่อเข้าใจสิ่งที่เกิดขึ้น

// Pure Function
function USDtoEUR(USD, todayRate) {
  return USD * todayRate;
}

// Impure Function 
function USDtoEUR(USD) {
  const todayRate = getTodayRate();
  return USD * todayRate;
}

ในกรณีของ React / Redux

const mapState = async state => {
  const { data } = await whatDoINeed()

  let mappedState = {}

  if (data.needDolphin) {
    mappedState.dolphin = state.dolphin
  }

  if (data.needShark) {
    mappedState.shark= state.shark
  }

  return mappedState;
}

// Or for Redux Reducer
// Bad
{
  setData: (state, payload) => {
   const set = whatToSet()
   return {
     ...state,
     set.dolphin ? ...{ dolphin: payload.dolphin } : ...{},
     set.shark ? ...{ shark : payload.shark } : ...{},
   }
  }
}

// Good
{
  setData: (state, payload) => {
   return {
     ...state,
     // Just send only the things need
     // to be sent
     ...payload
   }
  }
}

นี้ไม่ควรทำ ทุกอย่างที่ฟังก์ชั่นการเชื่อมต่อหรือฟังก์ชั่นการลดความต้องการจะต้องได้รับการโต้แย้งหรือเขียนภายในฟังก์ชั่นของมัน ไม่ควรออกมาจากข้างนอก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.