反应:在哪里扩展对象原型



我使用 create-react-app 创建了一个纯 React 应用程序。我想扩展String类并在一个或多个组件中使用它。例如:

String.prototype.someFunction = function () {
//some code
}

(您可能需要查看此问题以了解有关扩展对象原型的更多信息。

是的,我可以在组件旁边定义它并在其中使用它。但是什么是最好和最干净的方法?

我应该把它写成class method还是componentDidMount里面或其他东西?

编辑:

在 React(或 JavaScript)中扩展对象原型甚至"可以"吗?

TLDR 答:无处可去!

--细致入微的答案 --

我正在尝试做的是扩展纯JavaScript类,例如String class,这是javascript中非常常见的任务。

在 React(或 JavaScript)中扩展对象原型甚至"可以"吗?

在 JavaScript 中扩展/修改原生原型是一个有争议的话题,与你所说的相反,大多数专业开发人员并不经常这样做。普遍的共识是,扩展原生JS原型是要避免的编程反模式,因为它打破了封装原则并修改了全局状态。但是,与许多规则一样,它可能有极少数例外。例如:你正在做一个不需要生产质量的玩具项目,你是唯一一个会接触这个代码库的开发人员,或者你的代码永远不会成为其他人的依赖。

如果你有一个很好的理由,并且真正知道你在做什么,并且完全意识到你修改本机数据类型/行为对运行时环境和依赖项的潜在后果,那么也许你会找到一些有效的用例。但很可能不是,或者至少不是经常。就像几乎从来没有。

如果你只是追求方便/句法糖,你最好拉入实用函数(来自lodash,underscore或ramda)并学习练习函数组合。但是,如果您真的致力于面向对象范式,那么您可能应该只是"子类化"本机数据类型,而不是修改它们

因此,与其像这样改变类的原型:

String.prototype.somethingStupid = function () {
return [].map.call(this, function(letter) {
if ((Math.random() * 1) > .5) return letter.toUpperCase()
else return letter.toLowerCase()
}).join('')
}
console.log('This is a silly string'.somethingStupid())

您将创建一个子类(仅适用于 ES6 类语法),如下所示:

class MyString extends String {
constructor(x = '') {
super(x)
this.otherInstanceProp = ':)'
}

somethingStupid() {
return [].map.call(this, function(letter) {
if ((Math.random() * 1) > .5) return letter.toUpperCase()
else return letter.toLowerCase()
}).join('')
}
}
const myStr = new MyString('This is a silly string')
console.log(myStr)
console.log(myStr.valueOf())
console.log(myStr.somethingStupid() + ', don't you think?')

这个子类在各个方面都像内置字符串一样工作,当然,除了你不能像字符串文字那样编写 MyString 文字。

我使用 create-react-app 创建了一个纯 React 应用程序。我想扩展 String 类并在一个或多个组件中使用它......是的,我可以在组件旁边定义它并在其中使用它。但是什么是最好和最干净的方法?...我应该把它写成类方法还是在 componentDidMount 或其他东西中?

因为修改内置原型(通过改变String.prototype之类的东西)会改变应用程序的全局状态,所以你只需要执行一次,而且几乎肯定在任何其他代码执行之前(因为你正在设置字符串行为的全局状态对于之后执行的所有代码)。因此,从 React 组件实例方法中更改内置原型没有多大意义。

如果你要做肮脏的事情,我建议为你想要修改的每个本机类型创建一个单独的模块,并将这些模块保留在像src/lib/extend-built-ins/之类的地方,然后将它们作为src/index.js的第一件事import。您无需导出任何内容。执行import src/lib/extend-built-ins/String.js将执行代码,这将改变您的全局状态。这将至少提供体面的组织,并确保在应用程序的其余代码运行之前完全修改应用程序环境。这样,您就可以在整个应用程序中使用扩展类型,而无需考虑从某个位置导入它们。

如果你要走子类化路线(class MyThing extends NativeThing),那么我建议你在单独的模块中类似地定义你的自定义类,比如src/lib/native-subclasses/。但在这种情况下,您必须将类构造函数import到要使用它们的任何/每个模块中。

但是,如果你想开发干净、可测试、可重构的代码,让其他人和你未来的自己更容易理解,你就不应该做这种事情。相反,考虑采用 React 及其生态系统的函数式编程原则。任何人都可以快速阅读和理解纯函数,因此请使用它们来完成数据和状态转换,而不是依赖难以跟踪的黑客,例如修改全局对象。理解这个小技巧可能很可爱和微不足道,但即使在项目中这样做一次也会促进和鼓励自己和他人使用额外的快捷方式和反模式。

适用于 TypeScript 语言用户

使用 expo-cli 36 在基于 javascript 的 react-native 项目上进行测试,并在 Angular 7 上工作时编写

我有错误把我带到这里... 我正在使用基于 js 的 react-native。 以前我写了一个JS库,然后由于一些需要,我把它改成了TS,切换到Angular后,我不得不用它创建一个固定的库才能在那个环境中工作......

不,我在这里复制了这些文件,我遇到了错误,我来到了这个线程,但是一旦我第一次编译它,所有错误都消失了,并且它完全有效......

这是我如何使用它:

我有实用程序类Utility.ts

export class Utility {
/**
* Returns False If Incoming Variable Is Not Null, void or Undefined, Otherwise True
*/
public static isNullOrUndefined(obj: any|void): boolean {
// return obj == null // juggling-check
return typeof obj === 'undefined' || obj === null; // strict-check
}
/**
* Returns False If Incoming Variable Does Not Contains Data, Otherwise True
*/
public static isNullOrUndefinedOrEmpty(obj: any|void): boolean {
if (Utility.isNullOrUndefined(obj)) {
return true;
}
// typeof for primitive string, instanceof for objective string
if (typeof(obj) === 'string' || obj instanceof String) {
return (obj as string).valueOf() === '';
} else if (obj instanceof Array) {
return (obj as Array<any>).length === 0;
}
throw new Error('Not Supported Exception');
}
...
}

我有一个定义类index.d.ts(我不确定,但我认为文件名在 Angular 时间非常重要,请随时测试......

declare global {
interface StringConstructor { // for static methods
format: (str: string, ...args: any[]) => string;
}
interface String { // for instance methods
/**
* Support StringComparision Options
* https://stackoverflow.com/questions/10636871/ordinal-string-compare-in-javascript
* @param searchString {!string}
* @param position {?number}
* @param comparision {?StringComparison}
* @returns {number}
*/
indexOfString: (searchString: string, position?: number, comparision?: StringComparison) => number;
insertAt: (index: number, strText: string) => string;
removeAt: (index: number, count: number) => string;
replaceAt: (index: number, count: number, strReplacement: string) => string;
overrideAt: (index: number, strText: string) => string;
...
}

最后,我的扩展文件Extensions.ts

import { Utility } from './Utility';
/**
* Support StringComparision Options
* https://stackoverflow.com/questions/10636871/ordinal-string-compare-in-javascript
*/
String.prototype.indexOfString = function(searchString: string, position?: number, comparision?: StringComparison): number {
return Utility.indexOfString(this, searchString, position, comparision);
};
String.prototype.insertAt = function(index: number, strText: string): string {
return this.substr(0, index) + strText + this.substr(index);
};
String.prototype.removeAt = function(index: number, count: number): string {
if (Utility.isNullOrUndefined(count)) {
count = this.length - index;
}
return this.substr(0, index) + this.substr(index + count);
};

在这里,我将所有这些文件放在一个名为util的文件夹中,这根本不重要,我可以直接从扩展到实用程序,反之亦然(或者直到现在我还没有任何问题。

现在,虽然我仍然无法在 react 组件中使用我的扩展,但如果我添加一个简单的导入,我可以:import '../util/Extensions';

我像这样测试它:

import React from 'react';
import { Text, View } from 'react-native';
import '../util/Extensions'; //my import
const SurveyScreen = props => {
const z = 'sdwqew';
const x = z.insertAt(2,'hassan'); //function from my custom extensions
return (
<View>
<Text>Hello World</Text>
<Text>{x}</Text>
</View>
);
}
export default SurveyScreen;

请注意,目前我没有足够的时间来全面概述我的旧代码,但事情是这样的

第二个注意:如果我在实用程序中导入扩展,它会给我警告需要循环是允许的,但可能会导致未初始化的值。考虑重构以消除对循环的需求。如果我没有,我在 VSCode 上看到一些错误,但它编译正常,没有警告......

最新更新