React:在哪里扩展对象原型
React: Where To Extend Object Prototype
我使用 create-react-app 创建了一个纯 React 应用程序。我想扩展 String
class 并在一个或多个组件中使用它。例如:
String.prototype.someFunction = function () {
//some code
}
(您可能想看看 this question 以了解有关扩展对象原型的更多信息。)
是的,我可以在组件旁边定义它并在其中使用它。但最好和最干净的方法是什么?
我应该把它写成 class method
还是写在 componentDidMount
里面还是别的什么?
编辑:
甚至 "OK" 在 React(或 JavaScript)中扩展对象原型吗?
TLDR 答案:无处可去!
-- 细致入微的回答--
What I'm trying to do is extend pure JavaScript classes, like String class, which is a very common task in javascript
Is it even "OK" to extend object prototype in React (or in JavaScript) at all?
Extending/modifying native prototypes in JavaScript 是一个controversial topic, and, contrary to what you said, not something most professional developers do very often. The general consensus is that extending the native JS prototypes is a programming anti-pattern to be avoided,因为它打破了封装原则,修改了全局状态。然而,与许多规则一样,可能会有极少数的例外情况。例如:您正在从事一个不需要生产质量的玩具项目,您是唯一会接触该代码库的开发人员,或者您的代码永远不会成为其他任何人的依赖项。
如果您有充分的理由并且真正知道自己在做什么,并且充分意识到修改本机数据 types/behaviors 对您的 run-time 环境和依赖项的潜在后果,那么也许您会发现一些适用于这种做法的有效用例。但很可能不会,或者至少不会经常发生。几乎从来没有。
如果您只是追求方便/语法糖,最好使用实用函数(来自 lodash、underscore 或 ramda 等)并学习练习函数组合。但是,如果您真的致力于面向对象的范例,那么 您可能应该只是“subclassing”本机数据类型而不是修改它们.
所以与其像这样改变 class 的原型:
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())
您将创建一个 sub-class(仅适用于 ES6 class 语法),如下所示:
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?')
这个 subclass 在各个方面都像 built-in String 一样工作,当然除了你不能像 String 文字一样编写 MyString 文字。
I created a pure React application using create-react-app. I would like to extend the String class and use it in one or more components ... Yes I can define it beside a component and use it within. But what is the best and cleanest way? ... Should I write it as a class method or inside componentDidMount or something else?
因为修改 built-in 原型(通过改变 String.prototype
之类的东西)会改变应用程序的全局状态,所以您只想执行一次并且几乎肯定会在任何其他代码之前执行执行(因为您正在设置 Strings 对之后执行的所有代码的行为方式的全局状态)。因此,从 React 组件实例方法中更改 built-in 原型没有多大意义。
如果你打算做肮脏的事,我建议为你想要修改的每个原生类型创建一个单独的模块,并将这些模块保存在 src/lib/extend-built-ins/
之类的地方,然后 import
它们是 src/index.js
中的第一件事。你不需要导出任何东西。执行 import src/lib/extend-built-ins/String.js
将执行代码,这将改变您的全局状态。这将至少提供体面的组织,并确保在应用程序的其余代码运行之前完全修改您的应用程序环境。这样您就可以在整个应用程序中使用扩展类型,而无需考虑从某个地方导入它们。
如果您要走子classing 路线 (class MyThing extends NativeThing
),那么我建议您类似地在单独的模块中定义您的自定义 classes,例如 src/lib/native-subclasses/
。但在这种情况下,您必须 import
您的 class 构造函数到要使用它们的 any/every 模块中。
但是,如果您想开发干净、可测试、可重构的代码,让其他人和您未来的自己都容易理解,那么您不应该做这种事情。相反,考虑采用 React 及其生态系统的函数式编程原则。任何人都可以快速阅读和理解纯函数,因此请使用它们来完成您的数据和状态转换,而不是依赖 hard-to-track hack,例如修改全局对象。理解这个小 hack 可能很有趣而且微不足道,但即使在一个项目中这样做一次也会促进和鼓励你自己和其他人使用更多的快捷方式和 anti-patterns.
对于 TypeScript 语言用户
使用 expo-cli 36 在基于 javascript 的 react-native 项目上测试并在 Angular7
上工作时编写
我有错误把我带到这里...
我正在使用基于 js 的 react-native。
之前写了一个JS库,后来因为一些需要,改成TS,改成Angular后,不得不自己创建一个固定的库在那种环境下工作...
不,我在这里复制了这些文件,我在进入这个线程时遇到了错误,但是当我第一次编译它时所有错误都消失了,并且它完全可以工作...
以下是我的使用方法:
我有实用性 class 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');
}
...
}
我有一个定义 class 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
*
* @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
*
*/
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
的文件夹中,这根本不重要,我可以直接从 Extensions 访问 Utility,反之亦然(或者直到我没有任何问题现在。
现在 虽然我仍然不能在 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;
请注意,目前我没有足够的时间来全面了解我的旧代码,但事情是这样的
第二个注意:如果我在 Utility 中导入扩展,它会给我警告 Require cycles are allowed, but can result in uninitialized values。考虑重构以消除对循环的需求。
如果我没有在 VSCode 上看到一些错误,但它编译正常,没有警告...
我使用 create-react-app 创建了一个纯 React 应用程序。我想扩展 String
class 并在一个或多个组件中使用它。例如:
String.prototype.someFunction = function () {
//some code
}
(您可能想看看 this question 以了解有关扩展对象原型的更多信息。)
是的,我可以在组件旁边定义它并在其中使用它。但最好和最干净的方法是什么?
我应该把它写成 class method
还是写在 componentDidMount
里面还是别的什么?
编辑:
甚至 "OK" 在 React(或 JavaScript)中扩展对象原型吗?
TLDR 答案:无处可去!
-- 细致入微的回答--
What I'm trying to do is extend pure JavaScript classes, like String class, which is a very common task in javascript
Is it even "OK" to extend object prototype in React (or in JavaScript) at all?
Extending/modifying native prototypes in JavaScript 是一个controversial topic, and, contrary to what you said, not something most professional developers do very often. The general consensus is that extending the native JS prototypes is a programming anti-pattern to be avoided,因为它打破了封装原则,修改了全局状态。然而,与许多规则一样,可能会有极少数的例外情况。例如:您正在从事一个不需要生产质量的玩具项目,您是唯一会接触该代码库的开发人员,或者您的代码永远不会成为其他任何人的依赖项。
如果您有充分的理由并且真正知道自己在做什么,并且充分意识到修改本机数据 types/behaviors 对您的 run-time 环境和依赖项的潜在后果,那么也许您会发现一些适用于这种做法的有效用例。但很可能不会,或者至少不会经常发生。几乎从来没有。
如果您只是追求方便/语法糖,最好使用实用函数(来自 lodash、underscore 或 ramda 等)并学习练习函数组合。但是,如果您真的致力于面向对象的范例,那么 您可能应该只是“subclassing”本机数据类型而不是修改它们.
所以与其像这样改变 class 的原型:
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())
您将创建一个 sub-class(仅适用于 ES6 class 语法),如下所示:
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?')
这个 subclass 在各个方面都像 built-in String 一样工作,当然除了你不能像 String 文字一样编写 MyString 文字。
I created a pure React application using create-react-app. I would like to extend the String class and use it in one or more components ... Yes I can define it beside a component and use it within. But what is the best and cleanest way? ... Should I write it as a class method or inside componentDidMount or something else?
因为修改 built-in 原型(通过改变 String.prototype
之类的东西)会改变应用程序的全局状态,所以您只想执行一次并且几乎肯定会在任何其他代码之前执行执行(因为您正在设置 Strings 对之后执行的所有代码的行为方式的全局状态)。因此,从 React 组件实例方法中更改 built-in 原型没有多大意义。
如果你打算做肮脏的事,我建议为你想要修改的每个原生类型创建一个单独的模块,并将这些模块保存在 src/lib/extend-built-ins/
之类的地方,然后 import
它们是 src/index.js
中的第一件事。你不需要导出任何东西。执行 import src/lib/extend-built-ins/String.js
将执行代码,这将改变您的全局状态。这将至少提供体面的组织,并确保在应用程序的其余代码运行之前完全修改您的应用程序环境。这样您就可以在整个应用程序中使用扩展类型,而无需考虑从某个地方导入它们。
如果您要走子classing 路线 (class MyThing extends NativeThing
),那么我建议您类似地在单独的模块中定义您的自定义 classes,例如 src/lib/native-subclasses/
。但在这种情况下,您必须 import
您的 class 构造函数到要使用它们的 any/every 模块中。
但是,如果您想开发干净、可测试、可重构的代码,让其他人和您未来的自己都容易理解,那么您不应该做这种事情。相反,考虑采用 React 及其生态系统的函数式编程原则。任何人都可以快速阅读和理解纯函数,因此请使用它们来完成您的数据和状态转换,而不是依赖 hard-to-track hack,例如修改全局对象。理解这个小 hack 可能很有趣而且微不足道,但即使在一个项目中这样做一次也会促进和鼓励你自己和其他人使用更多的快捷方式和 anti-patterns.
对于 TypeScript 语言用户
使用 expo-cli 36 在基于 javascript 的 react-native 项目上测试并在 Angular7
上工作时编写我有错误把我带到这里... 我正在使用基于 js 的 react-native。 之前写了一个JS库,后来因为一些需要,改成TS,改成Angular后,不得不自己创建一个固定的库在那种环境下工作...
不,我在这里复制了这些文件,我在进入这个线程时遇到了错误,但是当我第一次编译它时所有错误都消失了,并且它完全可以工作...
以下是我的使用方法:
我有实用性 class 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');
}
...
}
我有一个定义 class 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
*
* @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
*
*/
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
的文件夹中,这根本不重要,我可以直接从 Extensions 访问 Utility,反之亦然(或者直到我没有任何问题现在。
现在 虽然我仍然不能在 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;
请注意,目前我没有足够的时间来全面了解我的旧代码,但事情是这样的
第二个注意:如果我在 Utility 中导入扩展,它会给我警告 Require cycles are allowed, but can result in uninitialized values。考虑重构以消除对循环的需求。 如果我没有在 VSCode 上看到一些错误,但它编译正常,没有警告...