nodejs node multithread cluster javascript prototypal-inheritance

javascript - cluster - nodejs multithread



Ampliación de tipos de núcleo sin modificar prototipo. (5)

¿Cómo se pueden extender los tipos de JavaScript principales (Cadena, Fecha, etc.) sin modificar sus prototipos? Por ejemplo, supongamos que quisiera crear una clase de cadena derivada con algunos métodos prácticos:

function MyString() { } MyString.prototype = new String(); MyString.prototype.reverse = function() { return this.split('''').reverse().join(''''); }; var s = new MyString("Foobar"); // Hmm, where do we use the argument? s.reverse(); // Chrome - TypeError: String.prototype.toString is not generic // Firefox - TypeError: String.prototype.toString called on incompatible Object

El error parece originarse en los métodos de base de cadena, probablemente "divididos" en este caso, ya que sus métodos se aplican a algún objeto que no es de cadena. Pero si no podemos aplicar los objetos sin cadena, ¿podemos realmente reutilizarlos automáticamente?

[Editar]

Obviamente, mi intento es defectuoso en muchos aspectos, pero creo que demuestra mi intención. Después de pensar un poco, parece que no podemos reutilizar ninguna de las funciones del objeto prototipo String sin llamarlos explícitamente en un String.

¿Es posible extender los tipos de núcleo como tales?


Creo que la respuesta básica es que probablemente no puedas. Lo que puedes hacer es lo que hace Sugar.js: crea un objeto similar a un objeto y extiéndelo a partir de eso:

http://sugarjs.com/

Sugar.js tiene que ver con las extensiones de objetos nativos, y no extienden Object.prototype.


En primer lugar, en este código:

MyString.prototype = String.prototype; MyString.prototype.reverse = function() { this.split('''').reverse().join(''''); };

Las variables MyString.prototype y String.prototype están haciendo referencia al mismo objeto. Asignar a uno es asignar a otro. Cuando soltó un método reverse en MyString.prototype también lo estaba escribiendo en String.prototype . Así que prueba esto:

MyString.prototype = String.prototype; MyString.prototype.charAt = function () {alert("Haha");} var s = new MyString(); s.charAt(4); "dog".charAt(3);

Las dos últimas líneas ambas alertan porque sus prototipos son el mismo objeto. Realmente String.prototype .

Ahora sobre tu error. MyString reverse en tu objeto MyString . ¿Dónde se define este método? En el prototipo, que es lo mismo que String.prototype . Usted sobrescribió el reverse . ¿Qué es lo primero que hace? Se llama a split en el objeto de destino. Ahora la cosa es que, para que String.prototype.split funcione, tiene que llamar String.prototype.toString . Por ejemplo:

var s = new MyString(); if (s.split("")) {alert("Hi");}

Este código genera un error:

TypeError: String.prototype.toString is not generic

Lo que esto significa es que String.prototype.toString usa la representación interna de una cadena para hacer lo suyo (es decir, devolviendo su cadena primitiva interna), y no se puede aplicar a objetos de destino arbitrarios que comparten el prototipo de cadena . Entonces, cuando llamó a split , la implementación de split dijo "oh, mi objetivo no es una cadena, déjeme llamar a toString ", pero luego toString dijo "mi objetivo no es una cadena y no soy genérico", así que lanzó el TypeError .

Si desea obtener más información sobre los genéricos en JavaScript, puede ver esta sección de MDN en Genéricos de matrices y cadenas .

En cuanto a que esto funcione sin el error, vea la respuesta de Alxandr.

En cuanto a la extensión de los tipos incorporados exactos como String y Date etc. sin cambiar sus prototipos, realmente no lo hace, sin crear envolturas, delegados o subclases. Pero entonces esto no permitirá la sintaxis como

d1.itervalTo(d2)

donde d1 y d2 son instancias de la clase Date incorporada cuyo prototipo no extendió . :-) JavaScript usa cadenas de prototipos para este tipo de sintaxis de llamadas a métodos. Simplemente lo hace. Excelente pregunta, sin embargo ... pero, ¿es esto lo que tenías en mente?


Solo tienes una parte equivocada aquí. MyString.prototype no debería ser String.prototype, debería ser así:

function MyString(s) { } MyString.prototype = new String(); MyString.prototype.reverse = function() { this.split('''').reverse().join(''''); }; var s = new MyString("Foobar"); s.reverse();

[Editar]

Para responder mejor a tu pregunta, no, no debería ser posible. Si echa un vistazo a esto: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/constructor explica que no puede cambiar el tipo en bools, ints y cadenas, por lo que no pueden ser "subclasificado".


2 años después: mutar cualquier cosa en el ámbito global es una idea terrible.

Original:

Al estar algo "mal" con la extensión de los prototipos nativos, está el FUD en los navegadores ES5.

Object.defineProperty(String.prototype, "my_method", { value: function _my_method() { ... }, configurable: true, enumerable: false, writeable: true });

Sin embargo, si tiene que admitir navegadores ES3, entonces hay problemas con las personas que usan for ... in loops en cadenas.

Mi opinión es que puede cambiar los prototipos nativos y debería dejar de usar cualquier código mal escrito que se rompa


Actualización: incluso este código no extiende completamente el tipo de String nativo (la propiedad de length no funciona).

Imo probablemente no vale la pena seguir este enfoque. Hay demasiadas cosas que considerar y tiene que invertir demasiado tiempo para garantizar que funcione completamente ( si es que lo hace). @Raynos proporciona otro enfoque interesante .

Sin embargo aquí está la idea:

Parece que no puede llamar String.prototype.toString en otra cosa que no sea una cadena real. Podrías anular este método:

// constructor function MyString(s) { String.call(this, s); // call the "parent" constructor this.s_ = s; } // create a new empty prototype to *not* override the original one tmp = function(){}; tmp.prototype = String.prototype; MyString.prototype = new tmp(); MyString.prototype.constructor = MyString; // new method MyString.prototype.reverse = function() { return this.split('''').reverse().join(''''); }; // override MyString.prototype.toString = function() { return this.s_; }; MyString.prototype.valueOf = function() { return this.s_; }; var s = new MyString("Foobar"); alert(s.reverse());

Como ve, también tuve que anular valueOf para que funcione.

Pero: no sé si estos son los únicos métodos que tiene que anular y para otros tipos incorporados es posible que tenga que anular otros métodos. Un buen comienzo sería tomar la especificación ECMAScript y echar un vistazo a la especificación de los métodos.

Por ejemplo, el segundo paso en el algoritmo String.prototype.split es:

Sea S el resultado de llamar a ToString, dándole el valor de este como su argumento.

Si un objeto se pasa a ToString , entonces básicamente llama al método toString de este objeto. Y es por eso que funciona cuando anulamos toString .

Actualización: Lo que no funciona es de s.length . Entonces, aunque podría hacer que los métodos funcionen, otras propiedades parecen ser más difíciles.