ejemplos - ¿Cómo "correctamente" crear un objeto personalizado en JavaScript?
javascript html (15)
Me pregunto cuál es la mejor manera de crear un objeto de JavaScript que tenga propiedades y métodos.
He visto ejemplos en los que la persona usó var self = this
y luego usa self.
en todas las funciones para asegurarse de que el alcance es siempre correcto.
Luego he visto ejemplos de uso de .prototype
para agregar propiedades, mientras que otros lo hacen en línea.
¿Puede alguien darme un ejemplo adecuado de un objeto JavaScript con algunas propiedades y métodos?
Creando un objeto
La forma más fácil de crear un objeto en JavaScript es usar la siguiente sintaxis:
var test = {
a : 5,
b : 10,
f : function(c) {
return this.a + this.b + c;
}
}
console.log(test);
console.log(test.f(3));
Esto funciona muy bien para almacenar datos de una manera estructurada.
Sin embargo, para casos de uso más complejos, a menudo es mejor crear instancias de funciones:
function Test(a, b) {
this.a = a;
this.b = b;
this.f = function(c) {
return this.a + this.b + c;
};
}
var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));
Esto le permite crear múltiples objetos que comparten el mismo "plano", similar a cómo usa las clases en, por ejemplo. Java.
Sin embargo, esto puede hacerse de manera más eficiente mediante el uso de un prototipo.
Cuando diferentes instancias de una función comparten los mismos métodos o propiedades, puede moverlos al prototipo de ese objeto. De esa manera, cada instancia de una función tiene acceso a ese método o propiedad, pero no necesita ser duplicada para cada instancia.
En nuestro caso, tiene sentido mover el método f
al prototipo:
function Test(a, b) {
this.a = a;
this.b = b;
}
Test.prototype.f = function(c) {
return this.a + this.b + c;
};
var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));
Herencia
Una forma simple pero efectiva de hacer herencia en JavaScript, es usar las siguientes dos líneas:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
Eso es similar a hacer esto:
B.prototype = new A();
La principal diferencia entre ambos es que el constructor de A
no se ejecuta cuando se utiliza Object.create
, que es más intuitivo y más similar a la herencia basada en clases.
Siempre puede optar por ejecutar opcionalmente el constructor de A
al crear una nueva instancia de B
añadiéndolo al constructor de B
:
function B(arg1, arg2) {
A(arg1, arg2); // This is optional
}
Si desea pasar todos los argumentos de B
a A
, también puede usar Function.prototype.apply()
:
function B() {
A.apply(this, arguments); // This is optional
}
Si desea mezclar otro objeto en la cadena de constructores de B
, puede combinar Object.create
con Object.assign
:
B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;
Manifestación
function A(name) {
this.name = name;
}
A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;
function B() {
A.apply(this, arguments);
this.street = "Downing Street 10";
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
function mixin() {
}
mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;
mixin.prototype.getProperties = function() {
return {
name: this.name,
address: this.street,
year: this.year
};
};
function C() {
B.apply(this, arguments);
this.year = "2018"
}
C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;
var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());
Nota
Object.create
puede usar de forma segura en todos los navegadores modernos, incluido IE9 +. Object.assign
no funciona en ninguna versión de IE ni en algunos navegadores móviles. Se recomienda hacer polyfill Object.create
y / o Object.assign
si desea usarlos y admitir navegadores que no los implementen.
Puede encontrar un polyfill para Object.create
Object.create y uno para Object.assign
Object.assign .
Además de la respuesta aceptada de 2009. Si puede dirigirse a navegadores modernos, puede hacer uso de Object.defineProperty .
El método Object.defineProperty () define una nueva propiedad directamente en un objeto, o modifica una propiedad existente en un objeto, y devuelve el objeto. Fuente: Mozilla
var Foo = (function () {
function Foo() {
this._bar = false;
}
Object.defineProperty(Foo.prototype, "bar", {
get: function () {
return this._bar;
},
set: function (theBar) {
this._bar = theBar;
},
enumerable: true,
configurable: true
});
Foo.prototype.toTest = function () {
alert("my value is " + this.bar);
};
return Foo;
}());
// test instance
var test = new Foo();
test.bar = true;
test.toTest();
Para ver una lista de compatibilidad de equipos de escritorio y dispositivos móviles, consulte Mozilla Sí, IE9 + lo admite, así como Safari móvil.
Continuar fuera de la respuesta de Bobince.
En es6 puedes crear una class
Así que ahora puedes hacer:
class Shape {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `Shape at ${this.x}, ${this.y}`;
}
}
Así que extiende a un círculo (como en la otra respuesta) que puedes hacer:
class Circle extends Shape {
constructor(x, y, r) {
super(x, y);
this.r = r;
}
toString() {
let shapeString = super.toString();
return `Circular ${shapeString} with radius ${this.r}`;
}
}
Termina un poco más limpio en es6 y un poco más fácil de leer.
Aquí hay un buen ejemplo de ello en acción:
class Shape {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `Shape at ${this.x}, ${this.y}`;
}
}
class Circle extends Shape {
constructor(x, y, r) {
super(x, y);
this.r = r;
}
toString() {
let shapeString = super.toString();
return `Circular ${shapeString} with radius ${this.r}`;
}
}
let c = new Circle(1, 2, 4);
console.log('''' + c, c);
Cuando uno usa el truco de cerrar "esto" durante una invocación del constructor, es para escribir una función que pueda ser utilizada como devolución de llamada por otro objeto que no quiera invocar un método en un objeto. No está relacionado con "hacer correcto el alcance".
Aquí hay un objeto JavaScript de vainilla:
function MyThing(aParam) {
var myPrivateVariable = "squizzitch";
this.someProperty = aParam;
this.useMeAsACallback = function() {
console.log("Look, I have access to " + myPrivateVariable + "!");
}
}
// Every MyThing will get this method for free:
MyThing.prototype.someMethod = function() {
console.log(this.someProperty);
};
Es posible que tenga mucho que leer lo que Douglas Crockford tiene que decir sobre JavaScript. John Resig también es brillante. ¡Buena suerte!
Hay dos modelos para implementar clases e instancias en JavaScript: la forma de creación de prototipos y la forma de cierre. Ambos tienen ventajas e inconvenientes, y hay muchas variaciones extendidas. Muchos programadores y bibliotecas tienen diferentes enfoques y funciones de utilidad de manejo de clase para examinar algunas de las partes más feas del lenguaje.
El resultado es que en una empresa mixta tendrá una mezcla de metaclases, todas con un comportamiento ligeramente diferente. Lo que es peor, la mayoría del material tutorial de JavaScript es terrible y ofrece algún tipo de compromiso intermedio para cubrir todas las bases, lo que te deja muy confundido. (Probablemente el autor también esté confundido. El modelo de objetos de JavaScript es muy diferente a la mayoría de los lenguajes de programación, y en muchos lugares está mal diseñado).
Empecemos por el prototipo . Es lo más nativo de JavaScript que puede obtener: hay un mínimo de código de sobrecarga y instanceof funcionará con instancias de este tipo de objeto.
function Shape(x, y) {
this.x= x;
this.y= y;
}
Podemos agregar métodos a la instancia creada por la new Shape
escribiéndolos en la búsqueda de prototype
de esta función constructora:
Shape.prototype.toString= function() {
return ''Shape at ''+this.x+'', ''+this.y;
};
Ahora, para subclasificarlo, en la medida en que pueda llamar a lo que JavaScript hace subclases. Lo hacemos reemplazando completamente esa extraña propiedad de prototype
mágico:
function Circle(x, y, r) {
Shape.call(this, x, y); // invoke the base class''s constructor function to take co-ords
this.r= r;
}
Circle.prototype= new Shape();
Antes de agregarle métodos:
Circle.prototype.toString= function() {
return ''Circular ''+Shape.prototype.toString.call(this)+'' with radius ''+this.r;
}
Este ejemplo funcionará y verás un código similar en muchos tutoriales. Pero hombre, esa new Shape()
es fea: estamos creando una instancia de la clase base aunque no se cree una Forma real. Sucede que funciona en este caso simple porque JavaScript es tan descuidado: no permite pasar ningún argumento, en cuyo caso x
e y
convierten en undefined
y se asignan a los prototipos " this.x
y " this.y
. Si la función del constructor hiciera algo más complicado, caería de bruces.
Entonces, lo que debemos hacer es encontrar una manera de crear un objeto prototipo que contenga los métodos y otros miembros que deseamos a nivel de clase, sin llamar a la función constructora de la clase base. Para hacer esto tendremos que empezar a escribir el código de ayuda. Este es el enfoque más simple que conozco:
function subclassOf(base) {
_subclassOf.prototype= base.prototype;
return new _subclassOf();
}
function _subclassOf() {};
Esto transfiere los miembros de la clase base en su prototipo a una nueva función de constructor que no hace nada, luego usa ese constructor. Ahora podemos escribir simplemente:
function Circle(x, y, r) {
Shape.call(this, x, y);
this.r= r;
}
Circle.prototype= subclassOf(Shape);
En lugar del new Shape()
error de new Shape()
. Ahora tenemos un conjunto aceptable de primitivas para las clases construidas.
Hay algunos refinamientos y extensiones que podemos considerar en este modelo. Por ejemplo, aquí hay una versión de azúcar sintáctica:
Function.prototype.subclass= function(base) {
var c= Function.prototype.subclass.nonconstructor;
c.prototype= base.prototype;
this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};
...
function Circle(x, y, r) {
Shape.call(this, x, y);
this.r= r;
}
Circle.subclass(Shape);
Cualquiera de las versiones tiene el inconveniente de que la función constructora no se puede heredar, ya que está en muchos idiomas. Entonces, incluso si su subclase no agrega nada al proceso de construcción, debe recordar llamar al constructor base con los argumentos que la base deseaba. Esto puede ser ligeramente automatizado usando apply
, pero aún así tienes que escribir:
function Point() {
Shape.apply(this, arguments);
}
Point.subclass(Shape);
Por lo tanto, una extensión común es dividir las cosas de inicialización en su propia función en lugar del propio constructor. Esta función puede heredar de la base simplemente bien:
function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
this.x= x;
this.y= y;
};
function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!
Ahora tenemos la misma función constructora repetitiva para cada clase. Tal vez podamos moverlo a su propia función auxiliar, de modo que no tengamos que seguir teclándolo, por ejemplo, en lugar de Function.prototype.subclass
, dándole la vuelta y dejando que la función de la clase base escupa las subclases:
Function.prototype.makeSubclass= function() {
function Class() {
if (''_init'' in this)
this._init.apply(this, arguments);
}
Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};
...
Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
this.x= x;
this.y= y;
};
Point= Shape.makeSubclass();
Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
Shape.prototype._init.call(this, x, y);
this.r= r;
};
... que está empezando a parecerse un poco más a otros idiomas, aunque con una sintaxis un poco torpe. Usted puede esparcir en algunas características adicionales si lo desea. Tal vez quiera que makeSubclass
tome y recuerde un nombre de clase y proporcione un toString
predeterminado al usarlo. Tal vez quiera hacer que el constructor detecte cuando se ha llamado accidentalmente sin el new
operador (que de lo contrario a menudo resultaría en una depuración muy molesta):
Function.prototype.makeSubclass= function() {
function Class() {
if (!(this instanceof Class))
throw(''Constructor called without "new"'');
...
Tal vez quiera pasar a todos los nuevos miembros y hacer que makeSubclass
agregue al prototipo, para evitar que tenga que escribir el Class.prototype...
tanto. Muchos sistemas de clase hacen eso, por ejemplo:
Circle= Shape.makeSubclass({
_init: function(x, y, z) {
Shape.prototype._init.call(this, x, y);
this.r= r;
},
...
});
Hay muchas características potenciales que podría considerar deseables en un sistema de objetos y nadie realmente está de acuerdo con una fórmula en particular.
La forma de cierre , entonces. Esto evita los problemas de la herencia basada en el prototipo de JavaScript, al no utilizar la herencia en absoluto. En lugar:
function Shape(x, y) {
var that= this;
this.x= x;
this.y= y;
this.toString= function() {
return ''Shape at ''+that.x+'', ''+that.y;
};
}
function Circle(x, y, r) {
var that= this;
Shape.call(this, x, y);
this.r= r;
var _baseToString= this.toString;
this.toString= function() {
return ''Circular ''+_baseToString(that)+'' with radius ''+that.r;
};
};
var mycircle= new Circle();
Ahora, cada instancia de Shape
tendrá su propia copia del método toString
(y cualquier otro método u otro miembro de la clase que agreguemos).
Lo malo de que cada instancia tenga su propia copia de cada miembro de la clase es que es menos eficiente. Si está tratando con un gran número de instancias subclasificadas, la herencia prototípica puede servirle mejor. También es un poco molesto llamar a un método de la clase base, como puede ver: tenemos que recordar qué era el método antes de que el constructor de la subclase lo sobrescribiera o se pierda.
[También porque no hay herencia aquí, el operador instanceof
no funcionará; Tendría que proporcionar su propio mecanismo para rastrear las clases si lo necesita. Si bien puedes manipular los objetos prototipo de forma similar a la herencia de prototipo, es un poco complicado y realmente no vale la pena solo para ponerte en práctica.]
Lo bueno de que cada instancia tenga su propio método es que el método puede estar vinculado a la instancia específica que lo posee. Esto es útil debido a la extraña forma en que JavaScript vincula this
en las llamadas de método, que tiene el resultado de que si separa un método de su propietario:
var ts= mycircle.toString;
alert(ts());
entonces this
dentro del método no será la instancia de Circle como se esperaba (en realidad será el objeto de la window
global, lo que causará un error de depuración generalizado). En realidad, esto suele suceder cuando se toma un método y se asigna a un setTimeout
, onclick
o EventListener
en general.
Con la forma de prototipo, debe incluir un cierre para cada una de estas tareas:
setTimeout(function() {
mycircle.move(1, 1);
}, 1000);
o, en el futuro (o ahora si hackeas Function.prototype) también puedes hacerlo con function.bind()
:
setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);
Si sus instancias se realizan de forma cerrada, la vinculación se realiza de forma gratuita mediante la clausura sobre la variable de instancia (generalmente llamada that
o self
, aunque personalmente desaconsejo a este último, ya que self
ya tiene otro significado diferente en JavaScript). Sin embargo, no obtiene los argumentos 1, 1
en el fragmento anterior de forma gratuita, por lo que aún necesitará otro cierre o un bind()
si necesita hacerlo.
También hay muchas variantes en el método de cierre. Puede preferir omitir this
completamente, creando un nuevo y devolviéndolo en lugar de usar el new
operador:
function Shape(x, y) {
var that= {};
that.x= x;
that.y= y;
that.toString= function() {
return ''Shape at ''+that.x+'', ''+that.y;
};
return that;
}
function Circle(x, y, r) {
var that= Shape(x, y);
that.r= r;
var _baseToString= that.toString;
that.toString= function() {
return ''Circular ''+_baseToString(that)+'' with radius ''+r;
};
return that;
};
var mycircle= Circle(); // you can include `new` if you want but it won''t do anything
¿En qué sentido es “correcto”? Ambos. Cuál es el mejor"? Eso depende de tu situación. FWIW Tiendo a crear prototipos para la herencia de JavaScript real cuando estoy haciendo cosas OO con fuerza, y cierres para efectos de página simples y desechables.
Pero ambas formas son bastante contraintuitivas para la mayoría de los programadores. Ambos tienen muchas posibles variaciones desordenadas. Se reunirá con ambos (así como con muchos esquemas intermedios y generalmente rotos) si utiliza los códigos / bibliotecas de otras personas. No hay una respuesta generalmente aceptada. Bienvenido al maravilloso mundo de los objetos de JavaScript.
[Esta ha sido la parte 94 de Por qué JavaScript no es mi lenguaje de programación favorito.]
Me gustaría mencionar que podemos usar un Título o una Cadena para declarar un Objeto.
Hay diferentes maneras de llamar a cada tipo de ellos. Vea abajo:
var test = {
useTitle : "Here we use ''a Title'' to declare an Object",
''useString'': "Here we use ''a String'' to declare an Object",
onTitle : function() {
return this.useTitle;
},
onString : function(type) {
return this[type];
}
}
console.log(test.onTitle());
console.log(test.onString(''useString''));
Otra forma sería http://jsfiddle.net/nnUY4/ (no sé si este tipo de manejo de creación de objetos y funciones reveladoras siguen algún patrón específico)
// Build-Reveal
var person={
create:function(_name){ // ''constructor''
// prevents direct instantiation
// but no inheritance
return (function() {
var name=_name||"defaultname"; // private variable
// [some private functions]
function getName(){
return name;
}
function setName(_name){
name=_name;
}
return { // revealed functions
getName:getName,
setName:setName
}
})();
}
}
// … no (instantiated) person so far …
var p=person.create(); // name will be set to ''defaultname''
p.setName("adam"); // and overwritten
var p2=person.create("eva"); // or provide ''constructor parameters''
alert(p.getName()+":"+p2.getName()); // alerts "adam:eva"
También puedes hacerlo de esta manera, utilizando estructuras:
function createCounter () {
var count = 0;
return {
increaseBy: function(nb) {
count += nb;
},
reset: function {
count = 0;
}
}
}
Entonces :
var counter1 = createCounter();
counter1.increaseBy(4);
Utilizo este patrón con bastante frecuencia: he descubierto que me proporciona una gran cantidad de flexibilidad cuando lo necesito. En uso es bastante similar a las clases de estilo Java.
var Foo = function()
{
var privateStaticMethod = function() {};
var privateStaticVariable = "foo";
var constructor = function Foo(foo, bar)
{
var privateMethod = function() {};
this.publicMethod = function() {};
};
constructor.publicStaticMethod = function() {};
return constructor;
}();
Esto utiliza una función anónima que se llama en la creación, devolviendo una nueva función de constructor. Debido a que la función anónima se llama solo una vez, puede crear variables estáticas privadas en ella (están dentro del cierre, visibles para los demás miembros de la clase). La función constructora es básicamente un objeto Javascript estándar: usted define atributos privados dentro de él, y los atributos públicos se adjuntan a this
variable.
Básicamente, este enfoque combina el enfoque crockfordiano con objetos estándar de Javascript para crear una clase más poderosa.
Puedes usarlo como lo harías con cualquier otro objeto Javascript:
Foo.publicStaticMethod(); //calling a static method
var test = new Foo(); //instantiation
test.publicMethod(); //calling a method
también puedes probar esto
function Person(obj) {
''use strict'';
if (typeof obj === "undefined") {
this.name = "Bob";
this.age = 32;
this.company = "Facebook";
} else {
this.name = obj.name;
this.age = obj.age;
this.company = obj.company;
}
}
Person.prototype.print = function () {
''use strict'';
console.log("Name: " + this.name + " Age : " + this.age + " Company : " + this.company);
};
var p1 = new Person({name: "Alex", age: 23, company: "Google"});
p1.print();
Closure
es versátil. Bobince ha resumido bien los enfoques de prototipo frente a cierre al crear objetos. Sin embargo, puede imitar algunos aspectos de la OOP
utilizando el cierre de una manera funcional de programación. Recuerde que las funciones son objetos en JavaScript ; así que usa la función como objeto de una manera diferente.
Aquí hay un ejemplo de cierre:
function outer(outerArg) {
return inner(innerArg) {
return innerArg + outerArg; //the scope chain is composed of innerArg and outerArg from the outer context
}
}
Hace un tiempo me encontré con el artículo de Mozilla sobre el cierre. Esto es lo que saltan a la vista: "Un cierre le permite asociar algunos datos (el entorno) con una función que opera con esos datos. Esto tiene evidentes paralelos con la programación orientada a objetos, donde los objetos nos permiten asociar algunos datos (las propiedades del objeto ) con uno o más métodos ". Fue la primera vez que leí un paralelismo entre el cierre y el OOP clásico sin referencia al prototipo.
¿Cómo?
Supongamos que desea calcular el IVA de algunos artículos. Es probable que el IVA se mantenga estable durante la vida útil de una aplicación. Una forma de hacerlo en OOP (pseudo código):
public class Calculator {
public property VAT { get; private set; }
public Calculator(int vat) {
this.VAT = vat;
}
public int Calculate(int price) {
return price * this.VAT;
}
}
Básicamente, usted pasa un valor de IVA a su constructor y su método de cálculo puede operar a través de un cierre . Ahora, en lugar de usar una clase / constructor, pase su IVA como un argumento a una función. Debido a que lo único que le interesa es el cálculo en sí mismo, devuelve una nueva función, que es el método de cálculo:
function calculator(vat) {
return function(item) {
return item * vat;
}
}
var calculate = calculator(1.10);
var jsBook = 100; //100$
calculate(jsBook); //110
En su proyecto, identifique los valores de nivel superior que sean buenos candidatos de lo que el IVA es para el cálculo. Como regla general, cada vez que pasa los mismos argumentos una y otra vez, hay una manera de mejorarlo utilizando el cierre. No hay necesidad de crear objetos tradicionales.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
Douglas Crockford discute ese tema ampliamente en The Good Parts . Recomienda evitar que el nuevo operador cree nuevos objetos. En su lugar se propone crear constructores personalizados. Por ejemplo:
var mammal = function (spec) {
var that = {};
that.get_name = function ( ) {
return spec.name;
};
that.says = function ( ) {
return spec.saying || '''';
};
return that;
};
var myMammal = mammal({name: ''Herb''});
En Javascript, una función es un objeto y se puede utilizar para construir objetos junto con el nuevo operador. Por convención, las funciones destinadas a ser utilizadas como constructores comienzan con una letra mayúscula. A menudo ves cosas como:
function Person() {
this.name = "John";
return this;
}
var person = new Person();
alert("name: " + person.name);**
En caso de que olvide utilizar el nuevo operador al crear una instancia de un nuevo objeto, lo que obtiene es una llamada de función ordinaria, y esto está vinculado al objeto global en lugar del nuevo objeto.
Básicamente, no existe un concepto de clase en JS, por lo que usamos la función como un constructor de clase que es relevante con los patrones de diseño existentes.
//Constructor Pattern
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.doSomething = function(){
alert(''I am Happy'');
}
}
Hasta ahora, JS no tiene idea de que desea crear un objeto, por lo que aquí viene la nueva palabra clave.
var person1 = new Person(''Arv'', 30, ''Software'');
person1.name //Arv
Ref: JS profesional para desarrolladores web - Nik Z
var Person = function (lastname, age, job){
this.name = name;
this.age = age;
this.job = job;
this.changeName = function(name){
this.lastname = name;
}
}
var myWorker = new Person(''Adeola'', 23, ''Web Developer'');
myWorker.changeName(''Timmy'');
console.log("New Worker" + myWorker.lastname);
var Klass = function Klass() {
var thus = this;
var somePublicVariable = x
, somePublicVariable2 = x
;
var somePrivateVariable = x
, somePrivateVariable2 = x
;
var privateMethod = (function p() {...}).bind(this);
function publicMethod() {...}
// export precepts
this.var1 = somePublicVariable;
this.method = publicMethod;
return this;
};
Primero, puede cambiar su preferencia de agregar métodos a la instancia en lugar del objeto prototype
del constructor . Casi siempre declaro métodos dentro del constructor porque uso el secuestro de constructores muy a menudo para propósitos de herencia y decoradores.
Aquí es cómo decido dónde se escriben las declaraciones:
- Nunca declarar un método directamente en el objeto de contexto (
this
) - Deje que las declaraciones
var
tengan prioridad sobrefunction
declaraciones defunction
- Deje que los primitivos tengan prioridad sobre los objetos (
{}
y[]
) - Dejen que
public
declaracionespublic
tengan prioridad sobreprivate
declaracionesprivate
- Prefiero
Function.prototype.bind
overthus
,self
,vm
,etc
- Evite declarar una clase dentro de otra clase, a menos que:
- Debería ser obvio que los dos son inseparables.
- La clase interna implementa el patrón de comando
- La clase interna implementa el patrón Singleton
- La clase interna implementa el patrón de estado
- La Clase Interna implementa otro Patrón de Diseño que garantiza esto
- Siempre devuelva
this
desde dentro del Alcance Léxico del Espacio de Cierre.
He aquí por qué estas ayudan:
Secuestro de constructorvar Super = function Super() {
...
this.inherited = true;
...
};
var Klass = function Klass() {
...
// export precepts
Super.apply(this); // extends this with property `inherited`
...
};
Modelo de diseño
var Model = function Model(options) {
var options = options || {};
this.id = options.id || this.id || -1;
this.string = options.string || this.string || "";
// ...
return this;
};
var model = new Model({...});
var updated = Model.call(model, { string: ''modified'' });
(model === updated === true); // > true
Patrones de diseño
var Singleton = new (function Singleton() {
var INSTANCE = null;
return function Klass() {
...
// export precepts
...
if (!INSTANCE) INSTANCE = this;
return INSTANCE;
};
})();
var a = new Singleton();
var b = new Singleton();
(a === b === true); // > true
Como puede ver, realmente no tengo necesidad de .call
thus
ya que prefiero Function.prototype.bind
(o .call
o .apply
) en lugar de .apply
. En nuestra clase Singleton
, ni siquiera lo INSTANCE
thus
porque INSTANCE
transmite más información. Para Model
, devolvemos this
para que podamos invocar al Constructor utilizando .call
para devolver la instancia que le hemos pasado. Redundantemente, lo asignamos a la variable updated
, aunque es útil en otros escenarios.
Además, prefiero construir literales de objetos utilizando la new
palabra clave sobre {corchetes}:
var klass = new (function Klass(Base) {
...
// export precepts
Base.apply(this); //
this.override = x;
...
})(Super);
No preferido
var klass = Super.apply({
override: x
});
Como puede ver, este último no tiene capacidad para anular la propiedad "anular" de su Superclase.
Si agrego métodos al objeto prototype
la Clase, prefiero un objeto literal, con o sin usar la new
palabra clave:
Klass.prototype = new Super();
// OR
Klass.prototype = new (function Base() {
...
// export precepts
Base.apply(this);
...
})(Super);
// OR
Klass.prototype = Super.apply({...});
// OR
Klass.prototype = {
method: function m() {...}
};
No preferido
Klass.prototype.method = function m() {...};