亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

在JavaScript中生成不可修改屬性對(duì)象的方法

 更新時(shí)間:2024年12月04日 10:00:39   作者:JYeontu  
這篇文章主要介紹了在 JavaScript 中生成不可修改屬性對(duì)象的方法,包括相關(guān)函數(shù)及原理,并列舉了在狀態(tài)管理、數(shù)據(jù)緩存、函數(shù)式編程等場(chǎng)景中的實(shí)際應(yīng)用,還通過代碼示例進(jìn)行了詳細(xì)說明,需要的朋友可以參考下

說在前面

數(shù)據(jù)的可變性常常是一個(gè)需要謹(jǐn)慎處理的問題。可變數(shù)據(jù)可能會(huì)導(dǎo)致難以預(yù)測(cè)的副作用,尤其是在大型項(xiàng)目或復(fù)雜的應(yīng)用程序中。不可變數(shù)據(jù)結(jié)構(gòu)提供了一種解決方案,它能使代碼更加健壯、可維護(hù)和易于調(diào)試。

需求

編寫一個(gè)函數(shù),該函數(shù)接收一個(gè)對(duì)象 obj ,并返回該對(duì)象的一個(gè)新的 不可變 版本。

不可變 對(duì)象是指不能被修改的對(duì)象,如果試圖修改它,則會(huì)拋出錯(cuò)誤。

此新對(duì)象可能產(chǎn)生三種類型的錯(cuò)誤消息。

  • 如果試圖修改對(duì)象的鍵,則會(huì)產(chǎn)生以下錯(cuò)誤消息: `Error Modifying: ${key}` 。
  • 如果試圖修改數(shù)組的索引,則會(huì)產(chǎn)生以下錯(cuò)誤消息: `Error Modifying Index: ${index}` 。
  • 如果試圖調(diào)用會(huì)改變數(shù)組的方法,則會(huì)產(chǎn)生以下錯(cuò)誤消息: `Error Calling Method: ${methodName}` 。你可以假設(shè)只有以下方法能夠改變數(shù)組: ['pop', 'push', 'shift', 'unshift', 'splice', 'sort', 'reverse'] 。

obj 是一個(gè)有效的 JSON 對(duì)象或數(shù)組,也就是說,它是 JSON.parse() 的輸出結(jié)果。

請(qǐng)注意,應(yīng)該拋出字符串字面量,而不是 Error 對(duì)象。

示例

示例 1

輸入:
obj = {
  "x": 5
}
fn = (obj) => { 
  obj.x = 5;
  return obj.x;
}
輸出:{"value": null, "error": "Error Modifying: x"}
解釋:試圖修改對(duì)象的鍵會(huì)導(dǎo)致拋出錯(cuò)誤。請(qǐng)注意,是否將值設(shè)置為與之前相同的值并不重要。

示例 2

輸入: 
obj = [1, 2, 3]
fn = (arr) => { 
  arr[1] = {}; 
  return arr[2]; 
}
輸出:{"value": null, "error": "Error Modifying Index: 1"}
解釋:試圖修改數(shù)組會(huì)導(dǎo)致拋出錯(cuò)誤。

示例 3

輸入:
obj = {
  "arr": [1, 2, 3]
}
fn = (obj) => { 
  obj.arr.push(4);
  return 42;
}
輸出:{ "value": null, "error": "Error Calling Method: push"}
解釋:調(diào)用可能導(dǎo)致修改的方法會(huì)導(dǎo)致拋出錯(cuò)誤。

示例4

輸入:
obj = {
  "x": 2,
  "y": 2
}
fn = (obj) => { 
  return Object.keys(obj);
}
輸出:{"value": ["x", "y"], "error": null}
解釋:沒有嘗試進(jìn)行修改,因此函數(shù)正常返回。

代碼實(shí)現(xiàn)

1. 函數(shù)功能概述

通過遞歸地處理對(duì)象及其屬性,并利用 Proxy 對(duì)象來(lái)攔截對(duì)對(duì)象的修改操作,從而使得經(jīng)過處理后的對(duì)象無(wú)法被修改,達(dá)到了“不可變”的效果。

2. 函數(shù)參數(shù)及內(nèi)部邏輯分析

makeImmutable 函數(shù)

const makeImmutable = function (obj) {
  if (typeof obj !== "object" || !obj) return obj;
  // 遞歸對(duì)象進(jìn)行代理攔截
  Object.keys(obj).forEach((key) => {
    obj[key] = makeImmutable(obj[key]);
  });
  return createProxy(obj);
};

函數(shù)接受一個(gè)參數(shù) obj,代表要被處理成不可變對(duì)象的原始對(duì)象。

  • 首先進(jìn)行一個(gè)條件判斷:if (typeof obj!== "object" ||!obj) return obj;。這個(gè)判斷的目的是,如果傳入的 obj 不是對(duì)象類型(比如是基本數(shù)據(jù)類型,如數(shù)字、字符串、布爾值等)或者 obj 本身是 null,那么就直接返回這個(gè)原始的 obj,因?yàn)榛緮?shù)據(jù)類型和 null 本身就是不可變的,不需要進(jìn)行后續(xù)的處理。
  • 如果 obj 是對(duì)象類型且不為 null,那么就會(huì)進(jìn)入下面的處理邏輯:
    • 通過 Object.keys(obj).forEach((key) => { obj[key] = makeImmutable(obj[key]); }); 這行代碼,它會(huì)遍歷對(duì)象 obj 的所有鍵名(使用 Object.keys 方法獲取鍵名數(shù)組),對(duì)于每個(gè)鍵名對(duì)應(yīng)的屬性值,再次調(diào)用 makeImmutable 函數(shù)進(jìn)行遞歸處理。這樣做的目的是確保對(duì)象內(nèi)部的嵌套對(duì)象也能被正確地處理成不可變對(duì)象。
    • 最后,經(jīng)過遞歸處理后的對(duì)象 obj 會(huì)被傳遞給 createProxy 函數(shù)進(jìn)行進(jìn)一步處理,然后返回處理后的結(jié)果。

createProxy 函數(shù)

function createProxy(obj) {
  const isArray = Array.isArray(obj);
  // 攔截 Array 原生方法
  if (isArray) {
    ["pop", "push", "shift", "unshift", "splice", "sort", "reverse"].forEach(
      (method) => {
        obj[method] = () => {
          throw `Error Calling Method: ${method}`;
        };
      }
    );
  }
  return new Proxy(obj, {
    set(_, prop) {
      throw `Error Modifying${isArray ? " Index" : ""}: ${prop}`;
    },
  });
}

函數(shù)接受一個(gè)參數(shù) obj,就是經(jīng)過前面 makeImmutable 函數(shù)遞歸處理后的對(duì)象。

  • 首先通過 const isArray = Array.isArray(obj); 判斷傳入的對(duì)象 obj 是否是數(shù)組類型。
  • 如果 obj 是數(shù)組類型,那么會(huì)通過以下代碼攔截?cái)?shù)組的一些原生方法:
["pop", "push", "shift", "unshift", "splice", "sort", "reverse"].forEach(
  (method) => {
    obj[method] = () => {
      throw `Error Calling Method: ${method}`;
    };
  }
);

這里遍歷了數(shù)組的一些常見的修改方法,如 pop(刪除數(shù)組末尾元素)、push(在數(shù)組末尾添加元素)、shift(刪除數(shù)組開頭元素)、unshift(在數(shù)組開頭添加元素)、splice(插入、刪除或替換數(shù)組元素)、sort(對(duì)數(shù)組元素進(jìn)行排序)、reverse(反轉(zhuǎn)數(shù)組元素順序)等。對(duì)于每個(gè)方法,都將其重新定義為一個(gè)函數(shù),當(dāng)調(diào)用這些方法時(shí),會(huì)拋出一個(gè)包含方法名的錯(cuò)誤信息,比如調(diào)用 push 方法時(shí)會(huì)拋出 Error Calling Method: push,這樣就阻止了對(duì)數(shù)組進(jìn)行這些修改操作。

  • 最后,無(wú)論傳入的對(duì)象 obj 是數(shù)組還是其他普通對(duì)象,都會(huì)通過以下代碼創(chuàng)建一個(gè) Proxy 對(duì)象并返回:
return new Proxy(obj, {
  set(_, prop) {
    throw `Error Modifying${isArray? " Index" : ""}: ${prop}`;
  },
});

這里創(chuàng)建的 Proxy 對(duì)象定義了一個(gè) set 攔截器。當(dāng)嘗試對(duì)這個(gè)代理對(duì)象進(jìn)行屬性設(shè)置操作(比如 obj['newProp'] = 'value';)時(shí),就會(huì)觸發(fā)這個(gè) set 攔截器。攔截器內(nèi)部會(huì)拋出一個(gè)錯(cuò)誤信息,其中根據(jù)對(duì)象是否是數(shù)組來(lái)決定錯(cuò)誤信息中的用詞。如果是數(shù)組,錯(cuò)誤信息會(huì)顯示 Error Modifying Index: [屬性名],表示修改數(shù)組的索引位置相關(guān)的錯(cuò)誤;如果是普通對(duì)象,錯(cuò)誤信息會(huì)顯示 Error Modifying: [屬性名],總之就是阻止了對(duì)對(duì)象進(jìn)行屬性設(shè)置的修改操作。

3. 整體功能總結(jié)

通過 makeImmutable 函數(shù)和 createProxy 函數(shù)的協(xié)同工作,首先對(duì)傳入的對(duì)象進(jìn)行遞歸處理,確保其內(nèi)部嵌套的對(duì)象也能被處理成不可變對(duì)象,然后通過創(chuàng)建 Proxy 對(duì)象并設(shè)置相應(yīng)的攔截器,攔截了對(duì)對(duì)象的各種修改操作(包括數(shù)組的特定修改方法和普通對(duì)象的屬性設(shè)置操作),最終使得經(jīng)過處理后的對(duì)象成為一個(gè)不可變對(duì)象,任何試圖修改它的操作都會(huì)拋出相應(yīng)的錯(cuò)誤信息。

4.完整代碼

/**
 * @param {Array} arr
 * @return {(string | number | boolean | null)[][]}
 */
var jsonToMatrix = function (arr) {
  let keySet = new Set();
  const isObject = (x) => x !== null && typeof x === "object";
  const getKeyName = (object, name = "") => {
    if (!isObject(object)) {
      keySet.add(name);
      return;
    }
    for (const key in object) {
      getKeyName(object[key], name + (name ? "." : "") + key);
    }
  };
  arr.forEach((item) => getKeyName(item));
  keySet = [...keySet].sort();
  const getValue = (obj, path) => {
    const paths = path.split(".");
    let i = 0;
    let value = obj;
    while (i < paths.length) {
      if (!isObject(value)) break;
      value = value[paths[i++]];
    }
    if (i < paths.length || isObject(value) || value === undefined) return "";
    return value;
  };
  const res = [keySet];
  arr.forEach((item) => {
    const list = [];
    keySet.forEach((key) => {
      list.push(getValue(item, key));
    });
    res.push(list);
  });
  return res;
};

5、功能測(cè)試

(1)修改對(duì)象屬性

obj = makeImmutable({ x: 5 });
obj.x = 6;
//Error Modifying: x

(2)修改數(shù)組值

obj = makeImmutable([1, 2, 3]);
obj[1] = 222;
//Error Modifying Index: 1

(3)調(diào)用數(shù)組方法

arr = makeImmutable([1, 2, 3]);
arr.push(4)
//Error Calling Method: push

(4)獲取屬性值

obj = makeImmutable({ x: 5, y: 6 });
console.log(obj.x);  //5
console.log(Object.keys(obj));  //['x', 'y']

沒有嘗試進(jìn)行修改,因此函數(shù)正常返回。

實(shí)際應(yīng)用場(chǎng)景

1、狀態(tài)管理(如在React或Vue中)

在現(xiàn)代前端框架中,不可變數(shù)據(jù)結(jié)構(gòu)有助于優(yōu)化組件的更新機(jī)制。以React為例,當(dāng)組件的狀態(tài)(state)是不可變對(duì)象時(shí),React可以更高效地比較前后狀態(tài)的差異,從而決定是否需要重新渲染組件。

  • 代碼示例(以React為例)
import React, { useState } from 'react';

const initialState = {
    user: {
        name: 'John',
        age: 30
    },
    todos: ['Task 1', 'Task 2']
};

const immutableInitialState = makeImmutable(initialState);

const App = () => {
    const [state, setState] = useState(immutableInitialState);
    const handleUpdateUser = () => {
        try {
            // 嘗試修改會(huì)拋出錯(cuò)誤,這符合不可變數(shù)據(jù)的理念
            state.user.name = 'Jane';
        } catch (error) {
            console.log(error);
        }
        // 正確的更新方式(假設(shè)使用 immer.js等庫(kù)輔助更新)
        setState(prevState => {
            const newState = {...prevState };
            newState.user = {...prevState.user };
            newState.user.name = 'Jane';
            return makeImmutable(newState);
        });
    };
    return (
        <div>
            <p>User Name: {state.user.name}</p>
            <button onClick={handleUpdateUser}>Update User Name</button>
        </div>
    );
};

在這個(gè)示例中,通過 makeImmutable 函數(shù)將初始狀態(tài)對(duì)象轉(zhuǎn)換為不可變對(duì)象,然后在組件的狀態(tài)管理中使用。當(dāng)試圖直接修改不可變狀態(tài)對(duì)象的屬性時(shí)會(huì)拋出錯(cuò)誤,這提醒開發(fā)者使用正確的方式來(lái)更新狀態(tài),如創(chuàng)建一個(gè)新的對(duì)象副本并更新副本中的屬性,最后將新的不可變對(duì)象作為新狀態(tài)。這種方式可以確保React能夠準(zhǔn)確地檢測(cè)狀態(tài)變化,提高組件更新的性能。

2、數(shù)據(jù)緩存

在一些需要緩存數(shù)據(jù)的場(chǎng)景中,確保緩存數(shù)據(jù)不被意外修改是很重要的。使用不可變對(duì)象可以提供這種安全性,因?yàn)橐坏?shù)據(jù)被緩存,就不能被修改,從而保證了數(shù)據(jù)的一致性。

  • 代碼示例
const cache = {};

const getDataFromServer = async () => {
    // 假設(shè)這是從服務(wù)器獲取數(shù)據(jù)的異步函數(shù)
    const data = await fetch('https://example.com/api/data');
    const jsonData = await data.json();
    cache['data'] = makeImmutable(jsonData);
    return cache['data'];
};

const updateData = () => {
    try {
        cache['data'].someProperty = 'new value';
    } catch (error) {
        console.log('不能修改緩存數(shù)據(jù):', error);
    }
};

當(dāng)從服務(wù)器獲取數(shù)據(jù)后,通過 makeImmutable 函數(shù)將數(shù)據(jù)存儲(chǔ)在緩存對(duì)象 cache 中。如果后續(xù)有代碼試圖修改緩存中的數(shù)據(jù),會(huì)拋出錯(cuò)誤,這樣就保證了緩存數(shù)據(jù)的穩(wěn)定性和一致性,避免因?yàn)橐馔庑薷膶?dǎo)致數(shù)據(jù)不一致的問題。

3、函數(shù)式編程

在函數(shù)式編程中,不可變數(shù)據(jù)是一個(gè)核心概念。函數(shù)應(yīng)該是無(wú)副作用的,即不應(yīng)該修改外部的數(shù)據(jù)結(jié)構(gòu)。通過使用不可變對(duì)象,可以確保函數(shù)的純度。

  • 代碼示例
const addTask = (tasks, newTask) => {
    try {
        tasks.push(newTask);
    } catch (error) {
        console.log(error);
    }
    const newTasks = [...tasks, newTask];
    return makeImmutable(newTasks);
};

const tasks = ['Task 1', 'Task 2'];
const immutableTasks = makeImmutable(tasks);
const newTasks = addTask(immutableTasks, 'Task 3');

addTask 函數(shù)中,首先嘗試直接修改傳入的任務(wù)列表(這會(huì)因?yàn)榱斜硎遣豢勺兊亩鴴伋鲥e(cuò)誤),然后通過創(chuàng)建一個(gè)新的列表副本并添加新任務(wù)的方式來(lái)返回一個(gè)新的不可變?nèi)蝿?wù)列表。這種方式符合函數(shù)式編程的原則,即不修改傳入的參數(shù),而是返回一個(gè)新的值,保證了函數(shù)的可預(yù)測(cè)性和無(wú)副作用的特性。

以上就是在JavaScript中生成不可修改屬性對(duì)象的方法的詳細(xì)內(nèi)容,更多關(guān)于JavaScript不可修改屬性的對(duì)象的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論