Object.prototype.getB=函数(){//如何获取当前值a返回a.b;};常量a={b:'c'};a.获取B();

如您所见,我想为所有Object值创建一个函数。我需要得到这个函数中的对象值,然后做一些事情。

1答案1

重置为默认值
5

猴子修补

你想做的事叫做猴子修补 — 你改变了一个内置的原型。有许多错误的方法可以做到这一点,应该完全避免,因为它会对Web兼容性产生负面影响。然而,我将演示如何以最接近现有原型特性的方式实现这一点。

在您的示例中,函数体应该返回这个。b条.在作为方法调用的函数中,可以使用关键字。请参见“this”关键字是如何工作的?(第4节:“输入功能代码”,小节“函数属性”)了解更多详细信息。

您已将方法正确添加到对象.原型.请参见继承和原型链了解更多详细信息。

工具

在推理monkey-patching时,涉及到许多工具:

  1. 检查自有财产存在
  2. 使用属性描述符
  3. 使用正确的函数类型
  4. 吸气器和排气器

假设您正在尝试实现方法The类.

1.检查自有财产是否存在

根据您的用例,您可能需要检查您想要引入的方法是否已经存在。你可以用对象.hasOwn; 这是一种非常新的方法,但在较旧的环境中,它可以简单地由对象.原型.hasOwnProperty.call.或者,使用拥有自己的财产正常情况下,但要注意,如果你或其他人模仿了拥有自己的财产方法它本身,这可能会导致错误的结果。

请注意在里面不专门检查自己的属性,但也检查继承的属性,当您要创建拥有属性。另外,请注意if(TheClass.prototype.theMethod)不是属性存在检查;它是一个真实性检查。

代码示例
if(Object.hasOwn(TheClass.prototype,“theMethod”)){//定义方法。}
if(Object.prototype.hasOwnProperty.call(TheClass.protoype,“theMethod”)){//定义方法。}
if(TheClass.prototype.hasOwnProperty(“theMethod”)){//定义方法。}

2.使用属性描述符

您可以随意选择属性描述符,但现有的方法是可写、可配置和不可枚举(最后一个是使用时的默认值定义属性).定义属性可用于一次性定义多个属性。

当使用=,属性变为可写、可配置,并且可枚举的.

代码示例
//定义方法:Object.defineProperty(TheClass.prototype,“theMethod”{可写:true,可配置:true,值:函数(){}});
//定义方法:Object.defineProperties(类原型{方法:{可写:true,可配置:true,值:函数(){}}});

3.使用正确的函数种类

JavaScript有四种主要功能,它们具有不同的用例。“可调用”意味着可以调用没有 新的“可构造”意味着可以调用具有 新的.

函数种类 例子 可调用 是可构造的 结合
箭头函数 () => {} 是的
方法 ({method(){}}).方法 是的 是的
等级 (类{}) 是的 是的
功能功能 (函数(){}) 是的 是的 是的

我们正在寻找一种可以调用的方法(无需新的)并且有自己的结合。我们可以使用功能s、 但是,看看现有的方法,不仅是新的“HELLO”.charAt();奇怪,它也不起作用!因此,该方法也不应是可构建的。因此,适当方法就是我们要找的。

注意,这显然取决于您的用例。例如,如果您想要一个可构造的函数,请使用而不是。

代码示例

我们回到前面的代码示例,而不是函数(){}使用方法定义。

//定义方法:Object.defineProperty(TheClass.prototype,“theMethod”{可写:true,可配置:true,值:{方法(){//做这件事。}}.the方法});

为什么要麻烦2。和3。?

可枚举性和可构造性考虑的目标是创建与现有内置方法具有相同“外观”的东西。这些实现与原始实现之间的区别可以通过以下代码片段来演示:

类TheClass{}Class.prototype.theMethod=函数(){};

让我们将此与不同的,内置的方法,如字符串.原型.charAt:

代码段 天真的例子 内置示例
原型 Class.原型 字符串.原型
方法 类.原型.方法 字符串.原型.charAt
for(原型中的常量p){console.log(p);} “方法”将在某个时间点记录。 “charAt”将永远不会被记录。
新建方法 创建的实例方法. 类型错误:方法不是构造函数。

使用第2小节和第3小节中的工具,可以创建行为更像内置方法的方法。

4.吸气器和排气器

另一种方法是使用getter。考虑一下:

常量arr=[“a”,“b”,“c”,];console.log(arr.indexOfB);//1

如何B索引上的getter阵列原型看起来像吗?我们不能使用上述方法替换价值通过得到,否则我们将得到:

类型错误:属性描述符不能指定值,也不能在指定getter或setter时可写

财产可写的需要完全移除从描述符。现在价值可以替换为得到:

Object.defineProperty(数组原型,“indexOfB”{可配置:true,获取:{索引OfB(){return this.indexOf(“b”);}}.B索引});

还可以通过添加设置属性设置为描述符:

Object.defineProperty(数组原型,“indexOfB”{可配置:true,获取:{索引OfB(){return this.indexOf(“b”);}}.indexOfB,设置:{indexOfB(新值){//`newValue`是赋值。//对当前Array实例使用“this”。//不需要“return”。}}.B索引});

Web兼容性影响

任何人都希望扩展内置原型的原因如下:

  • 您自己有一个好主意,可以将一个新特性添加到您正在扩展的任何类的所有实例中,或者
  • 您希望将现有的指定功能反向移植到较旧的浏览器。

如果扩展目标对象,有两个选项需要考虑:

  • 如果不存在该属性,请提供该属性,否则,保留现有属性,或
  • 始终用您自己的实现替换该属性,无论它是否存在。

所有方法大多都有缺点:

如果您或其他人发明了他们自己的功能,但是出现了一个同名的标准方法,那么这些功能几乎肯定是不兼容的。

如果您或其他人试图实现一个标准功能,以便将其反向移植到不支持它的浏览器,但不阅读规范并“猜测”其工作方式,那么polyfilled功能几乎肯定与标准实现不兼容。即使严格遵守规范,谁又能确保跟上规范的变化?谁将负责实施过程中可能出现的错误?

如果您或其他人选择在覆盖该功能之前检查该功能是否存在,那么一旦有人使用支持该功能的浏览器访问您的页面,突然一切都会中断,因为实现结果是不兼容的。

如果您或其他人选择无论如何覆盖该功能,那么至少实现是一致的,但是迁移到标准功能可能会很困难。如果您编写了一个使用的库很多在其他软件中,迁移成本变得如此巨大,以至于标准本身必须改变; 这就是为什么数组原型包含必须重命名为数组.原型.包括[编辑] [ES讨论] [Bugzilla] [MooTools]、和数组.原型.扁平无法使用,必须命名阵列.原型.扁平相反[拉请求1] [拉请求2] [Bugzilla].换句话说,这就是我们不能拥有美好事物的原因.

此外,您的库可能无法与其他库进行互操作。

选择

最简单的替代方法是定义自己的普通函数:

const theMethod=(object)=>object.theProperty;//或者别的什么。const theProperty=方法(对象);

你也可以考虑代理.这样,您可以动态查询属性并对其作出响应。假设您有一个具有属性的对象通过z(z)你想实现方法获取A通过获取Z:

const theProxiedObject=新代理(对象{get(目标、属性、接收方){const letter=属性匹配(/^get(?<letter>[A-Z])$/)?。组?。letter.toLowerCase();if(字母){return()=>目标[字母];}return Reflect.get(目标、属性、接收方);}});console.assert(theProxiedObject.getB()===theProxidedObject.b);

您还可以使用另一个类扩展对象的原型,并使用以下方法:

类GetterOfB扩展对象{b;构造函数(init){super();Object.assign(this,init);}获取B(){返回此.b;}}const theObject=新GetterOfB({b: “c”});const theB=对象.getB();

你要记住的是不要修改你自己没有定义的东西。

0

你的答案

单击“发布您的答案”,表示您同意我们的服务条款并确认您已阅读我们的隐私政策.

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.