Подтвердить что ты не робот

Как создать метод, который может повлиять на любой тип значения?

Предупреждение: создание расширений для собственного объекта и/или свойств считается плохой формой и неизбежно вызывает проблемы. Не используйте это, если это для кода, который вы используете не для вас, или если вы не знаете, как правильно его использовать.


Я знаю, что вы можете использовать Object, String, Number, Boolean и т.д. для определения метода, примерно так:

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

Но мне нужно иметь возможность использовать это значение для любого значения и получать доступ к значению в функции.

Я googled и посмотрел здесь, но не нашел ничего подходящего.

2/18/15 Изменить: есть ли какое-либо обходное решение для того, чтобы это было свойством любого объекта, если я использую Object.prototype?
Per Request, вот текущая функция, которая используется для 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
4b9b3361

Ответ 1

Функция "точечных операторов" называется методом. Самый простой способ создать метод в 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;
}

Ответ 2

Прежде всего, почему неопределенные свойства объекта 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

Ответ 3

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

Если кто-нибудь может объяснить обходное решение для функции, являющейся свойством какого-либо объекта, и почему текущий метод не работает, я предоставил 125 rep.

Ответ:

Хорошо в javascript, когда вы вызываете вспомогательный метод/свойство объекта, как "myFunction" (object.myFunction или object [ "MyFunction" ])
он начнет видеть, есть ли у него сам объект.
ЕСЛИ НЕ: он будет следовать цепочке прототипов (например, суперкласс в обычном oop),
пока он не найдет "родительский/суперкласс" с методом /property.
Последним шагом в этой цепочке прототипов является Object.
Если Object dosnt имеет метод, он вернет "undefined".

Когда вы расширяете класс Object, он всегда будет смотреть на любой объект, вызывающий метод как объект (В 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 (NotNumber) - это номер!!! Это означает, что вам всегда нужно проверить, является ли переменная null, перед вызовом этого метода!

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

Заключительный скрипт: http://jsfiddle.net/xwshjk4x/5/

Трюк заключается в том, что эти методы возвращают результат метода toString, который вызывается с помощью "this", что означает, что в контексте метода toString объект, на который вы его вызываете, является его собственным классом (не просто любой супертип в цепочке прототипов)

Ответ 4

Выведенный код, который расширяет прототип объекта, будет работать, если исправлено.

Однако он делает неправильное предположение о том, что this находится внутри вызываемого метода. С опубликованным кодом следующий вывод является правильным и ожидаемым (запрет нескольких старых ошибок реализации);

"5".isString() //returns false

Это связано с тем, что JavaScript будет "обернуть" или "продвинуть" примитивное значение соответствующему типу объекта до того, как он вызовет метод - 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)

Другим решением этого является также использование полиморфизма; с теми же "подводными камнями" 1 для изменения стандартных прототипов.

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

1 Проблема добавления нового перечислимого свойства к глобальным протоидам может быть смягчена с помощью defineProperty который по умолчанию создает свойство "неперечислимое".

Просто измените

x.prototype.y = ..

к

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

(Я не защищаю использование модификации прототипа, просто объясняя исходный проблемный вывод и указывая способ предотвращения поведения перечисления.)

Ответ 5

Прежде чем начать, несколько важных утверждений, которые нужно помнить и знать (истинно для всех строковых литералов/примитивов, объекта String, номера литерала/примитива, объекта Number и т.д.):

  • Все объекты в JavaScript происходят от Object и наследуют методы и свойства от Object.prototype - String, Number и т.д. (подобно Java).
  • JS имеет 6 примитивных типов: string, number, boolean, null, undefined и symbol
  • JS имеет соответствующие объекты-оболочки - String, Number, Boolean и Symbol
  • Как вы можете видеть выше, JS имеет строку как примитив, а также Object
  • Примитив не относится к типу Object.
  • Строковый литерал является примитивным, а объект String имеет тип Object.
  • Оператор instanceof проверяет, имеет ли объект в своей прототипной цепочке свойство prototype конструктора. (первым условием получения TRUE здесь является то, что instanceof следует использовать против объекта или его подкласса)
  • Оператор typeof возвращает строку, указывающую тип неориентированного операнда.

Строка как примитивная:
Строковый примитив или литерал можно построить следующим образом:

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 можно построить, вызвав его конструктор из нового объекта:

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

Вы получаете FALSE как o/p из-за концепции, называемой Авто-бокс. Когда вы вызываете какой-либо метод в строковом литерале, он преобразуется в объект String. Прочитайте это из MSDN - "Методы для струнных литералов", чтобы убедиться в себе.

Итак, в вашем прототипе, когда вы будете проверять тип с помощью typeof, он никогда не будет литералом (typeof == "string"), потому что он уже преобразован в объект.. Это причина, по которой вы были получив false, если вы проверите typeof для Object, тогда вы получите правду, о чем я расскажу подробнее ниже:

  • typeof предоставит информацию о том, какой тип сущности это - объект или примитив (строка, номер и т.д.) или функция.
  • instanceof предоставит информацию о том, какой тип объекта JS - Object или String или Number или Boolean или Symbol

Теперь позвольте мне поговорить о решении, которое вам предоставляется. Он говорит, что делает проверку instanceof, которая на 100% правильна, но с примечанием о том, что, достигнув своего прототипа, он может быть типа объекта или типа функции. Итак, решение, которое я предоставляю ниже, даст вам изображение того же самого.

Моя рекомендация состоит в том, чтобы иметь общую функцию, которая вернет вам тип экземпляра, а затем вы можете делать все, что хотите, на основе, если это Number или String и т.д. 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

Завершить: Ваши проблемы, как показано ниже

Но то, что мне нужно сделать, это использовать это для любого значения и доступ значение в функции.

Вы можете получить доступ к значению в своей функции, используя this. В моем решении вы можете увидеть console.log(this.valueOf());

Есть ли какое-либо обходное решение для того, чтобы это было свойством любого объекта, если Я использую Object.prototype?

Вы можете достичь этого из Object.prototype.getInstanceType в соответствии с приведенным выше решением, и вы можете вызвать его на любом действительном объекте JS, и вы получите требуемую информацию.

Надеюсь, это поможет!

Ответ 6

Чтобы показать u пример:

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

эта функция добавит "asd" к каждой строке, когда вызывается myFunction().

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

Ответ 7

Из MDN Описание Object:

Все объекты в JavaScript происходят от 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

Ответ 8

Обсуждая в сторону, что это не хорошая практика, а не общий подход, такой как конструктор-обертка, вы должны достичь этого, запросив имя конструктора :

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 для defineProperty.

Ответ 9

Как несколько других указали, что ваш код почти корректен для части typeof this === 'string', которая не работает из-за необычного поведения JavaScript, когда дело касается примитивов и объектов. Один из самых надежных способов проверить, является ли объект строкой, с помощью Object.prototype.toString.call(this) === '[object String]' (проверьте в этой статье). Имея это в виду, вы можете просто написать свою реализацию isString следующим образом:

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

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

Ответ 10

Это потому, что строковые литералы имеют встроенную строку type, а не экземпляр объекта 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...
}