免費(fèi)咨詢問題的網(wǎng)站好的搜索引擎推薦
odoo前端js對象的擴(kuò)展方法
在 Odoo 中,你可以使用兩種方法來擴(kuò)展 JavaScript 對象:extends
和 patch
。這兩種方法在功能上有一些區(qū)別。
-
extends
方法:- 使用
extends
方法可以創(chuàng)建一個新的 JavaScript 對象,并繼承自現(xiàn)有的對象。這意味著你可以在新對象中添加、修改或覆蓋現(xiàn)有對象的屬性和方法。 extends
方法適用于對現(xiàn)有對象進(jìn)行全面的擴(kuò)展和修改,以滿足特定的需求。你可以通過創(chuàng)建一個新的對象,并在其中添加自定義的屬性和方法來實(shí)現(xiàn)擴(kuò)展。- 使用
extends
方法時,你需要確保新對象的原型鏈正確設(shè)置,以便正確繼承和覆蓋現(xiàn)有對象的屬性和方法。
- 使用
-
patch
方法:- 使用
patch
方法可以直接修改現(xiàn)有的 JavaScript 對象,而無需創(chuàng)建新的對象。你可以通過patch
方法來修改對象的屬性和方法,添加新的屬性和方法,或者刪除現(xiàn)有的屬性和方法。 patch
方法適用于對現(xiàn)有對象進(jìn)行局部的修改和調(diào)整,以滿足特定的需求。你可以直接在現(xiàn)有對象上進(jìn)行修改,而無需創(chuàng)建新的對象。- 使用
patch
方法時,你需要確保正確地定位到要修改的對象,并進(jìn)行相應(yīng)的修改操作。
- 使用
總的來說,extends
方法適用于全面的擴(kuò)展和修改,而 patch
方法適用于局部的修改和調(diào)整。選擇使用哪種方法取決于你的具體需求和場景。
1、patch的實(shí)現(xiàn)
patch 一個簡單的例子
/** @odoo-module **/import { _t } from "@web/core/l10n/translation";
import { patch } from "@web/core/utils/patch";
import { SearchBar } from "@web/search/search_bar/search_bar";patch(SearchBar.prototype, {getPreposition(searchItem) {let preposition = super.getPreposition(searchItem);if (this.fields[searchItem.fieldName].name == 'payment_date') {preposition = _t("until");}return preposition}
});
2、patch.js
先學(xué)習(xí)一下object對象,再來學(xué)習(xí)patch.js
patch對象的原型,調(diào)用了很多Object的靜態(tài)方法,原則上就是在原型對象上動態(tài)增加或者替換新的屬性。
// Replace the old property by the new one.Object.defineProperty(objToPatch, key, newProperty);
附錄: patch.js 源文件
/** @odoo-module **//*** @typedef {{* originalProperties: Map<string, PropertyDescriptor>;* skeleton: object;* extensions: Set<object>;* }} PatchDescription*//** @type {WeakMap<object, PatchDescription>} */
const patchDescriptions = new WeakMap();/*** Create or get the patch description for the given `objToPatch`.* @param {object} objToPatch* @returns {PatchDescription}*/
function getPatchDescription(objToPatch) {if (!patchDescriptions.has(objToPatch)) {patchDescriptions.set(objToPatch, {originalProperties: new Map(),skeleton: Object.create(Object.getPrototypeOf(objToPatch)),extensions: new Set(),});}return patchDescriptions.get(objToPatch);
}/*** @param {object} objToPatch* @returns {boolean}*/
function isClassPrototype(objToPatch) {// class A {}// isClassPrototype(A) === false// isClassPrototype(A.prototype) === true// isClassPrototype(new A()) === false// isClassPrototype({}) === falsereturn Object.hasOwn(objToPatch, "constructor") && objToPatch.constructor?.prototype === objToPatch;
}/*** Traverse the prototype chain to find a potential property.* @param {object} objToPatch* @param {string} key* @returns {object}*/
function findAncestorPropertyDescriptor(objToPatch, key) {let descriptor = null;let prototype = objToPatch;do {descriptor = Object.getOwnPropertyDescriptor(prototype, key);prototype = Object.getPrototypeOf(prototype);} while (!descriptor && prototype);return descriptor;
}/*** Patch an object** If the intent is to patch a class, don't forget to patch the prototype, unless* you want to patch static properties/methods.** @template T* @template {T} U* @param {T} objToPatch The object to patch* @param {U} extension The object containing the patched properties* @returns {() => void} Returns an unpatch function*/
export function patch(objToPatch, extension) {if (typeof extension === "string") {throw new Error(`Patch "${extension}": Second argument is not the patch name anymore, it should be the object containing the patched properties`);}const description = getPatchDescription(objToPatch);description.extensions.add(extension);const properties = Object.getOwnPropertyDescriptors(extension);for (const [key, newProperty] of Object.entries(properties)) {const oldProperty = Object.getOwnPropertyDescriptor(objToPatch, key);if (oldProperty) {// Store the old property on the skeleton.Object.defineProperty(description.skeleton, key, oldProperty);}if (!description.originalProperties.has(key)) {// Keep a trace of original property (prop before first patch), useful for unpatching.description.originalProperties.set(key, oldProperty);}if (isClassPrototype(objToPatch)) {// A property is enumerable on POJO ({ prop: 1 }) but not on classes (class A {}).// Here, we only check if we patch a class prototype.newProperty.enumerable = false;}if ((newProperty.get && 1) ^ (newProperty.set && 1)) {// get and set are defined together. If they are both defined// in the previous descriptor but only one in the new descriptor// then the other will be undefined so we need to apply the// previous descriptor in the new one.const ancestorProperty = findAncestorPropertyDescriptor(objToPatch, key);newProperty.get = newProperty.get ?? ancestorProperty?.get;newProperty.set = newProperty.set ?? ancestorProperty?.set;}// Replace the old property by the new one.Object.defineProperty(objToPatch, key, newProperty);}// Sets the current skeleton as the extension's prototype to make// `super` keyword working and then set extension as the new skeleton.description.skeleton = Object.setPrototypeOf(extension, description.skeleton);return () => {// Remove the description to start with a fresh base.patchDescriptions.delete(objToPatch);for (const [key, property] of description.originalProperties) {if (property) {// Restore the original property on the `objToPatch` object.Object.defineProperty(objToPatch, key, property);} else {// Or remove the property if it did not exist at first.delete objToPatch[key];}}// Re-apply the patches without the current one.description.extensions.delete(extension);for (const extension of description.extensions) {patch(objToPatch, extension);}};
}