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

Tra*_*vis 25 javascript methods prototype object

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


我知道你可以使用Object,String,Number,Boolean,等来定义一个方法,是这样的:

String.prototype.myFunction = function(){return this;} //only works on strings.
Run Code Online (Sandbox Code Playgroud)

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

我一派,看着这里,也没有找到什么合适.

2/18/15编辑:如果我使用这个是任何对象的属性,有没有解决方法Object.prototype
Per Request,这是当前用于的函数isString()

function isString(ins) {
    return typeof ins === "string";
}
Run Code Online (Sandbox Code Playgroud)

在几个答案之后,我想出了一些由它引起的代码和错误.

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

Aad*_*hah 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;
}
Run Code Online (Sandbox Code Playgroud)

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

  1. 您的代码不会与其他人的代码混淆.
  2. 其他人的代码不会弄乱您的代码.
  3. 您可以保持全局范围和原生原型的清洁.

几个流行的JavaScript库,如下划线lodash为此目的创建包装器构造函数.


Tib*_*bos 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));
Run Code Online (Sandbox Code Playgroud)

请注意,如何简单地定义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
Run Code Online (Sandbox Code Playgroud)

你当前代码的原因

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

无法工作是因为当您访问原始值的属性时,JS会创建适当类型的包装器对象,并在该包装器对象上解析该属性(之后它会将其抛出).这是一个应该阐明它的例子:

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

console.log((5).getWrapper()); // Number [[PrimitiveValue]]:5
console.log("42".getWrapper()); // String [[PrimitiveValue]]:"42"
Run Code Online (Sandbox Code Playgroud)

注意,原始值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
Run Code Online (Sandbox Code Playgroud)


Alf*_*sen 5

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

如果有人可以解释函数是任何对象的属性的解决方法,以及当前方法无法工作的原因,我将提供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"
Run Code Online (Sandbox Code Playgroud)

正如大家所说,扩展任何本机JS类是非常糟糕的,但解决方法将从这样的事情开始:

Object.prototype.objectTypeString = function(){
   return Object.prototype.toString.call(this);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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


use*_*740 5

如果更正,那么扩展Object原型的代码起作用.

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

"5".isString() //returns false
Run Code Online (Sandbox Code Playgroud)

这是因为JavaScript 调用方法之前将原始值"包装""提升"到相应的对象类型- 实际上是一个String 对象,而不是字符串.(JavaScript有效地伪造了对原始值的调用方法.)this

将功能替换为:

Object.prototype.isString = function() {
    return this instanceof String;
}
Run Code Online (Sandbox Code Playgroud)

然后:

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

对此的另一个解决方案也是使用多态性; 与修改标准原型相同的"陷阱" 1.

Object.prototype.isString = function () { return false; }
String.prototype.isString = function () { return true; }
Run Code Online (Sandbox Code Playgroud)

1使用defineProperty可以减轻向全局原型添加新的枚举属性的问题,默认情况下会创建"不可枚举"属性.

简单地改变

x.prototype.y = ..
Run Code Online (Sandbox Code Playgroud)

Object.defineProperty(x.prototype, 'y', { value: .. })
Run Code Online (Sandbox Code Playgroud)

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