如何制作一个可以影响任何值类型的方法?

How to make a method that can affect any value type?

警告:创建本机对象 and/or 属性的扩展被认为是错误的形式,必然会导致问题。如果它不是为你单独使用的代码,或者你不知道如何正确使用它,请不要使用它


我知道你可以使用ObjectStringNumberBoolean等来定义一个方法,像这样:

String.prototype.myFunction = function(){return this;} //only works on strings.

但我需要做的是在任何值上使用它,并在函数中访问该值。

我用谷歌搜索并查看 here,但找不到合适的内容。

2/18/15 编辑:如果我使用 Object.prototype,是否有任何解决方法可以使它成为任何对象的 属性?
根据请求,这是用于 isString()

的当前函数
function isString(ins) {
    return typeof ins === "string";
}

根据几个答案,我想出了一些代码和由此引起的错误。

Object.prototype.isString = function() {
    return typeof this === "string";
}
"5".isString() //returns false
"".isString()  //returns false
var str = "string"
str.isString() //returns false

举个例子:

String.prototype.myFunction = function() {
    return this+"asd";
};

此函数会在调用 myFunction() 时将 "asd" 添加到每个字符串。

var s = "123";
s = s.myFunction();
//s is now "123asd"

来自MDN Description of Object

All objects in JavaScript are descended from Object

因此,您可以将方法添加到 Object.prototype,然后可以在 任何东西 上调用它。例如:

Object.prototype.isString = function() {
    return this.constructor.name === 'String';
}

console.log("hi".isString()); //logs true
console.log([].isString()); //logs false
console.log(5..isString()); //logs false

如果需要,您可以为现有的每种类型的原语创建此 isX 函数。无论哪种方式,您 都可以 为每种类型添加方法,因为 JavaScript 中的所有内容都来自 Object.

希望对您有所帮助,祝您好运:)

--编辑--

我确实想指出,仅仅因为你可以这样做并不意味着你应该。扩展 JavaScript 的内置功能通常是一种不好的做法,对于其他人将使用的库更是如此。不过,这取决于您的用例。祝你好运。

“点运算符函数”称为方法。在 JavaScript 中创建一个可以处理任何数据类型的方法的最简洁的方法是创建一个包装器。例如:

var Wrapper = defclass({
    constructor: function (value) {
        this.value = value;
    },
    isString: function () {
        return typeof this.value === "string";
    },
    describe: function () {
        if (this.isString()) {
            alert('"' + this.value + '" is a string.');
        } else {
            alert(this.value + " is not a string.");
        }
    }
});

var n = new Wrapper(Math.PI);
var s = new Wrapper("Hello World!");

n.describe(); // 3.141592653589793 is not a string.
s.describe(); // "Hello World!" is a string.

function defclass(prototype) {
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
}

通过创建自己的包装器构造函数,您可以确保:

  1. 您的代码不会与其他人的代码混淆。
  2. 其他人的代码不会与您的代码混淆。
  3. 您保持全局范围和本机原型干净。

几个流行的 JavaScript 库,例如 underscore and lodash 为此目的创建了包装器构造函数。

首先,为什么不赞成在对象(或其他内置类型)上定义属性 - 它们出现在意想不到的地方。这是一些输出某些字符的总英尺数的代码:

var feetMap = {
  jerry : 4,
  donald : 2,
  humpty: 0  
}

function countFeet(feetMap) {
  var allFeet = 0;
  for (var character in feetMap) {
    allFeet += feetMap[character];
  }
  return allFeet;
}


console.log('BEFORE DEFINITION', countFeet(feetMap));
Object.prototype.isString = function() {
    return typeof this === "string";
};
console.log('AFTER DEFINITION', countFeet(feetMap));

请注意,简单地定义 isString 函数将如何影响 countFeet 函数的结果,该函数现在迭代一个意外 属性。当然,如果使用 hasOwnProperty 检查保护迭代,或者如果 属性 被定义为不可枚举的

,则可以避免这种情况。

避免在内置类型上定义属性的另一个原因是冲突的可能性。如果每个人都定义了自己的 isNumber 方法,根据用例给出略有不同的结果——一个人可以认为字符串“42”是一个数字,另一个人可能认为它不是——当人们使用多个库时,微妙的错误会到处出现。

问题是 - 为什么需要可以影响任何值类型的 方法 ?方法应该是它所属的对象 class 所固有的东西。使用 isString 方法对 Number 对象毫无意义 - 它与 Numbers 根本没有任何关系。

更有意义的是 function/method 可以 return 作为参数给它的值的类型:

var Util = Util || {};
Util.isString(value) {
  return typeof value === "string";
}

Util.isString('test') // true
Util.isString(5) // false

你当前代码的原因

Object.prototype.isString = function() {
    return typeof this === "string";
}
"5".isString() //returns false
"".isString()  //returns false
var str = "string"
str.isString() //returns false

不起作用是因为当您访问原始值上的 属性 时,JS 会创建一个适当类型的包装对象并解析该包装对象上的 属性 (之后它扔掉)。这是一个应该阐明它的例子:

Object.prototype.getWrapper = function(){
  return this;
}

console.log((5).getWrapper()); // Number [[PrimitiveValue]]:5
console.log("42".getWrapper()); // String [[PrimitiveValue]]:"42"

注意原始值5和对象new Number(5)是不同的概念

您可以通过 returning 原始值的类型来改变您的函数以使其主要工作。另外,不要忘记让它不可枚举,这样当你遍历随机对象时它就不会出现。

Object.defineProperty(Object.prototype, 'isString', {
   value : function() {
             return typeof this.valueOf() === "string";
           },
   enumerable : false
});
"5".isString() //returns true
"".isString()  //returns true
var str = "string"
str.isString() //returns true
Object.prototype.isString = function() {
    return typeof this === "string";
}
"5".isString() //returns false
"".isString()  //returns false
var str = "string"
str.isString() //returns false

如果有人可以解释该函数是任何对象的 属性 的解决方法,以及为什么当前方法不起作用,我将提供 125 个代表。

答案:

好吧,在 javascript 中,当您调用对象的子 method/property 时,
如 "myFunction"(object.myFunction 或对象["MyFunction"])
它将开始查看对象本身是否具有它。
如果不是:它将遵循原型链(就像普通 oop 中的 superclass),
直到找到 "parent/superclass" 和 method/property.
这个原型链的最后一步是 Object.
如果对象没有方法,它将 return "undefined".

当您扩展对象 class 本身时,它总是会查看 任何将方法调用为对象的对象(在 oop 中:除了自己的 class 类型之外,所有 classes 也是对象) 这就像在普通 OOP 中缺少 "cast"。

所以 你的函数 return 错误的原因是它是 "object" 而不是 "string" 在这种情况下
尝试制作这个功能:

Object.prototype.typeString = function() {
    return typeof this;
}
"5".typeString() //returns "object"

正如大家所说,扩展任何本机 JSclasses 确实是个坏主意,但解决方法将从以下内容开始:

Object.prototype.objectTypeString = function(){
   return Object.prototype.toString.call(this);
}

这是一个fiddle: http://jsfiddle.net/fwLpty10/13/

注意 null dosnt 有原型,NaN (NotANumber) 被认为是一个数字!!! 这意味着 您将始终需要检查变量是否为 null, 在调用此方法之前!

Object.prototype.isString = function(){
   return Object.prototype.toString.call(this) === "[object String]";
};

最终 fiddle:http://jsfiddle.net/xwshjk4x/5/

这里的技巧是这个方法 return 是 toString 方法的结果,用 "this" 调用,这意味着在 toString 方法的上下文中,你调用的对象它是它自己的 class(不仅仅是原型链中的任何超类型)

已发布的扩展 Object 原型的代码 工作,如果更正的话。

但是,它对调用方法中的 this 内容做出了错误的假设。使用发布的代码,以下输出是 正确的 并且符合预期(除了一些旧的实施错误);

"5".isString() //returns false

这是因为JavaScript将"wrap""promote"原始值相应的对象类型 before 它调用方法 - this 实际上是一个字符串 object,而不是字符串 value。 (JavaScript 根据原始值有效伪造调用方法。)

将函数替换为:

Object.prototype.isString = function() {
    return this instanceof String;
}

然后:

"5".isString() // => true (a string was "promoted" to a String)
(5).isString() // => false (`this` is a Number)

另一种解决方法也是使用多态性;与修改标准原型相同"pitfalls"1

Object.prototype.isString = function () { return false; }
String.prototype.isString = function () { return true; }

1使用 [=18 可以减轻对全局原型添加新的 enumerable 属性 的担忧=] 默认创建 "not enumerable" 属性。

简单改变

x.prototype.y = ..

Object.defineProperty(x.prototype, 'y', { value: .. })

(我不是在捍卫修改原型的使用;只是解释原来有问题的输出并指出一种防止枚举行为的方法。)

抛开讨论,这不是好的做法,也不是像包装构造函数那样的常见方法,您应该通过询问 构造函数的名称 来实现:

Object.defineProperty(Object.prototype, 'isString', {
  value: function() { return this.constructor.name === "String"; }
});

或使用已经提到的 instanceof 方法:

Object.defineProperty(Object.prototype, 'isString', {
  value: function() { return this instanceof String; }
});

.

中解释了为什么您的方法不起作用

如果您希望新定义的 属性 是可枚举的、可配置的或可写的,您应该查看 docs for defineProperty.

正如其他一些人所指出的,您的代码几乎是正确的 typeof this === 'string' 部分,但由于 JavaScript 在基元与对象方面的古怪行为而无法正常工作.测试对象是否为字符串的最可靠方法之一是使用 Object.prototype.toString.call(this) === '[object String]'(查看 this article)。考虑到这一点,您可以像这样简单地编写 isString 的实现:

Object.prototype.isString = function () {
    return Object.prototype.toString.call(this) === '[object String]';
};

"abc".isString(); // ==> true
"".isString(); // ==> true
1..isString(); // ==> false
{}.isString(); // ==> false    

这是因为字符串字面值是原生字符串类型,实际上不是String对象的实例,所以实际上,您实际上不能从Object或String原型调用任何方法。

发生的事情是,当您尝试对类型为字符串的变量调用任何方法时,Javascript 会自动将该值强制转换为新构造的 String 对象.

所以

"abc".isString();

等同于:

(new String("abc")).isString();

这样做的副作用是您在 isString() 方法中收到的是一个(类型变量)对象,它也是 String 对象的一个​​实例。

也许你可以通过一个简化的例子看得更清楚:

var a = "Hello";
console.log(typeof a); // string

var b = new String("Hello");
console.log(typeof b); // object

顺便说一句,正如许多其他人所说,您必须在函数中检测字符串的最佳机会是检查它是否是具有以下内容的 String 对象的实例:

foo instanceof String

如果您还想检查其他可能的类型,您应该像下面这样进行双重检查:

function someOtherFn(ins) {
    var myType = typeOf ins;
    if (myType == "object" && ins instanceof String) myType = "string";
    // Do more stuf...
}

在我们开始之前,需要记住和注意一些重要的语句(对所有字符串 literal/primitive、字符串对象、数字 literal/primitive、数字对象等都是如此):

  • JavaScript 中的所有对象都是 Object 的后代,并从 Object.prototype 继承方法和属性 – StringNumber 等(很像 Java).
  • JS有6种原始类型——string, number, boolean, nullundefinedsymbol
  • JS 有其相应的包装器对象 – StringNumberBooleanSymbol
  • 正如你在上面看到的,JS 有字符串作为原语,还有一个 Object
  • 原始类型不是 Object
  • 字符串文字是原始字符串,String 对象的类型是 Object
  • instanceof 运算符测试对象的原型链中是否具有构造函数的原型 属性。 (这里获得 TRUE 的第一个条件是 instanceof 应该用于对象或其子类
  • typeof 运算符 return 是一个字符串,指示未计算的操作数的类型。

原始字符串:
String primitive 或 literal 可以通过以下方式构造:

var str1 = “Hello”;
var str2 = String(“Hello”);
typeof str1;                    //Output = "string"
typeof str2;                    //Output = "string"
str1 instanceof (String || Object)      //Output = false because it is a primitive not object
str2 instanceof (String || Object)      //Output = false because it is a primitive not object

字符串作为对象:
可以通过从新对象调用其构造函数来构造字符串对象:

var str3 = new String(“Hello”);
typeof str3;        //Output = "string"
str3 instanceof (String)        //Output = true because it is a String object
str3 instanceof (Object)        //Output = true because it is an Object

以上可能看起来不太明显,但有必要奠定基础。
现在,让我谈谈你的情况。

Object.prototype.isString = function() {
    return typeof this === "string";
}
"5".isString() //returns false
"".isString()  //returns false
var str = "string"
str.isString() //returns false

由于称为 自动装箱的概念,您得到的 o/p 是假的。当您在字符串文字上调用任何方法时,它会被转换为 String 对象。 Read this 来自 MSDN - “Methods for String Literals”,您自己可以确定。

因此,在您的原型中,当您使用 typeof 检查类型时,它永远不会是文字 (typeof == "string"),因为它已经转换为对象。 这就是你得到 false 的原因,如果你检查 typeof 是否有 object 那么你就会得到 true,我将在下面详细讨论:

  • typeof 将提供有关实体类型的信息 - 对象或基元(字符串、数字等)或函数。
  • instanceof 将提供有关它是什么类型的 JS 对象的信息 - 对象或字符串或数字或布尔值或符号

下面说一下提供给你的解决方案。它说要进行 instanceof 检查,这是 100% 正确的,但请注意,在到达您的原型时,它可能是对象类型或函数类型。因此,我在下面提供的解决方案将为您提供相同的图片。

我的建议是有一个通用函数,它会 return 你的实例类型,然后你可以根据它是数字还是字符串等来做任何你想做的事情。 isString很好,但是你必须写 isNumber 等等,所以只有一个函数可以 return 你的实例类型,甚至可以处理函数类型。
以下是我的解决方案:

Object.prototype.getInstanceType = function() {
    console.log(this.valueOf());
    console.log(typeof this);
    if(typeof this == "object"){
        if(this instanceof String){
            return "String";
        } else if(this instanceof Boolean){
            return "Boolean";
        } else if(this instanceof Number){
            return "Number";
        }  else if(this instanceof Symbol){
            return "Symbol";
        } else{
            return "object or array";   //JSON type object (not Object) and array
        }
    } else if(typeof this == "function"){
        return "Function";
    } else{
        //This should never happen, as per my knowledge, glad if folks would like to add...
        return "Nothing at all";
    }
}

输出:

new String("Hello").getInstanceType()               //String
"Hello".getInstanceType()               //String
(5).getInstanceType()               //Number
(true).getInstanceType()            //Boolean
Symbol().getInstanceType()          //Symbol
var ddd = function(){}
var obj = {}
obj.getInstanceType()               //object or array
var arr = []
arr.getInstanceType()               //object or array
ddd.getInstanceType()               //Function
($).getInstanceType()               //Function, because without double quotes, $ will treated as a function
("$").getInstanceType()             //String, since it came in double quotes, it became a String

总结:您的 2 个问题如下

But what I need to be able to do is use that on any value, and access the value in the function.

您可以使用 this 访问函数中的值。在我的解决方案中,您可以看到 console.log(this.valueOf());

Is there any workaround to having this be a property of any Object if I use Object.prototype?

您可以按照上述解决方案从 Object.prototype.getInstanceType 实现它,您可以在任何有效的 JS 对象上调用它,您将获得所需的信息。

希望对您有所帮助!