JavaScript Proxy ãã³ãã©ãŒãã§ãŒã³ã®é«åºŠãªæŠå¿µãæ¢æ±ããæŽç·Žããã倿®µéãªããžã§ã¯ãã€ã³ã¿ãŒã»ãã·ã§ã³ãå®çŸããŸãããã¹ããããæ§é å šäœã®ããŒã¿ã¢ã¯ã»ã¹ãšæäœã匷åã«å¶åŸ¡ããéçºè ãæ¯æŽããŸãã
JavaScript Proxy ãã³ãã©ãŒãã§ãŒã³: 倿®µéãªããžã§ã¯ãã€ã³ã¿ãŒã»ãã·ã§ã³ã®ç¿åŸ
çŸä»£ã®JavaScriptéçºã«ãããŠãProxyãªããžã§ã¯ãã¯åŒ·åãªã¡ã¿ããã°ã©ãã³ã°ããŒã«ãšããŠæ©èœããéçºè ãã¿ãŒã²ãããªããžã§ã¯ãã«å¯Ÿããåºæ¬çãªæäœãååããåå®çŸ©ããããšãå¯èœã«ããŸããProxyã®åºæ¬çãªäœ¿ãæ¹ã¯ååã«ææžåãããŠããŸãããProxyãã³ãã©ãŒã®ãã§ã€ã³ã®æè¡ãç¿åŸãããšãç¹ã«è€éãªå€æ®µéã®ãã¹ãããããªããžã§ã¯ããæ±ãéã«ãæ°ããªå¶åŸ¡ã®æ¬¡å ãéãããŸãããã®é«åºŠãªãã¯ããã¯ã¯ãè€éãªæ§é å šäœã®ããŒã¿ãæŽç·Žãããæ¹æ³ã§ååããæäœããããšãå¯èœã«ãããªã¢ã¯ãã£ãã·ã¹ãã ã®èšèšããã现ããã¢ã¯ã»ã¹å¶åŸ¡ã®å®è£ ãè€éãªæ€èšŒã«ãŒã«ã®åŒ·å¶ã«ãããŠæ¯é¡ã®ãªãæè»æ§ãæäŸããŸãã
JavaScript Proxyã®æ žãçè§£ãã
ãã³ãã©ãŒãã§ãŒã³ã«æ·±ãèžã¿èŸŒãåã«ãJavaScript Proxyã®åºæ¬ãçè§£ããããšãéèŠã§ããProxyãªããžã§ã¯ãã¯ããã®ã³ã³ã¹ãã©ã¯ã¿ã«2ã€ã®åŒæ°ãããªãã¡targetãªããžã§ã¯ããšhandlerãªããžã§ã¯ããæž¡ãããšã«ãã£ãŠäœæãããŸããtargetã¯Proxyã管çãããªããžã§ã¯ãã§ãããhandlerã¯Proxyã«å¯ŸããŠå®è¡ãããæäœã®ã«ã¹ã¿ã åäœãå®çŸ©ãããªããžã§ã¯ãã§ãã
handlerãªããžã§ã¯ãã«ã¯ãç¹å®ã®æäœãååããã¡ãœããã§ããããŸããŸãªãã©ãããå«ãŸããŠããŸããäžè¬çãªãã©ããã¯æ¬¡ã®ãšããã§ãã
get(target, property, receiver): ããããã£ã¢ã¯ã»ã¹ãååããŸããset(target, property, value, receiver): ããããã£ã®å²ãåœãŠãååããŸããhas(target, property):inæŒç®åãååããŸããdeleteProperty(target, property):deleteæŒç®åãååããŸããapply(target, thisArg, argumentsList): 颿°åŒã³åºããååããŸããconstruct(target, argumentsList, newTarget):newæŒç®åãååããŸãã
Proxyã€ã³ã¹ã¿ã³ã¹ã«å¯ŸããŠæäœãå®è¡ããããšãhandlerã«å¯Ÿå¿ãããã©ãããå®çŸ©ãããŠããã°ããã®ãã©ãããå®è¡ãããŸããå®çŸ©ãããŠããªãå Žåã¯ãå
ã®targetãªããžã§ã¯ãã«å¯ŸããŠæäœãç¶è¡ãããŸãã
ãã¹ãããããªããžã§ã¯ãã®èª²é¡
æ·±ããã¹ãããããªããžã§ã¯ããé¢ããã·ããªãªãèããŠã¿ãŸããããäŸãã°ãè€éãªã¢ããªã±ãŒã·ã§ã³ã®æ§æãªããžã§ã¯ãããè€æ°ã®æš©éã¬ãã«ãæã€ãŠãŒã¶ãŒãããã¡ã€ã«ã衚ãéå±€åããŒã¿æ§é ãªã©ã§ãããã®ãã¹ãã®ã©ã®ã¬ãã«ã®ããããã£ã«å¯ŸããŠããæ€èšŒããã®ã³ã°ãã¢ã¯ã»ã¹å¶åŸ¡ãšãã£ãäžè²«ããããžãã¯ãé©çšããå¿ èŠãããå Žåãåäžã®ãã©ãããªProxyã䜿çšããã®ã¯éå¹çã§é¢åã«ãªããŸãã
äŸãã°ããŠãŒã¶ãŒèšå®ãªããžã§ã¯ããæ³åããŠã¿ãŠãã ããã
const userConfig = {
id: 123,
profile: {
name: 'Alice',
address: {
street: '123 Main St',
city: 'Anytown',
zip: '12345'
}
},
settings: {
theme: 'dark',
notifications: {
email: true,
sms: false
}
}
};
ãã¹ãŠã®ããããã£ã¢ã¯ã»ã¹ããã°ã«èšé²ãããããã¹ãŠã®æååå€ã空ã§ãªãããšã匷å¶ãããå Žåãéåžžããªããžã§ã¯ããæåã§èµ°æ»ããååž°çã«Proxyãé©çšããå¿ èŠããããŸããããã¯ãã€ã©ãŒãã¬ãŒãã³ãŒããšããã©ãŒãã³ã¹ã®ãªãŒããŒãããã«ã€ãªããå¯èœæ§ããããŸãã
Proxyãã³ãã©ãŒãã§ãŒã³ã®å°å ¥
Proxyãã³ãã©ãŒãã§ãŒã³ã®æŠå¿µã¯ãProxyã®ãã©ãããã¿ãŒã²ãããçŽæ¥æäœãããå€ãè¿ããããã代ããã«ãå¥ã®ProxyãäœæããŠè¿ããšãã«çŸããŸããããã«ãããProxyã«å¯Ÿããæäœããã¹ããããProxyã«å¯Ÿãããããªãæäœã«ã€ãªããé£éã圢æãããã¿ãŒã²ãããªããžã§ã¯ãã®éå±€ããã©ãŒãªã³ã°ãããã¹ããããProxyæ§é ã广çã«äœæãããŸãã
éèŠãªã¢ã€ãã¢ã¯ãProxyã§getãã©ãããåŒã³åºãããã¢ã¯ã»ã¹ãããŠããããããã£èªäœããªããžã§ã¯ãã§ããå Žåãgetãã©ããã¯ãªããžã§ã¯ãèªäœã§ã¯ãªãããã®ãã¹ãããããªããžã§ã¯ãã«å¯Ÿããæ°ããProxyã€ã³ã¹ã¿ã³ã¹ãè¿ãããšãã§ãããšããç¹ã§ãã
ã·ã³ãã«ãªäŸ: è€æ°ã¬ãã«ã§ã®ã¢ã¯ã»ã¹ãã°
ãã¹ãããããªããžã§ã¯ãå ã§ãããã¹ãŠã®ããããã£ã¢ã¯ã»ã¹ããã°ã«èšé²ããProxyãäœæããŠã¿ãŸãããã
function createLoggingProxy(obj, path = []) {
return new Proxy(obj, {
get(target, property, receiver) {
const currentPath = [...path, property].join('.');
console.log(`Accessing: ${currentPath}`);
const value = Reflect.get(target, property, receiver);
// If the value is an object and not null, and not a function (to avoid proxying functions themselves unless intended)
if (typeof value === 'object' && value !== null && !Array.isArray(value) && typeof value !== 'function') {
return createLoggingProxy(value, [...path, property]);
}
return value;
},
set(target, property, value, receiver) {
const currentPath = [...path, property].join('.');
console.log(`Setting: ${currentPath} to ${value}`);
return Reflect.set(target, property, value, receiver);
}
});
}
const userConfig = {
id: 123,
profile: {
name: 'Alice',
address: {
street: '123 Main St',
city: 'Anytown',
zip: '12345'
}
}
};
const proxiedUserConfig = createLoggingProxy(userConfig);
console.log(proxiedUserConfig.profile.name);
// Output:
// Accessing: profile
// Accessing: profile.name
// Alice
proxiedUserConfig.profile.address.city = 'Metropolis';
// Output:
// Accessing: profile
// Setting: profile.address.city to Metropolis
ãã®äŸã§ã¯ã次ã®ããã«ãªããŸãã
createLoggingProxyã¯ãäžãããããªããžã§ã¯ãã®Proxyãäœæãããã¡ã¯ããªé¢æ°ã§ããgetãã©ããã¯ã¢ã¯ã»ã¹ãã¹ããã°ã«èšé²ããŸãã- éèŠãªã®ã¯ãååŸããã
valueããªããžã§ã¯ãã§ããå ŽåãcreateLoggingProxyãååž°çã«åŒã³åºããŠããã®ãã¹ãããããªããžã§ã¯ãã®æ°ããProxyãè¿ãããšã§ããããããã§ãŒã³ã圢æãããä»çµã¿ã§ãã setãã©ããã倿Žããã°ã«èšé²ããŸãã
proxiedUserConfig.profile.nameãã¢ã¯ã»ã¹ããããšãæåã®getãã©ããã'profile'ã«å¯ŸããŠããªã¬ãŒãããŸããuserConfig.profileããªããžã§ã¯ãã§ãããããcreateLoggingProxyãå床åŒã³åºãããprofileãªããžã§ã¯ãã®æ°ããProxyãè¿ãããŸããæ¬¡ã«ããã®æ°ããProxyã®getãã©ããã'name'ã«å¯ŸããŠããªã¬ãŒãããŸãããããã®ãã¹ããããProxyãéããŠããã¹ãæ£ãã远跡ãããŸãã
倿®µéã€ã³ã¿ãŒã»ãã·ã§ã³ã«ããããã³ãã©ãŒãã§ãŒã³ã®å©ç¹
Proxyãã³ãã©ãŒããã§ã€ã³ããããšã«ã¯ã次ã®ãããªå€§ããªå©ç¹ããããŸãã
- äžè²«ããããžãã¯ã®é©çš: ãã¹ãããããªããžã§ã¯ãã®ãã¹ãŠã®ã¬ãã«ã«ããã£ãŠãå埩çãªã³ãŒããªãã«äžè²«ããããžãã¯ïŒæ€èšŒã倿ããã®ã³ã°ãã¢ã¯ã»ã¹å¶åŸ¡ïŒãé©çšã§ããŸãã
- ãã€ã©ãŒãã¬ãŒãã®åæž: åãã¹ãããããªããžã§ã¯ãã«å¯Ÿããæåã®èµ°æ»ãšProxyäœæãåé¿ã§ããŸãããã§ãŒã³ã®ååž°çãªæ§è³ªãèªåçã«åŠçããŸãã
- ä¿å®æ§ã®åäž: ã€ã³ã¿ãŒã»ãã·ã§ã³ããžãã¯ãäžç®æã«éäžãããããšã§ãæŽæ°ãä¿®æ£ãã¯ããã«å®¹æã«ãªããŸãã
- åçãªæå: ãã¹ããããProxyã蟿ãéã«ãæåããã®å Žã§å€æŽã§ãããããªé«åºŠã«åçãªããŒã¿æ§é ãäœæã§ããŸãã
é«åºŠãªãŠãŒã¹ã±ãŒã¹ãšãã¿ãŒã³
ãã³ãã©ãŒãã§ã€ã³ãã¿ãŒã³ã¯åçŽãªãã®ã³ã°ã«éå®ãããŸãããæŽç·Žãããæ©èœãå®è£ ããããã«æ¡åŒµã§ããŸãã
1. 倿®µéããŒã¿æ€èšŒ
ç¹å®ã®ãã£ãŒã«ããæ¡ä»¶ä»ãã§å¿ é ã§ãã£ãããç¹å®ã®åœ¢åŒå¶çŽãæã£ãŠãããããè€éãªãã©ãŒã ãªããžã§ã¯ãå šäœã§ãŠãŒã¶ãŒå ¥åãæ€èšŒããå Žé¢ãæ³åããŠã¿ãŠãã ããã
function createValidatingProxy(obj, path = [], validationRules = {}) {
return new Proxy(obj, {
get(target, property, receiver) {
const value = Reflect.get(target, property, receiver);
if (typeof value === 'object' && value !== null && !Array.isArray(value) && typeof value !== 'function') {
return createValidatingProxy(value, [...path, property], validationRules);
}
return value;
},
set(target, property, value, receiver) {
const currentPath = [...path, property].join('.');
const rules = validationRules[currentPath];
if (rules) {
if (rules.required && (value === null || value === undefined || value === '')) {
throw new Error(`Validation Error: ${currentPath} is required.`);
}
if (rules.type && typeof value !== rules.type) {
throw new Error(`Validation Error: ${currentPath} must be of type ${rules.type}.`);
}
if (rules.minLength && typeof value === 'string' && value.length < rules.minLength) {
throw new Error(`Validation Error: ${currentPath} must be at least ${rules.minLength} characters long.`);
}
// Add more validation rules as needed
}
return Reflect.set(target, property, value, receiver);
}
});
}
const userProfileSchema = {
name: { required: true, type: 'string', minLength: 2 },
age: { type: 'number', min: 18 },
contact: {
email: { required: true, type: 'string' },
phone: { type: 'string' }
}
};
const userProfile = {
name: '',
age: 25,
contact: {
email: '',
phone: '123-456-7890'
}
};
const proxiedUserProfile = createValidatingProxy(userProfile, [], userProfileSchema);
try {
proxiedUserProfile.name = 'Bo'; // Valid
proxiedUserProfile.contact.email = 'bo@example.com'; // Valid
console.log('Initial profile setup successful.');
} catch (error) {
console.error(error.message);
}
try {
proxiedUserProfile.name = 'B'; // Invalid - minLength
} catch (error) {
console.error(error.message);
}
try {
proxiedUserProfile.contact.email = ''; // Invalid - required
} catch (error) {
console.error(error.message);
}
try {
proxiedUserProfile.age = 'twenty'; // Invalid - type
} catch (error) {
console.error(error.message);
}
ããã§ã¯ãcreateValidatingProxy颿°ããã¹ãããããªããžã§ã¯ãã®Proxyãååž°çã«äœæããŸããsetãã©ããã¯ãå²ãåœãŠãèš±å¯ããåã«ãå®å
šä¿®é£Ÿãããããããã£ãã¹ïŒäŸ: 'profile.name'ïŒã«é¢é£ä»ããããæ€èšŒã«ãŒã«ããã§ãã¯ããŸãã
2. ãã现ããã¢ã¯ã»ã¹å¶åŸ¡
ãŠãŒã¶ãŒã®åœ¹å²ãã³ã³ããã¹ãã«åºã¥ããŠãç¹å®ã®ããããã£ãžã®èªã¿åããŸãã¯æžã蟌ã¿ã¢ã¯ã»ã¹ãå¶éããã»ãã¥ãªãã£ããªã·ãŒãå®è£ ããŸãã
function createAccessControlledProxy(obj, accessConfig, path = []) {
// Default access: allow everything if not specified
const defaultAccess = { read: true, write: true };
return new Proxy(obj, {
get(target, property, receiver) {
const currentPath = [...path, property].join('.');
const config = accessConfig[currentPath] || defaultAccess;
if (!config.read) {
throw new Error(`Access Denied: Cannot read property '${currentPath}'.`);
}
const value = Reflect.get(target, property, receiver);
if (typeof value === 'object' && value !== null && !Array.isArray(value) && typeof value !== 'function') {
// Pass down the access config for nested properties
return createAccessControlledProxy(value, accessConfig, [...path, property]);
}
return value;
},
set(target, property, value, receiver) {
const currentPath = [...path, property].join('.');
const config = accessConfig[currentPath] || defaultAccess;
if (!config.write) {
throw new Error(`Access Denied: Cannot write to property '${currentPath}'.`);
}
return Reflect.set(target, property, value, receiver);
}
});
}
const sensitiveData = {
id: 'user-123',
personal: {
name: 'Alice',
ssn: '123-456-7890'
},
preferences: {
theme: 'dark',
language: 'en-US'
}
};
// Define access rules: Admin can read/write everything. User can only read preferences.
const accessRules = {
'personal.ssn': { read: false, write: false }, // Only admins can see SSN
'preferences': { read: true, write: true } // Users can manage preferences
};
// Simulate a user with limited access
const userAccessConfig = {
'personal.name': { read: true, write: true },
'personal.ssn': { read: false, write: false },
'preferences.theme': { read: true, write: true },
'preferences.language': { read: true, write: true }
// ... other preferences are implicitly readable/writable by defaultAccess
};
const proxiedSensitiveData = createAccessControlledProxy(sensitiveData, userAccessConfig);
console.log(proxiedSensitiveData.id); // Accessing 'id' - falls back to defaultAccess
console.log(proxiedSensitiveData.personal.name); // Accessing 'personal.name' - allowed
try {
console.log(proxiedSensitiveData.personal.ssn); // Attempt to read SSN
} catch (error) {
console.error(error.message);
// Output: Access Denied: Cannot read property 'personal.ssn'.
}
try {
proxiedSensitiveData.preferences.theme = 'light'; // Modifying preferences - allowed
console.log(`Theme changed to: ${proxiedSensitiveData.preferences.theme}`);
} catch (error) {
console.error(error.message);
}
try {
proxiedSensitiveData.personal.name = 'Alicia'; // Modifying name - allowed
console.log(`Name changed to: ${proxiedSensitiveData.personal.name}`);
} catch (error) {
console.error(error.message);
}
try {
proxiedSensitiveData.personal.ssn = '987-654-3210'; // Attempt to write SSN
} catch (error) {
console.error(error.message);
// Output: Access Denied: Cannot write to property 'personal.ssn'.
}
ãã®äŸã¯ãç¹å®ã®ããããã£ãŸãã¯ãã¹ãããããªããžã§ã¯ãã«å¯ŸããŠã¢ã¯ã»ã¹ã«ãŒã«ãã©ã®ããã«å®çŸ©ã§ãããã瀺ããŠããŸããcreateAccessControlledProxy颿°ã¯ãProxyãã§ãŒã³ã®åã¬ãã«ã§ãèªã¿åãããã³æžãèŸŒã¿æäœããããã®ã«ãŒã«ã«å¯ŸããŠãã§ãã¯ãããããšãä¿èšŒããŸãã
3. ãªã¢ã¯ãã£ããªããŒã¿ãã€ã³ãã£ã³ã°ãšç¶æ 管ç
Proxyãã³ãã©ãŒãã§ãŒã³ã¯ããªã¢ã¯ãã£ãã·ã¹ãã ãæ§ç¯ããããã®åºç€ãšãªããŸããããããã£ãèšå®ããããšãUIãã¢ããªã±ãŒã·ã§ã³ã®ä»ã®éšåã§æŽæ°ãããªã¬ãŒã§ããŸããããã¯ãå€ãã®çŸä»£ã®JavaScriptãã¬ãŒã ã¯ãŒã¯ãç¶æ 管çã©ã€ãã©ãªã«ãããäžå¿çãªæŠå¿µã§ãã
ç°¡ç¥åããããªã¢ã¯ãã£ãã¹ãã¢ãèããŠã¿ãŸãããã
function createReactiveStore(initialState) {
const listeners = new Map(); // Map of property paths to arrays of callback functions
function subscribe(path, callback) {
if (!listeners.has(path)) {
listeners.set(path, []);
}
listeners.get(path).push(callback);
}
function notify(path, newValue) {
if (listeners.has(path)) {
listeners.get(path).forEach(callback => callback(newValue));
}
}
function createProxy(obj, currentPath = '') {
return new Proxy(obj, {
get(target, property, receiver) {
const value = Reflect.get(target, property, receiver);
const fullPath = currentPath ? `${currentPath}.${String(property)}` : String(property);
if (typeof value === 'object' && value !== null && !Array.isArray(value) && typeof value !== 'function') {
// Recursively create proxy for nested objects
return createProxy(value, fullPath);
}
return value;
},
set(target, property, value, receiver) {
const oldValue = target[property];
const result = Reflect.set(target, property, value, receiver);
const fullPath = currentPath ? `${currentPath}.${String(property)}` : String(property);
// Notify listeners if the value has changed
if (oldValue !== value) {
notify(fullPath, value);
// Also notify for parent paths if the change is significant, e.g., an object modification
if (currentPath) {
notify(currentPath, receiver); // Notify parent path with the whole updated object
}
}
return result;
}
});
}
const proxyStore = createProxy(initialState);
return { store: proxyStore, subscribe, notify };
}
const appState = {
user: {
name: 'Guest',
isLoggedIn: false
},
settings: {
theme: 'light',
language: 'en'
}
};
const { store, subscribe } = createReactiveStore(appState);
// Subscribe to changes
subscribe('user.name', (newName) => {
console.log(`User name changed to: ${newName}`);
});
subscribe('settings.theme', (newTheme) => {
console.log(`Theme changed to: ${newTheme}`);
});
subscribe('user', (updatedUser) => {
console.log('User object updated:', updatedUser);
});
// Simulate state updates
store.user.name = 'Bob';
// Output:
// User name changed to: Bob
store.settings.theme = 'dark';
// Output:
// Theme changed to: dark
store.user.isLoggedIn = true;
// Output:
// User object updated: { name: 'Bob', isLoggedIn: true }
store.user = { ...store.user, name: 'Alice' }; // Reassigning a nested object property
// Output:
// User name changed to: Alice
// User object updated: { name: 'Alice', isLoggedIn: true }
ãã®ãªã¢ã¯ãã£ãã¹ãã¢ã®äŸã§ã¯ãsetãã©ããã¯å²ãåœãŠãå®è¡ããã ãã§ãªããå€ãå®éã«å€æŽããããã©ããããã§ãã¯ããŸãã倿Žãããå Žåããã®ç¹å®ã®ããããã£ãã¹ã®è³ŒèªããŠãããªã¹ããŒã«éç¥ãããªã¬ãŒããŸãããã¹ãããããã¹ã賌èªããå€æŽæã«æŽæ°ãåãåãæ©èœã¯ããã³ãã©ãŒãã§ã€ã³ã®çŽæ¥çãªå©ç¹ã§ãã
èæ ®äºé ãšãã¹ããã©ã¯ãã£ã¹
匷åã§ã¯ãããŸãããProxyãã³ãã©ãŒãã§ãŒã³ã䜿çšããã«ã¯æ éãªæ€èšãå¿ èŠã§ãã
- ããã©ãŒãã³ã¹ãªãŒããŒããã: åProxyã®äœæãšãã©ããã®åŒã³åºãã«ã¯ãããããªãªãŒããŒãããã䌎ããŸããéåžžã«æ·±ããã¹ããéåžžã«é »ç¹ãªæäœã®å Žåã«ã¯ãå®è£ ã®ãã³ãããŒã¯ãåãå¿ èŠããããŸãããã ããäžè¬çãªãŠãŒã¹ã±ãŒã¹ã§ã¯ããã®å©ç¹ãå°ããªããã©ãŒãã³ã¹ã³ã¹ããäžåãããšããããããŸãã
- ãããã°ã®è€éã: Proxyåããããªããžã§ã¯ãã®ãããã°ã¯ãããå°é£ã«ãªãå¯èœæ§ããããŸãããã©ãŠã¶ã®éçºè
ããŒã«ãšãã®ã³ã°ãåºç¯å²ã«äœ¿çšããŠãã ããããã©ããå
ã®
receiveråŒæ°ã¯ãæ£ããthisã³ã³ããã¹ããç¶æããããã«äžå¯æ¬ ã§ãã ReflectAPI: æ£ããåäœãä¿èšŒããç¹ã«ã²ãã¿ãŒãã»ãã¿ãŒããããã¿ã€ããšã®éã§Proxyãšãã®ã¿ãŒã²ããéã®äžå€ã®é¢ä¿ãç¶æããããã«ããã©ããå ã§ã¯åžžã«ReflectAPIïŒäŸ:Reflect.getãReflect.setïŒã䜿çšããŠãã ããã- 埪ç°åç §: ã¿ãŒã²ãããªããžã§ã¯ãå ã®åŸªç°åç §ã«æ³šæããŠãã ãããProxyããžãã¯ããµã€ã¯ã«ããã§ãã¯ããã«ç²ç®çã«ååž°ãããšãç¡éã«ãŒãã«é¥ãå¯èœæ§ããããŸãã
- é
åãšé¢æ°: é
åãšé¢æ°ãã©ã®ããã«æ±ãããæ±ºå®ããŸããäžèšã®äŸã§ã¯ãæå³ããªãéã颿°ãçŽæ¥Proxyåããããšãé¿ããæç€ºçã«ããã°ã©ã ãããŠããªãéãé
åã«ã¯ååž°ããªãããã«æ±ã£ãŠããŸããé
åãProxyåããã«ã¯ã
pushãpopãªã©ã®ã¡ãœããã«å¯ŸããŠç¹å®ã®ããžãã¯ãå¿ èŠã«ãªãå ŽåããããŸãã - ã€ãã¥ãŒã¿ããªã㣠vs ãã¥ãŒã¿ããªãã£: Proxyåããããªããžã§ã¯ãããã¥ãŒã¿ãã«ã§ããã¹ããã€ãã¥ãŒã¿ãã«ã§ããã¹ãããæ±ºå®ããŸããäžèšã®äŸã¯ãã¥ãŒã¿ãã«ãªãªããžã§ã¯ãã瀺ããŠããŸããã€ãã¥ãŒã¿ãã«ãªæ§é ã®å Žåã
setãã©ããã¯éåžžããšã©ãŒãã¹ããŒããããå²ãåœãŠãç¡èŠããgetãã©ããã¯æ¢åã®å€ãè¿ããŸãã ownKeysãšgetOwnPropertyDescriptor: å æ¬çãªã€ã³ã¿ãŒã»ãã·ã§ã³ã®ããã«ã¯ãownKeysïŒfor...inã«ãŒããObject.keysçšïŒãgetOwnPropertyDescriptorã®ãããªãã©ããã®å®è£ ãæ€èšããŠãã ããããããã¯ãå ã®ãªããžã§ã¯ãã®åäœãå®å šã«æš¡å£ããå¿ èŠãããProxyã«ãšã£ãŠäžå¯æ¬ ã§ãã
Proxyãã³ãã©ãŒãã§ãŒã³ã®ã°ããŒãã«ãªå¿çš
è€æ°ã¬ãã«ã§ããŒã¿ãååããã³ç®¡çããèœåã¯ãProxyãã³ãã©ãŒãã§ãŒã³ãããŸããŸãªã°ããŒãã«ã¢ããªã±ãŒã·ã§ã³ã³ã³ããã¹ãã§éåžžã«è²Žéãªãã®ã«ããŸãã
- åœéå (i18n) ãšå°åå (l10n): åœéåãããã¢ããªã±ãŒã·ã§ã³ã®è€éãªèšå®ãªããžã§ã¯ããæ³åããŠã¿ãŠãã ãããProxyã䜿çšããŠããŠãŒã¶ãŒã®ãã±ãŒã«ã«åºã¥ããŠç¿»èš³ãããæååãåçã«ååŸããã¢ããªã±ãŒã·ã§ã³ã®UIãšããã¯ãšã³ãã®ãã¹ãŠã®ã¬ãã«ã§äžè²«æ§ã確ä¿ã§ããŸããäŸãã°ãUIèŠçŽ ã®ãã¹ããããèšå®ã¯ãProxyã«ãã£ãŠååããããã±ãŒã«åºæã®ããã¹ãå€ãæã€ããšãã§ããŸãã
- ã°ããŒãã«èšå®ç®¡ç: å€§èŠæš¡ãªåæ£ã·ã¹ãã ã§ã¯ãèšå®ãé«åºŠã«éå±€çã§åçã§ããå ŽåããããŸããProxyã¯ãããã®ãã¹ããããèšå®ã管çããã«ãŒã«ãé©çšããç°ãªããã€ã¯ããµãŒãã¹éã®ã¢ã¯ã»ã¹ããã°ã«èšé²ãããµãŒãã¹ãã©ãã«ã°ããŒãã«ã«ãããã€ãããŠãããã«é¢ä¿ãªããç°å¢èŠå ãã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã«åºã¥ããŠæ£ããèšå®ãé©çšãããããšãä¿èšŒã§ããŸãã
- ããŒã¿åæãšç«¶å解決: è€æ°ã®ã¯ã©ã€ã¢ã³ããŸãã¯ãµãŒããŒéã§ããŒã¿ãåæããã忣ã¢ããªã±ãŒã·ã§ã³ïŒäŸ: ãªã¢ã«ã¿ã€ã å ±åç·šéããŒã«ïŒã§ã¯ãProxyã¯å ±æããŒã¿æ§é ãžã®æŽæ°ãååã§ããŸããProxyã¯ãå°ççäœçœ®ããããã¯ãŒã¯é å»¶ã«é¢ä¿ãªãããã¹ãŠã®åå ãšã³ãã£ãã£éã§åæããžãã¯ã管çããç«¶åãæ€åºãã解決æŠç¥ãäžè²«ããŠé©çšããããã«äœ¿çšã§ããŸãã
- 倿§ãªå°åã«ãããã»ãã¥ãªãã£ãšã³ã³ãã©ã€ã¢ã³ã¹: æ©å¯ããŒã¿ãæ±ããããŸããŸãªã°ããŒãã«èŠå¶ïŒäŸ: GDPRãCCPAïŒã«æºæ ããã¢ããªã±ãŒã·ã§ã³ã®å ŽåãProxyãã§ãŒã³ã¯ãã现ããã¢ã¯ã»ã¹å¶åŸ¡ãšããŒã¿ãã¹ãã³ã°ããªã·ãŒã匷å¶ã§ããŸããProxyã¯ããã¹ãããããªããžã§ã¯ãå ã®å人è奿 å ±ïŒPIIïŒãžã®ã¢ã¯ã»ã¹ãååãããŠãŒã¶ãŒã®å°åãŸãã¯å®£èšãããåæã«åºã¥ããŠé©åãªå¿ååãŸãã¯ã¢ã¯ã»ã¹å¶éãé©çšãã倿§ãªæ³çãã¬ãŒã ã¯ãŒã¯å šäœã§ã®ã³ã³ãã©ã€ã¢ã³ã¹ãä¿èšŒã§ããŸãã
çµè«
JavaScript Proxyãã³ãã©ãŒãã§ãŒã³ã¯ãç¹ã«è€éãªãã¹ããããããŒã¿æ§é å ã§ããªããžã§ã¯ãæäœã«å¯Ÿãããã现ããå¶åŸ¡ãéçºè ã«å¯èœã«ããæŽç·Žããããã¿ãŒã³ã§ãããã©ããã®å®è£ å ã§Proxyãååž°çã«äœæããæ¹æ³ãçè§£ããããšã§ãéåžžã«åçã§ä¿å®æ§ã®é«ãå ç¢ãªã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ã§ããŸããé«åºŠãªæ€èšŒãå ç¢ãªã¢ã¯ã»ã¹å¶åŸ¡ããªã¢ã¯ãã£ããªç¶æ 管çããŸãã¯è€éãªããŒã¿æäœãå®è£ ããŠãããã©ããã«ããããããProxyãã³ãã©ãŒãã§ãŒã³ã¯ãã°ããŒãã«èŠæš¡ã§ã®çŸä»£ã®JavaScriptéçºã®è€éãã管çããããã®åŒ·åãªãœãªã¥ãŒã·ã§ã³ãæäŸããŸãã
JavaScriptã¡ã¿ããã°ã©ãã³ã°ã®æ ãç¶ããäžã§ãProxyãšãã®ãã§ã€ã³æ©èœãæ·±ãæ¢æ±ããããšã¯ãééããªãã³ãŒãããŒã¹ã«æ°ããªã¬ãã«ã®åªé ããšå¹çæ§ãããããã§ããããã€ã³ã¿ãŒã»ãã·ã§ã³ã®åãæŽ»çšããäžçäžã®èŠèŽè ã«åããŠããã€ã³ããªãžã§ã³ãã§å¿çæ§ã®é«ããå®å šãªã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããŠãã ããã