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

时间:2015-02-18 01:26:21

标签: javascript object methods prototype

警告:创建对本机对象和/或属性的扩展被视为错误形式,并且必然会导致问题。如果您的代码不是仅供您使用,或者您不知道如何正确使用它,请不要使用此


我知道您可以使用ObjectStringNumberBoolean等来定义方法,如下所示:

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

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

我用Google搜索,看了here,但找不到合适的东西。

2015年2月18日编辑:如果我使用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

10 个答案:

答案 0 :(得分:14)

“点运算符函数”称为方法。在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. 保持全局范围和原生原型清洁。
  4. underscorelodash这样的流行JavaScript库为此目的创建了包装构造函数。

答案 1 :(得分:9)

首先,为什么定义Object(或其他内置类型)的属性是不受欢迎的 - 它们出现在意想不到的地方。以下是一些输出一些字符所具有的总英尺数的代码:

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"一个数字和另一个人可能会说它不是 - 当人们使用多个图书馆时,整个地方就会出现一些漏洞。

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

更有意义的是有一个函数/方法可以返回作为参数赋予它的值的类型:

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)是不同的概念。

您可以通过返回原始值的类型来改变您的功能。另外,不要忘记使其不可枚举,以便在迭代随机对象时不显示。

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

答案 2 :(得分:5)

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

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

<强>答案:

在javascript中,当你调用一个对象的子方法/属性时,如“myFunction”(object.myFunction或object [“MyFunction”])
它将开始查看对象本身是否具有它 如果不是:它将遵循原型链(如正常oop中的超类),
直到找到带有方法/属性的“父/超类” 这个原型链的最后一步是Object 如果Object dosnt有方法,它将返回“undefined”。

当您扩展Object类本身时,它总是会看 任何将该方法作为Object调用的对象(在oop中:所有类也是Object,另外还有自己的classtype) 这就像在正常的OOP中错过了“演员”一样
所以你的函数返回false的原因是在这种情况下它的“对象”不是“字符串”
尝试使用此功能:

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

正如大家所说,扩展任何原生JS 类非常糟糕,但解决方法将从以下内容开始:

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

这是一个小提琴: http://jsfiddle.net/fwLpty10/13/

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

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

最后的小提琴:http://jsfiddle.net/xwshjk4x/5/

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

答案 3 :(得分:5)

发布的代码扩展了对象原型工作,如果更正。

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

"5".isString() //returns false

这是因为JavaScript将&#34; wrap&#34; &#34;将原始值提升为相应的对象类型之前它调用方法 - this实际上是一个String 对象,而不是字符串。 (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)

另一种解决方案也是使用多态性;修改标准原型的相同&#34;陷阱&#34; 1

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

1 使用defineProperty可以减轻向全局原型添加新的可枚举属性的顾虑,这会创建一个&#34;不可枚举&# 34;属性默认。

只需更改

x.prototype.y = ..

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

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

答案 4 :(得分:4)

向你展示一些例子:

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

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

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

答案 5 :(得分:4)

在我们开始之前,很少有重要的语句要记住并注意(对于所有字符串文字/原语,String对象,数字文字/原语,数字对象等都是如此):

  • JavaScript中的所有对象都来自Object,并继承Object.prototype中的方法和属性 - StringNumber等(非常类似于Java)。
  • JS有6种原始类型 - string number boolean null undefined < / em>和符号
  • JS有相应的包装对象 - StringNumberBooleanSymbol
  • 如上所示,JS将字符串作为基元以及Object
  • 原语不属于Object类型。
  • 字符串文字是基元,String对象的类型为Object
  • instanceof运算符测试对象在其原型链中是否具有构造函数的prototype属性。 (这里获得TRUE的第一个条件是instanceof应该用于对象或其子类
  • typeof运算符返回一个字符串,指示未评估的操作数的类型。

String as primitive:
字符串原语或文字可以通过以下方式构建:

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

String as Object:
可以通过从新对象调用其构造函数来构造String对象:

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会变为FALSE。当您在字符串文字上调用任何方法时,它将转换为String对象。 Read this来自MSDN - “字符串文字的方法”,以确保自己。

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

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

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

我的建议是拥有一个通用函数,它会返回实例的类型,然后你可以做任何你想做的事情,如果它是一个数字或字符串等。isString是好的但是你有写isNumber等,所以改为只有一个函数可以返回实例的类型,甚至可以处理函数类型。
以下是我的解决方案:

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个问题如下

  

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

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

  

是否有任何解决方法让这是任何Object的属性if   我使用Object.prototype?

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

希望这有帮助!

答案 6 :(得分:2)

来自MDN Description of Object

  

JavaScript中的所有对象都来自Object

因此,您可以向Object.prototype添加方法,然后可以在任何上调用这些方法。例如:

&#13;
&#13;
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
&#13;
&#13;
&#13;

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

希望有所帮助,祝你好运:)。

- 编辑 -

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

答案 7 :(得分:2)

除了讨论之外,这不是一个好的做法而不是像包装构造函数这样的常用方法,你应该通过要求构造函数的名称来实现这一点:

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

或者已经提到的 instanceof 方法:

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

解释为什么你的方法不起作用是在this post中处理。

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

答案 8 :(得分:2)

正如其他一些人所指出的那样,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    

答案 9 :(得分:2)

这是因为字符串文字是本机字符串类型,实际上并不是String对象的实例,因此实际上,您无法实际调用Object或String原型中的任何方法。

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

所以

"abc".isString();

与:

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

这样做的副作用是你在isString()方法中接收的是一个(类型的变量)Object,它也是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...
}