diff --git a/es/index.html b/es/index.html
new file mode 100644
index 0000000..552b246
--- /dev/null
+++ b/es/index.html
@@ -0,0 +1,1411 @@
+
Jardín de JavaScript
Introducción
Introducción
El Jardín de JavaScript es una guía de documentación acerca de las
+partes más peculiares de este lenguaje de programación. Brinda consejos para evitar
+los errores más comunes y sutiles, así como problemas de rendimiento y de malas
+prácticas que los programadores menos experimentados en JavaScript pueden resolver
+en sus esfuerzos por profundizar en el lenguaje.
+
El Jardín de JavaScript no prentende enseñar JavaScript.
+Se recomienda un conocimiento sobre el lenguaje para entender los temas tratados en
+esta guía. Con el fin de aprender los conceptos básicos del lenguaje, por favor
+diríjase a la excelente guía de los desarrolladores de Mozilla.
+
Los autores
+
Esta guía es el trabajo de dos encantadores usuarios del foro Stack Overflow,
+Ivo Wetzel (Escrito) y Zhang Yi Jiang (Diseño).
El Jardín de JavaScript es publicado bajo la licencia MIT y es hospedado en
+GitHub. Si encuentra algún error o errata por favor publique una incidencia o
+envie un pull request a nuestro repositorio. También nos puede encontrar en la
+sala de chat de JavaScript en Stack Overflow.
+
Objetos
Uso de objetos y propiedades
Todo en JavaScript actúa como un objeto, con las dos únicas excepciones de
+null y undefined.
Un error muy común es el uso de literales númericos como objetos.
+Esto se debe a un error en el parser de JavaScript que intenta analizar la
+notación de puntos como un literal de punto flotante.
+
2.toString(); // lanza SyntaxError
+
Existe un par de soluciones que pueden utilizarse para hacer que los
+literales númericos actúen como objetos.
+
2..toString(); // el segundo punto es reconocido correctamente
+2 .toString(); // observe el espacio a la izquierda del punto
+(2).toString(); // el número 2 se evalúa primero
+
Objetos como un tipo de datos
+
Los objetos en JavaScript también pueden ser utilizados como una Tabla Hash o conocido como Hashmap en inglés, consisten
+principalmente en nombres de propiedades, y asignándoles valores a éstas.
+
El uso de un objeto literal - con notación {} - puede crear un
+objeto plano. Este nuevo objeto heredado desde Object.prototype
+no posee propiedades propias definidas.
+
var foo = {}; // un nuevo objeto vacío
+
+// un nuevo objeto con la propiedad llamada 'test' con el valor 12
+var bar = {test: 12};
+
Acceso a las propiedades
+
Se puede acceder a las propiedades de un objeto de dos maneras, ya sea a través de la
+notación de punto o desde la notación de corchetes.
Ambas notaciones son idénticas en su funcionamiento, la única diferencia es la
+notación de corchetes permite el ajuste dinámico de las propiedades, así como
+el uso de propiedades que de otro modo daría lugar a error de sintaxis.
+
Eliminando propiedades
+
La única manera de eliminar una propiedad desde un objeto es usando el
+operador delete; establecer la propiedad a undefined o null solamente
+elimina el valor asociado a la propiedad, pero no la key (valor clave).
+
var obj = {
+ bar: 1,
+ foo: 2,
+ baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ console.log(i, '' + obj[i]);
+ }
+}
+
Los resultados de la salida son bar undefined y foo null - sólo baz ha
+sido removido y por lo tanto no aparece en la salida.
+
Notación de Keys
+
var test = {
+ 'case': 'Soy una palabra clave y debo ser anotado como string',
+ delete: 'Soy una palabra clave también' // lanza SyntaxError
+};
+
Las propiedades de los objetos puede ser simbolizados como caracteres planos y como strings. Debido
+a otro mal diseño del parser de JavaScript, lo anterior es una excepción
+de SyntaxError antes de ECMAScript 5.
+
Este error se debe al eliminar una keyword; por lo tanto, debe ser
+anotado como un string literal para asegurarse que será interpretado correctamente
+por diversos motores de JavaScript.
+
Prototipo
JavaScript no posee en sus características un sistema clásico de herencia, sino que
+utiliza un prototipo para esto.
+
Si bien a menudo se considera uno de los puntos débiles de JavaScript, el
+modelo de herencia prototipado es de hecho más poderoso que el modelo clásico.
+Por ejemplo, es bastante trivial construir un modelo clásico a partir del modelo prototipado,
+mientras que al contrario es una tarea mucho más difícil.
+
Debido al hecho que JavaScript es básicamente el único lenguaje que utiliza
+ampliamente la herencia prototipada, se necesita algo de tiempo para adaptarse a
+las diferencias entre los dos modelos.
+
La primera gran diferencia es que la herencia en JavaScript se realiza usando
+llamadas de cadenas de prototipo (prototype chains).
+
+
function Foo() {
+ this.value = 42;
+}
+Foo.prototype = {
+ method: function() {}
+};
+
+function Bar() {}
+
+// Asigna el prototipo de Bar como una nueva instancia de Foo
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Hello World';
+
+// Asegura que el constructor sea Bar
+Bar.prototype.constructor = Bar;
+
+var test = new Bar() // crea una nueva instancia de Bar
+
+// Resultado de cadena de prototipos (prototype chain)
+test [instance of Bar]
+ Bar.prototype [instance of Foo]
+ { foo: 'Hello World' }
+ Foo.prototype
+ { method: ... }
+ Object.prototype
+ { toString: ... /* etc. */ }
+
En el código anterior, el objeto test hereda de Bar.prototype y Foo.prototype;
+por lo tanto, tendrá acceso a la función method que se ha definido en Foo.
+También se tendrá acceso a a la propiedad value de la única instancia de Foo
+que compone su prototipo. Es importante tomar en cuenta que new Bar()no creará una nueva
+instancia de Foo, pero retornará lo asignado en su prototipo; de este modo, todas las instancias
+de Bar tendrán que compartir el mismovalor de la propiedad.
+
+
Búsqueda de propiedades
+
Cuando se accede a las propiedades de un objeto, JavaScript recorre la cadena de
+prototipo hacia arriba hasta encontrar la propiedad con el nombre solicitado.
+
Cuando se llega al final de la cadena - concretamente Object.prototype - y aún
+no se ha encontrado la propiedad especificada, se retornará un valor
+undefined en su lugar.
+
La propiedad prototype
+
Aunque la propiedad prototype es usada por el lenguaje para construir la cadena
+de prototipos, es posible asignar cualquier valor. Aunque los tipos primitivos
+serán ignorados cuando se asigne en prototype.
+
function Foo() {}
+Foo.prototype = 1; // no tendrá efecto
+
La asignación de objetos, como se muestra en el ejemplo anterior, funcionará, y permitirá
+la creación dinámica de cadena de prototipos.
+
Rendimiento
+
El tiempo tomado en la búsqueda de propiedades es alta y la cadena de prototipo puede
+presentar un impacto negativo crítico en el rendimiento en partes del código. Además,
+si ha tratado de acceder a propiedades que no existen, esto provoca que se recorra la cadena de prototipo completa.
+
Además, al recorrer en iteración las propiedades de un objeto
+, cada propiedad encontrada en la cadena de prototipo será enumerada.
+
Extensión de prototipos nativos
+
Una mala característica que se suele utilizar para extender Object.prototype o cualquier
+otro prototipo construido.
+
Esta técnica es conocida en inglés como monkey patching y rompe la encapsulación del código.
+Si bien es utilizado en frameworks como Prototype, todavía no existen buenas razones para adoptarlo o integrarlo
+como tipos de dato o como funcionalidad no estándar.
+
La única razón coherente para extender un prototipo es para adaptarle nuevas
+características de los motores JavaScript más modernos; por ejemplo,
+Array.forEach.
+
En conclusión
+
Se debe entender por completo el módelo de herencia prototipado antes de
+escribir código complejo que lo utilice. Además, observe la longitud de la
+cadena de prototipo y modifíquela si es necesario para evitar posibles problemas de
+rendimiento. Con relación a los prototipos nativos, estos nunca deben ser extendidos a
+menos que sea para mantener la compatibilidad con nuevas características de JavaScript.
+
hasOwnProperty
Con el fin de comprobar si un objeto posee una propiedad definida en sí mismo y no
+en algún lugar de su cadena de prototipo, es necesario utilizar
+el método hasOwnProperty ya que todos los objetos herendan de Object.prototype.
+
+
hasOwnProperty es la única utilidad en JavaScript que se ocupa de las propiedades
+y no las salta en la cadena de prototipo.
Sólo hasOwnProperty retornará el resultado correcto y esperado, esto es
+ensencial cuando se repite una iteración en las propiedades de cualquier objeto. No hay
+otra maner de excluir las propiedades que no están definidas en el mismo objeto, pero
+en alguna parte de su cadena de prototipo si.
+
hasOwnProperty como propiedad
+
JavaScript no protege el nombre de la propiedad hasOwnProperty; de este modo, si existe
+la posibilidad de que un objeto tenga una propiedad con el mismo nombre, es necesario utilizar
+hasOwnProperty como propiedad externa con el fin de obtener resultados correctos.
+
var foo = {
+ hasOwnProperty: function() {
+ return false;
+ },
+ bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // siempre devolverá false
+
+// Utilice otro objeto con hasOwnProperty y llamelo con 'this' para asignarlo a foo
+({}).hasOwnProperty.call(foo, 'bar'); // true
+
En conclusión
+
Cuando se necesite comprobar la existencia de una propiedad en un objeto, hasOwnProperty es
+el único método para hacerlo. También se recomienda el uso de hasOwnProperty como
+parte de un bucle for in, esto evitará errores desde
+extenciones de prototipos nativos.
+
El bucle for in
Al igual que el operador in, el bucle for in también recorre sobre la
+cadena de prototipo cuando este se repite en una iteración en las propiedades de un objeto.
+
+
// Envenenamiento en Object.prototype
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+ console.log(i); // Imprime ambos bar y moo
+}
+
Dado que no es posible cambiar el comportamiento del bucle for in en sí mismo, es
+necesario filtrar las propiedades internas no deseadas dentro del bucle,
+esto se hace mediante el uso del método hasOwnProperty del
+Object.prototype.
+
+
Usando hasOwnProperty para filtrado
+
// Aún es el foo del código de arriba
+for(var i in foo) {
+ if (foo.hasOwnProperty(i)) {
+ console.log(i);
+ }
+}
+
Está versión es la única forma correcta de uso. Esto se debe sólo al uso de
+hasOwnProperty que imprimirá moo. Cuando hasOwnProperty se omita, el código es
+propenso a errores en los casos de prototipos nativos - ej. Object.prototype -
+se ha extendedido.
+
Uno de los frameworks más usado que implementa estas funcionalidades es Prototype. Cuando el
+framework es incluido, el bucle for in que no utilicen hasOwnProperty no podrá garantizar que
+se interrumpa.
+
En conclusión
+
Se recomienda utilizar siempre el uso de hasOwnProperty. Nunca debe suponer ningún entorno donde el código se ejecute, o si los prototipos
+nativos han sido extendidos o no.
+
Funciones
La declaración de funciones y expresiones
Las funciones en JavaScript son las primeras clases de objetos. Esto significa que se
+puede pasar como cualquier otro valor. Un uso común de está característica es pasar de
+una función anónima a otra, posiblemente una función asíncrona. Esto se conoce como callback.
+
La declaración function
+
function foo() {}
+
La función anterior se carga así mismo antes de iniciar la ejecución del
+programa; por lo tanto, está disponible en todo el scope (ámbito) de la aplicación
+donde se ha definido, aunque hubiera sido llamado antes de definirse en el código.
+
foo(); // Funciona porque foo ha sido creado antes que este código se ejecute
+function foo() {}
+
La expresión function
+
var foo = function() {};
+
Este ejemplo asigna una función sin nombre y anónima a la variable foo.
Debido a la declaración de var, que carga el nombre de la variable foo antes
+de la ejecución real del inicio del código, foo ya estará definidido cuando se
+ejecute el script.
+
Pero se asigna sólo si ocurre en tiempo de ejecución, el valor de foo de forma
+predetermina es undefined antes de que el código se ejecute.
+
Expresión nombre de función
+
Otro caso especial de asignación de nombre de funciones.
+
var foo = function bar() {
+ bar(); // Funciona
+}
+bar(); // ReferenceError
+
Aquí bar no está disponible en el ámbito externo (scope), ya que la función sólo es
+asignada a foo; Sin embargo, dentro de bar si está disponible. Esto se debe a la forma
+en como trabaja la resolución de nombres en JavaScript, el nombre de
+la función esta siempre disponible en el ámbito local de la propia función.
+
Cómo trabaja this
JavaScript tiene un concepto diferente sobre el nombre especial this referido a la
+mayoría de lenguajes de programación. Hay exactamente cinco formas distintas en donde
+es posible ver el valor de this dentro de lo posible en el lenguaje.
+
El ámbito global (Global Scope)
+
this;
+
Cuando se utiliza this en el ámbito global, simplemente se refiere al objeto global.
+
Llamar a una función
+
foo();
+
Aquí this se refiere al objeto global.
+
+
Llamar a un método
+
test.foo();
+
En este ejemplo this se referiere a test.
+
Llamar a un constructor
+
new foo();
+
Llamar a una función que esta precedida por la palabra clave new actúa como
+un constructor. Dentro de la función, this se refiere
+al Objetorecién creado.
+
Ajuste explícito de this
+
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // array que se apilará
+foo.call(bar, 1, 2, 3); // resultados a = 1, b = 2, c = 3
+
Cuando se utiliza los métodos call o apply en Function.prototype, el valor de
+this dentro de la función llamada se ajustará explícitamente al primer argumento
+correspondiente a la llamada de la función.
+
Como resultado, el ejemplo anterior sobre los casos de métodos estos no se aplican, y this
+dentro de foo puede establecerse en bar.
+
+
Errores comunes
+
Si bien en la mayoría de los casos esto tiene sentido, el primero puede cosiderarse como otro
+mal diseño del lenguaje, ya que nunca tiene un uso práctico.
+
Foo.method = function() {
+ function test() {
+ // this es establecido como un objeto global
+ }
+ test();
+}
+
Un error común es que this dentro de test haga referencia a Foo, mientras que en
+realidad esto no es así.
+
Con el fin de acceder a Foo desde dentro de test es necesario crear una variable local
+dentro del método para referirse a Foo.
+
Foo.method = function() {
+ var that = this;
+ function test() {
+ // Use that instead of this here
+ }
+ test();
+}
+
that es justo un nombre normal, pero es comúnmente usado para referenciar a this
+de forma externa. En combinación con closures, esto puede ser
+también usado para pasar this como valor.
+
Asignación de métodos
+
Otra cosa que no funciona en JavaScript son los alias en las funciones, es decir,
+asignar un método a una variable.
+
var test = someObject.methodTest;
+test();
+
Debido al primer caso, test actúa como una función de llamada; por lo que
+this dentro de este no estará referido a someObject.
+
Mientras que la unión de this puede parecer una mala idea en un principio, esto es en
+realidad lo que hace trabajar a la herencia de prototipo.
Cuando los métodos son llamados desde una instancia de Bar, this se referirá a una
+instancia.
+
Closures y referencias
Una de las características más poderosas de JavaScript es la disponibilidad de closures (cerraduras),
+esto significa que los ámbitos siempre podrán ser accedidos por ámbitos externos donde
+fueron definidos. Dado que sólo el alcance es único en JavaScript en el
+ámbito de la función, todas las funciones, por omisión, actúan como closures.
En este caso, Counter retorna dos closures. La función increment y la
+función get. Ambas funciones mantienen el ámbito de la referencia de Counter y, por lo tanto, siempre accede a la variable count que fue definido
+en el ámbito.
+
¿Por qué las variables privadas trabajan?
+
Dado que no es posible referenciar o asignar ámbitos en JavaScript, no hay
+manera de acceder a la variable count desde fuera. Sólo existe una forma para
+interactuar con estos vía los dos closures.
+
var foo = new Counter(4);
+foo.hack = function() {
+ count = 1337;
+};
+
El código anterior no ha cambiado la variable count en el ámbito de Counter,
+desde foo.hack no es definido en ese ámbito. En su lugar se creará - o
+se anulará - la variable globalcount.
+
Closures dentro de bucles
+
Un error frecuente en el uso de closures dentro de bucles, es como si se tratará
+de copiar el valor del índice de la variable del bucle.
+
for(var i = 0; i < 10; i++) {
+ setTimeout(function() {
+ console.log(i);
+ }, 1000);
+}
+
El código anterior no tendrá como salida los números del 0 al 9, sino
+simplementemente se imprimirá el número 10 diez veces.
+
La función anónima hace referencia a i y se llama a
+console.log, el bucle for ya ha terminado y finalizo el valor de
+i a 10.
+
Con el fin de obtener el comportamiento deseado, es necesario crear una copia
+del valor de i.
+
Evitando el problema de referencia
+
Con el fin de copiar el valor de la variable índice del bucle, lo mejor es utilizar
+un contenedor anónimo.
La función anónima externa llamará inmediatamente a i como su primer
+argumento y recibirá la copia del valor de i como parámetro de e.
+
La función anónima que se pasa a setTimeout ahora es una referencia a
+e, cuyo valor no han sido cambiados por el bucle.
+
No hay otra manera de lograr esto; se debe retornar una función desde
+el contenedor anónimo, que tendrá el mismo comportamiento que el código
+anterior.
Cada ámbito de la función de JavaScript puede acceder a la variable especial arguments.
+Está variable contiene una lista de todos los argumentos que se pasan a la función.
+
+
El objeto argumentsno es un Array. Si bien cuenta con la semántica
+de un array - concretamente la propiedad length - no hereda de
+Array.prototype y es de hecho un Objeto.
+
Debido a esto, no es posible usar los métodos estándar de los arrays como push,
+pop o slice en arguments. Mientras que la iteración es un simple bucle for que
+funciona muy bien, esto se convierte necesariamente en un Array real con el
+fin de utilizar los métodos de un Array.
+
Conversión de un Array
+
El siguiente código devuelve un nuevo Array que contiene todos los elementos del
+objeto arguments.
+
Array.prototype.slice.call(arguments);
+
Esta conversión es lenta, no es recomendable usarlo en puntos criticos que
+afecten el rendimiento del código.
+
Pasar Argumentos
+
El siguiente método es recomendado para pasar argumentos desde una función a
+otra.
+
function foo() {
+ bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+ // do stuff here
+}
+
Otro truco es utilizar tanto call y apply juntos para crear contenedores rápidos y
+consolidados.
+
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+ console.log(this, a, b, c);
+};
+
+// Crea una versión sin consolidar de "method"
+// Se toma los parámetros: this, arg1, arg2...argN
+Foo.method = function() {
+
+ // Resultado: Foo.prototype.method.call(this, arg1, arg2... argN)
+ Function.call.apply(Foo.prototype.method, arguments);
+};
+
Los parámetros formales y argumentos de índices
+
El objeto arguments crea las funciones de getter y setter para sus
+propiedades, así como parámetros formales de la función.
+
Como resultado, se ha cambiado el valor formal del parámetro también se cambio el
+valor de la propiedad correspondiente del objeto arguments, y al revés.
+
function foo(a, b, c) {
+ arguments[0] = 2;
+ a; // 2
+
+ b = 4;
+ arguments[1]; // 4
+
+ var d = c;
+ d = 9;
+ c; // 3
+}
+foo(1, 2, 3);
+
Mitos y verdades sobre el rendimiento
+
El objeto arguments es siempre creado con las dos únicas excepciones cuando es
+el caso en que declarado como un nombre dentro de la función o uno de los
+parámetros formales. No importa si se utiliza o no.
+
Ambos getters y setters son siempre creados; por lo tanto, con que casi no se
+tiene un impacto en el rendimiento en todo, especialemente no en el código real donde no
+es más que un simple acceso a las propiedades del objeto arguments.
+
+
Sin embargo, hay casos en que se reducirá drásticamente el rendimiento en los motores
+modernos de JavaScript. Este es el caso del uso de arguments.callee.
+
function foo() {
+ arguments.callee; // realiza algo con la función del objeto
+ arguments.callee.caller; // y llama a la función del objeto
+}
+
+function bigLoop() {
+ for(var i = 0; i < 100000; i++) {
+ foo(); // Debería ser normalmente entre líneas...
+ }
+}
+
El código anterior, foo no puede estar sujeto a la expansión en línea ya que se
+necesita saber acerca de sí mismo y la llamada. Esto no sólo denota los posibles beneficios
+de rendimiento que surgen con la expansión en línea, ya que también interrumpe la encapsulación
+ya que la función ahora puede ser dependiente de un contexto específico de llamada.
+
Es muy recomendablenunca hacer uso de arguments.callee o de cualquier
+de sus propiedades.
+
+
Constructores
Los constructores en JavaScript todavía son diferentes a los de otros lenguajes.
+Cualquier llamada que es precedida por la palabra new actua como un constructor.
+
Dentro del constructor - la función llama - el valor de this se refiere a un
+Objeto recién creado. El prototipo de este nuevo
+objeto se establece en el prototipo de la funcióno que es invocado como el
+constructor.
+
Si la función que se llama no tiene una sentencia return explícita, entonces
+implícitamente devuelve el valor de this - el nuevo objeto.
+
function Foo() {
+ this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+ console.log(this.bla);
+};
+
+var test = new Foo();
+
La llamada de Foo por encima del constructor y establece el prototipo del objeto
+recién creado a Foo.prototype.
+
En caso explícito de la sentencia return de la función devuelva el valor especificado
+que la declaración, pero sólo si el valor devuelto es un Object.
+
function Bar() {
+ return 2;
+}
+new Bar(); // a new object
+
+function Test() {
+ this.value = 2;
+
+ return {
+ foo: 1
+ };
+}
+new Test(); // the returned object
+
Cuando una nueva keyword es omitidad, la función no devuelve un nuevo objeto.
+
function Foo() {
+ this.bla = 1; // se establece en el objeto global
+}
+Foo(); // undefined
+
Auqnue el ejemplo anterior puede parecer que trabaja en algunos casos, debido
+a los trabajos de this en JavaScript, que usará el
+objeto global como valor de this.
+
Fábricas
+
Con el fin de ser capaz de omitir un nuevo keyword, la función del tiene
+explícitamente devolver un valor.
Ambos llamadas a Bar devuelven exactamente lo mismo, un reciente objeto creado que
+tiene como propiedad llamada el method, esto es un
+Closure.
+
También hay que notar que la llamada new Bar()no afecta al prototipo
+del objeto devuelto. Mientras que el prototipo se establece en el objeto recién creado,
+ Bar nunca devuelve un nuevo objeto.
+
En el ejemplo anterior, no hay diferencia funcional entre usar y no usar
+el keyword new.
+
Creación de nuevos objetos vía Factorias
+
Una recomendación a menudo es no utilizar new ya que su uso puede
+conducir a errores.
+
Con el fin de crear un nuevo objeto, uno bien debe utilizar una fábrica y un
+constructor para crear un nuevo objeto dentro de la fábrica.
Aunque lo anterior es robuesto frente a la keyword new y, ciertamente hace
+que el uso de variables privadas sea fácil, esto viene con
+algunas desventajas.
+
+
Se utiliza más memoria, ya que los objetos creados no comparten los métodos de
+un prototipo.
+
Con el fin de heredar de una fábrica se necesita copiar todos los métodos a otro
+objeto o poner todo en un prototipo de nuevo objeto.
+
La eliminación de una cadena de prototipo sólo por dejar la keyword new de
+alguna manera va en contra del espíritu del lenguaje.
+
+
En conclusión
+
Mientras que se omite el keyword new podría dar a errores, no es ciertamente
+una razón para abandonar el uso de prototipos por completo. Al final todo se reduce a
+la solución que se adapta mejor a las necesidades de la aplicación, especialmente si es
+importante elegir un estilo específico en la creación de objetos
+y resistirse.
+
Ámbitos y Namespaces
A pesar que JavaScript tiene una muy buena sintaxis de dos llaves para los bloques,
+está no es compatible con el soporte de ámbito de bloques; por lo que todo se deja
+al lenguaje con el ámbito de la función.
+
function test() { // un ámbito
+ for(var i = 0; i < 10; i++) { // no es un ámbito
+ // cuenta
+ }
+ console.log(i); // 10
+}
+
+
Tampoco hay distintos namespaces en JavaScript, lo que significa que todo se define
+en un namespace global y compartido.
+
Cada vez que una variable es referenciada, JavaScript recorre hacia arriba a través de todos
+los ámbitos hasta encontrarlo. En este caso que llegue al ámbito global y todavía no ha
+encontrado el nombre solicitado, se generará un error ReferenceError.
+
El terror de las variables globales
+
// script A
+foo = '42';
+
+// script B
+var foo = '42'
+
Estos dos scripts no tienen el mismo efecto. El script A define una variable
+llamada foo en el ámbito global y el script B define foo en el
+actual ámbito.
+
Una vez más, esto no tiene el mismo efecto para todo, no usar var puede tener
+mayor implicación.
Dejando de lador la sentencia var dentro de la función test sobre escribiría el
+valor de foo. Si bien al principio puede parecer un gran cambio, se tiene
+miles de líneas de código en JavaScript y no se usaría var introduciendose en un
+horrible y difícil detección de errores.
+
// ámbito global
+var items = [/* some list */];
+for(var i = 0; i < 10; i++) {
+ subLoop();
+}
+
+function subLoop() {
+ // ámbito de subLoop
+ for(i = 0; i < 10; i++) { // falta la sentencia var
+ // ¡realizar cosas asombrosas!
+ }
+}
+
El bucle externo terminará después de la primera llamada a subLoop, desde subLoop
+sobreescribe el valor global de i. Usando var para el segundo bucle for se hace
+fácil evitar este error. La sentencia var no debe nunca dejarse a menos que
+el efecto deseado es afectado por el ámbito exteriror.
+
Variables locales
+
La única fuente para las variables locales en JavaScript son los parámetros de la
+función y variables que fueron declaradas vía la sentencia
+var.
+
// ámbito global
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+ // ámbito local de la función test
+ i = 5;
+
+ var foo = 3;
+ bar = 4;
+}
+test(10);
+
Mientras foo y i son variables locales dentro del ámbitor de la función test,
+ela asignación de bar sobreescribe la variable global con el mismo nombre.
+
Hoisting
+
La declaración de hoists en JavaScript. Esto significa que tanto la declaración de var y
+la función declarada se translada a la parte superior de su ámbito que lo contiene.
+
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+ if (false) {
+ goo = 1;
+
+ } else {
+ var goo = 2;
+ }
+ for(var i = 0; i < 100; i++) {
+ var e = data[i];
+ }
+}
+
El código anterior transforma antes de ejecutarse. JavaScript mueve
+la declaracione var aspi como las declaraciones de la función a la parte superior a
+lo más cercano del ámbito circundante.
+
// declaraciones var movidas aquí
+var bar, someValue; // por omisión 'undefined'
+
+// la función declarada es movida aquí también
+function test(data) {
+ var goo, i, e; // se pierde el ámbito del bloque movido aquí
+ if (false) {
+ goo = 1;
+
+ } else {
+ goo = 2;
+ }
+ for(i = 0; i < 100; i++) {
+ e = data[i];
+ }
+}
+
+bar(); // falla con TypeError desde bar sigue en 'undefined'
+someValue = 42; // las asignaciones no se ven afectadas por hoisting
+bar = function() {};
+
+test();
+
La falta de alcance del bloque no sólo moverá la declaración var fuera de los bucles y
+su contenido, sino también hará que los resultados de ciertos constructores if
+no sean intuitivas.
+
En el código original la declaración de if si parecía modificar la variable
+globalgoo, mientras actualmente este modifica la variable local - después hoisting
+ha sido aplicado.
+
Sin el conocimiento acerca de hoisting, a continuación el código puede parecer
+un ReferenceError.
+
// comprueba si SomeImportantThing ha iniciado
+if (!SomeImportantThing) {
+ var SomeImportantThing = {};
+}
+
Pero, por supuesto, lo anterior funciona debido a que la declaración var es movida
+a la parte superior del ámbito global.
+
var SomeImportantThing;
+
+// otro código podría iniciar SomeImportantThing aqui, o no
+
+// asegúrese de que está ahí
+if (!SomeImportantThing) {
+ SomeImportantThing = {};
+}
+
Name Resolution Order
+
All scopes in JavaScript, including the global scope, have the special name
+this, defined in them, which refers to the current object.
+
Function scopes also have the name arguments, defined in
+them, which contains the arguments that were passed to a function.
+
For example, when trying to access a variable named foo inside the scope of a
+function, JavaScript will lookup the name in the following order:
+
+
In case there is a var foo statement in the current scope, use that.
+
If one of the function parameters is named foo, use that.
+
If the function itself is called foo, use that.
+
Go to the next outer scope, and start with #1 again.
+
+
+
Namespaces
+
A common problem of having only one global namespace is the likeliness of running
+into problems where variable names clash. In JavaScript, this problem can
+easily be avoided with the help of anonymous wrappers.
+
(function() {
+ // a self contained "namespace"
+
+ window.foo = function() {
+ // an exposed closure
+ };
+
+})(); // execute the function immediately
+
Unnamed functions are considered expressions; so in order to
+being callable, they must first be evaluated.
+
( // evaluate the function inside the paranthesis
+function() {}
+) // and return the function object
+() // call the result of the evaluation
+
There are other ways for evaluating and calling the function expression; which,
+while different in syntax, do behave the exact same way.
+
// Two other ways
++function(){}();
+(function(){}());
+
In Conclusion
+
It is recommended to always use an anonymous wrapper for encapsulating code in
+its own namespace. This does not only protect code against name clashes, but it
+also allows for better modularization of programs.
+
Additionally, the use of global variables is considered bad practice. Any
+use of them indicates badly written code that is prone to errors and hard to maintain.
+
Arrays
Iteración de un Array y sus propiedades
A pesar que los arrays en JavaScript son objetos, no existe un buena razón para
+usarlo en un bucle for para una interación de este. De
+hecho, hay un número de buenas razones contra el uso de for in en arrays.
+
+
Dado que el bucle for in enumera todas las propiedades que están en una cadena
+de prototipo y la única manera para excluir estas propiedades es el uso de
+hasOwnProperty, ya que es veinte veces más
+lento que un bucle for normal.
+
Iteración
+
Con el fin de obtener el mejor rendimiento cuando se repite la interación de arrays,
+es lo mejor hacer uso del clásico bucle for.
+
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+ console.log(list[i]);
+}
+
Hay una captura adicional en el ejemplo anterior, que es el almacenamiento de la
+caché de longitud del array vía l = list.length.
+
Aunque la propiedad length es definida en el mismo array, todavía posee una sobrecarga
+para realizar la búsqueda en cada interación del bucle. Y mientras que los últimos
+motores de JavaScript pueden aplicar optimizaciones en este caso, no hay manera
+de saber si el ćodigo se ejecutará en uno de estos nuevos motores nuevos o no.
+
De hecho, dejando de lado el almacenamiento en caché puede resultar que el bucle
+inicie sólo la mitad de rápido que con la longitud de la caché.
+
La propiedad length
+
Mientras que getter de la propiedad length simplemente retorne el número de
+elementos son contenidos en un array, el setter puede ser usado para
+truncar el array.
La asignación de un menor número de longitud trunca al array, pero incrementando la
+longitud no tiene ningún efecto sobre el array.
+
En conclusión
+
Para obtener el mejor rendimiento es recomendable siempre usar el bucle for
+y alamacenar en caché la propiedad length. El uso del bucle for in en un array
+es señal de un código mal escrito propenso a errores y un mal desempeño.
+
El constructor Array
Desde el constructor Array es ambiguo en la forma en que ocupa sus párametros,
+es recomendable siempre el uso de arrays literales - la notación [] -
+cuando se crean nuevos arrays.
En casos cuando sólo hay un argumento pasado al constructor del Array,
+y que el argumento es un Número, el contructor devolverá un array disperso
+con la propiedad length establecida al valor del argumento. Esto debe señalarse
+que la propiedad lengthsólo del nuevo array se establecerá de esa manera,
+los índices reales de la matriz no se iniciará.
+
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // falso, el índice no se ha establecido
+
El comportamiento de poder establecer la longitud de un array inicial sólo es útil
+en algunos casos array, como la repetición de una cadena, en la que se evita el uso
+del código de bucle for.
+
new Array(count + 1).join(stringToRepeat);
+
En conclusión
+
El uso de un constructor Array debe ser devuelto como sea posible.
+Los literales son definitivamente preferidos. Estos son más cortos y tienen una
+sintaxis más limpia; por lo tanto, también se incrementa la legibilidad del código.
+
Types
Equality and Comparisons
JavaScript has two different ways of comparing the values of objects for equality.
+
The Equality Operator
+
The equality operator consists of two equal signs: ==
+
JavaScript features weak typing. This means that the equality operator
+coerces types in order to compare them.
The above table shows the results of the type coercion, and it is the main reason
+why the use of == is widely regarded as bad practice. It introduces
+hard-to-track-down bugs due to its complicated conversion rules.
+
Additionally, there is also a performance impact when type coercion is in play;
+for example, a string has to be converted to a number before it can be compared
+to another number.
+
The Strict Equality Operator
+
The strict equality operator consists of three equal signs: ===.
+
It works exactly like the normal equality operator, except that strict equality
+operator does not perform type coercion between its operands.
The above results are a lot clearer and allow for early breakage of code. This
+hardens code to a certain degree and also gives performance improvements in case
+the operands are of different types.
+
Comparing Objects
+
While both == and === are stated as equality operators, they behave
+differently when at least one of their operands happens to be an Object.
Here, both operators compare for identity and not equality; that is, they
+will compare for the same instance of the object, much like is in Python
+and pointer comparison in C.
+
In Conclusion
+
It is highly recommended to only use the strict equality operator. In cases
+where types need to be coerced, it should be done explicitly
+and not left to the language's complicated coercion rules.
+
The typeof Operator
The typeof operator (together with
+instanceof) is probably the biggest
+design flaw of JavaScript, as it is near of being completely broken.
+
Although instanceof still has its limited uses, typeof really has only one
+practical use case, which does not happen to be checking the type of an
+object.
+
+
The JavaScript Type Table
+
Value Class Type
+-------------------------------------
+"foo" String string
+new String("foo") String object
+1.2 Number number
+new Number(1.2) Number object
+true Boolean boolean
+new Boolean(true) Boolean object
+new Date() Date object
+new Error() Error object
+[1,2,3] Array object
+new Array(1, 2, 3) Array object
+new Function("") Function function
+/abc/g RegExp object (function in Nitro/V8)
+new RegExp("meow") RegExp object (function in Nitro/V8)
+{} Object object
+new Object() Object object
+
In the above table, Type refers to the value that the typeof operator returns.
+As can be clearly seen, this value is anything but consistent.
+
The Class refers to the value of the internal [[Class]] property of an object.
+
+
In order to retrieve the value of [[Class]], one has to make use of the
+toString method of Object.prototype.
+
The Class of an Object
+
The specification gives exactly one way of accessing the [[Class]] value,
+with the use of Object.prototype.toString.
In the above example, Object.prototype.toString gets called with the value of
+this being set to the object whose [[Class]] value should be
+retrieved.
+
+
Testing for Undefined Variables
+
typeof foo !== 'undefined'
+
The above will check whether foo was actually declared or not; just
+referencing it would result in a ReferenceError. This is the only thing
+typeof is actually useful for.
+
In Conclusion
+
In order to check the type of an object, it is highly recommended to use
+Object.prototype.toString because this is the only reliable way of doing so.
+As shown in the above type table, some return values of typeof are not defined
+in the specification; thus, they can differ across various implementations.
+
Unless checking whether a variable is defined, typeof should be avoided at
+all costs.
+
The instanceof Operator
The instanceof operator compares the constructors of its two operands. It is
+only useful when comparing custom made objects. Used on built-in types, it is
+nearly as useless as the typeof operator.
+
Comparing Custom Objects
+
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// This just sets Bar.prototype to the function object Foo
+// But not to an actual instance of Foo
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
One important thing to note here is that instanceof does not work on objects
+that originate from different JavaScript contexts (e.g. different documents
+in a web browser), since their constructors will not be the exact same object.
+
In Conclusion
+
The instanceof operator should only be used when dealing with custom made
+objects that originate from the same JavaScript context. Just like the
+typeof operator, every other use of it should be avoided.
+
Type Casting
JavaScript is a weakly typed language, so it will apply type coercion
+wherever possible.
+
// These are true
+new Number(10) == 10; // Number.toString() is converted
+ // back to a number
+
+10 == '10'; // Strings gets converted to Number
+10 == '+10 '; // More string madness
+10 == '010'; // And more
+isNaN(null) == false; // null converts to 0
+ // which of course is not NaN
+
+// These are false
+10 == 010;
+10 == '-10';
+
+
In order to avoid the above, use of the strict equal operator
+is highly recommended. Although this avoids a lot of common pitfalls, there
+are still many further issues that arise from JavaScript's weak typing system.
+
Constructors of Built-In Types
+
The constructors of the built in types like Number and String behave
+differently when being used with the new keyword and without it.
+
new Number(10) === 10; // False, Object and Number
+Number(10) === 10; // True, Number and Number
+new Number(10) + 0 === 10; // True, due to implicit conversion
+
Using a built-in type like Number as a constructor will create a new Number
+object, but leaving out the new keyword will make the Number function behave
+like a converter.
+
In addition, having literals or non-object values in there will result in even
+more type coercion.
+
The best option is to cast to one of the three possible types explicitly.
+
Casting to a String
+
'' + 10 === '10'; // true
+
By prepending an empty string, a value can easily be casted to a string.
+
Casting to a Number
+
+'10' === 10; // true
+
Using the unary plus operator, it is possible to cast to a number.
+
Casting to a Boolean
+
By using the not operator twice, a value can be converted a boolean.
Pero eval sólo ejecutará en ámbito local cuando es llamado directamentey
+el nombre de la función llamada es eval.
+
var foo = 1;
+function test() {
+ var foo = 2;
+ var bar = eval;
+ bar('foo = 3');
+ return foo;
+}
+test(); // 2
+foo; // 3
+
El uso de eval debe evitarse a toda costa. El 99.9% de su "uso" puede
+lograrse sin su uso..
+
eval disfrazado
+
Las funciones de tiempo de esperasetTimeout y setInterval pueden
+tomar un string como primer argumento. En este caso, el string siempre se ejecutará en
+el ámbito global ya que eval no ha sido llamado directamente.
+
Problemas de seguridad
+
eval es también un problema de seguridad ya que ejecuta cualquier código enviado,
+y nunca debe usarse con strings que no se conozcan o tengan un origen no confiable.
+
En conclusión
+
eval nunca debe ser usado, cualquier código que haga uso del mismo debe ser cuestionado
+en su funcionamiento, rendimiento y seguridad. En caso de que se necesite trabajar con
+eval, el diseño ha de ser cuestionado y no debe utilizarse en primer lugar, se
+debe usar un mejor diseño, que no requiera el uso de eval.
+
undefined y null
JavaScript tiene dos valores distintos para nothing, el más útil de estos dos
+es undefined.
+
El valor undefined
+
undefined es un tipo de dato con exactamente el mismo valor: undefined.
+
El lenguaje también define una variable global que tiene el valor de undefined,
+Esta variable es también llamada undefined. Sin embargo, esta variable no es una
+constante, ni es una palabra reservada del lenguaje. Esto significa que el valor
+puede ser sobreescrito fácilmente.
+
+
Algunos ejemplos cuando el valor retorna undefined:
+
+
Acceso a la variable global (sin modificar) undefined.
+
Retorna implícitamente las funciones que no posean la sentencia return.
+
Sentencia return que no retorna nada de forma explicíta.
+
Búsquedas de propiedades inexistentes.
+
Párametros de la función que no tienen ningún valor explicíto pasado.
+
Cualquier valor que se estable en undefined.
+
+
Manejar los cambios en el valor deChanges undefined
+
Dado que la variable undefined sólo tiene una copia del value de undefined, assigna un nuevo valor que no cambie el valor del
+tipoundefined.
+
Aún con el fin de comparar con el valor de undefined es necesario
+recuperar el valor de undefined primero.
+
Con el fin de proteger una posible sobreescritura en la variable undefined,
+una técnica común es agregar un párametro adicional a un
+wrapper anónimo, que consiga ningún párametro que se le pase.
+
var undefined = 123;
+(function(something, foo, undefined) {
+ // undefined en el ámbito local
+ // ahora hace referencia al valor
+
+})('Hello World', 42);
+
Otra forma de lograrlo un mismo efecto es declarar dentro un
+wrapper.
+
var undefined = 123;
+(function(something, foo) {
+ var undefined;
+ ...
+
+})('Hello World', 42);
+
La única diferencia, es que está versión es de 4 bytes más que utiliza, y en
+caso se comprima y no hay otra declaración de 'var' dentro del
+wrapper anónimo.
+
Uso de null
+
Mientras que undefined en el contexto del lenguaje JavaScript es muy usado
+en el sentido tradicional como null, el actual null (ambos literal y de un tipo)
+es más o menos que otro tipo de datos.
+
Es utilizado en algunos detalles internos de JavaScript (como declarar al final de un
+cadena de prototipo estableciendo Foo.prototype = null), pero en casi todos los
+casos, puede ser reemplazado por undefined.
+
Automatic Semicolon Insertion
Although JavaScript has C style syntax, it does not enforce the use of
+semicolons in the source code, so it is possible to omit them.
+
JavaScript is not a semicolon-less language. In fact, it needs the
+semicolons in order to understand the source code. Therefore, the JavaScript
+parser automatically inserts them whenever it encounters a parse
+error due to a missing semicolon.
Chances are very high that log does not return a function; therefore,
+the above will yield a TypeError stating that undefined is not a function.
+
In Conclusion
+
It is highly recommended to never omit semicolons; it is also advocated to
+keep braces on the same line with their corresponding statements and to never omit
+them for one single-line if / else statements. Both of these measures will
+not only improve the consistency of the code, but they will also prevent the
+JavaScript parser from changing its behavior.
+
Otros
setTimeout and setInterval
Since JavaScript is asynchronous, it is possible to schedule the execution of a
+function by using the setTimeout and setInterval functions.
+
+
function foo() {}
+var id = setTimeout(foo, 1000); // returns a Number > 0
+
When setTimeout gets called, it will return the ID of the timeout and schedule
+foo to run in approximately one thousand milliseconds in the future.
+foo will then get executed exactly once.
+
Depending on the timer resolution of the JavaScript engine that is running the
+code, as well as the fact that JavaScript is single threaded and other code that
+gets executed might block the thread, it is by no means a safe bet that one
+will get the exact delay that was specified in the setTimeout call.
+
The function that was passed as the first parameter will get called by the
+global object, which means that this inside the called function
+refers to that very object.
+
function Foo() {
+ this.value = 42;
+ this.method = function() {
+ // this refers to the global object
+ console.log(this.value); // will log undefined
+ };
+ setTimeout(this.method, 500);
+}
+new Foo();
+
+
Stacking Calls with setInterval
+
While setTimeout only runs the function once, setInterval - as the name
+suggests - will execute the function everyX milliseconds, but its use is
+discouraged.
+
When code that is being executed blocks the timeout call, setInterval will
+still issue more calls to the specified function. This can, especially with small
+intervals, result in function calls stacking up.
+
function foo(){
+ // something that blocks for 1 second
+}
+setInterval(foo, 100);
+
In the above code, foo will get called once and will then block for one second.
+
While foo blocks the code, setInterval will still schedule further calls to
+it. Now, when foo has finished, there will already be ten further calls to
+it waiting for execution.
+
Dealing with Possible Blocking Code
+
The easiest solution, as well as most controllable solution, is to use setTimeout within
+the function itself.
+
function foo(){
+ // something that blocks for 1 second
+ setTimeout(foo, 100);
+}
+foo();
+
Not only does this encapsulate the setTimeout call, but it also prevents the
+stacking of calls and it gives additional control. foo itself can now decide
+whether it wants to run again or not.
+
Manually Clearing Timeouts
+
Clearing timeouts and intervals works by passing the respective ID to
+clearTimeout or clearInterval, depending which set function was used in
+the first place.
+
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
Clearing all timeouts
+
Because there is no built-in method for clearing all timeouts and/or intervals,
+it is necessary to use brute force in order to achieve this functionality.
+
// clear "all" timeouts
+for(var i = 1; i < 1000; i++) {
+ clearTimeout(i);
+}
+
There might still be timeouts that are unaffected by this arbitrary number;
+therefore, is is instead recommended to keep track of all the timeout IDs, so
+they can be cleared specifically.
+
Hidden use of eval
+
setTimeout and setInterval can also take a string as their first parameter.
+This feature should never be used because it internally makes use of eval.
+
+
function foo() {
+ // will get called
+}
+
+function bar() {
+ function foo() {
+ // never gets called
+ }
+ setTimeout('foo()', 1000);
+}
+bar();
+
Since eval is not getting called directly in this case, the string
+passed to setTimeout will get executed in the global scope; thus, it will
+not use the local variable foo from the scope of bar.
+
It is further recommended to not use a string for passing arguments to the
+function that will get called by either of the timeout functions.
+
function foo(a, b, c) {}
+
+// NEVER use this
+setTimeout('foo(1,2, 3)', 1000)
+
+// Instead use an anonymous function
+setTimeout(function() {
+ foo(a, b, c);
+}, 1000)
+
+
In Conclusion
+
Never should a string be used as the parameter of setTimeout or
+setInterval. It is a clear sign of really bad code, when arguments need
+to be supplied to the function that gets called. An anonymous function should
+be passed that then takes care of the actual call.
+
Furthermore, the use of setInterval should be avoided because its scheduler is not
+blocked by executing JavaScript.
+
\ No newline at end of file
diff --git a/site/favicon.ico b/favicon.ico
similarity index 100%
rename from site/favicon.ico
rename to favicon.ico
diff --git a/fi/index.html b/fi/index.html
new file mode 100644
index 0000000..a2bb8a2
--- /dev/null
+++ b/fi/index.html
@@ -0,0 +1,1019 @@
+JavaScript-puutarha
Yleisesti luullaan ettei numeroliteraaleja voida käyttää olioina. Tämä johtuu viasta JavaScriptin parserissa. Se yrittää parsia numeron pistenotaatiota liukulukuliteraalina.
+
2.toString(); // palauttaa SyntaxError-virheen
+
Tämä voidaan välttää esimerkiksi seuraavasti.
+
2..toString(); // toinen piste tunnistuu oikein
+2 .toString(); // huomaa pisteen vasemmalla puolen oleva väli
+(2).toString(); // 2 arvioidaan ensi
+
Oliot tietotyyppinä
+
JavaScriptin olioita voidaan käyttää myös hajautustauluna, koska ne muodostavat pääasiassa avaimien ja niihin liittyvien arvojen välisen mappauksen.
+
Olioliteraalinotaatiota - {} - käyttäen voidaan luoda tyhjä olio. Tämä olio periiObject.prototype-olion eikä sille ole määritelty omia ominaisuuksia.
+
var foo = {}; // uusi, tyhjä olio
+
+// uusi, tyhjä olio, joka sisältää ominaisuuden 'test' arvolla 12
+var bar = {test: 12};
+
Pääsy ominaisuuksiin
+
Olion ominaisuuksiin voidaan päästä käsiksi kahta eri tapaa käyttäen. Siihen voidaan käyttää joko piste- tai hakasulkunotaatiota.
+
var foo = {name: 'kitten'}
+foo.name; // kitten
+foo['name']; // kitten
+
+var get = 'name';
+foo[get]; // kitten
+
+foo.1234; // SyntaxError
+foo['1234']; // toimii
+
Kumpikin notaatio toimii samalla tavoin. Ainut ero liittyy siihen, että hakasulkunotaation avulla ominaisuuksien arvoja voidaan asettaa dynaamisesti. Se sallii myös muuten hankalien, virheeseen johtavien nimien käyttämisen.
+
Ominaisuuksien poistaminen
+
Ainut tapa poistaa olion ominaisuus on käyttää delete-operaattoria. Ominaisuuden asettaminen joko arvoon undefined tai null poistaa vain siihen liittyneen arvon muttei itse avainta.
+
var obj = {
+ bar: 1,
+ foo: 2,
+ baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ console.log(i, '' + obj[i]);
+ }
+}
+
Yllä oleva koodi tulostaa sekä both undefined että foo null. Ainoastaan baz on poistettu. Täten sitä ei myöskään näy tulosteessa.
+
Avainnotaatio
+
var test = {
+ 'case': 'Olen avainsana, joten minun tulee olla merkkijono',
+ delete: 'Myös minä olen avainsana' // palauttaa SyntaxError-virheen
+};
+
Olioiden ominaisuuksia voidaan notatoida käyttäen joko pelkkiä merkkejä tai merkkijonoja. Toisesta JavaScriptin suunnitteluvirheestä johtuen yllä oleva koodi palauttaa SyntaxError-virheen ECMAScript 5:ttä edeltävissä versioissa.
+
Tämä virhe johtuu siitä, että delete on avainsana. Täten se tulee notatoida merkkijonona. Tällöin myös vanhemmat JavaScript-tulkit ymmärtävät sen oikein.
+
Prototyyppi
JavaScript ei sisällä klassista perintämallia. Sen sijaan se käyttää prototyyppeihin pohjautuvaa ratkaisua.
+
Usein tätä pidetään JavaScriptin eräänä suurimmista heikkouksista. Itse asiassa prototyyppipohjainen perintämalli on voimakkaampi kuin klassinen malli. Sen avulla voidaan mallintaa klassinen malli melko helposti. Toisin päin mallintaminen on huomattavasti vaikeampaa.
+
JavaScript on käytännössä ainut laajasti käytetty kieli, joka tarjoaa tuen prototyyppipohjaiselle perinnälle. Tästä johtuen mallien väliseen eroon tottuminen voi viedä jonkin akaa.
+
Ensimmäinen suuri ero liittyy siihen, kuinka perintä toimii. JavaScriptissä se pohjautuu erityisiin prototyyppiketjuihin.
+
+
function Foo() {
+ this.value = 42;
+}
+Foo.prototype = {
+ method: function() {}
+};
+
+function Bar() {}
+
+// Aseta Barin prototyypin uuteen Foo-olioon
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Terve maailma';
+
+// Huolehdi siitä, että Bar on todellinen konstruktori
+Bar.prototype.constructor = Bar;
+
+var test = new Bar() // luo uusi bar
+
+// Prototyyppiketju
+test [Bar-olio]
+ Bar.prototype [Foo-olio]
+ { foo: 'Terve maailma' }
+ Foo.prototype
+ { method: ... }
+ Object.prototype
+ { toString: ... /* jne. */ }
+
Yllä olio test perii sekä Bar.prototype- että Foo.prototype-olion. Tällöin se pääsee käsiksi Foo:ssa määriteltyy funktioon method. Se pääsee käsiksi myös ominaisuuteen value, jonka luotu Foo-olio sisältää prototyypissään. On tärkeää huomata, että new Bar()ei luo uutta Foo-oliota vaan käyttää uudelleen sen prototyyppiin asetettua. Tässä tapauksessa kaikki Bar-oliot jakavat siis samanvalue-ominaisuuden.
+
+
Ominaisuushaut
+
Kun olion ominaisuuksien arvoa haetaan, JavaScript käy prototyyppiketjua läpi ylöspäin, kunnes se löytää ominaisuuden nimeä vastaavan arvon.
+
Jos se saavuttaa ketjun huipun - Object.prototype-olion - eikä ole vieläkään löytänyt haettua ominaisuutta, se palauttaa undefined arvon sen sijaan.
+
Prototyyppi-ominaisuus
+
Vaikka Prototyyppi-ominaisuutta käytetään prototyyppiketjujen rakentamiseen, voidaan siihen asettaa mikä tahansa arvo. Mikäli arvo on primitiivi, se yksinkertaisesti jätetään huomiotta.
+
function Foo() {}
+Foo.prototype = 1; // ei vaikutusta
+
Kuten esimerkissä yllä, prototyyppiin on mahdollista asettaa olioita. Tällä tavoin prototyyppiketjuja voidaan koostaa dynaamisesti.
+
Suorituskyky
+
Prototyyppiketjussa korkealla olevien ominaisuuksien hakeminen voi hidastaa koodin kriittisiä osia. Tämän lisäksi olemattomien ominaisuuksien hakeminen käy koko ketjun läpi.
+
Ominaisuuksia iteroidessa prototyyppiketjun jokainen ominaisuus käydään läpi.
+
Natiivien prototyyppien laajentaminen
+
JavaScript mahdollistaa Object.prototype-olion sekä muiden natiivityyppien laajentamisen.
+
Tätä tekniikkaa kutsutaan nimellä apinapätsäämiseksi. Se rikkoo kapseloinnin. Vaikka yleisesti käytetyt alustat, kuten Prototype, käyttävätkin sitä, ei ole olemassa yhtään hyvää syytä, minkä takia natiivityyppejä tulisi laajentaa epästandardilla* toiminnallisuudella.
+
Ainut hyvä syy on uudempien JavaScript-tulkkien sisältämien ominaisuuksien siirtäminen vanhemmille alustoille. Eräs esimerkki tästä on Array.forEach.
+
Yhteenveto
+
Ennen kuin kirjoitat monimutkaista prototyyppiperintää hyödyntävää koodia, on olennaista, että ymmärrät täysin kuinka se toimii. Ota huomioon myös prototyyppiketjujen pituus ja riko niitä tarpeen mukaan välttääksesi suorituskykyongelmia. Huomioi myös, että natiiveja prototyyppejä ei tule laajentaa milloinkaan ellei kyse ole vain yhteensopivuudesta uudempien JavaScript-ominaisuuksien kanssa.
+
hasOwnProperty
Jotta voimme tarkistaa onko olion ominaisuus määritelty siinä itsessään, tulee käyttää erityistä Object.prototype-oliosta periytyvää hasOwnProperty-metodia. Tällä tavoin vältämme prototyyppiketjun sisältämät ominaisuudet.
+
+
hasOwnProperty on ainut JavaScriptin sisältämä metodi, joka käsittelee ominaisuuksia eikä käy prototyyppiketjun sisältöä läpi.
Ainoastaan hasOwnProperty palauttaa oikean ja odotetun tuloksen. Sen tietäminen on olennaista minkä tahansa olion ominaisuuksia iteroidessa. Tämä on ainut tapa löytää olion itsensä ominaisuudet prototyyppiketjusta riippumatta.
+
hasOwnProperty ominaisuutena
+
JavaScript ei suojele hasOwnProperty-metodin nimeä. Täten on mahdollista, että olio voi sisältää samannimisen ominaisuuden. Jotta voimme saada oikeita tuloksia, tulee sen sijaan käyttää ulkoistahasOwnProperty-metodia.
+
var foo = {
+ hasOwnProperty: function() {
+ return false;
+ },
+ bar: 'Olkoon vaikka lohikäärmeitä'
+};
+
+foo.hasOwnProperty('bar'); // palauttaa aina epätoden
+
+// Käytä toisen olion hasOwnProperty-metodia ja kutsu sitä asettamalla
+// 'this' foohon
+({}).hasOwnProperty.call(foo, 'bar'); // tosi
+
Yhteenveto
+
Mikäli pitää selvittää kuuluuko ominaisuus olioon vai ei, ainoastaanhasOwnProperty voi kertoa sen. Tämän lisäksi on suositeltavaa käyttää hasOwnProperty-metodia osana jokaistafor in-luuppia. Tällä tavoin voidaan välttää natiivien prototyyppien laajentamiseen liittyviä ongelmia.
+
for in-luuppi
Aivan kuten in-operaattori, myös for in-luuppi käy olion prototyyppiketjun läpi iteroidessaan sen ominaisuuksia.
+
+
// Object.prototypen myrkyttäminen
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+ console.log(i); // tulostaa sekä bar että moo
+}
+
Koska for in-luupin käytöstapaa ei voida muokata suoraan, tulee ei-halutut ominaisuudet karsia itse luupin sisällä. Tämä on mahdollista käyttäen Object.prototype-olion hasOwnProperty-metodia.
+
+
hasOwnProperty-metodin käyttäminen karsimiseen
+
// foo kuten yllä
+for(var i in foo) {
+ if (foo.hasOwnProperty(i)) {
+ console.log(i);
+ }
+}
+
Tämä versio on ainut oikea. Se tulostaa ainoastaanmoo, koska se käyttää hasOwnProperty-metodia oikein. Kun se jätetään pois, on koodi altis virheille tapauksissa, joissa prototyyppejä, kuten Object.prototype, on laajennettu.
+
Prototype on eräs yleisesti käytetty ohjelmointialusta, joka tekee näin. Kun kyseistä alustaa käytetään, for in-luupit, jotka eivät käytä hasOwnProperty-metodia, menevät varmasti rikki.
+
Yhteenveto
+
On suositeltavaa käyttää ainahasOwnProperty-metodia. Ei ole kannattavaa tehdä ajoympäristöön tai prototyyppeihin liittyviä oletuksia.
+
Funktiot
Funktiomääreet ja lausekkeet
JavaScriptissä funktiot ovat ensimmäisen luokan olioita. Tämä tarkoittaa sitä, että niitä voidaan välittää kuten muitakin arvoja. Usein tätä käytetään takaisinkutsuissa käyttämällä nimettömiä, mahdollisesti asynkronisia funktioita.
+
function-määre
+
function foo() {}
+
Yllä oleva funktio hilataan ennen ohjelman suorituksen alkua. Se näkyy kaikkialle näkyvyysalueessaan, jossa se on määritelty. Tämä on totta jopa silloin, jos sitä kutsutaan ennen määrittelyään.
+
foo(); // Toimii, koska foo on luotu ennen kuin koodi suoritetaan
+function foo() {}
+
function-lauseke
+
var foo = function() {};
+
Tämä esimerkki asettaa nimeämättömän ja nimettömän funktion muuttujan foo arvoksi.
+
foo; // 'undefined'
+foo(); // tämä palauttaa TypeError-virheen
+var foo = function() {};
+
var on määre. Tästä johtuen se hilaa muuttujanimen foo ennen kuin itse koodia ryhdytään suorittamaan.
+
Sijoituslauseet suoritetaan vasta kun niihin saavutaan. Tästä johtuen foo saa arvokseen undefined ennen kuin varsinaista sijoitusta päästään suorittamaan.
+
Nimetty funktiolauseke
+
Nimettyjen funktioiden sijoitus tarjoaa toisen erikoistapauksen.
+
var foo = function bar() {
+ bar(); // Toimii
+}
+bar(); // ReferenceError
+
Tässä tapauksessa bar ei ole saatavilla ulommalla näkyvyysalueessa. Tämä johtuu siitä, että se on sidottu foo:n sisälle. Tämä johtuu siitä, kuinka näkyvyysalueet ja niihin kuuluvat jäsenet tulkitaan. Funktion nimi on aina saatavilla sen paikallisessa näkyvyysalueessa itsessään.
+
Kuinka this toimii
JavaScripting this toimii eri tavoin kuin useimmissa kielissä. Tarkalleen ottaen on olemassa viisi eri tapaa, joiden mukaan sen arvo voi määrittyä.
+
Globaali näkyvyysalue
+
this;
+
Kun this-muuttujaa käytetään globaalissa näkyvyysalueessa, viittaa se globaaliin olioon.
+
Funktiokutsu
+
foo();
+
Tässä tapauksessa this viittaa jälleen globaaliin olioon.
+
+
Metodikutsu
+
test.foo();
+
Tässä esimerkissä this viittaa test-olioon.
+
Konstruktorikutsu
+
new foo();
+
Funktiokutsu, jota edeltää new-avainsana toimii konstruktorina. Funktion sisällä this viittaa juuri luotuunObject-olioon.
+
this-arvon asettaminen
+
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // taulukko laajenee alla olevaksi
+foo.call(bar, 1, 2, 3); // tuloksena a = 1, b = 2, c = 3
+
Function.prototype-olion call- ja apply-metodeita käytettäessä this-ominaisuuden arvo määrittyy ensimmäisen annetun argumentin perusteella.
+
Seurauksena foo-funktion sisältämä this asettuu bar-olioon toisin kuin perustapauksessa.
+
+
Yleisiä ongelmakohtia
+
Useimmat näistä tapauksista ovat järkeviä. Ensimmäistä niistä tosin voidaan pitää suunnitteluvirheenä, jolle ei ole mitään järkevää käyttöä ikinä.
+
Foo.method = function() {
+ function test() {
+ // this asettuu globaaliin olioon
+ }
+ test();
+}
+
Yleisesti luullaan, että test-funktion sisältämä this viittaa tässä tapauksessa Foo-olioon. Todellisuudessa se ei kuitenkaan tee näin.
+
Jotta Foo-olioon voidaan päästä käsiksi test-funktion sisällä, tulee metodin sisälle luoda paikallinen muuttuja, joka viittaa Foo-olioon.
+
Foo.method = function() {
+ var that = this;
+ function test() {
+ // Käytä thatia thissin sijasta
+ }
+ test();
+}
+
that on normaali nimi, jota käytetään yleisesti viittaamaan ulompaan this-muuttujaan. Sulkeumia käytettäessä this-arvoa voidaan myös välittää edelleen.
+
Metodien sijoittaminen
+
JavaScriptissä funktioita ei voida nimetä uudelleen eli siis sijoittaa edelleen.
+
var test = someObject.methodTest;
+test();
+
Ensimmäisestä tapauksesta johtuen test toimii kuten normaali funktiokutsu; tällöin sen sisältämä this ei enää osoita someObject-olioon.
Kun method-metodia kutsutaan Bar-oliossa, sen this viittaa juurikin tuohon olioon.
+
Sulkeumat ja viitteet
Sulkeumat ovat eräs JavaScriptin voimakkaimmista ominaisuuksista. Näkyvyysalueilla on siis aina pääsy ulompaan näkyvyysalueeseensa. Koska JavaScriptissä ainut tapa määritellä näkyvyyttä pohjautuu funktionäkyvyyteen, kaikki funktiot käyttäytyvät oletuksena sulkeumina.
Tässä tapauksessa Counter palauttaa kaksi sulkeumaa. Funktion increment lisäksi palautetaan myös funktio get. Kumpikin funktio viittaaCounter-näkyvyysalueeseen ja pääsee siten käsiksi count-muuttujan arvoon.
+
Miksi paikalliset muuttujat toimivat
+
JavaScriptissä ei voida viitata näkyvyysalueisiin. Tästä seuraa etteicount-muuttujan arvoon voida päästä käsiksi funktion ulkopuolelta. Ainoastaan nämä kaksi sulkeumaa mahdollistavat sen.
+
var foo = new Counter(4);
+foo.hack = function() {
+ count = 1337;
+};
+
Yllä oleva koodi ei muuta muuttujan count arvoa Counter-näkyvyysalueessa. Tämä johtuu siitä, että foo.hack-ominaisuutta ei ole määritelty kyseisessä näkyvyysalueessa. Sen sijaan se luo - tai ylikirjoittaa - globaalin muuttujan count.
+
Sulkeumat luupeissa
+
Usein sulkeumia käytetään väärin luuppien sisällä indeksimuuttujien arvon kopiointiin.
+
for(var i = 0; i < 10; i++) {
+ setTimeout(function() {
+ console.log(i);
+ }, 1000);
+}
+
Yllä oleva koodi ei tulosta numeroita nollastayhdeksään. Sen sijaan se tulostaa numeron 10 kymmenen kertaa.
+
Nimetön funktio saa viitteeni-muuttujaan console.log-kutsuhetkellä. Tällöin luuppi on jo suoritettu ja i:n arvoksi on asetettu 10.
+
Päästäksemme haluttuun lopputulokseen on tarpeen luoda kopioi:n arvosta.
Jokainen JavaScriptin näkyvyysalue pääsee käsiksi erikoismuuttujaan nimeltään arguments. Tämä muuttuja sisältää listan kaikista funktiolle annetuista argumenteista.
+
+
arguments-olio ei ole Array. Sen semantiikka, erityisesti length-ominaisuus, muistuttaa taulukkoa. Tästä huolimatta se ei peri Array.prototype:stä ja on itse asiassa Object.
+
Tästä johtuen arguments-olioon ei voida soveltaa normaaleja taulukkometodeja, kuten push, pop tai slice. Vaikka iterointi onnistuukin for-luuppeja käyttäen, tulee se muuttaa aidoksi Array-olioksi ennen kuin siihen voidaan soveltaa näitä metodeja.
+
Array-olioksi muuttaminen
+
Alla oleva koodi palauttaa uuden Array-olion, joka sisältää arguments-olion kaikki jäsenet.
+
Array.prototype.slice.call(arguments);
+
Tämä muutos on luonteeltaan hidas eikä sitä suositella käytettävän suorituskykyä vaativissa osissa koodia.
+
Argumenttien antaminen
+
Funktiosta toiselle voidaan antaa argumentteja seuraavasti.
+
function foo() {
+ bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+ // tee jotain
+}
+
Toinen keino on käyttää sekä call- että apply-funktioita yhdessä ja luoda nopeita, sitomattomia kääreitä.
+
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+ console.log(this, a, b, c);
+};
+
+// Luo "metodin" sitomaton versio
+// Se ottaa seuraavat parametrit: this, arg1, arg2...argN
+Foo.method = function() {
+
+ // Tulos: Foo.prototype.method.call(this, arg1, arg2... argN)
+ Function.call.apply(Foo.prototype.method, arguments);
+};
+
Muodolliset parametrit ja argumenttien indeksit
+
arguments-olio luo sekä getter- että setter-funktiot sekä sen ominaisuuksille että myös funktion muodollisille parametreille.
+
Tästä seuraa, että muodollisen parametrin arvon muuttaminen muuttaa myös arguments-olion vastaavan ominaisuuden arvoa ja toisin päin.
+
function foo(a, b, c) {
+ arguments[0] = 2;
+ a; // 2
+
+ b = 4;
+ arguments[1]; // 4
+
+ var d = c;
+ d = 9;
+ c; // 3
+}
+foo(1, 2, 3);
+
Suorituskykyyn liittyviä myyttejä ja totuuksia
+
arguments-olio luodaan aina paitsi jos se on jo julistettu nimenä funktiossa tai sen muodollisena parametrina. Tämä siitä huolimatta käytetäänkö sitä vai ei.
+
Sekä getter- ja setter-funktiot luodaan aina. Tästä seuraa, että niiden käytöllä ei ole juurikaan merkitystä suorituskyvyn kannalta.
+
+
On kuitenkin eräs tapaus, jossa suorituskyky kärsii. Tämä liittyy arguments.callee-ominaisuuden käyttöön.
+
function foo() {
+ arguments.callee; // tee jotain tällä funktio-oliolla
+ arguments.callee.caller; // ja kutsuvalla funktio-oliolla
+}
+
+function bigLoop() {
+ for(var i = 0; i < 100000; i++) {
+ foo(); // normaalisti tämä olisi inline-optimoitu
+ }
+}
+
Yllä olevassa koodissa foo-kutsua ei voida käsitellä avoimesti, koska sen tulee tietää sekä itsestään että kutsujasta. Sen lisäksi, että se haittaa suorituskykyä, rikkoo se myös kapseloinnin. Tässä tapauksessa funktio voi olla riippuvainen tietystä kutsuympäristöstä.
+
On erittäin suositeltavaa ettei arguments.callee-ominaisuutta tai sen ominaisuuksia käytetä ikinä.
+
+
Konstruktorit
JavaScriptin konstruktorit eroavat monista muista kielistä selvästi. Jokainen funktiokutsu, joka sisältää avainsanan new toimii konstruktorina.
+
Konstruktorin - kutsutun funktion - this-muuttujan arvo viittaa luotuun Object-olioon. Tämän uuden olion prototyyppi asetetaan osoittamaan konstruktorin kutsuman funktio-olion prototyyppiin.
+
Mikäli kutsuttu funktio ei sisällä selvää return-lausetta, tällöin se palauttaa this-muuttujan arvon eli uuden olion.
+
function Foo() {
+ this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+ console.log(this.bla);
+};
+
+var test = new Foo();
+
Yllä Foo:ta kutsutaan konstruktorina. Juuri luodun olion prototyyppi asetetaan osoittamaan ominaisuuteen Foo.prototype.
+
Selvän return-lausekkeen tapauksessa funktio palauttaa ainoastaan määritellyn lausekkeen arvon. Tämä pätee tosin vain jos palautettava arvo on tyypiltään Object.
Tässä tapauksessa molemmat Bar-funktion kutsut käyttäytyvät samoin. Kumpikin kutsu palauttaa olion, joka sisältää method-ominaisuuden. Kyseinen ominaisuus on sulkeuma.
+
On myös tärkeää huomata, että kutsu new Bar()ei vaikuta palautetun olion prototyyppiin. Vaikka luodun olion prototyyppi onkin asetettu, Bar ei palauta ikinä kyseistä prototyyppioliota.
+
Yllä olevassa esimerkissä new-avainsanan käytöllä tai käyttämällä jättämisellä ei ole toiminnan kannalta mitään merkitystä.
+
Tehtaiden käyttö uusien olioiden luomiseen
+
Usein suositellaan new-avainsanan käytön välttämistä. Tämä johtuu siitä, että sen käyttämättä jättäminen voi johtaa bugeihin.
+
Sen sijaan suositellaan käytettävän tehdasta, jonka sisällä varsinainen olio konstruoidaan.
Vaikka yllä oleva esimerkki välttää new-avainsanan käyttöä ja tekee paikallisten muuttujien käytön helpommaksi, sisältää se joitain huonoja puolia.
+
+
Se käyttää enemmän muistia. Tämä johtuu siitä, että luodut oliot eivät jaa prototyypin metodeja.
+
Perinnän tapauksessa tehtaan tulee kopioida toisen olion kaikki metodit tai vaihtoehtoisesti asettaa kyseinen olio toisen prototyypiksi.
+
Prototyyppiketjun käsitteen unohtaminen on vain välttääksemme new-avainsanan käyttöä on vastoin kielen filosofista perustaa.
+
+
Yhteenveto
+
Vaikka new-avainsanan käyttö voi johtaa bugeihin, prototyyppien käyttöä ei kannata unohtaa kokonaan. Loppujen lopuksi kyse on siitä, kumpi tapa sopii sovelluksen tarpeisiin paremmin. On erityisen tärkeää valita jokin tietty tapa ja pitäytyä sen käytössä.
+
Näkyvyysalueet ja nimiavaruudet
Vaikka JavaScript-käyttääkin aaltosulkeita blokkien ilmaisuun, se ei tue blokkinäkyvyyttä. Tämä tarkoittaa sitä, että kieli tukee ainoastaan *funktionäkyvyyttä.
+
function test() { // näkyvyysalue
+ for(var i = 0; i < 10; i++) { // tämä ei ole näkyvyysalue
+ // count
+ }
+ console.log(i); // 10
+}
+
+
JavaScript ei myöskään sisällä erityistä tukea nimiavaruuksille. Tämä tarkoittaa sitä, että kaikki määritellään oletuksena globaalissa nimiavaruudessa.
+
Joka kerta kun muuttujaan viitataan, JavaScript käy kaikki näkyvyysalueet läpi alhaalta lähtien. Mikäli se saavuttaa globaalin näkyvyystalueen, eikä löydä haettua nimeä, se palauttaa ReferenceError-virheen.
+
Riesa nimeltä globaalit muuttujat
+
// skripti A
+foo = '42';
+
+// skripti B
+var foo = '42'
+
Yllä olevat skriptit käyttäytyvät eri tavoin. Skripti A määrittelee muuttujan nimeltä fooglobaalissa näkyvyysalueessa. Skripti B määrittelee foo-muuttujan vallitsevassa näkyvyysalueessa.
+
Tämä ei ole sama asia. var-avainsanan käyttämättä jättäminen voi johtaa vakaviin seurauksiin.
var-avainsanan pois jättäminen johtaa siihen, että funktio test ylikirjoittaa foo:n arvon. Vaikka tämä ei välttämättä vaikutakaan suurelta asialta, tuhansien rivien tapauksessa var-avainsanan käyttämättömyys voi johtaa vaikeasti löydettäviin bugeihin.
+
// globaali näkyvyysalue
+var items = [/* joku lista */];
+for(var i = 0; i < 10; i++) {
+ subLoop();
+}
+
+function subLoop() {
+ // aliluupin näkyvyysalue
+ for(i = 0; i < 10; i++) { // hups, var jäi pois
+ // jotain makeaa ja hienoa
+ }
+}
+
Tässä tapauksessa ulomman luupin suoritus lopetetaan ensimmäisen subLoop-kutsun jälkeen. Tämä johtuu siitä, että se ylikirjoittaa i:n globaalin arvon. Mikäli jälkimmäisessä luupissa olisi käytetty var-avainsanaa, olisi ikävyyksiltä vältytty. var-avainsanaa ei siis tule ikinä jättää pois ellei siihen ole hyvää syytä.
+
Paikalliset muuttujat
+
Ainoastaan funktion parametrit ja muuttujat, jotka sisältävät var-määreen ovat paikallisia.
+
// globaali näkyvyysalue
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+ // paikallinen näkyvyysalue
+ i = 5;
+
+ var foo = 3;
+ bar = 4;
+}
+test(10);
+
foo ja i ovatkin test-funktiolle paikallisia. bar sijoitus muuttaa globaalin muuttujan arvoa.
+
Hilaaminen
+
JavaScript hilaa määreitä. Tämä tarkoittaa sitä, että sekä var-lausekkeet että function-määreet siirretään ne sisältävän näkyvyysalueen huipulle.
+
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+ if (false) {
+ goo = 1;
+
+ } else {
+ var goo = 2;
+ }
+ for(var i = 0; i < 100; i++) {
+ var e = data[i];
+ }
+}
+
Yllä olevaa koodia muutetaan ennen suoritusta. JavaScript siirtää var-lausekkeet ja function-määreet lähimmän näkyvyysalueen huipulle.
+
// var-lausekkeet siirrettiin tänne
+var bar, someValue; // oletuksena 'undefined'
+
+// myös funktio-määre siirtyi tänne
+function test(data) {
+ var goo, i, e; // ei blokkinäkyvyyttä, siirretään siis tänne
+ if (false) {
+ goo = 1;
+
+ } else {
+ goo = 2;
+ }
+ for(i = 0; i < 100; i++) {
+ e = data[i];
+ }
+}
+
+bar(); // TypeError-virhe, baria ei ole vielä määritelty
+someValue = 42; // hilaus ei koske sijoituksia
+bar = function() {};
+
+test();
+
Sen lisäksi, että puuttuva blokkinäkyvyys siirtää var-lausekkeet luuppien ulkopuolelle, tekee se myös eräistä if-rakenteista vaikeita käsittää.
+
Alkuperäisessä koodissa if-lause näytti muokkaavan globaalia muuttujaagoo. Todellisuudessa se muokkaa paikallista muuttujaa varsinaisen hilauksen jälkeen.
+
Seuraava koodi saattaisi ensi näkemältä aiheuttaa ReferenceError-virheen. Näin ei kuitenkaan tapahdu hilauksen ansiosta.
+
// onko SomeImportantThing alustettu
+if (!SomeImportantThing) {
+ var SomeImportantThing = {};
+}
+
Tämä toimii, koska var-lauseke on hilattu globaalin näkyvyysalueen huipulle.
+
var SomeImportantThing;
+
+// mahdollista alustuskoodia
+
+// onhan se alustettu
+if (!SomeImportantThing) {
+ SomeImportantThing = {};
+}
+
Nimienerottelujärjestys
+
Kaikki JavaScriptin näkyvyysalueet, globaalin näkyvyysalue mukaanlukien, sisältävät erikoismuuttujan this. this viittaa tämänhetkiseen olioon.
+
Funktioiden näkyvyysalueet sisältävät myös arguments-olion. Se sisältää funktiolle annetut argumentit.
+
Mikäli näkyvyysalueen sisällä pyritään pääsemään käsiksi esimerkiksi foo:n arvoon JavaScript käyttäytyy seuraavasti:
+
+
Mikäli var foo-lauseke löytyy tämänhetkisestä näkyvyysalueesta, käytä sen arvoa.
+
Mikäli eräs funktion parametreista on foo, käytä sitä.
+
Mikäli funktion nimi itsessään on foo, käytä sitä.
+
Siirry ulompaan näkyvyysalueeseen ja suorita #1 uudelleen.
+
+
+
Nimiavaruudet
+
Globaalin nimiavaruuden ongelmana voidaan pitää nimitörmäyksiä. JavaScriptissä tätä ongelmaa voidaan kiertää käyttämällä nimettömiä kääreitä.
Samaan lopputulokseen voidaan päästä myös hieman eri syntaksia käyttäen.
+
// Kaksi muuta tapaa
++function(){}();
+(function(){}());
+
Yhteenveto
+
On suositeltavaa käyttää nimettömiä kääreitä nimiavaruuksina. Sen lisäksi, että se suojelee koodia nimitörmäyksiltä, se tarjoaa keinon jaotella ohjelma paremmin.
+
Globaalien muuttujien käyttöä pidetään yleisesti huonona tapana. Mikä tahansa niiden käyttö viittaa huonosti kirjoitettuun, virheille alttiiseen ja hankalasti ylläpidettävään koodiin.
+
Taulukot
Taulukon iterointi ja attribuutit
Vaikka taulukot ovatkin JavaScript-olioita, niiden tapauksessa ei välttämättä kannata käyttää for in loop-luuppia. Pikemminkin tätä tapaa tulee välttää.
+
+
for in-luuppi iteroi kaikki prototyyppiketjun sisältämät ominaisuudet. Tämän vuoksi tulee käyttää erityistä hasOwnProperty-metodia, jonka avulla voidaan taata, että käsitellään oikeita ominaisuuksia. Tästä johtuen iteroint on jo lähtökohtaisesti jopa kaksikymmentä kertaa hitaampaa kuin normaalin for-luupin tapauksessa.
+
Iterointi
+
Taulukkojen tapauksessa paras suorituskyky voidaan saavuttaa käyttämällä klassista for-luuppia.
+
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+ console.log(list[i]);
+}
+
Edelliseen esimerkkiin liittyy yksi mutta. Listan pituus on tallennettu välimuistiin erikseen käyttämällä l = list.length-lauseketta.
+
Vaikka length-ominaisuus määritelläänkin taulukossa itsessään, arvon hakeminen sisältää ylimääräisen operaation. Uudehkot JavaScript-ympäristöt saattavat optimoida tämän tapauksen. Tästä ei kuitenkaan ole mitään takeita.
+
Todellisuudessa välimuistin käytön pois jättäminen voi hidastaa luuppia jopa puolella.
+
length-ominaisuus
+
length-ominaisuuden getteri palauttaa yksinkertaisesti taulukon sisältämien alkioiden määrän. Sen setteriä voidaan käyttää taulukon typistämiseen.
Pituuden pienemmäksi asettaminen typistää taulukkoa. Sen kasvattaminen ei kuitenkaan vaikuta mitenkään.
+
Yhteenveto
+
Parhaan suorituskyvyn kannalta on parhainta käyttää tavallista for-luuppia ja tallentaa length-ominaisuus välimuistiin. for in-luupin käyttö taulukon tapauksessa on merkki huonosti kirjoitetusta koodista, joka on altis bugeille ja heikolle suorituskyvylle.
+
Array-konstruktori
Array-oletuskonstruktorin käytös ei ole lainkaan yksiselitteistä. Tämän vuoksi suositellaankin, että konstruktorin sijasta käytetään literaalinotaatiota [].
Mikäli Array-konstruktorille annetaan vain yksi argumentti ja se on tyypiltään Number, konstruktori palauttaa uuden harvan taulukon, jonka length-attribuutti on asetettu annetun numeron mukaisesti. On tärkeää huomata, että ainoastaanlength asetetaan tällä tavoin, todellisia taulukon indeksejä ei alusteta.
+
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // false, indeksiä ei ole alustettu
+
Tämä on käytännöllistä vain harvoin, kuten merkkijonon toiston tapauksessa. Tällöin voidaan välttää for-luupin käyttämistä.
+
new Array(count + 1).join(stringToRepeat);
+
Yhteenveto
+
Array-konstruktorin käyttöä tulee käyttää niin paljon kuin suinkin mahdollista. Sen sijaan on suositeltavaa käyttää literaalinotaatiota. Literaalit ovat lyhyempiä ja niiden syntaksi on selkeämpi. Tämän lisäksi ne tekevät koodista luettavampaa.
+
Tyypit
Yhtäsuuruus ja vertailut
JavaScript sisältää kaksi erilaista tapaa, joiden avulla olioiden arvoa voidaan verrata toisiinsa.
+
Yhtäsuuruusoperaattori
+
Yhtäsuuruusoperaattori koostuu kahdesta yhtäsuuruusmerkistä: ==
+
JavaScript tyypittyy heikosti. Tämä tarkoittaa sitä, että yhtäsuuruusoperaattori muuttaa tyyppejä verratakseen niitä keskenään.
Yllä oleva taulukko näyttää tyyppimuunnoksen tulokset. Tämä onkin eräs pääsyistä, minkä vuoksi ==-operaattorin käyttöä pidetään huonona asiana. Sen käyttö johtaa hankalasti löydettäviin bugeihin monimutkaisista muunnossäännöistä johtuen.
+
Tämän lisäksi tyyppimuunnos vaikuttaa suorituskykyyn. Esimerkiksi merkkijono tulee muuttaa numeroksi ennen kuin sitä voidaan verrata toiseen numeroon.
+
Tiukka yhtäsuuruusoperaattori
+
Tiukka yhtäsuuruusoperaattori koostuu kolmesta yhtäsuuruusmerkistä: ===
+
Se toimii aivan kuten normaali yhtäsuuruusoperaattori. Se ei tosin tee minkäänlaista tyyppimuunnosta ennen vertailua.
Yllä olevat tulokset ovat huomattavasti selkeämpiä ja mahdollistavat koodin menemisen rikki ajoissa. Tämä kovettaa koodia ja tarjoaa myös parempaa suorituskykyä siinä tapauksessa, että operandit ovat erityyppisiä.
+
Olioiden vertailu
+
Vaikka sekä == ja === ovat yhtäsuuruusoperaattoreita, ne toimivat eri tavoin, kun ainakin yksi operandeista sattuu olemaan Object.
Tässä tapauksessa molemmat operaattorit vertaavat olion identiteettiäeikä sen arvoa. Tämä tarkoittaa sitä, että vertailu tehdään olion instanssin tasolla aivan, kuten Pythonin is-operaattorin tai C:n osoitinvertailun tapauksessa.
+
Yhteenveto
+
On erittäin suositeltavaa, että ainoastaan tiukkaa yhtäsuuruusoperaattoria käytetään. Mikäli tyyppejä tulee muuttaa, tämä kannattaa tehdä selvästi sen sijaan että luottaisi kielen monimutkaisiin muunnossääntöihin.
+
typeof-operaattori
typeof-operaattori, kuten myös instanceof, on kenties JavaScriptin suurin suunnitteluvirhe. Tämä johtuu siitä, että nämä ominaisuudet ovat liki kokonaan käyttökelvottomia.
+
Vaikka instanceof-operaattorilla onkin tiettyjä rajattuja käyttötarkoituksia, typeof-operaattorille on olemassa vain yksi käytännöllinen käyttötapaus, joka ei tapahdu olion tyyppiä tarkasteltaessa.
+
+
JavaScriptin tyyppitaulukko
+
Arvo Luokka Tyyppi
+-------------------------------------
+"foo" String string
+new String("foo") String object
+1.2 Number number
+new Number(1.2) Number object
+true Boolean boolean
+new Boolean(true) Boolean object
+new Date() Date object
+new Error() Error object
+[1,2,3] Array object
+new Array(1, 2, 3) Array object
+new Function("") Function function
+/abc/g RegExp object (Nitro/V8-funktio)
+new RegExp("meow") RegExp object (Nitro/V8-funktio)
+{} Object object
+new Object() Object object
+
Yllä olevassa taulukossa Tyyppi viittaa arvoon, jonka typeof-operaattori palauttaa. Kuten voidaan havaita, tämä arvo voi olla varsin ristiriitainen.
+
Luokka viittaa olion sisäisen [[Luokka]]-ominaisuuden arvoon.
+
+
Jotta kyseiseen arvoon päästään käsiksi, tulee soveltaa Object.prototype-ominaisuuden toString-metodia.
+
Olion luokka
+
Määritelmä antaa tarkalleen yhden keinon, jonka avulla [[Luokka]] arvoon voidaan päästä käsiksi. Tämä on mahdollista Object.prototype.toString-metodia käyttäen.
Yllä olevassa esimerkissä Object.prototype.toString-metodia kutsutaan arvolla this, jonka arvo on asetettu olion [[Luokka]] arvoon.
+
+
Määrittelemättömien muuttujien testaaminen
+
typeof foo !== 'undefined'
+
Yllä oleva testi kertoo onko foo määritelty. Pelkästään siihen viittaaminen palauttaisi ReferenceError-virheen. Tämä on ainut asia, johon typeof-operaattoria kannattaa käyttää.
+
Yhteenveto
+
Ainut tapa, jonka avulla olion tyyppi voidaan tarkistaa luotettavasti, on Object.prototype.toString-metodin käyttö, kuten yllä. Kuten yllä oleva tyyppitaulu näyttää, osa typeof-operaattorin palautusarvoista on huonosti määritelty. Tästä johtuen ne voivat erota toteutuksesta riippuen.
+
Muuttujan määrittelemättömyyden testaaminen on ainut tapaus, jossa typeof-operaattoria kannattaa käyttää. Muutoin sen käyttöä kannattaa välttää hinnalla milla hyvänsä.
+
instanceof-operaattori
instanceof-operaattori vertaa kahden operandinsa konstruktoreita keskenään. Se on hyödyllinen ainoastaan, kun vertaillaan itsetehtyjä olioita. Natiivien tyyppien tapauksessa se on lähes yhtä hyödytön kuin typeof-operaattori.
+
Itsetehtyjen olioiden vertailu
+
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // tosi
+new Bar() instanceof Foo; // tosi
+
+// Tämä asettaa vain Bar.prototype-ominaisuudeksi
+// funktio-olion Foo
+// Se ei kuitenkaan ole Foon todellinen instanssi
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // epätosi
On tärkeää huomata, että instanceof ei toimi olioilla, jotka tulevat muista JavaScript-konteksteista (esim. selaimen eri dokumenteista). Tässä tapauksessa niiden konstruktorit viittaavat eri olioon.
+
Yhteenveto
+
instanceof-operaattoria tulee käyttää ainoastaan, mikäli käsitellään itsetehtyjä olioita saman JavaScript-kontekstin sisällä. Kuten typeof-operaattorikin, myös muita sen käyttöjä tulee välttää.
+
Tyyppimuunnokset
JavaScript on tyypitetty heikosti. Tämä tarkoittaa sitä, että se pyrkii pakottamaan tyyppejäaina kun se on mahdollista.
+
// Nämä ovat totta
+new Number(10) == 10; // Number.toString() muutetaan
+ // takaisin numeroksi
+
+10 == '10'; // Merkkijonot muutetaan Number-tyyppiin
+10 == '+10 '; // Lisää merkkijonohauskuutta
+10 == '010'; // Ja lisää
+isNaN(null) == false; // null muuttuu nollaksi,
+ // joka ei ole NaN
+
+// Nämä ovat epätosia
+10 == 010;
+10 == '-10';
+
+
Yllä havaittu käytös voidaan välttää käyttämällä tiukkaa vertailuoperaattoria. Sen käyttöä suositellaan lämpimästi. Vaikka se välttääkin useita yleisiä ongelma, sisältää se omat ongelmansa, jotka johtavat juurensa JavaScriptin heikkoon tyypitykseen.
+
Natiivien tyyppien konstruktorit
+
Natiivien tyyppien, kuten Number tai String, konstruktorit käyttäytyvät eri tavoin new-avainsanan kanssa ja ilman.
+
new Number(10) === 10; // Epätosi, Object ja Number
+Number(10) === 10; // Tosi, Number ja Number
+new Number(10) + 0 === 10; // Tosi, johtuu tyyppimuunnoksesta
+
Number-tyypin kaltaisen natiivityypin käyttäminen luo uuden Number-olion. new-avainsanan pois jättäminen tekee Number-funktiosta pikemminkin muuntimen.
+
Tämän lisäksi literaalit tai ei-oliomaiset arvot johtavat edelleen uusiin tyyppimuunnoksiin.
+
Paras tapa suorittaa tyyppimuunnoksia on tehdä niitä selvästi.
+
Muunnos merkkijonoksi
+
'' + 10 === '10'; // tosi
+
Arvo voidaan muuttaa merkkijonoksi helposti lisäämällä sen eteen tyhjä merkkijono.
+
Muunnos numeroksi
+
+'10' === 10; // tosi
+
Unaarinen plus-operaattori mahdollistaa numeroksi muuttamisen.
+
Muunnos totuusarvoksi
+
Arvo voidaan muuttaa totuusarvoksi käyttämällä not-operaattoria kahdesti.
eval suoritetaan paikallisessa näkyvyysalueessa ainoastaan kun sitä kutsutaan suorastija kutsutun funktion nimi on todellisuudessa eval.
+
var foo = 1;
+function test() {
+ var foo = 2;
+ var bar = eval;
+ bar('foo = 3');
+ return foo;
+}
+test(); // 2
+foo; // 3
+
eval-funktion käyttöä tulee välttää ehdottomasti. 99.9% sen "käyttötapauksista" voidaan toteuttaa ilman sitä.
+
Piilotettu eval
+
AikakatkaisufunktiotsetTimeout and setInterval voivat kumpikin ottaa merkkijonon ensimmäisenä argumenttinaan. Kyseinen merkkijono suoritetaan aina globaalissa näkyvyysalueessa, koska tuolloin eval-funktiota kutsutaan epäsuorasti.
+
Turvallisuusongelmat
+
eval on myös turvallisuusongelma. Se suorittaa minkä tahansa sille annetun koodin. Tämän vuoksi sitä ei tule ikinä käyttää tuntemattomasta tai epäluotttavasta lähteestä tulevien merkkijonojen kanssa.
+
Yhteenveto
+
eval-funktiota ei pitäisi käyttää koskaan. Mikä tahansa sitä käyttävä koodi on kyseenalaista sekä suorituskyvyn että turvallisuuden suhteen. Mikäli jokin tarvitsee eval-funktiota toimiakseen, tulee sen suunnittelutapa kyseenalaistaa. Tässä tapauksessa on parempi suunnitella toisin ja välttää eval-funktion käyttöä.
+
undefined ja null
JavaScript sisältää kaksi erillistä arvoa ei millekään. Näistä hyödyllisempti on undefined.
+
undefined ja sen arvo
+
undefined on tyyppi, jolla on vain yksi arvo: undefined.
+
Kieli määrittelee myös globaalin muuttujan, jonka arvo on undefined. Myös tätä arvoa kutsutaan nimellä undefined. Tämä muuttuja ei kuitenkaan ole vakio eikä kielen avainsana. Tämä tarkoittaa siis sitä, että sen arvo voidaan ylikirjoittaa.
+
+
Seuraavat tapaukset palauttavat undefined-arvon:
+
+
Globaalin (muokkaamattoman) muuttujan undefined arvon haku.
+
Puuttuvista return-lauseista seuraavat epäsuorat palautusarvot.
+
return-lauseet, jotka eivät palauta selvästi mitään.
+
Olemattomien ominaisuuksien haut.
+
Funktioparametrit, joiden arvoa ei ole asetettu.
+
Mikä tahansa, joka on asetettu arvoon undefined.
+
+
Arvon undefined muutosten hallinta
+
Koska globaali muuttuja undefined sisältää ainoastaan todellisen undefined-tyypin arvon kopion, ei sen asettamienn uudelleen muuta tyypinundefined arvoa.
+
Kuitenkin, jotta undefined-tyypin arvoa voidaan verrata, tulee sen arvo voida hakea jotenkin ensin.
+
Tätä varten käytetään yleisesti seuraavaa tekniikkaa. Ajatuksena on antaa itse arvo käyttäen nimetöntä käärettä.
+
var undefined = 123;
+(function(something, foo, undefined) {
+ // paikallisen näkyvyysalueen undefined
+ // voi viitata jälleen todelliseen arvoon
+
+})('Hello World', 42);
+
Samaan lopputuloksen voidaan päästä myös käyttämällä esittelyä kääreen sisällä.
+
var undefined = 123;
+(function(something, foo) {
+ var undefined;
+ ...
+
+})('Hello World', 42);
+
Tässä tapauksessa ainut ero on se, että pakattu versio vie 4 tavua enemmän tilaa 'var'-lauseen vuoksi.
+
null ja sen käyttötapaukset
+
Vaikka undefined-arvoa käytetäänkin usein perinteisen null-arvon sijasta, todellinen null (sekä literaali että tyyppi) on enemmän tai vähemmän vain tietotyyppi.
+
Sitä käytetään joissain JavaScriptin sisäisissä toiminnoissa, kuten prototyyppiketjun pään toteamisessa (Foo.prototype = null). Useimmissa tapauksissa se voidaan korvata undefined-arvoa käyttäen.
+
Automaattiset puolipisteet
Vaikka JavaScript käyttääkin C:n tapaista syntaksia, se ei pakota käyttämään puolipisteitä. Niiden käyttöä voidaan halutessa välttää.
+
Tästä huolimatta JavaScript ei kuitenkaan ole puolipisteetön kieli. Se tarvitsee niitä ymmärtääkseen lähdekoodia. Tämän vuoksi JavaScript-parseri lisää niitä tarpeen mukaan automaattisesti.
+
var foo = function() {
+} // parsimisvirhe, lisätään puolipiste
+test()
+
Lisäys tapahtuu ja parseri yrittää uudelleen.
+
var foo = function() {
+}; // ei virhettä, parsiminen jatkuu
+test()
+
Automaattista puolipisteiden lisäämistä pidetään eräänä JavaScriptin suurimmista suunnitteluvirheistä. Tämä johtuu siitä, että se voi muuttaa tapaa, jolla koodi käyttäytyy.
+
Kuinka se toimii
+
Alla oleva koodi ei sisällä puolipisteitä. Täten niiden lisääminen jää parserin tehtäväksi.
On hyvin mahdollista, että logei palauta funktiota. Tästä johtuen yllä oleva palauttanee TypeError-virheen, joka toteaa että undefined ei ole funktio.
+
Yhteenveto
+
On suositeltavaa ettei puolipisteitä jätetä pois milloinkaan. Tämän lisäksi sulut kannattaa pitää niitä vastaavien lausekkeiden kanssa samalla rivillään. if ja else-lauseiden tapauksessa sulkuja kannattaa käyttää aina. Sen lisäksi että edellä mainitut suositukset tekevät koodista johdonmukaisempaa, estävät ne myös JavaScript-parseria muuttamasta sen käytöstapaa.
+
Muuta
setTimeout ja setInterval
Koska JavaScript on luonteeltaan asynkroninen, voidaan funktioiden suoritusta ajastaa käyttäen setTimeout sekä setInterval-funktioita.
+
+
function foo() {}
+var id = setTimeout(foo, 1000); // palauttaa Numeron > 0
+
Kun setTimeout-funktiota kutsutaan, se palauttaa aikakatkaisun tunnisteen ja ajastaa foo-funktion suoritettavaksi suunnilleen tuhannen millisekunnin päästä. foo suoritetaan tarkalleen kerran.
+
Käytössä olevan JavaScript-tulkin ajastimen tarkkuudesta, JavaScriptin yksisäikeisyydestä sekä muusta koodista riippuen ei ole lainkaan taattua, että viive on tarkalleen sama kuin määritelty.
+
Ensimmäisenä annettu funktio suoritetaan globaalisti. Tämä tarkoittaa sitä, että sen this on asetettu osoittamaan globaaliin olioon.
setTimeout suoritetaan vain kerran. setInterval sen sijaan, kuten nimestä voi päätellä, suoritetaan ainaX millisekunnin välein. Sen käyttöä ei kuitenkaan suositella.
+
Mikäli suoritettava koodi blokkaa katkaisufunktion kutsun, setInterval lisää kutsuja pinoon. Tämä voi olla ongelmallista erityisesti, mikäli käytetään pieniä intervalliarvoja.
+
function foo(){
+ // jotain joka blokkaa sekunnin ajaksi
+}
+setInterval(foo, 1000);
+
Yllä olevassa koodissa foo-funktiota kutsutaan, jonka jälleen se blokkaa sekunnin ajan.
+
Tämän ajan aikana setInterval kasvattaa kutsupinon sisältöä. Kun foo on valmis, kutsupinoon on ilmestynyt jo kymmenen uutta kutsua suoritettavaksi.
+
Mahdollisesti blokkaavan koodin kanssa pärjääminen
+
Helpoin ja joustavin tapa on käyttää setTimeout-funktiota funktiossa itsessään.
+
function foo(){
+ // jotain joka blokkaa sekunnin ajaksi
+ setTimeout(foo, 1000);
+}
+foo();
+
Sen lisäksi että tämä ratkaisu kapseloi setTimeout-kutsun, se myös estää kutsujen pinoutumisen ja tarjoaa joustavuutta. foo voi päättää halutaanko se suorittaa uudelleen vai ei.
+
Katkaisujen poistaminen käsin
+
Katkaisuja ja intervalleja voidaan poistaa antamalla sopiva tunniste joko clearTimeout- tai clearInterval-funktiolle. Se kumpaa käytetään riippuu käytetystä set-funktiosta.
+
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
Kaikkien katkaisujen poistaminen
+
JavaScript ei sisällä erityistä funktiota kaikkien katkaisujen ja/tai intervallien poistamiseen. Sen sijaan tämä voidaan toteuttaa raakaa voimaa käyttäen.
+
// poista "kaikki" katkaisut
+for(var i = 1; i < 1000; i++) {
+ clearTimeout(i);
+}
+
On mahdollista, että jopa tämän jälkeen on olemassa katkaisuja, jotka ovat käynnissä. Onkin siis suositeltavaa tallentaa katkaisujen tunnisteet jotenkin. Tällä tavoin ne voidaan poistaa käsin.
+
Piilotettu eval
+
setTimeout ja setInterval voivat ottaa myös merkkijonon ensimmäisenä parametrinaan. Tätä ominaisuutta ei tule käyttää ikinä, koska se käyttää sisäisesti eval-funktiota.
+
+
function foo() {
+ // kutsutaan
+}
+
+function bar() {
+ function foo() {
+ // ei kutsuta ikinä
+ }
+ setTimeout('foo()', 1000);
+}
+bar();
+
Koska eval-funktiota ei kutsuta suoraan, setTimeout-funktiolle annettu merkkijono suoritetaan globaalissa näkyvyysalueessa. Tässä tapauksessa se ei siis käytä paikallista bar-funktion näkyvyysalueessa olevaa foo-funktiota.
+
Tämän lisäksi on suositeltavaa olla käyttämättä merkkijonoja parametrien antamiseen.
+
function foo(a, b, c) {}
+
+// Älä käytä tätä IKINÄ
+setTimeout('foo(1,2, 3)', 1000)
+
+// Käytä nimetöntä funktiota sen sijaan
+setTimeout(function() {
+ foo(a, b, c);
+}, 1000)
+
+
Yhteenveto
+
Merkkijonoa ei tule antaa setTimeout- tai setInterval-funktiolle koskaan. Tämä on selvä merkki erittäin huonosta koodista erityisesti mikäli sitä käytetään parametrien välittämiseen. Sen sijaan kannattaa käyttää nimetöntä funktiota, joka huolehtii varsinaisesta kutsusta.
+
Tämän lisäksi setInterval-funktion käyttöä tulee välttää. Tämä johtuu siitä, että sen JavaScript ei blokkaa sen vuorottajaa.
+
\ No newline at end of file
diff --git a/hu/index.html b/hu/index.html
new file mode 100644
index 0000000..78cb7df
--- /dev/null
+++ b/hu/index.html
@@ -0,0 +1,1434 @@
+JavaScript Garden
Bevezető
Objektumok
Objektumok és mezők használata
A JavaSciprtben minden objektumként működik, a null és az undefined kivételével.
Gyakori tévhitként terjed, hogy a JavaScriptben a számok nem használhatóak objektumként.
+Ez csak látszólag igaz, mivel a JavaScript a pont utáni részt úgy próbálja értelmezni,
+mintha lebegőpontos számot látna. Így hibát kaphatunk.
+
2.toString(); // SyntaxErrort vált ki
+
Azonban számos kifejezés létezik megoldásként, amelyekkel megkerülhető ez a probléma.
+
2..toString(); // így a második pont már az objektumra utal
+2 .toString(); // fontos a space-t észrevenni itt a pont előtt
+(2).toString(); // a 2 értékelődik ki hamarabb
+
Objektumok mint adattípusok
+
Az objektumok JavaScriptben Hash táblaként is használhatóak, mivel természetszerűleg kulcs-érték párokat tartalmaznak.
+
Az objektum literál leírásával - {} jelöléssel - lehet létrehozni egy új objektumot. Ez az új objektum az Object.prototype-ból származik és nincsenek saját mezői definiálva.
+
var foo = {}; // egy új, üres objektum
+
+// egy új objektum egy 'test' nevű mezővel, aminek 12 az értéke
+var bar = {test: 12};
+
Mezők elérése
+
Egy objektum mezői kétféle módon érhetőek el, vagy az 'objektum.mezőnév' jelöléssel,
+(Ford.: amit "dot notationként" emlegetünk) vagy a szögletes zárójelek kirakásával.
+
var foo = {name: 'macska'}
+foo.name; // macska
+foo['name']; // macska
+
+var get = 'name';
+foo[get]; // macska
+
+foo.1234; // SyntaxError
+foo['1234']; // működik
+
A két jelölés majdnem egyenértékűen használható, kivéve, hogy a szögletes zárójelekkel dinamkusan állíthatunk be mezőket és olyan neveket is választhatunk, amik amúgy szintaxis hibához vezetnének (Fordító: mivel a neveket stringbe kell rakni, megadhatunk a JS által "lefoglalt" kulcsszavakat is mezőnévként, habár ennek használata erősen kerülendő).
+
Mezők törlése
+
Egyetlen módon lehet mezőt törölni egy objektumból ez pedig a delete operátor
+használata; a mező értékének undefined-ra vagy null-ra való állítása csak
+magára az értékre van kihatással, de a kulcs ugyanúgy megmarad az objektumban.
+
var obj = {
+ bar: 1,
+ foo: 2,
+ baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ console.log(i, '' + obj[i]);
+ }
+}
+
A fenti ciklus a bar undefined és a foo null eredményeket fogja kiírni -
+egyedül a baz mező került törlésre, és emiatt hiányzik is az outputról.
+
Kulcsok jelölése
+
var test = {
+ 'case': 'Kulcsszó vagyok, ezért stringként kell leírnod',
+ delete: 'Én is az vagyok' // SyntaxError
+};
+
Az objektumok mezőnevei mind stringként, mind egyszerű szövegként (Ford.: aposztrófok nélkül)
+leírhatóak. A JavaScript értelmező hibája miatt, a fenti kód azonban SyntaxErrort eredményez ECMAScript 5 előtti verzió esetén.
+
Ez a hiba onnan ered, hogy a delete egy kulcsszó, viszont érdemes string literálként
+leírni hogy helyesen megértsék a régebbi JavaScript motorok is.
+
A Prototípus
A JavaScript nem a klasszikus öröklődést használja, hanem egy ún. prototípusos
+származtatást használ.
+
Míg ezt gyakran a JavaScript legnagyobb hibái között tartják számon, valójában
+ez a származtatási modell jóval kifejezőbb mint klasszikus barátja.
+Ezt jelzi, hogy például sokkal könnyebb megépíteni a klasszikus modellt, alapul véve
+a prototípusos modellt, míg a fordított irány kivitelezése igencsak nehézkes lenne.
+
A JavaScript az egyetlen széles körben elterjedt nyelv, amely ezt a származtatást
+használja, így mindenképp időt kell szánni a két modell közti különbség megértésére.
+
Az első feltűnő különbség, hogy ez a fajta származtatás prototípus láncokat
+használ.
+
+
function Foo() {
+ this.value = 42;
+}
+Foo.prototype = {
+ method: function() {}
+};
+
+function Bar() {}
+
+// Beállítjuk a Bar prototípusát a Foo egy új példányára
+Bar.prototype = new Foo(); // !
+Bar.prototype.foo = 'Hello World';
+
+// Beállítjuk a Bar konstruktorát
+Bar.prototype.constructor = Bar;
+
+var test = new Bar(); // új Bar példány létrehozása
+
+// A kapott prototípus lánc
+test [instance of Bar]
+ Bar.prototype [instance of Foo]
+ { foo: 'Hello World' }
+ Foo.prototype
+ { method: ... }
+ Object.prototype
+ { toString: ... /* stb. */ }
+
A fenti kódban a test objektum mind a Bar.prototype és Foo.prototype
+prototípusokból származik, így lesz hozzáférése a method nevű függvényhez amely
+a Foo prototípusában lett definiálva. A value mezőhöz szintén lesz hozzáférése,
+amely akkor jött létre, amikor (szám szerint) egy új Foo példányt hoztunk létre.
+Érdemes észrevenni hogy a new Bar() kifejezés nem hoz létre egy új Foo példányt
+minden alkalommal, azonban újrahasználja azt az egyetlen (//!) inicilalizált Foo pédlányunkat. Így az összes Bar példány egy és ugyanazt a value mezőt (és
+értéket) fogja használni.
+
+
Mezők keresése
+
Amikor olyan utasítást adunk ki, amellyel egy objektum mezőjét keressük, a
+JavaScript felfele bejárja az egész prototípus láncot, amíg meg nem találja
+a kért mezőt.
+
Hogyha eléri a lánc legtetejét - nevezetesen az Object.prototype-t és még
+ekkor sem találja a kért mezőt, akkor az undefined-dal fog
+visszatérni.
+
A Prototype mező
+
Alapjáraton, a JavaScript a prototype nevű mezőt használja a prototípus láncok
+kialakításához, de ettől függetlenül ez is ugyanolyan mező mint a többi, és
+bármilyen értéket belehet neki állítani. Viszont a primitív típusokat egyszerűen
+figyelmen kívül fogja hagyni a feldolgozó.
+
function Foo() {}
+Foo.prototype = 1; // nincs hatása
+
Az objektumok megadása, mint azt a fentebbi példában láthattuk, hatással van a prototype
+mezőkre és ezeknek az átállításával bele lehet szólni a prototípus láncok kialakításába.
+
Teljesítmény
+
Értelemszerűen, minnél nagyobb a prototípus lánc, annál tovább tart egy-egy mező
+felkeresése, és ez rossz hatással lehet a kód teljesítményére. Emellett, ha egy
+olyan mezőt próbálunk elérni amely nincs az adott objektum példányban, az mindig
+a teljes lánc bejárását fogja eredményezni.
+
Vigyázat! Akkor is bejárjuk a teljes láncot, amikor egy objektum mezőin próbálunk iterálni.
+
Natív prototípusok bővítése
+
Egy gyakran elkövetett hiba, hogy az Object.prototype prototípust vagy egy másik előre
+definiált prototípust próbálunk kiegészíteni új kóddal.
+
Ezt monkey patching-nek is hívják, és aktívan kerülendő, mivel megtöri
+az egységbe zárás elvét. Habár ezt a technikát olyan népszerű framework-ök
+is használják mint a Prototype, ettől függetlenül ne hagyjuk magunkat csőbe húzni;
+nincs ésszerű indok arra, hogy összezavarjuk a beépített típusokat, további
+nem standard saját funkcionalitással.
+
Az egyetlen ésszerű használati indok a natív prototípusokba nyúlásra az lehet,
+hogy megpróbáljuk szimulálni az új JavaScript motorok szolgáltatásait régebbi társaikon, például az Array.forEach implementálásával.
+
Zárásként
+
Nagyon fontos megérteni a prototípusos származtatási modellt, mielőtt olyan
+kódot próbálnánk írni, amely megpróbálja kihasználni a sajátosságait. Nagyon
+oda kell figyelni a prototípuslánc hosszára - osszuk fel több kis láncra ha
+szükséges - hogy elkerüljük a teljesítmény problémákat. Továbbá, a natív
+prototípusokat soha ne egészítsük ki, egészen addig amíg nem akarunk
+JavaScript motorok közötti kompatibilitási problémákat áthidalni.
+
hasOwnProperty
Hogy megtudjuk nézni egy adott objektum saját mezőit - azokat a mezőket amelyek
+az objektumon közvetlenül vannak definiálva, és nem valahol a
+prototípus láncon -, a hasOwnProperty függvényt használata
+ajánlott, amelyet az összes objektum amúgy is örököl az Object.prototype-ból.
+
+
A hasOwnProperty függvény az egyetlen olyan dolog amelyik anélkül tudja ellenőrizni
+az objektum mezőit, hogy megpróbálná bejárni a prototípus láncot.
+
// Az Object.prototype beszennyezése
+Object.prototype.bar = 1;
+var foo = {goo: undefined};
+
+foo.bar; // 1
+'bar' in foo; // igaz
+
+foo.hasOwnProperty('bar'); // hamis
+foo.hasOwnProperty('goo'); // igaz
+
Hogy megértsük a fontosságát, egyedül a hasOwnProperty tudja hozni a korrekt
+és elvárt eredményeket mezőellenőrzés szempontjából. Egyszerűen nincs más
+módja annak, hogy kizárjuk a szűrésünkből azokat a mezőket amelyek nem az objektumon,
+hanem valahol feljebb, a prototípus láncon lettek definiálva.
+
A hasOwnProperty mint mező
+
A JavaScript persze nem védi magát a hasOwnProperty nevet, így egy jókedvű
+programozóban mindig megvan a lehetőség, hogy így nevezze el a saját függvényét.
+Ennek kikerülése érdekében ajánlott mindig a hasOwnProperty-re kívülről hivatkozni
+(Értsd: A hackelt -saját hasOwnPropertyvel ellátott- objektum kontextusán kívüli objektum hasOwnPropertyjét hívjuk meg).
+
var foo = {
+ hasOwnProperty: function() {
+ return false;
+ },
+ bar: 'Mordor itt kezdődik'
+};
+
+foo.hasOwnProperty('bar'); // mindig hamissal tér vissza
+
+// Használhatjuk egy másik objektum hasOwnPropertyjét,
+// hogy meghívjuk a foo-n.
+({}).hasOwnProperty.call(foo, 'bar'); // ez már igaz
+
+// Szintén jó megoldás lehet közvetlenül az
+// Object prototypejából hívni ezt a függvényt.
+Object.prototype.hasOwnProperty.call(foo, 'bar'); // ez is igaz
+
Konklúzió
+
A hasOwnProperty használata az egyetlen megbízható módszer annak eldöntésére,
+hogy egy mező közvetlenül az objektumon lett-e létrehozva. Melegen ajánlott a
+hasOwnProperty-t mindenfor in ciklusban használni.
+Használatával ugyanis elkerülhetjük a kontár módon kiegészített natív prototípusokból
+fakadó esetleges hibákat, amire példát az imént láttunk.
+
A for in ciklus
Csak úgy mint a jó öreg in operátor, a for in is bejárja az egész
+prototípus láncot, amikor egy objektum mezőin próbálnánk iterálni.
+
+
// Mérgezzük Object.prototypeot!
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+ console.log(i); // mind a moo és bar is kiírásra kerül
+}
+
Mivel -hála égnek- magának a for in ciklusnak a működését nem lehet befolyásolni,
+így más módszert kell találnunk ahhoz hogy száműzzük a váratlan mezőket a ciklus magból.
+(Értsd: Azokat amelyek a prototípus láncon csücsülnek csak). Ezt pedig az Object.prototype-ban
+lakó hasOwnProperty függvény használatával érhetjük el.
+
+
Szűrés használata a hasOwnProperty-vel
+
// még mindig a fenti foo-nál tartunk
+for(var i in foo) {
+ if (foo.hasOwnProperty(i)) {
+ console.log(i);
+ }
+}
+
Ez az egyetlen helyes útja annak hogy az objektum saját mezőin iteráljunk csak végig.
+Mivel a hasOwnProperty-t használjuk, így csak a várt moo-t fogja kiírni. Tehén jó
+kódunk van! Hogyha a hasOwnProperty-t kihagynánk, a kódunk ki lenne téve nem várt
+hibáknak, amik pl. abból fakadnak hogy valaki ocsmányul kiterjesztette az
+Object.prototype-t.
+
Például, ha a Prototype frameworköt használjuk, és nem ilyen stílusban írjuk a
+ciklusainkat, a hibák szinte garantáltak, ugyanis ők saját szájízükre kiterjesztik az
+Object.prototype-t.
+
Konklúzió
+
A hasOwnProperty használata erősen javasolt. Soha ne éljünk pozitív
+feltételezésekkel a futó kódot illetően, főleg olyan döntésekben nem érdemes
+orosz rulettezni, mint hogy kiterjeszti-e valaki a natív prototípusokat vagy nem.
+Mert általában igen.
+
Függvények
Függvény deklarációk és kifejezések
A függvények JavaScriptben egyben objektumok is. Ez azt jelenti, hogy
+ugyanúgy lehet őket passzolgatni mint bármelyik más értékeket. Ezt a featuret
+gyakran használják arra, hogy egy névtelen (callback) függvényt átadjunk
+egy másik -aszinkron- függvény paramétereként.
+
A függvény deklaráció
+
function foo() {}
+
Ez a függvény felkerül a scope tetejére (hoisting), mielőtt a kód végrehajtása megtörténne. Így abban a scopeban ahol definiálták, mindenhol elérhető,
+még abban a trükkös esetben is, hogyha a kód azon pontján hívjuk ezt a függvényt, mielőtt
+definiáltuk volna (látszólag).
+
foo(); // Így is működik, mivel a foo fgv. létrejön mielőtt meghívnánk.
+function foo() {}
+
A függvény kifejezés (expression)
+
var foo = function() {};
+
A fentebbi példában egy névtelen függvényt adunk értékül a foo változónak.
Habár ebben a példában a var deklaráció futás előtt a kód tetejére kúszik,
+ettől függetlenül a foo mint függvény meghívásakor hibát fogunk kapni.
+
Ugyanis a deklaráció felkúszott, azonban az értékadás csak futásidőben fog megtörténni,
+addig is a foo változó értéke undefined marad. Az undefinedet pedig hiába hívjuk függvényként, TypeErrort kapunk végeredményül.
+
Névvel ellátott függvény kifejezés
+
Egy másik érdekes eset, amikor névvel ellátott függvényeket adunk értékül változóknak.
+
var foo = function bar() {
+ bar(); // Működik
+}
+bar(); // ReferenceError
+
Ebben a példában a bart önmagában nem lehet elérni egy külső scopeból (utolsó sor),
+mivel egyből értékül adtuk a foo változónak. Ennek ellenére a baron belül elérhető
+a bar név. A tanulság az, hogy a függvény önmagát mindig eléri a saját scopeján belül, és ez a JavaScriptben található névfeloldásnak köszönhető.
+
A this mágikus működése
A this kicsit másképp működik a JavaScriptben mint ahogy azt megszokhattuk
+más nyelvekben. Ugyanis pontosan ötféle módja lehet annak hogy a this
+éppen mire utal a nyelvben.
+
A Globális hatókör
+
this;
+
Amikor globális hatókörben van használva a this, akkor pontosan a globális objektumra utal.
+
Függvény híváskor
+
foo();
+
Itt, a this megint a globális objektumra fog utalni.
+
+
Eljárás hívásakor
+
test.foo();
+
Ebben a példában a this a test objektumra fog hivatkozni.
+
Konstuktor hívásakor
+
new foo();
+
Ha a függvény hívását a new kulcsszóval előzzük meg, akkor a függvény konstruktorként fog viselkedni. A függvényen belül, a this
+az újonnan létrehozottObjektumra fog hivatkozni.
+
A this explicit beállítása
+
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // ugyanaz mint egy sorral lejjebb
+foo.call(bar, 1, 2, 3); // argumentumok: a = 1, b = 2, c = 3
+
A Function.prototype-ban levő call vagy apply használatakor aztán elszabadul a pokol :).
+Ezekben az esetekben ugyanis a this a foo hívásakor egzaktan be lesz állítva az apply/call
+első argumentumára.
+
Ennek eredményképp az előzőekben említett Eljárás hívásakor rész nem érvényes,
+a foo fentebbi meghívásakor a this értéke a bar objektumra lesz beállítva.
+
+
Gyakori buktatók
+
Míg a fent megtalálható eseteknek van gyakorlatban vett értelme, az első
+a nyelv rossz designjára utal, ugyanis ennek soha nem lesz semmilyen
+praktikus felhasználási módja.
+
Foo.method = function() {
+ function test() {
+ // A this itt a globális ojjektum.
+ }
+ test();
+}
+
Gyakori hiba, hogy úgy gondolják a fenti példában az emberek, hogy a this a test függvényen
+belül az őt körülvevő Foo-ra fog mutatni, pedig nem.
+
Megoldásképp, hogy a Foo-hoz hozzáférhessük a test-en belül, szükségszerű egy változót
+lokálisan elhelyezni a method-on belül, ami már valóban a kívánt this-re (Foo-ra) mutat.
+
Foo.method = function() {
+ var that = this;
+ function test() {
+ // Használjuk a that-et a this helyett
+ }
+ test();
+}
+
A that tuladjonképpen egy mezei változónév (nem kulcsszó), de sokszor használják arra,
+hogy egy másik this-re hivatkozzanak vele. A colsureökkel kombinálva
+ez a módszer arra is használható hogy this-eket passzolgassunk a vakvilágban és mégtovább.
+
Eljárások értékül adása
+
Egy másik koncepció ami nem fog a JavaScriptben működni, az az alias függvények létrehozása, ami tulajdonképpen egy függvény másik névhez való kötését jelentené.
+
var test = someObject.methodTest;
+test();
+
Az első eset miatt a test egy sima függvényhívásként működik, azonban a this értéke
+a függvényen belül a továbbiakban nem a someObject lesz.
+
Elsőre a this alábbi módon való utánkötése (late binding) nem tűnik jó ötletnek.
+Azonban ez az, amitől a prototípusos öröklődés is működni tud,
+ami a nyelv egyik fő erőssége.
Amikor a method meghívódik a Bar példányaként, a this pontosan a Bar
+megfelelő példányára fog mutatni.
+
Closure-ök és referenciák
A JavaScript nyelv egyik legerőteljesebb tulajdonsága a closure-ök használatában rejlik.
+Ezek használatával a hatókörök egymásba ágyazhatóak, és egy belső hatókör mindig hozzáfér
+az őt körülvevő, külső hatókör változóihoz. Miután JavaScriptben egyetlen dologgal lehet
+hatóköröket kifejezni, és ez a funkció (bizony az if, try/catch és hasonló blokkok nem jelentenek új hatókört, mint pl. a Javaban), az összes funkció closure-ként szerepel.
Ebben a példában a Counterkét closure-rel tér vissza: az increment és
+a get funkcióval. Mind a két funkció referenciát tárol a Counter hatókörre,
+és így mindketten hozzáférnek a count változóhoz, ami ebben a hatókörben lett
+definiálva.
+
Miért működnek a privát változók?
+
Mivel a JavaScriptben egyszerűen nem lehet hatókörre referálni, vagy hatókört
+értékül adni, így ezért szintén lehetetlen elérni az iménti count változót a külvilág számára.
+Egyetlen mód van a megszólítására, ezt pedig láttuk a fentebbi két closure-ön belül.
+
var foo = new Counter(4);
+foo.hack = function() {
+ count = 1337;
+};
+
A fentebbi kód nem fogja megváltoztatni a Counter hatókör count változóját,
+mivel a foo.hack mező nem abban a hatókörben lett létrehozva. Ehelyett, okosan,
+létre fogja hozni, vagy felül fogja írni a globáliscount változót (window.count).
+
Closure-ök használata ciklusokban
+
Az egyik leggyakoribb hiba amit el lehet követni, az a closure-ök ciklusokban való használata.
+Annak is azon speciális esete amikor a ciklus indexváltozóját szeretnénk lemásolni a closure-ön belül.
+
for(var i = 0; i < 10; i++) {
+ setTimeout(function() {
+ console.log(i);
+ }, 1000);
+}
+
A fenti kódrészlet marhára nem a számokat fogja kiírni 0-tól 9-ig, de inkább
+a 10-et fogja tízszer kiírni.
+
Ugyanis a belső névtelen függvény egy referenciát fog tárolni a külső i változóra, és
+akkor, amikor végül a console.log sor lefut, a for loop már végzett az egész ciklussal,
+így az i értéke 10-re lesz beállítva.
+
Ahhoz, hogy a várt működést kapjuk (tehát a számokat 0-tól 9-ig), szükségszerű az i változó
+értékét lemásolni.
+
A referencia probléma elkerülése
+
Az előző problémára megoldást úgy lehet jól adni, hogy az utasításoknak megfelelően
+lemásoljuk a ciklusváltozót, úgy hogy a jelenlegi ciklusmagöt körbevesszük egy névtelen
+függvénnyel.
A külső (wrapper) névtelen függvény így azonnal meghívódik az i ciklusváltozóval, mint paraméterrel,
+és így mindig egy másolatot fog kapni az i változó értékéről, amit ő e néven emészt tovább.
+
Így a setTimeoutban lévő névtelen fgv. mindig az e nevű referenciára fog mutatni, aminek az értéke így már nem változik meg a ciklus futása során.
+
Egy másik lehetséges út a megoldáshoz az, hogy egy wrapper függvényt visszatérítünk a setTimeoutból, aminek ugyanaz lesz a hatása, mint a fentebbi példának.
Minden függvényhatókörben hozzáférhető az arguments nevű speciális változó,
+amely azon argumentumok listáját tartalmazza, amelyekkel a függvényt meghívták.
+
+
Lehet hogy úgy néz ki, de az arguments objektum nem egy tömb. Látszólag hasonlít rá,
+mivel van például egy length nevű mezője, de igazából nem az Array.prototype-ból "származik",
+hanem tisztán az Object-ből.
+
Itt jön a trükk lényege, hogy ennek köszönhetően nem használhatóak rajta a standard
+tömb műveletek mint például a push, pop vagy a slice. Míg a sima for ciklusos iterálás
+működik itt is, ahhoz hogy az előbb említett műveleteket is tudjuk rajta használni, át kell
+konvertálni egy valódi Array objektummá.
+
Tömbbé konvertálás
+
Ez a kódrészlet egy új Array objektummá varázsolja az emlegetett arguments szamarat.
+
Array.prototype.slice.call(arguments);
+
De, ez a konverzió meglehetősen lassú így egyáltalán nem ajánlott teljesítmény kirtikus
+alkalmazások írásakor.
+
Argumentumok kezelése
+
A következő módszer ajánlott arra az esetre hogyha az egyik függvény paramétereit egy-az-egyben
+át szeretnénk adni egy másik függvény számára.
+
function foo() {
+ bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+ // sok okos kód ide
+}
+
Egy másik trükk arra hogy teljesen független wrapper függvényeket gyártsunk, a call
+és apply együttes használata.
+
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+ console.log(this, a, b, c);
+};
+
+// Elkészíti a "method" (this) független verzióját
+// Ezeket kapja paraméterül: this, arg1, arg2...argN
+Foo.method = function() {
+
+ // Eredmény: Foo.prototype.method.call(this, arg1, arg2... argN)
+ Function.call.apply(Foo.prototype.method, arguments);
+};
+
Paraméterek és argumentum indexek
+
A háttérben az arguments objektum minden egyes indexére (elemére) egy getter és egy setter
+függvényt is kap, csak úgy ahogy a függvény paramétereit is felül tudjuk írni, illetve eltudjuk érni.
+
Ennek eredményeképp, az arguments objektumon véghezvitt változtatások szinkronban
+változtatják a függvény névvel ellátott paramétereit is.
+
function foo(a, b, c) {
+ arguments[0] = 2;
+ a; // 2
+
+ b = 4;
+ arguments[1]; // 4
+
+ var d = c;
+ d = 9;
+ c; // 3
+}
+foo(1, 2, 3);
+
Teljesítmény mítoszok és trükkök
+
Ahogy már azt korábban körvonalaztuk, az arguments objektum csak akkor nem jön létre,
+hogyha a függvényhatókörön belül definiálunk egy változót ezzel a névvel, vagy a függvényünk
+egyik paraméterének ezt a nevet választjuk.
+
Azonban a getterek és setterek mindig létrejönnek, de ez ne zavarjon meg minket, mert
+semmiféle befolyása nincs a teljesítményre, pláne olyan kódban ahol sokkal több mindennel
+is foglalkozunk mint az arguments objetkumhoz való hozzáférés.
+
+
Habár, egyetlen eset van, amelynek komoly hatása lehet a kód teljesítményére a modern
+JavaScript motorokban. Ez pedig az arguments.callee használata.
+
function foo() {
+ arguments.callee; // csináljunk valamit ezzel a függvény objektummal
+ arguments.callee.caller; // és ennek a hívójával..
+}
+
+function bigLoop() {
+ for(var i = 0; i < 100000; i++) {
+ foo(); // Így viszont nem lehet behelyettesíteni ide...
+ }
+}
+
A fenti kódban a foo helyére nem lehet egyszerűen behelyettesíteni a függvény törzsét,
+mivel a függvény törzsének fogalma kell legyen mind magáról, mind az ő hívójáról. Ez nem csak
+hogy azt akadályozza meg, hogy a behelyettesítéssel nyerjünk egy kis többlet performanciát,
+de az egységbe zárás elvét is erősen keresztbevágja, hiszen a függvény így erősen támaszkodni
+fog a hívó környezetére (kontextusára).
+
Emiatt is, az arguments.callee, vagy bármely mezőjének használata erősen kerülendő.
+
+
Konstruktorok
Csak úgy mint minden más, a konstruktorok működése szintén különbözik
+a megszokottól. Itt minden függvényhívás amelyet a new kulcsszó előz meg,
+konstruktor hívásnak számít.
+
A this értéke a konstruktoron - hívott függvényen - belül az újonnan létrehozott objektumra
+mutat. Az új objektum prototípusa a konstruktor függvény prototípusával fog megegyezni.
+
Ha a konstruktor függvényben nincs return utasítás, akkor automatikusan a this értékével tér vissza - a létrehozott objektummal.
+
function Foo() {
+ this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+ console.log(this.bla);
+};
+
+var test = new Foo();
+
A fenti kódban a Foo függvényt mint konstruktort hívjuk meg, ami a test változóban
+egy új objektumot fog eredményezni. Ennek az objektumnak a prototípusa a Foo prototípusa lesz.
+
Trükkös ugyan, de ha mégis van return utasítás az éppen konstruált függvényben, akkor
+a függvény hívása az annak megfelelő értékkel fog visszatérni, de csak akkor, ha a
+visszatérített érték Objektum típusú.
+
function Bar() {
+ return 2;
+}
+new Bar(); // ez egy új üres objektum lesz: {}, a 2 helyett
+
+function Test() {
+ this.value = 2;
+
+ return {
+ foo: 1
+ };
+}
+new Test(); // ez a returnben található objektumot fogja eredményezni
+
Hogyha kihagyjuk a new kulcsszó használatát, a függvény nem egy új objektummal fog visszatérni.
+
function Foo() {
+ this.bla = 1; // ez a globális objektumon állítja be a bla értékét 1-re
+}
+Foo(); // undefined
+
A this JavaScript beli működésének köszönhetően, mégha le is
+fut az előbbi kód, akkor a this helyére a globális objektumot képzeljük.
+
Gyárak (Factory-k)
+
Ahhoz, hogy teljesen eltudjuk hagyni a new kulcsszó használatát, a konstruktor
+függvény explicit értékkel kell visszatérjen.
Mindkét Bar-ra történő hívásmód ugyanazt fogja eredményezni. Kapunk általuk
+egy újonnan létrehozott objektumot, amelynek lesz egy method nevű mezője,
+ami egyébiránt egy Closure.
+
Azt is érdekes itt megjegyezni, hogy a new Bar() hívás nem befolyásolja a
+visszatérített objektum prototípusát. Mivel a prototípus csak az újonnan
+létrehozott objektumon létezik, amit a Bar nem térít vissza (mivel egy explicit
+értéket ad vissza).
+
A fenti példában nincs funkcionális különbség aközött hogy kiírjuk-e a new
+varázsszót avagy nem.
+
Új objektumok létrehozása gyárakon keresztül
+
Gyakran bevett módszer egy projetkben, hogy a new varázsszó használatát
+teljesen elhagyjuk, mert a kiírásának elfelejtése bugokhoz vezetne.
+
Ennek érdekében egy új objektum létrehozásához inkább egy gyárat kell
+implementálni, és annak a belsejében létrehozni az új objektumot.
A fenti kód ugyan ellenálló a hiányzó new kulcsszó hibáját illetően és
+megfelelően használ privát változókat, érdemes
+megemlíteni a dolgok kontra részét is.
+
+
Több memóriát használ, mivel az így létrehozott objektumok nem
+osztják meg a prototípusukat egymás között.
+
A származtatás macerás, mivel a gyár kénytelen ilyenkor lemásolni
+az összes származtatandó metódust egy másik objektumról, vagy ezt az objektumot
+be kell állítsa a létrehozott új objektum prototípusának.
+
Az a megközelítés miszerint egy kifelejtett new kulcsszó miatt eldobjuk
+az objektum teljes prototípusát, ellenkezik a nyelv szellemiségével.
+
+
Összefoglaló
+
A new varázsszó kihagyása ugyan bugokhoz vezethet, de ez nem megfelelő indok
+arra hogy ezért eldobjuk a prototípusok használatát. Végeredményben mindig
+az fog dönteni a különböző stílusok megválasztása között, hogy mire van
+szüksége éppen az aktuális programunknak. Egy dolog azért elengedhetetlenül
+fontos, ez pedig hogy megválasszuk melyik stílust fogjuk használni objektumok
+létrehozásra, és ezt konzisztensen használjuk a teljes megoldáson keresztül.
+
Névterek és hatókörök
Habár látszólag a kapcsos zárójelek jelentik a blokkok határait JavaScriptben,
+fontos megjegyezni hogy nincsen blokk szintű hatókör, csakis függvény hatókörök
+léteznek.
+
function test() { // ez egy hatókör
+ for(var i = 0; i < 10; i++) { // ez meg nem
+ // utasítások...
+ }
+ console.log(i); // 10
+}
+
+
A nyelvben nincsenek beépített névterek, ami azt jelenti hogy minden, egyetlen
+globálisan megosztott névtérben kerül deklarálásra.
+
Akárhányszor egy változóra hivatkozunk, a JavaScript elkezdi felfele utazva
+megkeresni hatókörökön, amíg csak meg nem találja. Hogyha elérjük
+a globális hatókört és még mindig nem találjuk a keresett változót, akkor egy
+ReferenceError hibával gazdagodik a futásidőnk.
+
A globális változók csapása
+
// A script
+foo = '42';
+
+// B script
+var foo = '42'
+
Érdemes észrevenni, hogy a fenti két scriptnek nem ugyanaz a hatása. Az A script
+egy foo nevű változót vezet be a globális hatókörben, a B script pedig egy foo
+nevű változót deklarál az ő hatókörében.
+
Mégegyszer tehát, ez a kettő nemugyanazt jelenti: a var elhagyásának jópár
+beláthatatlan következménye is lehet.
Itt, a var elhagyása azt eredményezi, hogy a test függvény mindig felülírja
+a globális hatókörben definiált foo változó értékét. Habár ez elsőre nem tűnik
+nagy dolognak, ha a varokat több száz sornyi JavaScript kódból hagyjuk el, az
+olyan hibákhoz vezethet, amit még az anyósunknak se kívánnánk.
+
// globális hatókör
+var items = [/* random lista */];
+for(var i = 0; i < 10; i++) {
+ subLoop();
+}
+
+function subLoop() {
+ // a subLoop hatóköre
+ for(i = 0; i < 10; i++) { // hiányzik a var
+ // elképesztő dolgokat művelünk itt
+ }
+}
+
Ennél a kódnál a külső ciklus az első subLoop hívás után megáll, mivel a subLoop
+felülírja az i változó globális értékét. Hogyha a második for ciklusban használtuk
+volna var-t azzal könnyen elkerülhettük volna ezt a hibát. Sose hagyjuk el a var utasítást, ha csak nem direkt az a kívánt hatás, hogy befolyásoljuk a
+külső hatókört.
+
Lokális változók
+
Kétféleképp (és nem több módon) lehet lokális változókat JavaScriptben leírni; ez vagy a függvény paraméter vagy a var utasítás.
+
// globális hatókör
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+ // a test függvény lokális hatóköre
+ i = 5;
+
+ var foo = 3;
+ bar = 4;
+}
+test(10);
+
Itt a foo és i lokális változók a test hatókörén belül, viszont a baros
+értékadás felül fogja írni a hasonló nevű globális változót.
+
Hoisting
+
A JS hoistolja (megemeli) a deklarációkat. Ez azt jelenti hogy minden var
+utasítás és függvény deklaráció az őt körülvevő hatókör tetejére kerül.
+
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+ if (false) {
+ goo = 1;
+
+ } else {
+ var goo = 2;
+ }
+ for(var i = 0; i < 100; i++) {
+ var e = data[i];
+ }
+}
+
A fenti kód átalakul egy másik formára mielőtt lefutna. A JavaScript felmozgatja
+a var utasításokat és a függvény deklarációkat, az őket körülvevő legközelebbi
+hatókör tetejébe.
+
// a var utasítások felkerülnek ide
+var bar, someValue; // alapból mindegyik 'undefined' értékű lesz
+
+// a függvény deklaráció is felkerül ide
+function test(data) {
+ var goo, i, e; // mivel nincs blokk hatókör, ezek is felkerülnek
+ if (false) {
+ goo = 1;
+
+ } else {
+ goo = 2;
+ }
+ for(i = 0; i < 100; i++) {
+ e = data[i];
+ }
+}
+
+bar(); // Ez TypeErrorral elszáll, mivel a bar még 'undefined'
+someValue = 42; // az értékadásokat nem piszkálja a hoisting
+bar = function() {};
+
+test();
+
A hiányzó blokk hatókör ténye nem csak azt eredményezi, hogy a var utasítások
+kikerülnek a ciklusmagokból, hanem az if utasítások kimenetele is megjósolhatatlan
+lesz.
+
Habár úgy látszik az eredeti kódban, hogy az if utasítás a googlobális
+változót módosítja, a hoisting után látjuk hogy valójában a lokális változóra
+lesz befolyással. Trükkös.
+
A hoisting tudása nélkül valaki azt hihetné, hogy az alábbi kód egy ReferenceError
+-t fog eredményezni.
+
// nézzük meg hogy a SomeImportantThing inicializálva lett-e
+if (!SomeImportantThing) {
+ var SomeImportantThing = {};
+}
+
Persze ez működik, annak köszönhetően hogy a var utasítás a globális hatókör
+tetejére lett mozgatva.
+
var SomeImportantThing;
+
+// más kódok még inicializálhatják a SomeImportantThing változót itt...
+
+// ellenőrizzük hogy létezik-e
+if (!SomeImportantThing) {
+ SomeImportantThing = {};
+}
+
Névfeloldási sorrend
+
JavaScriptben az összes hatókörnek -beleértve a globálisat is- megvan a maga
+this változója, amelyik mindig az aktuális objektumra utal.
+
A függvény hatókörökben van még egy speciális arguments
+változó is mindig definiálva, amely a függvénynek átadott argumentumokat
+tartalmazza.
+
Hogy hozzunk egy példát, amikor valaki a foo nevű változót próbálja elérni egy
+függvény hatókörön belül, a JavaScript az alábbi sorrendben fogja keresni az adott
+változó nevet.
+
+
Abban az esetben ha találunk var foo utasítást, használjuk azt.
+
Hogyha bármelyik függvény paraméter neve foo, használjuk azt.
+
Hogyha magának a függvénynek a neve `foo, használjuk azt.
+
Menjünk a külső hatókörre, és kezdjük újra #1-től.
+
+
+
Névterek
+
Hogyha egyetlen globális névterünk van, akkor egy gyakori probléma lehet az,
+hogy névütközésekbe futunk. A JavaScriptben szerencsére ez a gond könnyen
+elkerülhető a névtelen wrapper függvények használatával.
+
(function() {
+ // egy 'öntartalmazó' névtér
+
+ window.foo = function() {
+ // egy exportált closure
+ };
+
+})(); // a függvényt azonnal végre is hajtjuk
+
A névtelen függvények kifejezésekként vannak értelmezve; így
+ahhoz hogy meghívhatóak legyenek, először ki kell értékelni őket.
+
( // a függvény kiértékelése a zárójeleken belül
+function() {}
+) // a függvény objektum visszatérítése
+() // az eredmény meghívása
+
Persze más kifejezések is használhatóak arra hogy kiértékeljük és meghívjuk
+a függvény kifejezést, amelyek habár szintaxisukban eltérnek, ugyanazt eredményezik.
+
// Még több stílus anonymus függvények azonnali hívásához...
+!function(){}()
++function(){}()
+(function(){}());
+// és a lista folytatódik...
+
Összegzésül
+
Az anonym wrapper függvények használata erősen ajánlott a kód egységbezárása
+érdekében, saját névtér alkotásához. Ez nem csak hogy megvédi a kódunkat a
+névütközésektől, de jobb modularizációhoz is vezet.
+
Emelett a globális változók használata nem ajánlott. Bármilyen fajta
+használata rosszul megírt kódról árulkodik, amelyik könnyen eltörik és nehezen
+karbantartható.
+
Tömbök
Tömb iteráció és tulajdonságok
Habár a tömbök a JavaScriptben objektumok, nincsen jó ok arra, hogy a for in ciklussal járjuk be őket.
+Valójában sokkal több jó ok van arra, hogy miért ne így tegyünk.
+
+
Mivel a for in ciklus a prototípus láncon levő összes tulajdonságon végigmegy,
+és mivel az egyetlen út ennek megkerülésére a hasOwnProperty használata, így majdnem hússzor
+lassabb mint egy sima for ciklus.
+
Iteráció
+
Annak érdekébern hogy a legjobb teljesítményt érjük el a tömbökön való iteráció során,
+a legjobb hogyha a klasszikus for ciklust használjuk.
+
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+ console.log(list[i]);
+}
+
Még egy érdekesség van a fenti példában, ami a tömb hosszának cachelését végzi
+a l = list.length kifejezés használatával.
+
Habár a length tulajdonság mindig magán a tömbön van definiálva, még mindig
+lehet egy kis teljesítmény kiesés amiatt hogy minden iterációban újra meg kell
+keresni ezt a tulajdonságot. Persze a legújabb JavaScript motorok talán
+használnak erre optimalizációt, de nem lehet biztosan megmondani hogy ahol a kódunk
+futni fog, az egy ilyen motor-e vagy sem.
+
Valójában, a cachelés kihagyása azt eredményezheti, hogy a ciklusunk csak
+fele olyan gyors lesz mintha a cachelős megoldást választottuk volna.
+
A length mező
+
Míg a length mező getter függvénye egyszerűen csak visszaadja a tömbben
+levő elemek számát, addig a setter függvény használható arra (is), hogy
+megcsonkítsuk a tömbünket.
Egy rövidebb hossz alkalmazása csonkítja a tömböt. A nagyobb hossz megadása
+értelemszerűen növeli.
+
Összegzésül
+
A megfelelő teljesítmény érdekében, a for ciklus használata és a length cachelése
+ajánlott. A for in ciklus használata a tömbökön a rosszul megírt kód jele, amely
+tele lehet hibákkal, és teljesítményben sem jeleskedik.
+
Az Array konstruktor
Mivel az Array konstruktora kétértelműen bánik a paraméterekkel, melegen
+ajánlott mindig a tömb literált - [] jelölés - használni új tömbök létrehozásakor.
Abban az esetben, hogyha ez a konstruktor csak egy szám paramétert kap, akkor
+visszatérési értékül egy olyan tömböt fog létrehozni amelynek a length mezője
+akkorára van beállítva, ahogy azt megadtuk az argumentumban. Megjegyzendő hogy
+csak a length tulajdonság lesz ekkor beállítva; az egyes indexek külön-külön
+nem lesznek inicializálva.
+
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // hamis, nincs ilyen index
+
A tömb hosszának közvetlen állítása amúgy is csak elég kevés esetben
+használható értelmesen, mint például alább, hogyha el akarjuk kerülni a
+for ciklus használatát egy string ismétlésekor.
+
new Array(count + 1).join(ismetlendoString);
+
Összegzésül
+
Az Array konstruktor közvetlen használata erősen kerülendő. A literálok használata
+elfogadott inkább, mivel rövidebbek, tisztább a szintaxisuk és olvashatóbb kódot
+eredményeznek.
+
Típusok
Egyenlőség vizsgálat
A JavaScriptben két különböző megoldás létezik az objektumok egyenlőségének
+vizsgálatára
+
Az egyenlőség operátor
+
Az egyenlőség vizsgálatot végző (egyik) operátort így jelöljük: ==
+
A JavaScript egy gyengén típusos nyelv. Ez azt jelenti hogy az egyenlőség
+operátor típuskényszerítést alkalmaz ahhoz, hogy össze tudjon hasonlítani
+két értéket.
+
"" == "0" // hamis
+0 == "" // igaz
+0 == "0" // igaz
+false == "false" // hamis
+false == "0" // igaz
+false == undefined // hamis
+false == null // hamis
+null == undefined // igaz
+" \t\r\n" == 0 // igaz
+
A fenti táblázat szépen mutatja hogy mi a típuskényszerítés eredménye, és egyben
+azt is, hogy miért rossz szokás a == használata. Szokás szerint, ez megint
+olyan fícsör ami nehezen követhető kódhoz vezethet a komplikált konverziós
+szabályai miatt.
+
Pláne, hogy a kényszerítés teljesítmény problémákhoz is vezet; ugyanis, mielőtt
+egy stringet egy számhoz hasonlítanánk azelőtt a karakterláncot át kell konvertálni
+a megfelelő típusra.
+
A szigorú(bb) egyenlőség operátor
+
Ez az operátor már három egyenlőségjelből áll: ===.
+
Ugyanúgy működik mint az előbbi, kivéve hogy ez a változat nem alkalmaz
+típuskényszerítést az operandusai között.
A felső eredmények sokkal egyértelműbbek és ennek köszönhetően sokkal hamarabb
+eltörik a kód egy-egy ellenőrzésen. Ettől sokkal hibatűrőbb lesz
+a produktumunk, és ráadásul teljesítménybeli gondjaink sem lesznek.
+
Objektumok összehasonlítása
+
Habár mind a ==-t és a ===-t is egyenlőség operátornak hívjuk, eltérően
+viselkednek hogyha legalább az egyik operandusuk egy objektum.
Ebben az esetben mindkét operátor identitást és nem egyenlőséget
+ellenőriz; tehát azt fogják ellenőrizni hogy az operandus két oldalán
+ugyanaz az objektum referencia áll-e, mint az is operátor Pythonban
+vagy a pointerek összehasonlítása C-ben. (A ford.: Tehát nem azt, hogy a
+két oldalon álló objektumnak például ugyanazok-e a mezői, hanem azt hogy ugyanazon
+a memóriacímen található-e a két operandus).
+
Összegzésül
+
Azt érdemes tehát megjegyezni, hogy a szigorú egyenlőség vizsgálatot érdemes
+mindig használni. Amikor szeretnék típuskényszerítést alkalmazni, akkor azt
+inkább tegyük meg direkt módon, és ne a nyelv komplikált
+automatikus szabályaira bízzuk magunkat.
+
A typeof vizsgálat
A typeof operátor (az instanceof-al karöltve)
+lehetőség szerint a JavaScript nyelv egyik legnagyobb buktatója, mivel majdnem
+teljesen rosszul működik.
+
Habár az instanceof-nak korlátozottan még lehet értelme, a typeof operátor
+tényleg csak egyetlen praktikus use case-zel rendelkezik és ez nem az hogy egy
+objektum típusvizsgálatát elvégezzük.
+
+
Az instanceof operátor
Az instanceof operátor a két operandusának konstruktorait hasonlítja össze.
+Csak akkor bizonyul hasznosnak, amikor saját készítésű objektumokon alkalmazzuk.
+Beépített típusokon ugyanolyan hasztalan alkalmazni mint a typeof operátort.
+
Saját objektumok összehasonlítása
+
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // igaz
+new Bar() instanceof Foo; // igaz
+
+// Ez csak a Bar.prototypeot beállítja a Foo fv. objektumra,
+// de nem egy kimondott Foo példányra
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // hamis
+
Az instanceof reakciója natív típusokra
+
new String('foo') instanceof String; // igaz
+new String('foo') instanceof Object; // igaz
+
+'foo' instanceof String; // hamis
+'foo' instanceof Object; // hamis
+
Érdemes itt megjegyezni hogy az instanceof nem működik olyan objektumokon,
+amelyek különböző JavaScript kontextusokból származnak (pl. különböző dokumentumok
+a böngészőn belül), mivel a konstruktoruk nem pontosan ugyanaz az objektum lesz.
+
Összegzésül
+
Az instanceof-ot tehát csak megegyező JS kontextusból származó, saját készítésű objektumoknál használjuk. Minden más felhasználása kerülendő, csak úgy mint a typeof operátor esetén.
+
Típus kasztolás
Előre kössük le, hogy a JavaScript egy gyengén típusos nyelv, így ahol
+csak tud, ott típus kényszerítést használ.
+
// Ezek igazak
+new Number(10) == 10; // A Number.toString() számmá lesz
+ // visszaalakítva
+
+10 == '10'; // A Stringek visszaalakulnak számmá
+10 == '+10 '; // Mégtöbb string varázslat
+10 == '010'; // és mégtöbb
+isNaN(null) == false; // a null varázslatosan 0-vá alakul
+ // ami persze nem NaN
+
+// Ezek hamisak
+10 == 010;
+10 == '-10';
+
+
Hogy elkerüljük a fenti varázslatokat, a szigorú egyenlőség ellenőrzésmelegen ajánlott. Habár ezzel elkerüljük
+a problémák farkasrészét, még mindig tartogat a JS gyengén típusos rendszere
+meglepetéseket.
+
Natív típusok konstruktorai
+
A jó hír az, hogy a natív típusok mint a Number és a String különféle
+módon viselkednek hogyha a new kulcsszóval avagy anélkül vannak inicializálva.
+
new Number(10) === 10; // Hamis, Objektum vs. Szám
+Number(10) === 10; // Igaz, Szám vs. szám
+new Number(10) + 0 === 10; // Igaz, az implicit konverziónak hála
+
Ha egy natív típust mint a Number konstruktorként kezelünk, akkor egy új
+Number objektumot kapunk. De ha kihagyjuk a new kulcsszót akkor a Number
+egy egyszerű konverter függvényként fog viselkedni.
+
Ráadásul a literálok passzolgatásakor még több típuskonverzió üti fel a fejét.
+
A legjobb megoldás hogyha a három típus valamelyikére expliciten kasztolunk.
+
Stringre kasztolás
+
'' + 10 === '10'; // igaz
+
Egy üres string hozzáfűzésével könnyen tudunk egy értéket stringgé kasztolni.
+
Számra kaszt
+
+'10' === 10; // igaz
+
Az unáris plusz operátor használatával lehetséges egy értéket számra alakítani.
+
Booleanre kasztolás
+
A nem operátor kétszeri alkalmazásával tudunk booleanné kasztolni.
+
!!'foo'; // igaz
+!!''; // hamis
+!!'0'; // igaz
+!!'1'; // igaz
+!!'-1' // igaz
+!!{}; // igaz
+!!true; // igaz
+
Lényeg
Miért Ne Használjuk az eval-t
Az eval (evil) funkció egy stringbe ágyazott JavaScript kódot futtat a
+lokális scopeon belül.
Viszont az eval csak akkor viselkedik így, hogyha expliciten hívjuk meg
+és a meghívott funkció neve valóban eval.
+
var foo = 1;
+function test() {
+ var foo = 2;
+ var bar = eval;
+ bar('foo = 3');
+ return foo;
+}
+test(); // 2
+foo; // 3
+
Az eval használata kerülendő. A "felhasználása" az esetek 99.9%-ban
+mellőzhető.
+
Az eval ezer arca
+
A setTimeout és setInterval nevű timeout függvények is
+tudnak úgy működni, hogy első paraméterükként egy stringbe ágyazott kódot várnak.
+Ez a string mindig a globális hatókörben lesz végrehajtva, mivel az evalt
+így nem direktben hívjuk meg.
+
Biztonsági problémák
+
Az eval azért is veszélyes, mert bármilyen JS kódot végrehajt, amit odaadunk
+neki. Éppen ezért sose használjuk olyan kódok végrehajtására amiknek az eredete
+nem megbízható/ismeretlen.
+
Összegzésül
+
Soha ne használjunk evalt. Bármilyen kód működése, teljesítménye, ill. biztonsága
+megkérdőjelezhető amely használja ezt a nyelvi elemet. Semmilyen megoldás
+használata nem ajánlott amely első sorban evalra épül. Ekkor egy jobb
+megoldás szükségeltetik, amely nem függ az evaltól.
+
Az undefined és a null
A JavaScript két értéket is tartogat a semmi kifejezésére, ezek a null és az
+undefined és ezek közül az utóbbi a hasznosabb.
+
Az undefined
+
Ha az előbbi bevezetőtől nem zavarodtál volna össze; az
+undefined egy típus amelynek pontosan egy értéke van, az undefined.
+
A nyelvben szintén van egy undefined nevű globális változó amelynek az értékét
+hogy-hogy nem undefined-nak hívják. Viszont ez a változó nem konstans vagy
+kulcsszó a nyelvben. Ez azt jeletni hogy az értéke könnyedén felülírható.
+
+
Itt van pár példa, hogy mikor is találkozhatunk az undefined értékkel:
+
+
Az undefined globális változó elérésekor
+
Egy deklarált, de nem inicializált változó elérésekor.
+
Egy függvény hívásakor ez a visszatérési érték, return utasítás híján.
+
Egy olyan return utasítás lefutásakor, amely nem térít vissza értéket.
+
Nem létező mezők lekérésekor.
+
Olyan függvény paraméterek elérésekor amelyeknek a hívó oldalon nem kaptak értéket.
+
Bármikor amikor az undefined érték van valaminek beállítva.
+
Bármelyik void(kifejezés) utasítás futtatásakor.
+
+
undefined megőrzési trükkök
+
Mivel az undefined nevű globális változó csak egy másolatot tárol az
+undefined elnevezésű értékből, az értékének megváltoztatása nem írja
+felül az eredeti undefinedtípus értékét.
+
Ezért, ha valamilyen értékkel össze szeretnénk hasonlítani az undefined értéket,
+nem árt hogyha először magát az undefined-ot el tudjuk érni.
+
Egy gyakori technika annak érdekében hogy megvédjük a kódunkat az
+undefined lehetséges felüldefiniálásaitól, hogy egy névtelen (wrapper) függvénybe
+csomagoljuk az egész kódunkat, amelynek lesz egy direkt üres paramétere.
+
var undefined = 123;
+(function(something, foo, undefined) {
+ // az undefined ebben a hatókörben
+ // megint valóban az `undefined` értékre referáll.
+
+})('Hello World', 42);
+
Egy másik módja ennek, hogy használunk egy "üres" deklarációt a wrapper függvényen
+belül.
+
var undefined = 123;
+(function(something, foo) {
+ var undefined;
+ ...
+
+})('Hello World', 42);
+
Az egyetlen különbség ebben a változatban, hogyha minifikáljuk ezt a kódot,
+és nem definiálunk további változókat ezen a részen belül, akkor ezzel a
+változattal extra 4 byte "veszteséget" szenvedünk el.
+
Mikor használjunk nullt
+
Miközben az undefined a natív JavaScript megvalósításokban inkább a (más
+nyelvekben levő) tradícionális null helyett használandó, azalatt maga a null
+inkább csak egy különböző adattípusnak számít, mindenféle különös jelentés nélkül.
+
Egy pár belső JavaScriptes megoldásban ugyan használják (ahol pl. a prototípus lánc végét a Foo.prototype = null beállítással jelölik), de a legtöbb esetben ez
+felcserélhető az undefined-al.
+
(A ford.: A null annak az esetnek a jelölésére hasznos, amikor
+egy referencia típusú változót deklarálunk, de még nem adunk neki értéket. Pl. a
+var ezObjektumLesz = null kifejezés ezt jelöli. Tehát a null leginkább
+kezdeti értékként állja meg a helyét, minden másra ott az undefined)
+
Automatic Semicolon Insertion
Bár a JavaScriptnek látszólag C-s szintaxisa van, mégsem kötelező benne
+kirakni a pontosvesszőket, így (helyenként) kihagyhatóak a forrásból.
+(A ford.: hiszen interpretált nyelv lévén nincsenek fordítási hibák, így
+nyelvi elemek meglétét sem tudja erőltetni a nyelv)
+
Itt jön a csel, hogy ennek ellenére a JavaScript csak pontosvesszőkkel
+értelmezi megfelelően a beírt kódot. Következésképp, a JS automatikusan
+illeszti be a pontosvesszőket (megpróbálja kitalálni a gondolataink)
+azokra a helyekre, ahol amúgy emiatt értelmezési hibába futna.
Az esélyek arra elég magasak, hogy a lognem egy függvényt fog visszatéríteni; így a fenti kód egy TypeError típusú hibát fog dobni
+undefined is not a function üzenettel.
+
Összefoglalásképp
+
Szükségszerűen soha ne hagyjuk ki a pontoszvesszőket. Nem árt a kapcsos
+zárójeleket is ugyanazon a soron tartani, mint amelyiken az utasítást elkezdtük,
+így nem ajánlott az egysoros if / else kifejezések kedvéért elhagyni
+őket. Ezek a szempontok nem csak a kódot (és annak olvashatóságát) tartják
+konzisztensen, de megelőzik azt is hogy a JavaScript értelmező valamit rosszul
+"találjon ki".
+
A delete Operátor
Röviden, lehetetlen globális változókat, függvényeket és olyan dolgokat törölni
+JavaScriptben amelyeknek a DontDelete attribútuma be van állítva.
+
Globális kód és Függvény kód
+
Amikor egy változó/függvény, globális vagy
+függvény hatókörben van definiálva,
+akkor az vagy az Aktivációs (Activation) vagy a Globális (Global) objektum egyik mezőjeként
+jön létre. Az ilyen mezőknek van egy halom attribútuma, amelyek közül az egyik
+a DontDelete. A változó és függvény deklarációk a globális vagy függvény kódon
+belül mindig DontDelete tulajdonságú mezőket hoznak létre, így nem lehet őket
+törölni.
+
// globális változó
+var a = 1; // A DontDelete be lett állítva
+delete a; // hamis
+a; // 1
+
+// függvény:
+function f() {} // A DontDelete be lett állítva
+delete f; // hamis
+typeof f; // "function"
+
+// új értékadással sem megy
+f = 1;
+delete f; // hamis
+f; // 1
+
Explicit mezők
+
Az expliciten beállított mezőket persze normálisan lehet törölni.
A fenti példábna az obj.x és obj.y törölhető, mivel nincs DontDelete
+attribútuma egyik mezőnek sem. Ezért működik az alábbi példa is.
+
// működik, kivéve IE-ben
+var GLOBAL_OBJECT = this;
+GLOBAL_OBJECT.a = 1;
+a === GLOBAL_OBJECT.a; // igaz - egy globális változó
+delete GLOBAL_OBJECT.a; // igaz
+GLOBAL_OBJECT.a; // undefined
+
Itt egy trükköt használunk az a törlésére. A this itt
+a Globális objektumra mutat, és expliciten bezetjük rajta az a változót, mint
+egy mezőjét, így törölni is tudjuk.
+
Mint az szokás, a fenti kód egy kicsit bugos IE-ben (legalábbis 6-8-ig).
+
Függvény argumentumok és beépített dolgaik
+
A függvény argumentumok, az arguments objektum
+és a beépített mezők szintén DontDelete tulajdonságúak.
A delete operátor működése megjósolhatatlan a vendég objektumokra. A specifikáció
+szerint ezek az objektumok szükség szerint bármilyen viselkedést implementálhatnak.
+
(A ford.: Vendég objektumok azok az objektumok, amelyek nincsenek konkrétan
+meghatározva az ES aktuális verziójú specifikációjában, pl. a window)
+
Összegzésképp
+
A delete működése helyenként megjósolhatatlan, így biztonsággal csak olyan
+objektumok mezőin használhatjuk amelyeket expliciten mi állítottunk be.
+
Egyéb
A varázslatos setTimeout és setInterval
Mivel a JavaScript aszinkron, a setTimeout és setInterval használatával
+lehetséges késleltetni a kódok lefutási idejét.
+
+
function foo() {}
+var id = setTimeout(foo, 1000); // Egy számmal (> 0) tér vissza
+
Amikor a setTimeout függvényt meghívjuk, válaszul egy timeout ID-t kapunk
+valamint be lesz ütemezve a foo függvényhívás, hogy körülbelül 1000 miliszekundum múlva fusson le a jövőben. A fooegyszer lesz végrehajtva.
+
Az aktuális JavaScript motor időzítésétől függően, és annak figyelembe vételével
+hogy a JavaScript mindig egyszálú, tehát a megelőző kódok blokkolhatják a szálat,
+soha nem lehet biztonságosan meghatározni hogy valóban a kért időzítéssel
+fog lefutni a kód amit megadtunk a setTimeoutban. Erre semmilyen biztosíték nincs.
+
Az első helyen bepasszolt függvény a globális objektum által lesz meghívva, ami
+azt jelenti hogy a this a függvényen belül a globális objektumra
+utal.
+
function Foo() {
+ this.value = 42;
+ this.method = function() {
+ // a this egy globális objektumra utal, nem a Foo-ra
+ console.log(this.value); // undefined-ot logol ki
+ };
+ setTimeout(this.method, 500);
+}
+new Foo();
+
+
Híváshalmozás a setIntervalal
+
Míg a setTimeout csak egyszer futtatja le a megadott függvényt, a setInterval
+
+
ahogy a neve is mutatja - mindenX miliszekundumban végrehajtja a
+neki átadott kódot, használata pedig erősen kerülendő.
+
+
Nagy hátulütője, hogy még akkor is ütemezi az újabb és újabb
+hívásokat, hogyha az aktuálisan futattot kód a megadott időintervallumon
+felül blokkolja a további kód futtatást. Ez, hogyha megfelelően rövid
+intervallumokat állítunk be, felhalmozza a függvényhívásokat a call stacken.
+
function foo(){
+ // kód ami 1 másodpercig feltartja a futtatást
+}
+setInterval(foo, 100);
+
A fenti kódban amikor a foo meghívódik, 1 másodpercig feltartja a további futtatást.
+
A setInterval persze ütemezni fogja a jövőbeli foo hívásokat továbbra is, amíg
+blokkolódik a futtatás. Így tíz további hívás fog várakozni, miután a foo
+futtatása először végzett.
+
Hogyan Bánjunk El a Blokkolással
+
A legkönnyebb és kontrollálhatóbb megoldásnak az bizonyul, hogyha a setTimeout
+függvényt a rögtön a foo-n belül használjuk.
+
function foo(){
+ // 1 másodpercig blokkoló kód
+ setTimeout(foo, 100);
+}
+foo();
+
Ez nem csak egységbe zárja a setTimeout hívást, de meggátolja a felesleges hívások
+felhalmozását, és több irányítást ad a kezünkbe. A foo így magától eltudja
+dönteni, hogy akarja-e újra futtatni önmagát vagy sem.
+
Timeout Tisztogatás Kézzel
+
A clearTimeout vagy clearInterval hívással tudjuk a timeoutjainkat
+megszüntetni, természetesen attól függ hogy melyiket használjuk,
+hogy melyik set függvénnyel indítottuk útjára a timeoutunkat.
+
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
Az Összes Timeout Megszüntetése
+
Mivel nincsen beépített megoldás az összes timeout és/vagy interval
+hívás törlésére, ezért bruteforce módszerekhez kell folyamodjunk.
+
// az "összes" timeout kitörlése
+for(var i = 1; i < 1000; i++) {
+ clearTimeout(i);
+}
+
Persze ez csak véletlenszerű lövöldözés, semmi sem garantálja hogy a fenti
+módszerrel nem marad timeout a rendszerben (A ford.: például az ezredik timeout vagy
+afelett). Szóval egy másik módszer ennek megoldására, hogy feltételezzük hogy
+minden setTimeout hívással az azonosítók száma egyel növekszik.
Habár ez a megoldás minden böngészőben megy (egyenlőre), ez az azonosítókról született mondás nincs specifikációban rögzítve, és ennek megfelelően változhat.
+Az ajánlott módszer továbbra is az, hogy kövessük nyomon az összes timeout azonosítót amit generáltunk, és így ki is tudjuk őket rendesen törölni.
+
eval A Színfalak Mögött
+
Habár a setTimeout és a setInterval (kód) stringet is tud első paramétereként
+fogdani, ezt a fajta formáját használni kimondottan tilos, mivel a függöny
+mögött ő is csak evalt használ.
+
+
function foo() {
+ // meg lesz hívva
+}
+
+function bar() {
+ function foo() {
+ // soha nem hívódik meg
+ }
+ setTimeout('foo()', 1000);
+}
+bar();
+
Mivel az evalt nem direkt módon hívjuk meg a fenti esetben,
+a setTimeoutnak passzolt string a globális hatókörben fog lefutni; így
+a lokális foo függvényt sosem használjuk a bar hatóköréből.
+
Továbbá nem ajánlott argumentumokat átadni annak a függvénynek amelyik
+a timeout függvények által meg lesz hívva a későbbiekben.
+
function foo(a, b, c) {}
+
+// SOHA ne használd így!
+setTimeout('foo(1, 2, 3)', 1000)
+
+// Ehelyett csomagoljuk névtelen függvénybe
+setTimeout(function() {
+ foo(a, b, c);
+}, 1000)
+
+
Összegzésképp
+
Soha ne használjunk stringeket a setTimeout vagy setInterval első
+paramétereiként. Ha argumentumokat kell átadni a meghívandó függvénynek, az
+egyértelműen rossz kódra utal. Ebben az esetben a függvényhívás
+lebonyolításához egy anoním függvény használata ajánlott.
+
Továbbá, mivel az ütemező kódja nem blokkolódik a JavaScript futás által, a
+setInterval használata úgy általában kerülendő.
+
\ No newline at end of file
diff --git a/site/image/sidebar-icon.png b/image/sidebar-icon.png
similarity index 100%
rename from site/image/sidebar-icon.png
rename to image/sidebar-icon.png
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..a90a6a5
--- /dev/null
+++ b/index.html
@@ -0,0 +1,1499 @@
+JavaScript Garden
Intro
Intro
JavaScript Garden is a growing collection of documentation about the most
+quirky parts of the JavaScript programming language. It gives advice to
+avoid common mistakes and subtle bugs, as well as performance issues and bad
+practices, that non-expert JavaScript programmers may encounter on their
+endeavours into the depths of the language.
+
JavaScript Garden does not aim to teach you JavaScript. Former knowledge
+of the language is strongly recommended in order to understand the topics covered
+in this guide. In order to learn the basics of the language, please head over to
+the excellent guide on the Mozilla Developer Network.
JavaScript Garden is published under the MIT license and hosted on
+GitHub. If you find errors or typos please file an issue or a pull
+request on the repository. You can also find us in the JavaScript room on
+Stack Overflow chat.
+
Objects
Object Usage and Properties
Everything in JavaScript acts like an object, with the only two exceptions being
+null and undefined.
A common misconception is that number literals cannot be used as
+objects. That is because a flaw in JavaScript's parser tries to parse the dot
+notation on a number as a floating point literal.
+
2.toString(); // raises SyntaxError
+
There are a couple of workarounds that can be used to make number literals act
+as objects too.
+
2..toString(); // the second point is correctly recognized
+2 .toString(); // note the space left to the dot
+(2).toString(); // 2 is evaluated first
+
Objects as a Data Type
+
Objects in JavaScript can also be used as Hashmaps; they mainly consist
+of named properties mapping to values.
+
Using an object literal - {} notation - it is possible to create a
+plain object. This new object inherits from Object.prototype and
+does not have own properties defined.
+
var foo = {}; // a new empty object
+
+// a new object with a 'test' property with value 12
+var bar = {test: 12};
+
Accessing Properties
+
The properties of an object can be accessed in two ways, via either the dot
+notation or the square bracket notation.
+
var foo = {name: 'kitten'}
+foo.name; // kitten
+foo['name']; // kitten
+
+var get = 'name';
+foo[get]; // kitten
+
+foo.1234; // SyntaxError
+foo['1234']; // works
+
The notations work almost identically, with the only difference being that the
+square bracket notation allows for dynamic setting of properties and
+the use of property names that would otherwise lead to a syntax error.
+
Deleting Properties
+
The only way to remove a property from an object is to use the delete
+operator; setting the property to undefined or null only removes the
+value associated with the property, but not the key.
+
var obj = {
+ bar: 1,
+ foo: 2,
+ baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ console.log(i, '' + obj[i]);
+ }
+}
+
The above outputs both bar undefined and foo null - only baz was
+removed and is therefore missing from the output.
+
Notation of Keys
+
var test = {
+ 'case': 'I am a keyword, so I must be notated as a string',
+ delete: 'I am a keyword, so me too' // raises SyntaxError
+};
+
Object properties can be both notated as plain characters and as strings. Due to
+another mis-design in JavaScript's parser, the above will throw
+a SyntaxError prior to ECMAScript 5.
+
This error arises from the fact that delete is a keyword; therefore, it must be
+notated as a string literal to ensure that it will be correctly interpreted by
+older JavaScript engines.
+
The Prototype
JavaScript does not feature a classical inheritance model; instead, it uses a
+prototypal one.
+
While this is often considered to be one of JavaScript's weaknesses, the
+prototypal inheritance model is in fact more powerful than the classic model.
+It is, for example, fairly trivial to build a classic model on top of a
+prototypal model, while the other way around is a far more difficult task.
+
JavaScript is the only widely used language that features prototypal
+inheritance, so it can take time to adjust to the differences between the two
+models.
+
The first major difference is that inheritance in JavaScript uses prototype
+chains.
+
+
function Foo() {
+ this.value = 42;
+}
+Foo.prototype = {
+ method: function() {}
+};
+
+function Bar() {}
+
+// Set Bar's prototype to a new instance of Foo
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Hello World';
+
+// Make sure to list Bar as the actual constructor
+Bar.prototype.constructor = Bar;
+
+var test = new Bar(); // create a new bar instance
+
+// The resulting prototype chain
+test [instance of Bar]
+ Bar.prototype [instance of Foo]
+ { foo: 'Hello World' }
+ Foo.prototype
+ { method: ... }
+ Object.prototype
+ { toString: ... /* etc. */ }
+
In the code above, the object test will inherit from both Bar.prototype and
+Foo.prototype; hence, it will have access to the function method that was
+defined on Foo. It will also have access to the property value of the
+oneFoo instance that is its prototype. It is important to note that new
+Bar() does not create a new Foo instance, but reuses the one assigned to
+its prototype; thus, all Bar instances will share the samevalue property.
+
+
Property Lookup
+
When accessing the properties of an object, JavaScript will traverse the
+prototype chain upwards until it finds a property with the requested name.
+
If it reaches the top of the chain - namely Object.prototype - and still
+hasn't found the specified property, it will return the value
+undefined instead.
+
The Prototype Property
+
While the prototype property is used by the language to build the prototype
+chains, it is still possible to assign any given value to it. However,
+primitives will simply get ignored when assigned as a prototype.
+
function Foo() {}
+Foo.prototype = 1; // no effect
+
Assigning objects, as shown in the example above, will work, and allows for dynamic
+creation of prototype chains.
+
Performance
+
The lookup time for properties that are high up on the prototype chain can have
+a negative impact on performance, and this may be significant in code where
+performance is critical. Additionally, trying to access non-existent properties
+will always traverse the full prototype chain.
+
Also, when iterating over the properties of an object
+every property that is on the prototype chain will be enumerated.
+
Extension of Native Prototypes
+
One mis-feature that is often used is to extend Object.prototype or one of the
+other built in prototypes.
+
This technique is called monkey patching and breaks encapsulation. While
+used by popular frameworks such as Prototype, there is still no good
+reason for cluttering built-in types with additional non-standard functionality.
+
The only good reason for extending a built-in prototype is to backport
+the features of newer JavaScript engines; for example,
+Array.forEach.
+
In Conclusion
+
It is essential to understand the prototypal inheritance model before
+writing complex code that makes use of it. Also, be aware of the length of the
+prototype chains in your code and break them up if necessary to avoid possible
+performance problems. Further, the native prototypes should never be
+extended unless it is for the sake of compatibility with newer JavaScript
+features.
+
hasOwnProperty
To check whether an object has a property defined on itself and not somewhere
+on its prototype chain, it is necessary to use the
+hasOwnProperty method which all objects inherit from Object.prototype.
+
+
hasOwnProperty is the only thing in JavaScript which deals with properties and
+does not traverse the prototype chain.
Only hasOwnProperty will give the correct and expected result; this is
+essential when iterating over the properties of any object. There is no other
+way to exclude properties that are not defined on the object itself, but
+somewhere on its prototype chain.
+
hasOwnProperty as a Property
+
JavaScript does not protect the property name hasOwnProperty; thus, if the
+possibility exists that an object might have a property with this name, it is
+necessary to use an externalhasOwnProperty to get correct results.
+
var foo = {
+ hasOwnProperty: function() {
+ return false;
+ },
+ bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // always returns false
+
+// Use another Object's hasOwnProperty and call it with 'this' set to foo
+({}).hasOwnProperty.call(foo, 'bar'); // true
+
+// It's also possible to use hasOwnProperty from the Object
+// prototype for this purpose
+Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
+
In Conclusion
+
Using hasOwnProperty is the only reliable method to check for the
+existence of a property on an object. It is recommended that hasOwnProperty
+is used in everyfor in loop to avoid errors from
+extended native prototypes.
+
The for in Loop
Just like the in operator, the for in loop traverses the prototype
+chain when iterating over the properties of an object.
+
+
// Poisoning Object.prototype
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+ console.log(i); // prints both bar and moo
+}
+
Since it is not possible to change the behavior of the for in loop itself, it
+is necessary to filter out the unwanted properties inside the loop body;
+this is done using the hasOwnProperty method of
+Object.prototype.
+
+
Using hasOwnProperty for Filtering
+
// still the foo from above
+for(var i in foo) {
+ if (foo.hasOwnProperty(i)) {
+ console.log(i);
+ }
+}
+
This version is the only correct one to use. Due to the use of hasOwnProperty, it
+will only print out moo. When hasOwnProperty is left out, the code is
+prone to errors in cases where the native prototypes - e.g. Object.prototype -
+have been extended.
+
One widely used framework that extends Object.prototype is Prototype.
+When this framework is included, for in loops that do not use
+hasOwnProperty are guaranteed to break.
+
In Conclusion
+
It is recommended to always use hasOwnProperty. Assumptions should never
+be made about the environment the code is running in, or whether the native
+prototypes have been extended or not.
+
Functions
Function Declarations and Expressions
Functions in JavaScript are first class objects. That means they can be
+passed around like any other value. One common use of this feature is to pass
+an anonymous function as a callback to another, possibly an asynchronous function.
+
The function Declaration
+
function foo() {}
+
The above function gets hoisted before the execution of the
+program starts; thus, it is available everywhere in the scope it was
+defined, even if called before the actual definition in the source.
+
foo(); // Works because foo was created before this code runs
+function foo() {}
+
The function Expression
+
var foo = function() {};
+
This example assigns the unnamed and anonymous function to the variable foo.
+
foo; // 'undefined'
+foo(); // this raises a TypeError
+var foo = function() {};
+
Due to the fact that var is a declaration that hoists the variable name foo
+before the actual execution of the code starts, foo is already declared when
+the script gets executed.
+
But since assignments only happen at runtime, the value of foo will default
+to undefined before the corresponding code is executed.
+
Named Function Expression
+
Another special case is the assignment of named functions.
+
var foo = function bar() {
+ bar(); // Works
+}
+bar(); // ReferenceError
+
Here, bar is not available in the outer scope, since the function only gets
+assigned to foo; however, inside of bar, it is available. This is due to
+how name resolution in JavaScript works, the name of the
+function is always made available in the local scope of the function itself.
+
How this Works
JavaScript has a different concept of what the special name this refers to
+than most other programming languages. There are exactly five different
+ways in which the value of this can be bound in the language.
+
The Global Scope
+
this;
+
When using this in global scope, it will simply refer to the global object.
+
Calling a Function
+
foo();
+
Here, this will again refer to the global object.
+
+
Calling a Method
+
test.foo();
+
In this example, this will refer to test.
+
Calling a Constructor
+
new foo();
+
A function call that is preceded by the new keyword acts as
+a constructor. Inside the function, this will refer
+to a newly createdObject.
+
Explicit Setting of this
+
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // array will expand to the below
+foo.call(bar, 1, 2, 3); // results in a = 1, b = 2, c = 3
+
When using the call or apply methods of Function.prototype, the value of
+this inside the called function gets explicitly set to the first argument
+of the corresponding function call.
+
As a result, in the above example the method case does not apply, and this
+inside of foo will be set to bar.
+
+
Common Pitfalls
+
While most of these cases make sense, the first can be considered another
+mis-design of the language because it never has any practical use.
+
Foo.method = function() {
+ function test() {
+ // this is set to the global object
+ }
+ test();
+}
+
A common misconception is that this inside of test refers to Foo; while in
+fact, it does not.
+
In order to gain access to Foo from within test, it is necessary to create a
+local variable inside of method that refers to Foo.
+
Foo.method = function() {
+ var that = this;
+ function test() {
+ // Use that instead of this here
+ }
+ test();
+}
+
that is just a normal variable name, but it is commonly used for the reference to an
+outer this. In combination with closures, it can also
+be used to pass this values around.
+
Assigning Methods
+
Another thing that does not work in JavaScript is function aliasing, which is
+assigning a method to a variable.
+
var test = someObject.methodTest;
+test();
+
Due to the first case, test now acts like a plain function call; therefore,
+this inside it will no longer refer to someObject.
+
While the late binding of this might seem like a bad idea at first, in
+fact, it is what makes prototypal inheritance work.
When method gets called on an instance of Bar, this will now refer to that
+very instance.
+
Closures and References
One of JavaScript's most powerful features is the availability of closures.
+With closures, scopes always keep access to the outer scope, in which they
+were defined. Since the only scoping that JavaScript has is
+function scope, all functions, by default, act as closures.
Here, Counter returns two closures: the function increment as well as
+the function get. Both of these functions keep a reference to the scope of
+Counter and, therefore, always keep access to the count variable that was
+defined in that scope.
+
Why Private Variables Work
+
Since it is not possible to reference or assign scopes in JavaScript, there is
+no way of accessing the variable count from the outside. The only way to
+interact with it is via the two closures.
+
var foo = new Counter(4);
+foo.hack = function() {
+ count = 1337;
+};
+
The above code will not change the variable count in the scope of Counter,
+since foo.hack was not defined in that scope. It will instead create - or
+override - the global variable count.
+
Closures Inside Loops
+
One often made mistake is to use closures inside of loops, as if they were
+copying the value of the loop's index variable.
+
for(var i = 0; i < 10; i++) {
+ setTimeout(function() {
+ console.log(i);
+ }, 1000);
+}
+
The above will not output the numbers 0 through 9, but will simply print
+the number 10 ten times.
+
The anonymous function keeps a reference to i. At the time
+console.log gets called, the for loop has already finished, and the value of
+i has been set to 10.
+
In order to get the desired behavior, it is necessary to create a copy of
+the value of i.
+
Avoiding the Reference Problem
+
In order to copy the value of the loop's index variable, it is best to use an
+anonymous wrapper.
The anonymous outer function gets called immediately with i as its first
+argument and will receive a copy of the value of i as its parameter e.
+
The anonymous function that gets passed to setTimeout now has a reference to
+e, whose value does not get changed by the loop.
+
There is another possible way of achieving this, which is to return a function
+from the anonymous wrapper that will then have the same behavior as the code
+above.
There's yet another way to accomplish this by using .bind, which can bind
+a this context and arguments to function. It behaves identially to the code
+above
+
for(var i = 0; i < 10; i++) {
+ setTimeout(console.log.bind(console, i), 1000);
+}
+
The arguments Object
Every function scope in JavaScript can access the special variable arguments.
+This variable holds a list of all the arguments that were passed to the function.
+
+
The arguments object is not an Array. While it has some of the
+semantics of an array - namely the length property - it does not inherit from
+Array.prototype and is in fact an Object.
+
Due to this, it is not possible to use standard array methods like push,
+pop or slice on arguments. While iteration with a plain for loop works
+just fine, it is necessary to convert it to a real Array in order to use the
+standard Array methods on it.
+
Converting to an Array
+
The code below will return a new Array containing all the elements of the
+arguments object.
+
Array.prototype.slice.call(arguments);
+
Because this conversion is slow, it is not recommended to use it in
+performance-critical sections of code.
+
Passing Arguments
+
The following is the recommended way of passing arguments from one function to
+another.
+
function foo() {
+ bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+ // do stuff here
+}
+
Another trick is to use both call and apply together to create fast, unbound
+wrappers.
+
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+ console.log(this, a, b, c);
+};
+
+// Create an unbound version of "method"
+// It takes the parameters: this, arg1, arg2...argN
+Foo.method = function() {
+
+ // Result: Foo.prototype.method.call(this, arg1, arg2... argN)
+ Function.call.apply(Foo.prototype.method, arguments);
+};
+
Formal Parameters and Arguments Indices
+
The arguments object creates getter and setter functions for both its
+properties, as well as the function's formal parameters.
+
As a result, changing the value of a formal parameter will also change the value
+of the corresponding property on the arguments object, and the other way around.
+
function foo(a, b, c) {
+ arguments[0] = 2;
+ a; // 2
+
+ b = 4;
+ arguments[1]; // 4
+
+ var d = c;
+ d = 9;
+ c; // 3
+}
+foo(1, 2, 3);
+
Performance Myths and Truths
+
The only time the arguments object is not created is where it is declared as
+a name inside of a function or one of its formal parameters. It does not matter
+whether it is used or not.
+
Both getters and setters are always created; thus, using it has nearly
+no performance impact at all, especially not in real world code where there is
+more than a simple access to the arguments object's properties.
+
+
However, there is one case which will drastically reduce the performance in
+modern JavaScript engines. That case is the use of arguments.callee.
+
function foo() {
+ arguments.callee; // do something with this function object
+ arguments.callee.caller; // and the calling function object
+}
+
+function bigLoop() {
+ for(var i = 0; i < 100000; i++) {
+ foo(); // Would normally be inlined...
+ }
+}
+
In the above code, foo can no longer be a subject to inlining since it
+needs to know about both itself and its caller. This not only defeats possible
+performance gains that would arise from inlining, but it also breaks encapsulation
+because the function may now be dependent on a specific calling context.
+
Making use of arguments.callee or any of its properties is highly discouraged.
+
+
Constructors
Constructors in JavaScript are yet again different from many other languages. Any
+function call that is preceded by the new keyword acts as a constructor.
+
Inside the constructor - the called function - the value of this refers to a
+newly created object. The prototype of this new
+object is set to the prototype of the function object that was invoked as the
+constructor.
+
If the function that was called has no explicit return statement, then it
+implicitly returns the value of this - the new object.
+
function Foo() {
+ this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+ console.log(this.bla);
+};
+
+var test = new Foo();
+
The above calls Foo as constructor and sets the prototype of the newly
+created object to Foo.prototype.
+
In case of an explicit return statement, the function returns the value
+specified by that statement, but only if the return value is an Object.
+
function Bar() {
+ return 2;
+}
+new Bar(); // a new object
+
+function Test() {
+ this.value = 2;
+
+ return {
+ foo: 1
+ };
+}
+new Test(); // the returned object
+
When the new keyword is omitted, the function will not return a new object.
+
function Foo() {
+ this.bla = 1; // gets set on the global object
+}
+Foo(); // undefined
+
While the above example might still appear to work in some cases, due to the
+workings of this in JavaScript, it will use the
+global object as the value of this.
+
Factories
+
In order to be able to omit the new keyword, the constructor function has to
+explicitly return a value.
Both calls to Bar return the same thing, a newly create object that
+has a property called method, which is a
+Closure.
+
It should also be noted that the call new Bar() does not affect the
+prototype of the returned object. While the prototype will be set on the newly
+created object, Bar never returns that new object.
+
In the above example, there is no functional difference between using and
+not using the new keyword.
+
Creating New Objects via Factories
+
It is often recommended to not use new because forgetting its use may
+lead to bugs.
+
In order to create a new object, one should rather use a factory and construct a
+new object inside of that factory.
While the above is robust against a missing new keyword and certainly makes
+the use of private variables easier, it comes with some
+downsides.
+
+
It uses more memory since the created objects do not share the methods
+on a prototype.
+
In order to inherit, the factory needs to copy all the methods from another
+object or put that object on the prototype of the new object.
+
Dropping the prototype chain just because of a left out new keyword
+is contrary to the spirit of the language.
+
+
In Conclusion
+
While omitting the new keyword might lead to bugs, it is certainly not a
+reason to drop the use of prototypes altogether. In the end it comes down to
+which solution is better suited for the needs of the application. It is
+especially important to choose a specific style of object creation and use it
+consistently.
+
Scopes and Namespaces
Although JavaScript deals fine with the syntax of two matching curly
+braces for blocks, it does not support block scope; hence, all that is left
+in the language is function scope.
+
function test() { // a scope
+ for(var i = 0; i < 10; i++) { // not a scope
+ // count
+ }
+ console.log(i); // 10
+}
+
+
There are also no distinct namespaces in JavaScript, which means that everything
+gets defined in one globally shared namespace.
+
Each time a variable is referenced, JavaScript will traverse upwards through all
+the scopes until it finds it. In the case that it reaches the global scope and
+still has not found the requested name, it will raise a ReferenceError.
+
The Bane of Global Variables
+
// script A
+foo = '42';
+
+// script B
+var foo = '42'
+
The above two scripts do not have the same effect. Script A defines a
+variable called foo in the global scope, and script B defines a foo in the
+current scope.
+
Again, that is not at all the same effect: not using var can have major
+implications.
Leaving out the var statement inside the function test will override the
+value of foo. While this might not seem like a big deal at first, having
+thousands of lines of JavaScript and not using var will introduce horrible,
+hard-to-track-down bugs.
+
// global scope
+var items = [/* some list */];
+for(var i = 0; i < 10; i++) {
+ subLoop();
+}
+
+function subLoop() {
+ // scope of subLoop
+ for(i = 0; i < 10; i++) { // missing var statement
+ // do amazing stuff!
+ }
+}
+
The outer loop will terminate after the first call to subLoop, since subLoop
+overwrites the global value of i. Using a var for the second for loop would
+have easily avoided this error. The var statement should never be left out
+unless the desired effect is to affect the outer scope.
+
Local Variables
+
The only source for local variables in JavaScript are
+function parameters and variables declared via the
+var statement.
+
// global scope
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+ // local scope of the function test
+ i = 5;
+
+ var foo = 3;
+ bar = 4;
+}
+test(10);
+
While foo and i are local variables inside the scope of the function test,
+the assignment of bar will override the global variable with the same name.
+
Hoisting
+
JavaScript hoists declarations. This means that both var statements and
+function declarations will be moved to the top of their enclosing scope.
+
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+ if (false) {
+ goo = 1;
+
+ } else {
+ var goo = 2;
+ }
+ for(var i = 0; i < 100; i++) {
+ var e = data[i];
+ }
+}
+
The above code gets transformed before execution starts. JavaScript moves
+the var statements, as well as function declarations, to the top of the
+nearest surrounding scope.
+
// var statements got moved here
+var bar, someValue; // default to 'undefined'
+
+// the function declaration got moved up too
+function test(data) {
+ var goo, i, e; // missing block scope moves these here
+ if (false) {
+ goo = 1;
+
+ } else {
+ goo = 2;
+ }
+ for(i = 0; i < 100; i++) {
+ e = data[i];
+ }
+}
+
+bar(); // fails with a TypeError since bar is still 'undefined'
+someValue = 42; // assignments are not affected by hoisting
+bar = function() {};
+
+test();
+
Missing block scoping will not only move var statements out of loops and
+their bodies, it will also make the results of certain if constructs
+non-intuitive.
+
In the original code, although the if statement seemed to modify the global
+variablegoo, it actually modifies the local variable - after hoisting
+has been applied.
+
Without knowledge of hoisting, one might suspect the code below would raise a
+ReferenceError.
+
// check whether SomeImportantThing has been initialized
+if (!SomeImportantThing) {
+ var SomeImportantThing = {};
+}
+
But of course, this works due to the fact that the var statement is being
+moved to the top of the global scope.
+
var SomeImportantThing;
+
+// other code might initialize SomeImportantThing here, or not
+
+// make sure it's there
+if (!SomeImportantThing) {
+ SomeImportantThing = {};
+}
+
Name Resolution Order
+
All scopes in JavaScript, including the global scope, have the special name
+this, defined in them, which refers to the current object.
+
Function scopes also have the name arguments, defined in
+them, which contains the arguments that were passed to the function.
+
For example, when trying to access a variable named foo inside the scope of a
+function, JavaScript will look up the name in the following order:
+
+
In case there is a var foo statement in the current scope, use that.
+
If one of the function parameters is named foo, use that.
+
If the function itself is called foo, use that.
+
Go to the next outer scope, and start with #1 again.
+
+
+
Namespaces
+
A common problem associated with having only one global namespace is the
+likelihood of running into problems where variable names clash. In JavaScript,
+this problem can easily be avoided with the help of anonymous wrappers.
+
(function() {
+ // a self contained "namespace"
+
+ window.foo = function() {
+ // an exposed closure
+ };
+
+})(); // execute the function immediately
+
Unnamed functions are considered expressions; so in order to
+be callable, they must first be evaluated.
+
( // evaluate the function inside the parentheses
+function() {}
+) // and return the function object
+() // call the result of the evaluation
+
There are other ways to evaluate and directly call the function expression
+which, while different in syntax, behave the same way.
+
// A few other styles for directly invoking the
+!function(){}()
++function(){}()
+(function(){}());
+// and so on...
+
In Conclusion
+
It is recommended to always use an anonymous wrapper to encapsulate code in
+its own namespace. This does not only protect code against name clashes, but it
+also allows for better modularization of programs.
+
Additionally, the use of global variables is considered bad practice. Any
+use of them indicates badly written code that is prone to errors and hard to maintain.
+
Arrays
Array Iteration and Properties
Although arrays in JavaScript are objects, there are no good reasons to use
+the for in loop. In fact, there
+are a number of good reasons against the use of for in on arrays.
+
+
Because the for in loop enumerates all the properties that are on the prototype
+chain and because the only way to exclude those properties is to use
+hasOwnProperty, it is already up to twenty times
+slower than a normal for loop.
+
Iteration
+
In order to achieve the best performance when iterating over arrays, it is best
+to use the classic for loop.
+
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+ console.log(list[i]);
+}
+
There is one extra catch in the above example, which is the caching of the
+length of the array via l = list.length.
+
Although the length property is defined on the array itself, there is still an
+overhead for doing the lookup on each iteration of the loop. And while recent
+JavaScript engines may apply optimization in this case, there is no way of
+telling whether the code will run on one of these newer engines or not.
+
In fact, leaving out the caching may result in the loop being only half as
+fast as with the cached length.
+
The length Property
+
While the getter of the length property simply returns the number of
+elements that are contained in the array, the setter can be used to
+truncate the array.
Assigning a smaller length truncates the array. Increasing it creates a sparse array.
+
In Conclusion
+
For the best performance, it is recommended to always use the plain for loop
+and cache the length property. The use of for in on an array is a sign of
+badly written code that is prone to bugs and bad performance.
+
The Array Constructor
Since the Array constructor is ambiguous in how it deals with its parameters,
+it is highly recommended to use the array literal - [] notation -
+when creating new arrays.
In cases when there is only one argument passed to the Array constructor
+and when that argument is a Number, the constructor will return a new sparse
+array with the length property set to the value of the argument. It should be
+noted that only the length property of the new array will be set this way;
+the actual indexes of the array will not be initialized.
+
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // false, the index was not set
+
Being able to set the length of the array in advance is only useful in a few
+cases, like repeating a string, in which it avoids the use of a loop.
+
new Array(count + 1).join(stringToRepeat);
+
In Conclusion
+
Literals are preferred to the Array constructor. They are shorter, have a clearer syntax, and increase code
+readability.
+
Types
Equality and Comparisons
JavaScript has two different ways of comparing the values of objects for equality.
+
The Equality Operator
+
The equality operator consists of two equal signs: ==
+
JavaScript features weak typing. This means that the equality operator
+coerces types in order to compare them.
The above table shows the results of the type coercion, and it is the main reason
+why the use of == is widely regarded as bad practice. It introduces
+hard-to-track-down bugs due to its complicated conversion rules.
+
Additionally, there is also a performance impact when type coercion is in play;
+for example, a string has to be converted to a number before it can be compared
+to another number.
+
The Strict Equality Operator
+
The strict equality operator consists of three equal signs: ===.
+
It works like the normal equality operator, except that strict equality
+operator does not perform type coercion between its operands.
The above results are a lot clearer and allow for early breakage of code. This
+hardens code to a certain degree and also gives performance improvements in case
+the operands are of different types.
+
Comparing Objects
+
While both == and === are called equality operators, they behave
+differently when at least one of their operands is an Object.
Here, both operators compare for identity and not equality; that is, they
+will compare for the same instance of the object, much like is in Python
+and pointer comparison in C.
+
In Conclusion
+
It is highly recommended to only use the strict equality operator. In cases
+where types need to be coerced, it should be done explicitly
+and not left to the language's complicated coercion rules.
+
The typeof Operator
The typeof operator (together with
+instanceof) is probably the biggest
+design flaw of JavaScript, as it is almost completely broken.
+
Although instanceof still has limited uses, typeof really has only one
+practical use case, which does not happen to be checking the type of an
+object.
+
+
The JavaScript Type Table
+
Value Class Type
+-------------------------------------
+"foo" String string
+new String("foo") String object
+1.2 Number number
+new Number(1.2) Number object
+true Boolean boolean
+new Boolean(true) Boolean object
+new Date() Date object
+new Error() Error object
+[1,2,3] Array object
+new Array(1, 2, 3) Array object
+new Function("") Function function
+/abc/g RegExp object (function in Nitro/V8)
+new RegExp("meow") RegExp object (function in Nitro/V8)
+{} Object object
+new Object() Object object
+
In the above table, Type refers to the value that the typeof operator returns.
+As can be clearly seen, this value is anything but consistent.
+
The Class refers to the value of the internal [[Class]] property of an object.
+
+
In order to retrieve the value of [[Class]], one has to make use of the
+toString method of Object.prototype.
+
The Class of an Object
+
The specification gives exactly one way of accessing the [[Class]] value,
+with the use of Object.prototype.toString.
In the above example, Object.prototype.toString gets called with the value of
+this being set to the object whose [[Class]] value should be
+retrieved.
+
+
Testing for Undefined Variables
+
typeof foo !== 'undefined'
+
The above will check whether foo was actually declared or not; just
+referencing it would result in a ReferenceError. This is the only thing
+typeof is actually useful for.
+
In Conclusion
+
In order to check the type of an object, it is highly recommended to use
+Object.prototype.toString because this is the only reliable way of doing so.
+As shown in the above type table, some return values of typeof are not defined
+in the specification; thus, they can differ between implementations.
+
Unless checking whether a variable is defined, typeof should be avoided.
+
The instanceof Operator
The instanceof operator compares the constructors of its two operands. It is
+only useful when comparing custom made objects. Used on built-in types, it is
+nearly as useless as the typeof operator.
+
Comparing Custom Objects
+
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// This just sets Bar.prototype to the function object Foo,
+// but not to an actual instance of Foo
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
One important thing to note here is that instanceof does not work on objects
+that originate from different JavaScript contexts (e.g. different documents
+in a web browser), since their constructors will not be the exact same object.
+
In Conclusion
+
The instanceof operator should only be used when dealing with custom made
+objects that originate from the same JavaScript context. Just like the
+typeof operator, every other use of it should be avoided.
+
Type Casting
JavaScript is a weakly typed language, so it will apply type coercion
+wherever possible.
+
// These are true
+new Number(10) == 10; // Number.toString() is converted
+ // back to a number
+
+10 == '10'; // Strings gets converted to Number
+10 == '+10 '; // More string madness
+10 == '010'; // And more
+isNaN(null) == false; // null converts to 0
+ // which of course is not NaN
+
+// These are false
+10 == 010;
+10 == '-10';
+
+
To avoid the issues above, use of the strict equal operator
+is highly recommended. Although this avoids a lot of common pitfalls, there
+are still many further issues that arise from JavaScript's weak typing system.
+
Constructors of Built-In Types
+
The constructors of the built in types like Number and String behave
+differently when being used with the new keyword and without it.
+
new Number(10) === 10; // False, Object and Number
+Number(10) === 10; // True, Number and Number
+new Number(10) + 0 === 10; // True, due to implicit conversion
+
Using a built-in type like Number as a constructor will create a new Number
+object, but leaving out the new keyword will make the Number function behave
+like a converter.
+
In addition, passing literals or non-object values will result in even more
+type coercion.
+
The best option is to cast to one of the three possible types explicitly.
+
Casting to a String
+
'' + 10 === '10'; // true
+
By prepending an empty string, a value can easily be cast to a string.
+
Casting to a Number
+
+'10' === 10; // true
+
Using the unary plus operator, it is possible to cast to a number.
+
Casting to a Boolean
+
By using the not operator twice, a value can be converted a boolean.
However, eval only executes in the local scope when it is being called
+directly and when the name of the called function is actually eval.
+
var foo = 1;
+function test() {
+ var foo = 2;
+ var bar = eval;
+ bar('foo = 3');
+ return foo;
+}
+test(); // 2
+foo; // 3
+
The use of eval should be avoided. 99.9% of its "uses" can be achieved
+without it.
+
eval in Disguise
+
The timeout functionssetTimeout and setInterval can both
+take a string as their first argument. This string will always get executed
+in the global scope since eval is not being called directly in that case.
+
Security Issues
+
eval also is a security problem, because it executes any code given to it.
+It should never be used with strings of unknown or untrusted origins.
+
In Conclusion
+
eval should never be used. Any code that makes use of it should be questioned
+in its workings, performance and security. If something requires eval in
+order to work, it should not be used in the first place. A better design
+should be used, that does not require the use of eval.
+
undefined and null
JavaScript has two distinct values for nothing, null and undefined, with
+the latter being more useful.
+
The Value undefined
+
undefined is a type with exactly one value: undefined.
+
The language also defines a global variable that has the value of undefined;
+this variable is also called undefined. However, this variable is neither a constant
+nor a keyword of the language. This means that its value can be easily
+overwritten.
+
+
Here are some examples of when the value undefined is returned:
+
+
Accessing the (unmodified) global variable undefined.
+
Accessing a declared but not yet initialized variable.
+
Implicit returns of functions due to missing return statements.
+
return statements that do not explicitly return anything.
+
Lookups of non-existent properties.
+
Function parameters that do not have any explicit value passed.
+
Anything that has been set to the value of undefined.
+
Any expression in the form of void(expression)
+
+
Handling Changes to the Value of undefined
+
Since the global variable undefined only holds a copy of the actual value of
+undefined, assigning a new value to it does not change the value of the
+typeundefined.
+
Still, in order to compare something against the value of undefined, it is
+necessary to retrieve the value of undefined first.
+
To protect code against a possible overwritten undefined variable, a common
+technique used is to add an additional parameter to an anonymous
+wrapper that gets no argument passed to it.
+
var undefined = 123;
+(function(something, foo, undefined) {
+ // undefined in the local scope does
+ // now again refer to the value `undefined`
+
+})('Hello World', 42);
+
Another way to achieve the same effect would be to use a declaration inside the
+wrapper.
+
var undefined = 123;
+(function(something, foo) {
+ var undefined;
+ ...
+
+})('Hello World', 42);
+
The only difference here is that this version results in 4 more bytes being
+used in case it is minified, and there is no other var statement inside the
+anonymous wrapper.
+
Uses of null
+
While undefined in the context of the JavaScript language is mostly used in
+the sense of a traditional null, the actual null (both a literal and a type)
+is more or less just another data type.
+
It is used in some JavaScript internals (like declaring the end of the
+prototype chain by setting Foo.prototype = null), but in almost all cases, it
+can be replaced by undefined.
+
Automatic Semicolon Insertion
Although JavaScript has C style syntax, it does not enforce the use of
+semicolons in the source code, so it is possible to omit them.
+
JavaScript is not a semicolon-less language. In fact, it needs the
+semicolons in order to understand the source code. Therefore, the JavaScript
+parser automatically inserts them whenever it encounters a parse
+error due to a missing semicolon.
Chances are very high that log does not return a function; therefore,
+the above will yield a TypeError stating that undefined is not a function.
+
In Conclusion
+
It is highly recommended to never omit semicolons. It is also recommended
+that braces be kept on the same line as their corresponding statements and to
+never omit them for single-line if / else statements. These measures will
+not only improve the consistency of the code, but they will also prevent the
+JavaScript parser from changing code behavior.
+
The delete Operator
In short, it's impossible to delete global variables, functions and some other
+stuff in JavaScript which have a DontDelete attribute set.
+
Global code and Function code
+
When a variable or a function is defined in a global or a function
+scope it is a property of either the Activation object or
+the Global object. Such properties have a set of attributes, one of which is
+DontDelete. Variable and function declarations in global and function code
+always create properties with DontDelete, and therefore cannot be deleted.
+
// global variable:
+var a = 1; // DontDelete is set
+delete a; // false
+a; // 1
+
+// normal function:
+function f() {} // DontDelete is set
+delete f; // false
+typeof f; // "function"
+
+// reassigning doesn't help:
+f = 1;
+delete f; // false
+f; // 1
+
Explicit properties
+
Explicitly set properties can be deleted normally.
In the example above, obj.x and obj.y can be deleted because they have no
+DontDelete atribute. That's why the example below works too.
+
// this works fine, except for IE:
+var GLOBAL_OBJECT = this;
+GLOBAL_OBJECT.a = 1;
+a === GLOBAL_OBJECT.a; // true - just a global var
+delete GLOBAL_OBJECT.a; // true
+GLOBAL_OBJECT.a; // undefined
+
Here we use a trick to delete a. this here refers
+to the Global object and we explicitly declare variable a as its property
+which allows us to delete it.
+
IE (at least 6-8) has some bugs, so the code above doesn't work.
+
Function arguments and built-ins
+
Functions' normal arguments, arguments objects
+and built-in properties also have DontDelete set.
The behaviour of delete operator can be unpredictable for hosted objects. Due
+to the specification, host objects are allowed to implement any kind of behavior.
+
In conclusion
+
The delete operator often has unexpected behaviour and can only be safely
+used to delete explicitly set properties on normal objects.
+
Other
setTimeout and setInterval
Since JavaScript is asynchronous, it is possible to schedule the execution of a
+function using the setTimeout and setInterval functions.
+
+
function foo() {}
+var id = setTimeout(foo, 1000); // returns a Number > 0
+
When setTimeout is called, it returns the ID of the timeout and schedule
+foo to run approximately one thousand milliseconds in the future.
+foo will then be executed once.
+
Depending on the timer resolution of the JavaScript engine running the code, as
+well as the fact that JavaScript is single threaded and other code that gets
+executed might block the thread, it is by no means a safe bet that one will
+get the exact delay specified in the setTimeout call.
+
The function that was passed as the first parameter will get called by the
+global object, which means that this inside the called function
+refers to the global object.
+
function Foo() {
+ this.value = 42;
+ this.method = function() {
+ // this refers to the global object
+ console.log(this.value); // will log undefined
+ };
+ setTimeout(this.method, 500);
+}
+new Foo();
+
+
Stacking Calls with setInterval
+
While setTimeout only runs the function once, setInterval - as the name
+suggests - will execute the function everyX milliseconds, but its use is
+discouraged.
+
When code that is being executed blocks the timeout call, setInterval will
+still issue more calls to the specified function. This can, especially with small
+intervals, result in function calls stacking up.
+
function foo(){
+ // something that blocks for 1 second
+}
+setInterval(foo, 1000);
+
In the above code, foo will get called once and will then block for one second.
+
While foo blocks the code, setInterval will still schedule further calls to
+it. Now, when foo has finished, there will already be ten further calls to
+it waiting for execution.
+
Dealing with Possible Blocking Code
+
The easiest solution, as well as most controllable solution, is to use setTimeout within
+the function itself.
+
function foo(){
+ // something that blocks for 1 second
+ setTimeout(foo, 1000);
+}
+foo();
+
Not only does this encapsulate the setTimeout call, but it also prevents the
+stacking of calls and gives additional control. foo itself can now decide
+whether it wants to run again or not.
+
Manually Clearing Timeouts
+
Clearing timeouts and intervals works by passing the respective ID to
+clearTimeout or clearInterval, depending on which set function was used
+in the first place.
+
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
Clearing All Timeouts
+
As there is no built-in method for clearing all timeouts and/or intervals,
+it is necessary to use brute force in order to achieve this functionality.
+
// clear "all" timeouts
+for(var i = 1; i < 1000; i++) {
+ clearTimeout(i);
+}
+
But there might still be timeouts that are unaffected by this arbitrary number.
+Another way of doing this is to consider that the ID given to a timeout is
+incremented by one every time you call setTimeout.
Even though this works on all major browsers today, it isn't specified that
+the IDs should be ordered that way and it may change. Therefore, it is instead
+recommended to keep track of all the timeout IDs, so they can be cleared
+specifically.
+
Hidden Use of eval
+
setTimeout and setInterval can also take a string as their first parameter.
+This feature should never be used because it internally makes use of eval.
+
+
function foo() {
+ // will get called
+}
+
+function bar() {
+ function foo() {
+ // never gets called
+ }
+ setTimeout('foo()', 1000);
+}
+bar();
+
Since eval is not getting called directly in this case, the string
+passed to setTimeout will be executed in the global scope; thus, it will
+not use the local variable foo from the scope of bar.
+
It is further recommended to not use a string to pass arguments to the
+function that will get called by either of the timeout functions.
+
function foo(a, b, c) {}
+
+// NEVER use this
+setTimeout('foo(1, 2, 3)', 1000)
+
+// Instead use an anonymous function
+setTimeout(function() {
+ foo(a, b, c);
+}, 1000)
+
+
In Conclusion
+
A string should never be used as the parameter of setTimeout or
+setInterval. It is a clear sign of really bad code, when arguments need
+to be supplied to the function that gets called. An anonymous function should
+be passed that then takes care of the actual call.
+
Furthermore, the use of setInterval should be avoided because its scheduler is not
+blocked by executing JavaScript.
+
\ No newline at end of file
diff --git a/it/index.html b/it/index.html
new file mode 100644
index 0000000..bb36de9
--- /dev/null
+++ b/it/index.html
@@ -0,0 +1,1587 @@
+JavaScript Garden
Introduzione
Introduzione
JavaScript Garden è una collezione in continua crescita di documentazione
+relativa alle parti più peculiari del linguaggio di programmazione JavaScript.
+Il suo intento è quello di mostrare come evitare i più comuni errori, i
+problemi legati alla performance e le cattive abitudini che i programmatori
+JavaScript non esperti possono incontrare lungo il loro cammino di
+approfondimento del linguaggio.
+
L'obiettivo di JavaScript Garden non è quello di insegnarti JavaScript.
+Una conoscenza pregressa del linguaggio è fortemenete consigliata, in modo da
+capire gli argomenti trattati da questa guida. Per poter imparare le basi del
+linguaggio, ti suggeriamo di leggere l'eccellente guida su Mozilla
+Developer Network.
JavaScript Garden è pubblicato sotto la licenza MIT ed ospitato su
+GitHub. Se trovi inesattezze o errori di battitura, ti prego di
+segnalare il problema o fare un pull request sul nostro repository.
+Puoi anche trovarci nella stanza JavaScript della chat di Stack
+Overflow.
+
Oggetti
Utilizzo di oggetti e proprietà
Tutto in JavaScript funziona come un oggetto, con la sola eccezione di
+null e undefined.
Un'idea comunemente errata è che i numeri letterali non possano essere
+usati come oggetti. Questo a causa di una scorretta gestione da parte del
+parser di JavaScript, che tenta di analizzare la dot notation di un
+numero come se fosse un letterale in virgola mobile.
+
2.toString(); // solleva SyntaxError
+
Esistono un paio di soluzioni che possono essere usate per far sì che i
+numeri letterali vengano considerati come oggetti.
+
2..toString(); // il secondo punto viene correttamente riconosciuto
+2 .toString(); // notate lo spazio tra il numero e il punto
+(2).toString(); // viene prima valutato 2
+
Oggetti come un tipo di dato
+
Gli oggetti in JavaScript possono anche essere usati come tabelle hash e
+consistono principalmente di proprietà con un nome che mappano dei valori.
+
Usando un oggetto letterale (notazione {}) è possibile creare un
+semplice oggetto. Questo nuovo oggetto eredita da
+Object.prototype e non ha proprietà definite.
+
var foo = {}; // un nuovo oggetto vuoto
+
+// un nuovo oggetto con una proprietà `test` con valore 12
+var bar = {test: 12};
+
Accedere alle proprietà
+
È possibile accedere alle proprietà di un oggetto in due modi.
+Usando il punto oppure attraverso l'uso delle parentesi quadre.
Le due notazioni funzionano quasi in modo identico, con la sola differenza
+che usando le parentesi quadre è possibile impostare dinamicamente le
+proprietà ed il loro nome identificatore, cosa che altrimenti genererebbe
+un errore di sintassi.
+
Cancellazione delle proprietà
+
Il solo modo per rimuovere una proprietà da un oggetto è quello di usare
+l'operatore delete. Impostando la proprietà a undefined o null, infatti,
+si rimuove solo il valore associato alla proprietà, ma non la chiave.
+
var obj = {
+ bar: 1,
+ foo: 2,
+ baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ console.log(i, '' + obj[i]);
+ }
+}
+
Il codice qui sopra, stamperà sia bar undefined che foo null. Soltanto
+baz è stato rimosso, e quindi non compare nell'output.
+
Notazione delle chiavi
+
var test = {
+ 'case': 'Parola chiave, scrivimi come stringa',
+ // solleva SyntaxError
+ delete: 'Parola chiave, anche io devo essere una stringa'
+};
+
Le proprietà di un oggetto possono essere scritte sia come normali caratteri
+che come stringhe. A causa di un altro errore di progettazione del parser di
+JavaScript, il codice appena visto genererà un SyntaxError in ECMAScript
+precedente alla versione 5.
+
Questo errore nasce dal fatto che delete è una parola chiave, quindi,
+deve essere scritta come una stringa letterale per assicurarsi che venga
+correttamente interpretata dai vecchi motori JavaScript.
+
Il prototipo
JavaScript non segue il classico modello di ereditarietà ma, piuttosto,
+utilizza quello prototipale.
+
Anche se ciò viene considerato come uno dei punti più deboli del JavaScript,
+il modello ad ereditarietà prototipale è difatto più potente di quello
+classico. Ad esempio, è piuttosto semplice creare un modello classico
+sulle basi di quello prototipale, mentre l'operazione inversa è piuttosto
+complessa.
+
JavaScript è il solo linguaggio ampiamente utilizzato che sfrutta l'ereditarietà
+prototipale, quindi è possibile prendersi il proprio tempo per adeguarsi alle
+differenze esistenti tra i due modelli.
+
La prima grande differenza è che l'ereditarietà in JavaScript utilizza le
+catene di prototipi.
+
+
function Foo() {
+ this.value = 42;
+}
+Foo.prototype = {
+ method: function() {}
+};
+
+function Bar() {}
+
+// Imposta il prototipo di Bar ad una nuova istanza di Foo
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Hello World';
+
+// Si assicura di elencare Bar come l'attuale costruttore
+Bar.prototype.constructor = Bar;
+
+var test = new Bar(); // crea una nuova istanza di bar
+
+// La catena di prototipi finale
+test [istanza di Bar]
+ Bar.prototype [istanza di Foo]
+ { foo: 'Hello World' }
+ Foo.prototype
+ { method: ... }
+ Object.prototype
+ { toString: ... /* ecc. */ }
+
Nel codice qui sopra, l'oggetto test erediterà sia da Bar.prototype che da
+Foo.prototype, e quindi avrà accesso alla funzione method che era stata
+definita in Foo. Avrà anche accesso alla proprietà value dell'unica
+istanza di Foo, cioè il suo prototipo. È importante notare come
+new Bar()non crei una nuova istanza di Foo, ma piuttosto riutilizzi
+quella assegnata al suo prototipo. Perciò, tutte le istanze di Bar
+condivideranno la stessa proprietà value.
+
+
Tabella delle proprietà
+
Quando si accede alle proprietà di un oggetto, JavaScript risale la
+catena di prototipi fino a che non incontra una proprietà con il nome
+richiesto.
+
Se raggiunge la cima della catena (cioè Object.prototype) senza aver
+trovato le specifica proprietà, ritorna il valore undefined.
+
La proprietà Prototype
+
Anche se la proprietà prototype viene usata dal linguaggio per creare la
+catena di prototipi, è comunque sempre possibile assegnarvi un qualsiasi
+dato valore. Nonostante cio, i dati primitivi verranno semplicemente ignorati
+quando assegnati ad un prototipo.
+
function Foo() {}
+Foo.prototype = 1; // nessun effetto
+
L'assegnazione di oggetti, come mostrato nell'esempio precedente, funzionerà,
+e permette la creazione dinamica di catene di prototipi.
+
Performance
+
Il tempo di ricerca per proprietà presenti in alto (all'inizio) della catena
+di prototipi, può avere un impatto negativo sulla performance, e questo deve
+essere tenuto bene in considerazione in codice dove la performance è un fattore
+critico. Inoltre, il tentativo di accedere a proprietà inesistenti obbligherà
+comunque ad attraversare tutta la catena di prototipi.
+
Oltre a ciò, iterando tra le proprietà di un oggetto,
+ogni proprietà presente nella catena di prototipi verrà enumerata.
+
Estensione di prototipi nativi
+
Una caratteristica che viene spesso abusata, è quella di estendere
+Object.prototype o uno degli altri prototipi interni al linguaggio.
+
Questa tecnica viene detta monkey patching e vìola il principio di
+incapsulamento. Anche se usata da popolari framework come Prototype,
+non c'è una valida ragione per pasticciare, aggiungendo ai tipi interni del
+linguaggio funzionalità non standard.
+
La sola buona ragione per estendere un prototipo interno è quella di
+effettuare il backport di funzionalità presenti nei motori JavaScript
+più recenti, come ad esempio Array.forEach.
+
In conclusione
+
È essenziale capire il modello di ereditarietà prototipale prima
+di scrivere codice complesso che ne faccia uso. Bisogna, inoltre, tenere
+sotto controllo la lunghezza della catena di prototipi nel proprio codice,
+e suddividerla in più catene se necessario, per evitare possibili problemi di
+performance. Inoltre, i prototipi nativi non dovrebbero mai essere
+estesi a meno che non sia per garantire compatibilità con le funzionalità
+più recenti di JavaScript.
+
hasOwnProperty
Per verificare se un oggetto ha (possiede) una proprietà definita dentro
+se stesso piuttosto che in qualche parte della sua
+catena di prototipi, è necessario usare il metodo
+hasOwnProperty che tutti gli oggetti ereditano da Object.prototype.
+
+
hasOwnProperty è la sola cosa in JavaScript che si occupa delle proprietà
+senza attraversare la catena di prototipi.
Solo hasOwnProperty darà il risultato atteso e corretto. Questo è essenziale
+quando si itera tra le proprietà di un qualsiasi oggetto. Non c'è altro
+modo per escludere proprietà che non sono definite all'interno dell'oggetto
+stesso, ma da qualche altra parte nella sua catena di prototipi.
+
hasOwnProperty come proprietà
+
JavaScript non protegge il nome di proprietà hasOwnProperty. Quindi, se
+esiste la possibilità che un oggetto possa avere una proprietà con questo
+nome, è necessario usare un hasOwnPropertyesterno per ottenere il
+risultato corretto.
+
var foo = {
+ hasOwnProperty: function() {
+ return false;
+ },
+ bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // ritorna sempre false
+
+// Usa un altro hasOwnProperty di Object e lo richiama con 'this' impostato a foo
+({}).hasOwnProperty.call(foo, 'bar'); // true
+
+// E' anche possibile usare hasOwnProperty dal prototipo di
+// Object per questo scopo
+Object.prototype.hasOwnProperty.call(foo, 'bar'); // true
+
In conclusione
+
Usare hasOwnProperty è l'unico metodo affidabile per verificare
+l'esistenza di una proprietà in un oggetto. È raccomandabile usare
+hasOwnProperty in ogniciclo for in per
+evitare errori con i prototipi nativi estesi.
+
Il ciclo for in
Come per l'operatore in, il ciclo for in attraversa la catena di
+prototipi quando itera tra le proprietà di un oggetto.
+
+
// Modifichiamo Object.prototype
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+ console.log(i); // stampa sia bar che moo
+}
+
Dato che non è possibile modificare il comportamento del ciclo for in,
+è necessario filtrare le proprietà indesiderate all'interno del ciclo stesso.
+Questo può essere fatto usando il metodo hasOwnProperty
+di Object.prototype.
+
+
Usare hasOwnProperty per il filtraggio
+
// questo è il foo dell'esempio precedente
+for(var i in foo) {
+ if (foo.hasOwnProperty(i)) {
+ console.log(i);
+ }
+}
+
Questa è la sola versione corretta da usare. Proprio a causa dell'utilizzo di
+hasOwnProperty, soltantomoo verrà stampato; mentre omettendone l'uso,
+il codice sarà soggetto ad errori nei casi dove i prototipi nativi (ad esempio
+Object.prototype) sono stati estesi.
+
Un framework ampiamente usato che estende Object.prototype è Prototype.
+Quando questo framework viene incluso, è sicuro che i cicli for in che non
+utilizzano hasOwnProperty non funzioneranno.
+
In conclusione
+
Si raccomanda di usare semprehasOwnProperty. Non si dovrebbe mai dare
+per scontato l'ambiente in cui il codice sta girando, o se i prototipi
+nativi sono stati estesi o meno.
+
Funzioni
Dichiarazioni ed espressioni di funzione
Le funzioni in JavaScript sono oggetti di prima classe. Ciò significa che
+possono essere usate come ogni altro valore. Un uso comune di questa
+caratteristica è quello di passare una funzione anonima come funzione di
+callback ad un'altra, possibilmente asincrona, funzione.
+
La dichiarazione di function
+
function foo() {}
+
La funzione qui sopra viene elevata (hoisted) prima
+che inizi l'esecuzione del programma. Questo vuol dire che essa è disponibile
+da un qualsasi punto dello scope in cui è stata definita, anche se
+richiamata prima dell'effettiva definizione nel sorgente.
+
foo(); // funziona perché foo è stata creata prima di eseguire il codice
+function foo() {}
+
L'espressione function
+
var foo = function() {};
+
Questo esempio assegna la funzione anonima alla variabile foo.
+
foo; // 'undefined'
+foo(); // questo solleva un TypeError
+var foo = function() {};
+
Dato che var è una dichiarazione che eleva il nome di variabile foo
+prima che l'esecuzione del codice inizi, foo è già dichiarata quando lo
+script viene eseguito.
+
Ma, dal momento che le assegnazioni avvengono solo a runtime, il valore di
+foo sarà undefined per default, prima che il relativo
+codice sia eseguito.
+
Espressione di funzione con nome
+
Un altro caso speciale è l'assegnazione di funzioni con nome.
+
var foo = function bar() {
+ bar(); // funziona
+}
+bar(); // ReferenceError
+
Qui, bar non è disponibile nello scope più esterno, dal momento che la
+funzione viene assegnata solo a foo, mentre è disponibile all'interno di
+bar. Ciò è dato dal modo in cui funziona la risoluzione dei nomi
+in JavaScript: il nome della funzione è sempre reso disponibile nello scope
+locale della funzione stessa.
+
Come funziona this
JavaScript ha una concezione differente di ciò a cui il nome speciale this
+fa normalmente riferimento nella maggior parte degli altri linguaggi di
+programmazione. Ci sono esattamente cinque differenti modi nei quali
+il valore di this può essere associato nel linguaggio.
+
Lo scope globale
+
this;
+
Usando this nello scope globale, esso farà semplicemente riferimento
+all'oggetto globale.
+
Richiamando una funzione
+
foo();
+
Qui, this farà ancora riferimento all'oggetto globale.
+
+
Richiamando un metodo
+
test.foo();
+
In questo esempio, this farà riferimento a test.
+
Richiamando un costruttore
+
new foo();
+
Una chiamata di funzione che viene preceduta dalla parola chiave new
+agisce come un costruttore. Dentro la funzione,
+this farà riferimento all'Objectappena creato.
+
Impostazione esplicita di this
+
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // l'array verrà espanso come mostrato sotto
+foo.call(bar, 1, 2, 3); // risulterà in a = 1, b = 2, c = 3
+
Quando si usano i metodi call o apply di Function.prototype, il valore di
+this all'interno della funzione chiamata viene esplicitamente impostato
+al primo argomento della corrispondente chiamata di funzione.
+
Come risultato, nell'esempio sopra, il caso del metodonon viene applicato,
+e this all'interno di foo sarà impostato a bar.
+
+
Insidie comuni
+
Mentre molti di questi casi hanno senso, il primo può essere considerato
+un altro errore di progettazione del linguaggio perché non ha mai un
+uso pratico.
+
Foo.method = function() {
+ function test() {
+ // this viene impostato all'oggetto globale
+ }
+ test();
+}
+
Una comune credenza è che this all'interno di test faccia riferimento a
+Foo mentre, invece, non è così.
+
Per poter ottenere l'accesso a Foo dall'interno di test, è necessario creare
+una variabile locale all'interno di method che faccia riferimento a Foo.
+
Foo.method = function() {
+ var that = this;
+ function test() {
+ // Qui viene usato that invece di this
+ }
+ test();
+}
+
that è solo un normale nome di variabile, ma viene comunemente usato come
+riferimento ad un this più esterno. Abbinato alle closures
+può anche essere usato per passare il valore di this.
+
Metodi di asseganzione
+
Un'altra cosa che non funziona in JavaScript è la creazione di un alias ad
+una funzione, cioè l'assegnazione di un metodo ad una variabile.
+
var test = someObject.methodTest;
+test();
+
A causa della prima dichiarazione, test ora agisce da semplice chiamata a
+funzione e quindi, this all'interno di essa non farà più riferimento a
+someObject.
+
Mentre l'assegnazione tardiva di this potrebbe sembrare una cattiva idea
+in un primo momento, alla prova dei fatti è ciò che fa funzionare
+l'ereditarietà prototipale.
Quando method viene chiamato da un'istanza di Bar, this farà riferimento
+a quell'istanza.
+
Closures e riferimenti
Una delle caratteristiche più potenti di JavaScript è la disponibilità delle
+closure. Con le closure, gli scope hanno sempre accesso allo scope
+più esterno nel quale sono state definite. Dal momento che il solo scope che
+JavaScript ha è lo scope di funzione, tutte le funzioni,
+per default, agiscono da closure.
Qui, Counter ritorna due closure: la funzione increment e get.
+Entrambe mantengono un riferimento allo scope di Counter e, quindi,
+hanno sempre accesso alla variabile count definita in quello scope.
+
Perché le variabili private funzionano
+
Dato che non è possibile fare riferimento o assegnare scope in JavaScript,
+non c'è modo per accedere alla variabile count dall'esterno. Il solo
+modo per interagire con essa è tramite le due closure.
+
var foo = new Counter(4);
+foo.hack = function() {
+ count = 1337;
+};
+
Il codice sopra non modificherà la variabile count nello scope di Counter,
+dato che foo.hack non è stato definito in quello scope. Invece, creerà
+(o meglio, sostituirà) la variabile globalecount.
+
Closure nei cicli
+
Un errore che spesso viene fatto è quello di usare le closure all'interno dei
+cicli, come se stessero copiando il valore della variabile dell'indice del ciclo.
+
for(var i = 0; i < 10; i++) {
+ setTimeout(function() {
+ console.log(i);
+ }, 1000);
+}
+
Questo esempio non stamperà i numeri da 0 a 9, ma semplicemente il
+numero 10 dieci volte.
+
La funzione anonima mantiene un riferimento ad i, ma al momento in cui
+console.log viene richiamata, il ciclo for è già terminato, ed il valore
+di i è stato impostato a 10.
+
Per ottenere l'effetto desiderato, è necessario creare una copia del valore
+di i.
+
Evitare il problema del riferimento
+
Per copiare il valore della variabile indice del ciclo, è meglio usare un
+contenitore anonimo.
La funzione anonima più esterna viene chiamata immediatamente con i come
+suo primo argomento e riceverà una copia del valore di i come suo
+parametro e.
+
La funzione anonima che viene passata a setTimeout ora ha un riferimento a
+e, il cui valore non viene modificato dal ciclo.
+
C'è anche un altro possibile modo per ottenere il medesimo risultato, e cioè
+ritornare una funzione dal contenitore anonimo che avrà quindi lo stesso
+comportamento del codice visto precedentemente.
C'è un ulteriore modo per ottenere ciò, usando .bind, che può assegnare un
+contesto this e degli argomenti ad una funzione. Esso funziona allo stesso
+modo degli esempi precedenti
+
for(var i = 0; i < 10; i++) {
+ setTimeout(console.log.bind(console, i), 1000);
+}
+
L'oggetto arguments
Ogni scope di funzione in JavaScript può accedere alla speciale variabile
+arguments. Questa variabile mantiene un elenco di tutti gli argomenti
+che sono stati passati alla funzione.
+
+
L'oggetto argumentsnon è un Array. Sebbene abbia in parte la
+semantica di un array (nello specifico la proprietà length), esso non
+eredita da Array.prototype ed è a tutti gli effetti un Object.
+
Proprio per questo motivo, non è possibile usare su arguments i metodi
+standard degli array come push, pop, slice. E mentre l'iterazione con
+un semplice ciclo for funzionerà senza problemi, sarà necessario convertire
+l'oggetto in un vero Array per poter usare i metodi standard di Array con
+esso.
+
Conversione ad array
+
Il codice seguente ritornerà un nuovo Array contenenente tutti gli elementi
+dell'oggetto arguments.
+
Array.prototype.slice.call(arguments);
+
Dato che questa conversione è lenta, non è raccomandato usarla in sezioni
+di codice in cui la performance è un fattore critico.
+
Passaggio di argomenti
+
Quello che segue è il metodo raccomandato per passare argomenti da una funzione
+ad un'altra.
+
function foo() {
+ bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+ // codice da eseguire
+}
+
Un altro trucco è quello di usare call e apply insieme per creare veloci
+contenitori senza vincoli.
+
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+ console.log(this, a, b, c);
+};
+
+// Crea una versione senza vincoli di "method"
+// Richiede i parametri: this, arg1, arg2...argN
+Foo.method = function() {
+
+ // Risultato: Foo.prototype.method.call(this, arg1, arg2... argN)
+ Function.call.apply(Foo.prototype.method, arguments);
+};
+
Parametri formali e indici degli argomenti
+
L'oggetto arguments crea funzioni getter e setter sia per le sue
+proprietà che per i parametri formali della funzione.
+
Come risultato, la modifica del valore di un parametro formale modificherà
+anche il valore della corrispondente proprietà nell'oggetto arguments, e
+vice versa.
+
function foo(a, b, c) {
+ arguments[0] = 2;
+ a; // 2
+
+ b = 4;
+ arguments[1]; // 4
+
+ var d = c;
+ d = 9;
+ c; // 3
+}
+foo(1, 2, 3);
+
Miti e verità sulla performance
+
Il solo caso in cui l'oggetto arguments non viene creato, è quando esso
+viene dichiarato come un nome all'interno di una funzione o uno dei suoi
+parametri formali. Non importa che venga usato o meno.
+
Sia i getter che i setter vengono sempre creati. Perciò, il loro
+utilizzo non ha praticamente alcun impatto sulle prestazioni, specialmente
+nel mondo reale dove nel codice c'è più di un semplice accesso alle proprietà
+dell'oggetto arguments.
+
+
Ad ogni modo, c'è un caso che ridurrà drasticamente la performance nei motori
+JavaScript moderni. È il caso dell'utilizzo di arguments.callee.
+
function foo() {
+ arguments.callee; // fa qualcosa con questo oggetto funzione
+ arguments.callee.caller; // e l'oggetto funzione chiamante
+}
+
+function bigLoop() {
+ for(var i = 0; i < 100000; i++) {
+ foo(); // normalmente sarebbe sostituito con il suo codice...
+ }
+}
+
Nel codice qui sopra, foo non può più essere soggetto ad inlining
+dal momento che necessita di conoscere sia se stesso che il suo chiamante.
+Questo non solo annulla possibili guadagni prestazionali ottenibili con
+l'inlining, ma spezza anche il principio di incapsulazione perché la funzione
+ora potrebbe essere dipendente da uno specifico contesto di esecuzione.
+
L'utilizzo di arguments.callee o di qualsiasi altra delle sue proprietà
+è altamente sconsigliato.
+
+
Costruttori
I costruttori in JavaScript sono differenti da quelli di molti altri linguaggi.
+Qualsiasi chiamata a funzione preceduta dalla parola chiave new agisce come
+un costruttore.
+
Dentro al costruttore (la funzione chiamata) il valore di this fa riferimento
+al nuovo oggetto creato. Il prototype di questo nuovo
+oggetto viene impostato al prototype dell'oggetto funzione che è stato invocato
+come costruttore.
+
Se la funzione che è stata chiamata non ha un'istruzione return esplicita,
+allora essa ritorna implicitamente il valore di this (il nuovo oggetto).
+
function Foo() {
+ this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+ console.log(this.bla);
+};
+
+var test = new Foo();
+
Questo esempio chiama Foo come costruttore ed imposta il prototype del
+nuovo oggetto creato a Foo.prototype.
+
In caso di istruzione return esplicita, la funzione ritorna il valore
+specificato da quell'istruzione, ma solo se il valore di ritorno è un
+Object.
Quando la parola chiave new viene omessa, la funzione non ritornerà un
+nuovo oggetto.
+
function Foo() {
+ this.bla = 1; // imposta la proprietà dell'oggetto globale
+}
+Foo(); // undefined
+
Mentre l'esempio precedente potrebbe sembrare essere funzionante in alcuni
+casi, a causa del modo in cui lavora this in JavaScript,
+esso userà l'oggetto globale come valore di this.
+
Factory (Fabbriche di oggetti)
+
Per poter omettere la parola chiave new, la funzione costruttore deve
+esplicitamente ritornare un valore.
Entrambe le chiamate a Bar ritornano lo stesso risultato, un nuovo oggetto
+creato con una proprietà chiamata method, che è una Closure.
+
Bisogna anche notare che la chiamata new Bar()non influisce sul prototipo
+dell'oggetto ritornato. Mentre il prototipo sarà impostato con il nuovo oggetto
+creato, Bar non ritornerà mai quel nuovo oggetto.
+
Nell'esempio sopra, non c'è differenza funzionale nell'usare o meno la parola
+chiave new.
+
Creare nuovi oggetti tramite factory
+
Viene spesso raccomandato di non usare new perché una sua dimenticanza
+può portare a bug potenzialmente insidiosi da risolvere.
+
Per poter creare un nuovo oggetto, si dovrebbe invece usare una factory e
+costruire un nuovo oggetto all'interno di quella factory.
Sebbene questo esempio sia a prova di omissione della parola chiave new e
+renda sicuramente più semplice l'utilizzo delle variabili private,
+esso ha alcuni aspetti negativi.
+
+
Usa più memoria dal momento che gli oggetti creati non condividono
+i metodi di un prototipo.
+
Per poter ereditare, la factory deve copiare tutti i metodi da un altro
+oggetto oppure mettere quell'oggetto nel proptotipo del nuovo oggetto.
+
Perdere la catena di prototipi solo perché si vuole tralasciare la
+parola chiave new è contrario allo spirito del linguaggio.
+
+
In conclusione
+
Sebbene l'omissione della parola chiave new possa portare all'introduzione di
+bug, non è certo un motivo per privarsi completamente dell'uso dei prototipi.
+Alla fine si tratta di decidere quale sia la soluzione più adatta per
+l'applicazione. È specialmente importante scegliere uno specifico stile
+di creazione degli oggetti ed usarlo in maniera consistente.
+
Scope e spazi di nome (namespace)
Sebbene JavaScript non abbia problemi con la sintassi delle parentesi
+graffe per la definizione di blocchi, esso non supporta lo scope
+per blocco, quindi, tutto ciò che il linguaggio ci mette a disposizione
+è lo scope di funzione.
+
function test() { // questo è uno scope
+ for(var i = 0; i < 10; i++) { // questo non è uno scope
+ // conta
+ }
+ console.log(i); // 10
+}
+
+
Anche gli spazi di nome (namespace) non sono gestiti in JavaScript, e ciò
+significa che ogni cosa viene definita in un namespace globalmente condiviso.
+
Ogni volta che ci si riferisce ad una variabile, JavaScript risale attraverso
+tutti gli scope fino a che non la trova e, nel caso esso raggiunga lo scope
+globale senza aver trovato il nome richiesto, solleva un ReferenceError.
+
Il problema delle variabili globali
+
// script A
+foo = '42';
+
+// script B
+var foo = '42'
+
Questi due script non hanno lo stesso effetto. Lo script A definisce una
+variabile chiamata foo nello scope globale, mentre lo script B definisce
+una foo nello scope attuale.
+
Ancora una volta. Questo esempio non sortisce lo stesso effetto: il
+non utilizzo di var può avere importanti conseguenze.
L'omissione dell'istruzione var all'interno della funzione test sostituirà
+il valore di foo. Sebbene questo possa non sembrare un grosso problema in
+un primo momento, ritrovarsi con migliaia di linee di JavaScript senza
+utilizzare var introdurrà orribili bug molto difficili da individuare.
+
// scope globale
+var items = [/* un elenco */];
+for(var i = 0; i < 10; i++) {
+ subLoop();
+}
+
+function subLoop() {
+ // scope di subLoop
+ for(i = 0; i < 10; i++) { // istruzione var omessa
+ // fai qualcosa di eccezionale!
+ }
+}
+
Il ciclo esterno terminerà dopo la prima chiamata a subLoop, dato che subLoop
+sovrascriverà il valore globale di i. L'utilizzo di una var per il secondo ciclo
+for avrebbe facilmente evitato questo errore. L'istruzione var non dovrebbe
+mai essere omessa a meno che l'effetto desiderato non sia proprio quello
+di influenzare lo scope esterno.
+
Variabili locali
+
In JavaScript le sole sorgenti per le variabili locali sono i parametri
+funzione e le variabili dichiarate tramite l'istruzione
+var.
+
// scope globale
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+ // scope locale della funzione test
+ i = 5;
+
+ var foo = 3;
+ bar = 4;
+}
+test(10);
+
Mentre foo e i sono variabili locali all'interno dello scope della funzione
+test, l'assegnazione di bar sostituirà la variabile globale con lo stesso
+nome.
+
Elevamento (hoisting)
+
JavaScript eleva le dichiarazioni. Questo significa che le istruzioni var
+e le dichiarazioni function verranno spostate in cima agli scope che le
+racchiudono.
+
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+ if (false) {
+ goo = 1;
+
+ } else {
+ var goo = 2;
+ }
+ for(var i = 0; i < 100; i++) {
+ var e = data[i];
+ }
+}
+
Il codice qui sopra, viene trasformato prima che inizi l'esecuzione. JavaScript
+sposta sia le istruzioni var che le dichiarazioni function in cima al più
+vicino scope che le racchiude.
+
// le istruzioni var vengono spostate qui
+var bar, someValue; // di default a 'undefined'
+
+// la dichiarazione function viene spostate qui
+function test(data) {
+ var goo, i, e; // il blocco scope mancante sposta qui queste istruzioni
+ if (false) {
+ goo = 1;
+ } else {
+ goo = 2;
+ }
+ for(i = 0; i < 100; i++) {
+ e = data[i];
+ }
+}
+
+bar(); // fallisce con un TypeError dato che bar è ancora 'undefined'
+someValue = 42; // le assegnazioni non vengono influenzate dall'elevazione
+bar = function() {};
+
+test();
+
L'omissione del blocco di scope non solo muoverà le istruzioni var fuori dal
+corpo dei cicli, ma renderà anche i risultati di certi costrutti if poco
+intuitivi.
+
Nel codice originale, sebbene l'istruzione if sembrasse modificare la
+variabile globalegoo, effettivamente essa va a modificare la variabile locale
+(dopo che l'elevazione è stata eseguita).
+
Senza la conoscenza dell'elevazione, uno potrebbe pensare che il codice
+qui sotto sollevi un ReferenceError.
+
// verifica se SomeImportantThing è stato inizializzato
+if (!SomeImportantThing) {
+ var SomeImportantThing = {};
+}
+
Ma ovviamente tutto funziona grazie al fatto che l'istruzione var è stata
+spostata all'inzio dello scope globale.
+
var SomeImportantThing;
+
+// qui altro codice potrebbe o meno inizializzare SomeImportantThing
+
+// ci assicuriamo che ci sia
+if (!SomeImportantThing) {
+ SomeImportantThing = {};
+}
+
Ordine di risoluzione dei nomi
+
Tutti gli scope in JavaScript, scope globale incluso, hanno lo speciale
+nome this definito in essi, che fa riferimento
+all'oggetto attuale.
+
Gli scope di funzione hanno anche il nome arguments
+definito in essi, che contiene gli argomenti passati alla funzione.
+
Per esempio, cercando di accedere ad una variabile di nome foo all'interno
+dello scope di una funzione, JavaScript effettuerà una ricerca del nome nel
+seguente ordine:
+
+
Nel caso ci sia un'istruzione var foo nello scope attuale, usa quella.
+
Se uno dei parametri funzione si chiama foo, usa quello.
+
Se la funzione stessa si chiama foo, usa quella.
+
Vai al successivo scope esterno e ricomincia dal numero 1.
+
+
+
Spazi di nome (Namespace)
+
Un comune problema associato al fatto di avere un solo spazio nomi globale,
+è che facilmente si incappa in problemi dove i nomi di variabile si
+sovrappongono. In JavaScript queso problema può essere facilmente evitato
+con l'aiuto dei contenitori anonimi.
+
(function() {
+ // "namespace" auto contenuto
+
+ window.foo = function() {
+ // una closure esposta
+ };
+
+})(); // esecue immediatamente la funzione
+
Le funzioni anonime sono considerate espressioni, quindi
+per poter essere richiamabili, esse devono prima essere valutate.
+
( // valuta la funzione dentro le parentesi
+function() {}
+) // e ritorna l'oggetto funzione
+() // richiama il risultato della valutazione
+
Ci sono altri modi per valutare e chiamare direttamente l'espressione funzione
+i quali, sebbene differenti nella sintassi, hanno tutti il medesimo effetto.
+
// Alcuni modi per invocare direttamente la
+!function(){}()
++function(){}()
+(function(){}());
+// e così via...
+
In conclusione
+
Si raccomanda sempre di usare un contenitore anonimo per incapsulare il
+codice nel suo proprio namespace. Questo non solo protegge il codice da
+eventuali conflitti con i nomi, ma permette anche una migliore modularizzazione
+dei programmi.
+
Inoltre, l'uso delle variabili globali è considerato una cattiva pratica.
+Qualsiasi loro uso indica codice scritto male che è suscettibile ad errori
+e difficile da mantenere.
+
Array
Iterazione e proprietà degli Array
Sebbene gli array in JavaScript siano oggetti, non ci sono valide ragioni
+per usare il ciclo for in. Infatti, ci sono varie
+buone ragioni per evitare l'utilizzo di for in con gli array.
+
+
Dato che il ciclo for in enumera tutte le proprietà che sono presenti nella
+catena di prototipi, e dal momento che il solo modo per escludere queste
+proprietà è quello di usare hasOwnProperty,
+esso è già venti volte più lento di un normale ciclo for.
+
Iterazione
+
Per poter ottenere la miglior performance durante l'iterazione degli array,
+è meglio usare il classico ciclo for.
+
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+ console.log(list[i]);
+}
+
In questo esempio c'è un ulteriore particolare da notare, che è il caching
+della lunghezza dell'array tramite l = list.length.
+
Sebbene la proprietà length sia definita nell'array stesso, c'è ancora un
+sovraccarico di lavoro dato dal fatto che deve essere ricercata ad ogni
+iterazione del ciclo. E mentre i motori JavaScript recenti potrebbero
+applicare delle ottimizzazioni in questo caso, non c'è modo di dire se il
+codice verrà eseguito su uno di questi nuovi motori oppure no.
+
Infatti, l'omissione della parte di caching può risultare in un ciclo eseguito
+soltanto alla metà della velocità con cui potrebbe essere eseguito facendo
+il caching della lunghezza.
+
La proprietà length
+
Mentre il getter della proprietà length ritorna semplicemente il numero di
+elementi che sono contenuti nell'array, il setter può essere usato per
+troncare l'array.
Assegnando una lunghezza più piccola si tronca l'array. Incrementandola si
+crea un array frammentato.
+
In conclusione
+
Per la miglior performance, si raccomanda di usare sempre il ciclo for
+classico e fare il caching della proprietà length. L'uso di for in su di
+un array è segno di un codice scritto male che è suscettibile a bug e pessima
+performance.
+
Il costruttore Array
Dato che il costruttore Array è ambiguo riguardo a come esso gestisca i suoi
+parametri, si consiglia calorosamente di usare l'array letterale (notazione [])
+quando si creano array.
Nei casi in cui c'è solo un argomento passato al costruttore Array e quando
+l'argomento è un Number, il costruttore ritornerà un nuovo array frammentato
+con la proprietà length impostata al valore dell'argomento. Si noti
+che in questo modo solo la proprietà length del nuovo array verrà impostata,
+mentre gli indici dell'array non verranno inizializzati.
+
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // false, l'indice non è stato impostato
+
Essere in grado di impostare la lunghezza dell'array in anticipo è utile soltanto
+in poche situazioni, come ad esempio la ripetizione di una stringa, nel cui caso
+si eviterebbe l'uso di un ciclo.
+
new Array(count + 1).join(stringToRepeat);
+
In conclusione
+
I letterali sono da preferirsi al costruttore Array. Sono più concisi, hanno una
+sintassi più chiara ed incrementano la leggibilità del codice.
+
Tipi di dati
Uguaglianza e comparazioni
JavaScript usa due differenti metodi per comparare l'uguaglianza dei
+valori degli oggetti.
+
L'operatore di uguaglianza
+
L'operatore di uguaglianza consiste di due segni di uguaglianza: ==.
+
JavaScript supporta la tipizzazione debole. Questo significa che
+l'operatore di uguaglianza converte i tipi in modo da poterli
+confrontare.
Questa tabella mostra i risultati della conversione di tipo, ed è il
+principale motivo per cui l'uso di == è ampiamente considerato una
+cattiva pratica. Esso introduce bug difficili da rilevare a causa delle
+complesse regole di conversione.
+
Inoltre, c'è anche un impatto sulla performance quando entra in gioco la
+conversione di tipo. Ad esempio, una stringa deve essere convertita in un
+numero prima di poter essere confrontata con un altro numero.
+
L'operatore di uguaglianza stretta
+
L'operatore di uguaglianza stretta consiste di tre segni di uguaglianza: ===.
+
Funziona come il normale operatore di uguaglianza, con l'eccezione di
+non eseguire la conversione di tipo tra gli operandi.
I risultati qui sono più chiari e permettono di identificare subito un problema
+con il codice. Questo rende il codice più solido di un certo grado e fornisce anche
+migliorie alla performance nel caso di operandi di tipo differente.
+
Comparazione di oggetti
+
Nonostante == e === vengano definiti operatori di uguaglianza, essi
+funzionano differentemente quando almeno uno degli operandi è un Object.
Qui, entrambe gli operatori confrontano per identità e non per
+uguaglianza. Essi confrontano, cioè, che sia la stessa istanza dell'oggetto,
+in modo molto simile a is in Python e la comparazione di puntatori in C.
+
In conclusione
+
Si raccomanda calorosamente di usare solo l'operatore di uguaglianza stretta.
+Nei casi dove è necessario che i tipi vengano convertiti, questa operazione
+dovrebbe essere fatta esplicitamente piuttosto che essere
+lasciata alle complesse regole di conversione del linguaggio.
+
L'operatore typeof
L'operatore typeof (assieme a instanceof) è
+probabilmente il più grande difetto di progettazione di JavaScript,
+dato che è quasi completamente inusabile.
+
Sebbene instanceof abbia ancora limitati casi d'uso, typeof ha realmente
+un solo caso d'uso, che non è quello di verificare il tipo di un oggetto.
+
+
Tabella dei tipi di JavaScript
+
Valore Classe Tipo
+-------------------------------------
+"foo" String string
+new String("foo") String object
+1.2 Number number
+new Number(1.2) Number object
+true Boolean boolean
+new Boolean(true) Boolean object
+new Date() Date object
+new Error() Error object
+[1,2,3] Array object
+new Array(1, 2, 3) Array object
+new Function("") Function function
+/abc/g RegExp object (function in Nitro/V8)
+new RegExp("meow") RegExp object (function in Nitro/V8)
+{} Object object
+new Object() Object object
+
In questa tabella, Tipo fa riferimento al valore ritornato dall'operatore typeof.
+Come si può chiaramente vedere, questo valore è tutto fuorchè affidabile.
+
Classe si riferisce al valore della proprietà interna [[Class]] di un oggetto.
+
+
Per ottenere il valore di [[Class]], bisogna usare il metodo toString di
+Object.prototype.
+
La classe di un oggetto
+
Le specifiche forniscono esattamente un modo per accedere al valore di
+[[Class]], con l'uso di Object.prototype.toString.
Nel esempio qui sopra, Object.prototype.toString viene chiamato con il valore
+di this impostato all'oggetto di cui si vuole ottenere il
+valore di [[Class]].
+
+
Testare variabili non definite
+
typeof foo !== 'undefined'
+
Questo esempio verificherà se foo è stata attualmente dichiarata oppure no.
+Un semplice referenziamento ad essa risulterebbe in un ReferenceError.
+Questo è l'unico caso in cui typeof è utile a qualcosa.
+
In conclusione
+
Per verificare il tipo di un oggetto, è altamente raccomandato l'utilizzo di
+Object.prototype.toString, dato che questo è il solo modo affidabile per
+fare ciò. Come mostrato nella tabella precedente, alcuni valori di ritorno
+di typeof non sono definiti nelle specifiche, e ciò dimostra come essi
+potrebbero differire tra implementazioni differenti.
+
A meno che non si debba verificare se una variabile è definta, typeof
+dovrebbe essere evitato.
+
L'operatore instanceof
L'operatore instanceof confronta i costruttori dei suoi due operandi.
+È utile soltanto per la comparazione di oggetti realizzati dal
+programmatore. Se usato sui tipi interni del linguaggio, esso è
+praticamente inutile alla stregua dell'operatore typeof.
+
Confronto di oggetti personalizzati
+
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// Questo imposta Bar.prototype all'oggetto funzione Foo,
+// ma non ad un'istanza di Foo
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
Un'importante cosa da notare qui è che instanceof non funziona con oggetti
+originati da differenti contesti JavaScript (ad esempio, differenti
+documenti in un browser web), dato che i loro costruttori non saranno
+esattamente lo stesso oggetto.
+
In conclusione
+
L'operatore instanceof dovrebbe essere usto solo quando si ha a che fare
+con oggetti personalizzati creati dal programmatore, che provengono dallo
+stesso contesto JavaScript. Proprio come per l'operatore typeof,
+ogni altro tipo di utilizzo dovrebbe essere evitato.
+
Conversione di tipo (Type Casting)
JavaScript è un linguaggio debolmente tipizzato, perciò esso applicherà
+una conversione di tipoovunque sia possibile.
+
// Queste sono vere
+new Number(10) == 10; // Number.toString() viene convertito
+ // nuovamente in un numero
+
+10 == '10'; // String viene convertita in Number
+10 == '+10 '; // Stringa più assurda
+10 == '010'; // a ancora di più
+isNaN(null) == false; // null viene convertito in 0
+ // che ovviamente non è NaN
+
+// Queste sono false
+10 == 010;
+10 == '-10';
+
+
Per evitare i problemi appena visti, l'uso
+dell'operatore di uguaglianza stretta è altamente
+raccomandato. Sebbene questo eviti molti dei comuni problemi, ci sono ancora
+molti ulteriori problemi che possono essere generati dal sistema debolemente
+tipizzato di JavaScript.
+
Costruttori di tipi interni
+
I costruttori dei tipi interni del linguaggio, come Number e String,
+funzionano in modo differente a seconda che venga usata o meno la
+parola chiave new.
+
new Number(10) === 10; // False, Object e Number
+Number(10) === 10; // True, Number e Number
+new Number(10) + 0 === 10; // True, a causa della conversione implicita
+
L'uso di un tipo di dato interno come Number come costruttore, creerà un
+nuovo oggetto Number, ma l'omissione della parola chiave new farà sì
+che la funzione Number agisca da convertitore.
+
Inoltre, il passaggio di valori letterali o non oggetto risulterà in un'ancora
+maggiore conversione di tipo.
+
La miglior opzione è quella di fare esplicitamente la conversione ad uno
+dei tre possibili tipi.
+
Convertire in una stringa
+
'' + 10 === '10'; // true
+
Anteponendo una stringa vuota, un valore può facilmente essere convertito in
+una stringa.
+
Convertire in un numero
+
+'10' === 10; // true
+
Usando l'operatore unario di addizione, è possibile convertire in un numero.
+
Convertire in un booleano
+
Usando due volte l'operatore not, un valore può essere convertito in un
+booleano.
Comunque, eval esegue solo nello scope locale quando viene chiamata
+direttamente e quando il nome della funzione chiamata è eval.
+
var foo = 1;
+function test() {
+ var foo = 2;
+ var bar = eval;
+ bar('foo = 3');
+ return foo;
+}
+test(); // 2
+foo; // 3
+
L'uso di eval dovrebbe essere evitato. Il 99.9% dei suoi "utilizzi" può
+essere ottenuto senza di essa.
+
eval sotto mentite spoglie
+
Le funzioni di timeoutsetTimeout e setInterval possono
+entrambe accettare una stringa come loro primo argomento. Questa stringa verrà
+sempre eseguita nello scope globale dato che eval non viene chiamato
+direttamente in questo caso.
+
Problemi di sicurezza
+
eval è anche un problema di sicurezza, perché essa esegue qualsiasi
+codice le viene passato. Non si dovrebbe mai usare con stringhe di origine
+sconosciuta o inaffidabile.
+
In conclusione
+
eval non dovrebbe mai essere usata. Qualsiasi codice che ne faccia uso dovrebbe
+essere messo in discussione sotto l'aspetto della funzionalità, della performance
+e della sicurezza. Se qualcosa richiede eval per poter funzionare, allora non
+dovrebbe essere usato in primo luogo, ma si dovrebbe prevedere una
+miglior progettazione che non richieda l'uso di eval.
+
undefined e null
JavaScript usa due valori distinti per il nulla, null e undefined, e
+quest'ultimo è il più utile.
+
Il valore undefined
+
undefined è un tipo con esattamente un valore: undefined.
+
Il linguaggio definisce anche una variabile globale che ha il valore di undefined.
+Questa variabile è anche chiamata undefined. Comunque, questa variabile non è
+né una costante né una parola chiave del linguaggio. Ciò significa che il suo valore
+può facilmente essere sovrascritto.
+
+
Ecco alcuni esempi di quando il valore undefined viene ritornato:
+
+
Accedendo la variabile globale (non modificata) undefined.
+
Accedendo una variabile dichiarata ma non ancora inizializzata.
+
Ritorno implicito da funzioni che non hanno l'istruzione return.
+
Istruzioni return che non ritornano esplicitamente alcun valore.
+
Ricerca di proprietà inesistenti.
+
Parametri funzione a cui non viene esplicitamente passato alcun valore.
+
Qualsiasi cosa a cui sia stato assegnato il valore undefined.
+
Qualsiasi espressione nella forma di void(espressione).
+
+
Gestire le modifiche al valore di undefined
+
Dato che la variabile globale undefined mantiene solo una copia dell'attuale
+valore di undefined, assegnandole un nuovo valore non cambia il valore del
+tipoundefined.
+
Inoltre, per confrontare qualcosa con il valore di undefined, è necessario
+ottenere prima il valore di undefined.
+
Per proteggere il codice da possibili sovrascritture della variabile undefined,
+viene usata una comune tecnica che prevede l'aggiunta di un ulteriore parametro
+ad un contenitore anonimo al quale non viene passato alcun
+argomento.
+
var undefined = 123;
+(function(something, foo, undefined) {
+ // ora undefined nello scope locale
+ // fa nuovamente riferimento al valore `undefined`
+
+})('Hello World', 42);
+
Un altro modo per ottenere lo stesso effetto sarebbe quello di usare una
+dichiarazione all'interno del contenitore.
+
var undefined = 123;
+(function(something, foo) {
+ var undefined;
+ ...
+
+})('Hello World', 42);
+
La sola differenza è che questa versione si traduce in 4 byte in più quando
+minificata, e non c'è nessun'altra istruzione var al'interno del contenitore
+anonimo.
+
Utilizzi di null
+
Mentre undefined nel contesto del linguaggio JavaScript viene principalmente
+usato come un tradizionale null, l'attuale null (sia letterale che tipo di
+dati) è più o meno solo un altro tipo di dato.
+
Viene usato in alcune funzioni interne al JavaScript (come la dichiarazione
+del termine della catena di prototipi, impostando Foo.prototype = null), ma
+nella maggior parte dei casi, può essere rimpiazzato da undefined.
+
Inserimento automatico dei punti-e-virgola
Sebbene JavaScript utilizzi lo stile di sintassi del C, esso non
+obbliga l'uso dei punti-e-virgola nel codice sorgente, perciò è possibile
+ometterli.
+
Detto questo, JavaScript non è un linguaggio che fa a meno dei punti-e-virgola.
+Infatti, esso necessita di punti-e-virgola per poter comprendere il codice
+sorgente. Quindi, il parser del JavaScript li inserisce automaticamente
+ogni volta che incontra un errore di analisi dato dalla mancanza di un
+punto-e-virgola.
+
var foo = function() {
+} // errore di analisi, atteso punto-e-virgola
+test()
+
Quindi avviene l'inserimento, ed il parser prova nuovamente.
+
var foo = function() {
+}; // nessun errore, il parser continua
+test()
+
L'inserimento automatico dei punti-e-virgola è considerato essere uno dei
+più grandi errori di progettazione del linguaggio, perché può
+modificare il comportamento del codice.
+
Come funziona
+
Il codice qui sotto non ha punti-e-virgola, quindi sta al parser decidere dove
+inserirli.
Le possibilità che lognon ritorni una funzione sono veramente alte,
+perciò il codice qui sopra porterà ad un TypeError dichiarando che
+undefined is not a function (undefined non è una funzione).
+
In conclusione
+
È fortemente raccomandato di non omettere mai i punti-e-virgola.
+Si raccomanda anche di mantenere le parentesi sulla stessa linea della
+corrispondente istruzione, e di non ometterle mai in istruzioni if / else
+a linea singola. Queste misure precauzionali non solo miglioreranno la
+consistenza del codice, ma preverranno anche che il parser JavaScript
+modifichi il comportamento del codice in modo inaspettato.
+
L'operatore delete
In breve, è impossibile eliminare variabili globali, funzioni e qualche
+altra cosa in JavaScript che ha l'attributo DontDelete impostato.
+
Codice globale e codice funzione
+
Quando una variabile o una funzione viene definita in un scope globale o
+funzione, essa è una proprietà dell'oggetto Activation
+o dell'oggetto Global. Queste proprietà hanno un set di attributi, tra i quali
+DontDelete. Dichiarazioni di variabile o funzione nel codice globale o
+funzione, creano sempre proprietà con DontDelete, e quindi non possono essere
+eliminate.
+
// variabile globale:
+var a = 1; // DontDelete è impostato
+delete a; // false
+a; // 1
+
+// funzione normale:
+function f() {} // DontDelete è impostato
+delete f; // false
+typeof f; // "function"
+
+// la riassegnazione non aiuta:
+f = 1;
+delete f; // false
+f; // 1
+
Proprietà esplicite
+
Proprietà esplicitamente impostate possono essere eliminate normalmente.
Nel codice qui sopra, obj.x e obj.y possono essere eliminate perché
+non hanno l'attributo DontDelete. Ecco perché anche l'esempio seguente
+funziona.
+
// questo funziona, tranne che per IE:
+var GLOBAL_OBJECT = this;
+GLOBAL_OBJECT.a = 1;
+a === GLOBAL_OBJECT.a; // true - solo una variabile globale
+delete GLOBAL_OBJECT.a; // true
+GLOBAL_OBJECT.a; // undefined
+
Qui usiamo un trucco per eliminare a. this qui fa
+riferimento all'oggetto Global e noi dichiariamo esplicitamente la
+variabile a come sua proprietà, il che ci permette di eliminarla.
+
IE (almeno 6-8) ha alcuni bug, quindi il codice precedente non funziona.
+
Argomenti funzione e proprietà interne
+
Anche i normali argomenti delle funzioni, gli
+oggetti arguments e le proprietà interne hanno
+DontDelete impostato.
Il comportamento dell'operatore delete può essere inaspettato con gli oggetti
+non nativi. A causa delle specifiche, agli oggetti non nativi è permesso di
+implementare qualsiasi tipo di funzionalità.
+
In conclusione
+
L'operatore delete spesso ha un comportamento inaspettato e può solo essere
+usato con sicurezza per eliminare proprietà esplicitamente impostate in oggetti
+normali.
+
Varie
setTimeout e setInterval
Dato che JavaScript è asincrono, è possibile programmare l'esecuzione di una
+funzione usando le funzioni setTimeout e setInterval.
+
+
function foo() {}
+var id = setTimeout(foo, 1000); // ritorna un Number > 0
+
Quando chiamato, setTimeout ritorna l'ID del timeout e programma foo per
+essere eseguito approssimativamente un migliaio di millisecondi nel futuro.
+foo verrà quindi eseguito una volta.
+
Dipendendo dalla risoluzione del timer del motore JavaScript che esegue il codice,
+come anche dal fatto che JavaScript è single threaded e quindi altro codice
+potrebbe essere eseguito bloccando il thread, non è mai sicuro scommettere
+che una funzione verrà eseguita esattamente al ritardo specifiato nella chiamata
+a setTimeout.
+
La funzione che è stata passata come primo parametro verrà chiamata dall'oggetto globale,
+e ciò significa che this all'interno della funzione chiamata
+farà riferimento all'oggetto globale.
Mentre setTimeout esegue solo una volta la funzione, setInterval (come il
+nome suggerisce) eseguirà la funzione ogniX millisecondi, ma il suo
+utilizzo è sconsigliato.
+
Quando il codice che viene eseguito blocca la chiamata timeout, setInterval
+eseguirà ancora più chiamate alla specifica funzione. Questo può, specialmente
+con intervalli molto brevi, tradursi in chiamate a funzione che si sovrappongono.
+
function foo(){
+ // qualcosa che blocca per 1 secondo
+}
+setInterval(foo, 1000);
+
Nel codice precedente, foo verrà chiamato una volta e quindi bloccherà per
+un secondo.
+
Mentre foo blocca il codice, setInterval continuerà a programmare ulteriori
+chiamate ad essa. Ora, quando foo ha finito, ci saranno già dieci ulteriori
+chiamate ad essa in attesa per essere eseguite.
+
Gestione di potenziale codice bloccante
+
La soluzione più semplice, come anche la più controllabile, è quella di usare
+setTimeout all'interno di se stessa.
+
function foo(){
+ // qualcosa che blocca per 1 secondo
+ setTimeout(foo, 1000);
+}
+foo();
+
Non solo questo incapsula la chiamata a setTimeout, ma previene anche la
+sovrapposizione delle chiamate e da un controllo addizionale. foo stessa
+può ora decidere se vuole continuare ad essere eseguita oppure no.
+
Pulizia manuale dei timeout
+
La pulizia di timeout ed intervalli funziona passando il rispettivo ID a
+clearTimeout o clearInterval, in base a quale set di funzioni è stato
+usato precedentemente.
+
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
Pulizia di tutti i timeout
+
Dato che non c'è un metodo interno per la pulizia di tutti i timeout e/o
+intervalli, è necessario usare la forza bruta per poter raggiungere questo
+scopo.
+
// pulisce "tutti" i timeout
+for(var i = 1; i < 1000; i++) {
+ clearTimeout(i);
+}
+
Ma ci potrebbero ancora essere timeout che non vengono toccati da questo
+numero arbitrario. Un altro modo per ottenere ciò, è considerare che l'ID
+dato ad un timeout viene incrementato di uno ogni volta che si chiama
+setTimeout.
Sebbene questo funzioni con la maggior parte dei browser odierni, non è
+specificato che gli ID debbano essere ordinati in quel modo e ciò potrebbe
+anche cambiare in futuro. Perciò, si raccomanda di tener traccia di tutti
+gli ID dei timeout, così che possano essere puliti in modo specifico.
+
Uso nascosto di eval
+
setTimeout e setInterval possono anche accettare una stringa come loro
+primo parametro. Questa caratteristica non dovrebbe essere mai usata
+perché internamente fa uso di eval.
+
+
function foo() {
+ // verrà chiamata
+}
+
+function bar() {
+ function foo() {
+ // non verrà mai chiamata
+ }
+ setTimeout('foo()', 1000);
+}
+bar();
+
Dal momento che eval non viene chiamata direttamente in questo
+caso, la stringa passata a setTimeout verrà eseguita nello scope globale.
+Quindi, non verrà usata la variabile locale foo dallo scope di bar.
+
Si raccomanda inoltre di non usare una stringa per passare argomenti alla
+funzione che verrà chiamata da una delle funzioni di timeout.
+
function foo(a, b, c) {}
+
+// non usare MAI questo
+setTimeout('foo(1, 2, 3)', 1000)
+
+// Usare invece una funzione anonima
+setTimeout(function() {
+ foo(a, b, c);
+}, 1000)
+
+
In conclusione
+
Una stringa non dovrebbe mai essere usata come parametro di setTimeout o
+setInterval. È un chiaro segno di codice veramente pessimo, quando
+gli argomenti necessitano di essere passati alla funzione che deve essere
+chiamata. Dovrebbe invece essere passata una funzione anonima che si incarichi
+di gestire l'effettiva chiamata.
+
Inoltre, l'uso di setInterval dovrebbe essere evitato perché il suo schedulatore
+non viene bloccato dall'esecuzione di JavaScript.
+
\ No newline at end of file
diff --git a/ja/index.html b/ja/index.html
new file mode 100644
index 0000000..927a51a
--- /dev/null
+++ b/ja/index.html
@@ -0,0 +1,1127 @@
+JavaScript Garden
\ No newline at end of file
diff --git a/site/javascript/garden.js b/javascript/garden.js
similarity index 100%
rename from site/javascript/garden.js
rename to javascript/garden.js
diff --git a/site/javascript/html5.js b/javascript/html5.js
similarity index 100%
rename from site/javascript/html5.js
rename to javascript/html5.js
diff --git a/site/javascript/plugin.js b/javascript/plugin.js
similarity index 100%
rename from site/javascript/plugin.js
rename to javascript/plugin.js
diff --git a/site/javascript/prettify.js b/javascript/prettify.js
similarity index 100%
rename from site/javascript/prettify.js
rename to javascript/prettify.js
diff --git a/ko/index.html b/ko/index.html
new file mode 100644
index 0000000..0e14c09
--- /dev/null
+++ b/ko/index.html
@@ -0,0 +1,1103 @@
+JavaScript Garden
소개
Intro
JavaScript 언어의 핵심에 대한 내용을 모아 JavaScript Garden을 만들어 었다. 이 글이 초보자가 JavaScript 익히면서 자주 겪는 실수, 미묘한 버그, 성능 이슈, 나쁜 습관들 줄일 수 있도록 도와줄 것이다.
+
JavaScript Garden은 단순히 JavaScript 언어 자체를 설명하려 만들지 않았다. 그래서 이 글에서 설명하는 주제들을 이해하려면 반드시 언어에 대한 기본 지식이 필요하다. 먼저 Mozilla Developer Network에 있는 문서로 JavaScript 언어를 공부하기 바란다.
숫자 리터럴은 객체처럼 사용되지 못할꺼라는 오해가 있는데 이것은 단지 JavaScript 파서의 문제일 뿐이다. JavaScript 파서는 숫자에 Dot Notation이 들어가면 오류라고 생각한다.
+
2.toString(); // SyntaxError가 난다.
+
하지만, 숫자를 객체처럼 사용할수 있는 꼼수가 몇 가지 있다.
+
2..toString(); // 두 번째 점은 잘 된다.
+2 .toString(); // 왼쪽 공백이 있으면 잘 된다.
+(2).toString(); // 2를 먼저 해석한다.
+
Object 타입
+
JavaScript 객체는 name/value 쌍으로 된 프로퍼티로 구성되기 때문에 Hashmap처럼 사용될 수도 있다.
+
객체 리터럴인 Object Notation으로 객체를 만들면 Object.prototype을 상속받고 프로퍼티를 하나도 가지지 않은 객체가 만들어진다.
+
var foo = {}; // 깨끗한 새 객체를 만든다.
+
+// 값이 12인 'test' 프로퍼티가 있는 객체를 만든다.
+var bar = {test: 12};
+
프로퍼티 접근
+
객체의 프로퍼티는 객체이름 다음에 점을 찍어(Dot Notation) 접근하거나 각괄호를 이용해(Square Bracket Notation) 접근할 수 있다.
+
var foo = {name: 'kitten'}
+foo.name; // kitten
+foo['name']; // kitten
+
+var get = 'name';
+foo[get]; // kitten
+
+foo.1234; // SyntaxError
+foo['1234']; // works
+
두 방식 모두 거의 동일하게 동작한다. 다만 차이가 있다면 각괄호 방식은 프로퍼티 이름을 동적으로 할당해서 값에 접근 할수 있지만 점을 이용한 방식은 구문 오류를 발생시킨다.
+
프로퍼티 삭제
+
객체의 프로퍼티를 삭제하려면 delete를 사용해야만 한다. 프로퍼티에 undefined나 null을 할당하는 것은 프로퍼티를 삭제하는 것이 아니라 프로퍼티에 할당된 value만 지우고 key는 그대로 두는 것이다.
+
var obj = {
+ bar: 1,
+ foo: 2,
+ baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ console.log(i, '' + obj[i]);
+ }
+}
+
위 코드의 출력 결과는 baz만 제거했기 때문에 bar undefined와 foo null은 출력되고 baz와 관련된 것은 출력되지 않는다.
+
Notation of Keys
+
var test = {
+ 'case': 'I am a keyword, so I must be notated as a string',
+ delete: 'I am a keyword, so me too' // SyntaxError가 난다.
+};
+
프로퍼티는 따옴표 없는 문자열(plain characters)과 따옴표로 감싼 문자열(strings)을 모두 Key 값으로 사용할 수 있다. 하지만 위와 같은 코드는 JavaScript 파서의 잘못된 설계 때문에 구버전(ECMAScript 5 이전 버전)에서는 SystaxError가 발생할 것이다.
+
위 코드에서 문제가 되는 delete 키워드를 따옴표로 감싸면 구버전의 JavaScript 엔진에서도 제대로 해석될 것이다.
+
Prototype
Javascript는 클래스 스타일의 상속 모델을 사용하지 않고 프로토타입 스타일의 상속 모델을 사용한다.
+
'이 점이 JavaScript의 약점이다.'라고 말하는 사람들도 있지만 실제로는 prototypal inheritance 모델이 훨씬 더 강력하다. 그 이유는 프로토타입 모델에서 클래스 모델을 흉내 내기는 매우 쉽지만, 반대로 클래스 모델에서 프로토타입 모델을 흉내 내기란 매우 어렵기 때문이다.
+
실제로 Prototypal Inheritance 모델을 채용한 언어 중에서 JavaScript만큼 널리 사용된 언어가 없었기 때문에 두 모델의 차이점이 다소 늦게 정리된 감이 있다.
+
먼저 가장 큰 차이점은 프로토타입 체인이라는 것을 이용해 상속을 구현한다는 점이다.
+
+
function Foo() {
+ this.value = 42;
+}
+Foo.prototype = {
+ method: function() {}
+};
+
+function Bar() {}
+
+// Foo의 인스턴스를 만들어 Bar의 prototype에 할당한다.
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Hello World';
+
+// Bar 함수를 생성자로 만들고
+Bar.prototype.constructor = Bar;
+
+var test = new Bar() // bar 인스턴스를 만든다.
+
+// 결과적으로 만들어진 프로토타입 체인은 다음과 같다.
+test [instance of Bar]
+ Bar.prototype [instance of Foo]
+ { foo: 'Hello World' }
+ Foo.prototype
+ { method: ... }
+ Object.prototype
+ { toString: ... /* etc. */ }
+
위 코드에서 test 객체는 Bar.prototype과 Foo.prototype을 둘 다 상속받았기 때문에 Foo에 정의한 method 함수에 접근할 수 있다. 그리고 프로토타입 체인에 있는 Foo 인스턴스의 value 프로퍼티도 사용할 수 있다. new Bar()를 해도 Foo 인스턴스는 새로 만들어지지 않고 Bar의 prototype에 있는 것을 재사용한다. 그래서 모든 Bar 인스턴스는 같은value 프로퍼티를 공유한다.
+
+
프로토타입 탐색
+
객체의 프로퍼티에 접근하려고 하면 JavaScript는 해당 이름의 프로퍼티를 찾을 때까지 프로토타입 체인을 거슬러 올라가면서 탐색하게 된다.
+
프로토타입 체인을 끝까지 탐색했음에도(보통은 Object.prototype임) 불구하고 원하는 프로퍼티를 찾지 못하면 undefined를 반환한다.
+
prototype 프로퍼티
+
prototype 프로퍼티는 프로토타입 체인을 만드는 데 사용하고 어떤 값이든 할당할 수 있지만, primitive 값을 할당되면 무시한다.
+
function Foo() {}
+Foo.prototype = 1; // 무시됨
+
반면에 위 예제처럼 객체를 할당하면 프로토타입 체인이 동적으로 잘 만들어진다.
+
성능
+
프로토타입 체인을 탐색하는 시간이 오래걸릴수록 성능에 부정적인 영향을 줄수있다. 특히 성능이 중요한 코드에서 프로퍼티 탐색시간은 치명적인 문제가 될수있다. 가령, 없는 프로퍼티에 접근하려고 하면 항상 프로토타입 체인 전체를 탐색하게 된다.
+
뿐만아니라 객체를 순회(Iterate)할때도 프로토타입 체인에 있는 모든 프로퍼티를 탐색하게 된다.
+
네이티브 프로토타입의 확장
+
종종 Object.prototype을 이용해 내장 객체를 확장하는 경우가 있는데, 이것도 역시 잘못 설계된 것중에 하나다.
+
위와 같이 확장하는 것을 Monkey Patching라고 부르는데 캡슐화를 망친다. 물론 Prototype같은 유명한 프레임워크들도 이런 확장을 사용하지만, 기본 타입에 표준도 아닌 기능들을 너저분하게 추가하는 이유를 여전히 설명하지 못하고 있다.
+
기본 타입을 확장해야하는 유일한 이유는 Array.forEach같이 새로운 JavaScript 엔진에 추가된 기능을 대비해 미리 만들어 놓는 경우 말고는 없다.
+
결론
+
프로토타입을 이용해 복잡한 코드를 작성하기 전에 반드시 프로토타입 상속 (Prototypal Inheritance) 모델을 완벽하게 이해하고 있어야 한다. 뿐만아니라 프로토타입 체인과 관련된 성능 문제로 고생하지 않으려면 프로토타입 체인이 너무 길지 않도록 항상 주의하고 적당히 끊어줘야 한다. 마지막으로 새로운 JavaScript 기능에 대한 호환성 유지 목적이 아니라면 절대로 네이티브 프로토타입을 확장하지마라.
+
hasOwnProperty
어떤 객체의 프로퍼티가 자기 자신의 프로퍼티인지 아니면 프로토타입 체인에 있는 것인지 확인하려면 hasOwnProperty 메소드를 사용한다. 그리고 이 메소드는 Object.prototype으로 부터 상속받아 모든 객체가 가지고 있다.
+
+
hasOwnProperty메소드는 프로토타입 체인을 탐색하지 않고, 프로퍼티를 다룰수있는 유일한 방법이다.
hasOwnProperty 메소드는 어떤 프로퍼티가 자기 자신의 프로퍼티인지 아닌지 정확하게 알려주기 때문에 객체의 프로퍼티를 순회할때 꼭 필요하다. 그리고 프로토타입 체인 어딘가에 정의된 프로퍼티만을 제외하는 방법은 없다.
+
hasOwnProperty 메소드도 프로퍼티다
+
JavaScript는 hasOwnProperty라는 이름으로 프로퍼티를 덮어 쓸수도 있다. 그래서 객체 안에 같은 이름으로 정의된 hasOwnProperty가 있을 경우, 본래 hasOwnProperty의 값을 정확하게 얻고 싶다면 다른 객체의 hasOwnProperty 메소드를 빌려써야 한다.
+
var foo = {
+ hasOwnProperty: function() {
+ return false;
+ },
+ bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // 항상 false를 반환한다.
+
+// 다른 객체의 hasOwnProperty를 사용하여 foo 객체의 프로퍼티 유무를 확인한다.
+({}).hasOwnProperty.call(foo, 'bar'); // true
+
+// Object에 있는 hasOwnProperty를 사용해도 된다.
+Object.prototype.hasOwnProperty.call(obj, 'bar'); // true
+
결론
+
어떤 객체에 원하는 프로퍼티가 있는지 확인하는 가장 확실한 방법은 hasOwnProperty를 사용하는 것이다. for in loop에서 네이티브 객체에서 확장된 프로퍼티를 제외하고 순회하려면 hasOwnProperty와 함께 사용하길 권한다.
+
for in Loop
객체의 프로퍼티를 탐색할때 in 연산자와 마찬가지로 for in 문도 프로토타입 체인까지 탐색한다.
+
+
// Object.prototype을 오염시킨다.
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+ console.log(i); // bar와 moo 둘 다 출력한다.
+}
+
for in문에 정의된 기본 동작을 바꿀순 없기 때문에 루프 안에서 불필요한 프로퍼티를 필터링 해야한다. 그래서 Object.prototype의 hasOwnProperty메소드를 이용해 본래 객체의 프로퍼티만 골라낸다.
+
+
hasOwnProperty로 필터링 하기
+
// 위의 예제에 이어서
+for(var i in foo) {
+ if (foo.hasOwnProperty(i)) {
+ console.log(i);
+ }
+}
+
위와 같이 사용해야 올바른 사용법이다. hasOwnProperty 때문에 오직moo만 출력된다. hasOwnProperty가 없으면 이 코드는 Object.prototype으로 네이티브 객체가 확장될 때 에러가 발생할 수 있다.
+
따라서 Proptotype 라이브러리처럼 네이티브 객체를 프로토타입으로 확장한 프레임워크를 사용할 경우 for in 문에 hasOwnProperty를 사용하지 않을 경우 문제가 발생할 수 있다.
+
결론
+
hasOwnProperty를 항상 사용하길 권한다. 실제 코드가 동작하는 환경에서는 절대로 네이티브 객체가 프로토타입으로 확장됐다 혹은 확장되지 않았다를 가정하면 안된다.
+
함수
함수 선언과 함수 표현식
JavaScript에서 함수는 First Class Object다. 즉, 함수 자체가 또 다른 함수의 인자될 수 있다는 말이다. 그래서 익명 함수를 비동기 함수의 콜백으로 넘기는 것도 이런 특징을 이용한 일반적인 사용법이다.
+
함수 선언
+
function foo() {}
+
위와 같이 선언한 함수는 프로그램이 실행하기 전에 먼저 호이스트(Hoist) (스코프가 생성)되기 때문에 정의된 스코프(Scope) 안에서는 어디서든 이 함수를 사용할 수 있다. 심지어 함수를 정의하기 전에 호출해도 된다.
+
foo(); // 이 코드가 실행되기 전에 foo가 만들어지므로 잘 동작한다.
+function foo() {}
'var'문을 이용해 선언하는 경우, 코드가 실행되기 전에 'foo' 라는 이름의 변수를 스코프의 맨 위로 올리게 된다.(호이스트 된다) 이때 foo 값은 undefiend로 정의된다.
+
하지만 변수에 값을 할당하는 일은 런타임 상황에서 이루어지게 되므로 실제 코드가 실행되는 순간의 foo변수는 기본 값인 undefined이 된다.
+
이름있는 함수 표현식
+
이름있는 함수를 할당할때도 특이한 경우가 있다.
+
var foo = function bar() {
+ bar(); // 이 경우는 동작 하지만,
+}
+bar(); // 이 경우는 참조에러를 발생시킨다.
+
foo 함수 스코프 밖에서는 foo 변수 외에는 다른 값이 없기 때문에 bar는 함수 밖에서 사용할 수 없지만 함수 안에서는 사용할 수 있다. 이와 같은 방법으로 자바스크립트에서 어떤 함수의 이름은 항상 그 함수의 지역 스코프 안에서 사용할수있다.
+
this의 동작 원리
다른 프로그래밍 언어에서 this가 가리키는 것과 JavaScript에서 this가 가리키는 것과는 좀 다르다. this가 가리킬 수 있는 객체는 정확히 5종류나 된다.
+
Global Scope에서
+
this;
+
Global Scope에서도 this가 사용될 수 있고 이때에는 Global 객체를 가리킨다.
+
함수를 호출할 때
+
foo();
+
이때에도 this는 Global 객체를 가리킨다.
+
+
메소드로 호출할 때
+
test.foo();
+
이 경우에는 this가 test를 가리킨다.
+
생성자를 호출할 때
+
new foo();
+
new 키워드로 생성자를 실행시키는 경우에 이 생성자 안에서 this는 새로 만들어진 객체를 가리킨다.
+
this가 가리키는 객체 정해주기.
+
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // a = 1, b = 2, c = 3으로 넘어간다.
+foo.call(bar, 1, 2, 3); // 이것도...
+
Function.prototype의 call이나 apply 메소드를 호출하면 this가 무엇을 가리킬지 정해줄 수 있다. 호출할 때 첫 번째 인자로 this가 가리켜야 할 객체를 넘겨준다.
+
그래서 foo Function 안에서 this는 위에서 설명했던 객체 중 하나를 가리키는 것이 아니라 bar를 가리킨다.
+
+
대표적인 함정
+
this가 Global 객체를 가리키는 것도 잘못 설계된 부분 중 하나다. 괜찮아 보이지만 실제로는 전혀 사용하지 않는다.
+
Foo.method = function() {
+ function test() {
+ // 여기에서 this는 Global 객체를 가리킨다.
+ }
+ test();
+}
+
test 에서 this가 Foo를 가리킬 것으로 생각할 테지만 틀렸다. 실제로는 그렇지 않다.
+
test에서 Foo에 접근하려면 method에 Local 변수를 하나 만들고 Foo를 가리키게 하여야 한다.
+
Foo.method = function() {
+ var that = this;
+ function test() {
+ // 여기에서 this 대신에 that을 사용하여 Foo에 접근한다.
+ }
+ test();
+}
+
that은 this에 접근하기 위해 만든 변수다. closures와 함께 this의 값을 넘기는 데 사용할 수 있다.
+
Method할당 하기
+
JavaScript의 또다른 함정은 바로 함수의 별칭을 만들수 없다는 점이다. 별칭을 만들기 위해 메소드를 변수에 넣으면 자바스크립트는 별칭을 만들지 않고 바로 할당해 버린다.
+
var test = someObject.methodTest;
+test();
+
첫번째 코드로 인해 이제 test는 다른 함수와 똑같이 동작한다. 그래서 test 함수 내부의 this도 더이상 someObject를 가리키지 않는다. (역주: test가 methodTest의 별칭이라면 methodTest 함수 내부의 this도 someObject를 똑같이 가리켜야 하지만 test의 this는 더이상 someObject가 아니다.)
여기서 Counter는 increment 클로저와 get 클로저 두 개를 반환한다. 이 두 클로저는 Counter 함수 스코프에 대한 참조를 유지하고 있기 때문에 이 함수 스코프에 있는 count 변수에 계속 접근할 수 있다.
+
Private 변수의 동작 원리
+
JavaScript에서는 스코프(Scope)를 어딘가에 할당해두거나 참조할수 없기 때문에 스코프 밖에서는 count 변수에 직접 접근할 수 없다. 접근할수 있는 유일한 방법은 스코프 안에 정의한 두 클로저를 이용하는 방법밖에 없다.
+
var foo = new Counter(4);
+foo.hack = function() {
+ count = 1337;
+};
+
위 코드에서 foo.hack 함수는 Counter 함수 안에서 정의되지 않았기 때문에 이 함수가 실행되더라도 Counter 함수 스코프 안에 있는 count 값은 변하지 않는다. 대신 foo.hack 함수의 count는 Global 스코프에 생성되거나 이미 만들어진 변수를 덮어쓴다.
+
반복문에서 클로저 사용하기
+
사람들이 반복문에서 클로저를 사용할 때 자주 실수를 하는 부분이 있는데 바로 인덱스 변수를 복사할때 발생한다.
+
for(var i = 0; i < 10; i++) {
+ setTimeout(function() {
+ console.log(i);
+ }, 1000);
+}
+
이 코드는 0부터 9까지의 수를 출력하지 않고 10만 열 번 출력한다.
+
타이머에 설정된 익명 함수는 변수 i에 대한 참조를 들고 있다가 console.log가 호출되는 시점에 i의 값을 사용한다. console.log가 호출되는 시점에서 for loop는 이미 끝난 상태기 때문에 i 값은 10이 된다.
JavaScript의 모든 함수 스코프에는 arguments라는 특별한 변수가 있다. 이 변수는 함수에 넘겨진 모든 인자에 대한 정보가 담겨 있다.
+
+
arguments 객체는 Array가 아니다. 물론 length 프로퍼티도 있고 여러모로 Array와 비슷하게 생겼지만 Array.prototype을 상속받지는 않았다.
+
그래서 arguments에는 push, pop, slice 같은 표준 메소드가 없다. 일반 for문을 이용해 순회는 할수 있지만, Array의 메소드를 이용하려면 arguments를 Array로 변환해야 한다.
+
Array로 변환하기
+
다음 코드는 arguments에 있는 객체를 새로운 Array에 담아 반환한다.
+
Array.prototype.slice.call(arguments);
+
이 변환 과정은 느리기 때문에 성능이 중요한 부분에 사용하는 것은 별로 바람직하지 못 하다.
+
arguemnts 객체 넘기기
+
어떤 함수에서 다른 함수로 arguments 객체를 넘길 때에는 다음과 같은 방법을 권한다. (역주: foo 함수는 bar 함수 한번 랩핑한 함수다. )
+
function foo() {
+ bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+ // 내곡동에 땅이라도 산다.
+}
+
또 다른 방법으로는 함수를 랩핑하지 않고, 풀어서 call과 apply를 함께 사용하는 방법이 있다. (역주: 프로토타입에 있는 method를 호출하기 전에 Foo 객체 안에 있는 method로 한번더 필터링하는 효과가 있다. )
+
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+ console.log(this, a, b, c);
+};
+
+// "method"를 풀어 쓴(unbound) 버전
+// 이 Function의 인자: this, arg1, arg2...argN
+Foo.method = function() {
+
+ // 결과: Foo.prototype.method.call(this, arg1, arg2... argN)
+ Function.call.apply(Foo.prototype.method, arguments);
+};
+
일반 파라미터와 arguments 객체의 인덱스
+
일반 파라미터와 arguments 객체의 프로퍼티는 모두 getter와 setter를 가진다.
+
그래서 파라미터나 arguments 객체의 프로퍼티의 값을 바꾸면 둘 다 바뀐다.
+
function foo(a, b, c) {
+ arguments[0] = 2;
+ a; // 2
+
+ b = 4;
+ arguments[1]; // 4
+
+ var d = c;
+ d = 9;
+ c; // 3
+}
+foo(1, 2, 3);
+
성능에 대한 오해와 진실.
+
arguments 객체는 항상 만들어지지만 두가지 예외사항이 있다. arguments라는 이름으로 변수를 함수 안에 정의하거나 arguments 객체로 넘겨받는 인자중 하나라도 정식 인자로 받아서 사용하면 arguemnts 객체는 만들어지지 않는다. 하지만 뭐 이런 경우들은 어차피 arguments 객체를 안쓰겠다는 의미니까 상관 없다.
+
그리고 getter와 setter는 항상 생성되기 때문에 getter/setter를 사용하는 것은 성능에 별 영향을 끼치지 않는다. 예제처럼 단순한 코드가 아니라 arguments 객체를 다방면으로 활용하는 실제 코드에서도 마찬가지다.
+
+
그러나 예외도 있다. 최신 JavaScript 엔진에서 arguments.callee를 사용하면 성능이 확 떨어진다.
+
function foo() {
+ arguments.callee; // 이 함수를 가리킨다.
+ arguments.callee.caller; // 이 함수를 호출한 부모함수를 가리킨다.
+}
+
+function bigLoop() {
+ for(var i = 0; i < 100000; i++) {
+ foo(); // 원래 인라인 돼야 하는디...
+ }
+}
+
위 코드에서 'foo' 함수는 자기 자신과 자신을 호출한 함수를 알아야 하기 때문에 더이상 인라인되지 않는다. 이렇게 쓰면 인라인이 주는 성능상 장점을 포기해야 하는데다가 이 함수가 호출되는 상황(calling context)에 의존하게 돼 버려서 캡슐화(Encapsulation)도 해친다.
+(역주: 보통 코드가 컴파일 될때 코드를 인라인 시키면서 최적화 하는데, 위와 같이 arguments.callee나 caller를 사용하게 되면 런타임시에 해당 함수가 결정되므로 인라인 최적화를 할수가 없다.)
+
arguments.callee와 arguments.callee의 프로퍼티들은 절대 사용하지 말자!.
+
+
생성자
JavaScript의 생성자는 다른 언어들과 다르게 new 키워드로 호출되는 함수가 생성자가 된다.
+
생성자로 호출된 함수의 this 객체는 새로 생성된 객체를 가리키고, 새로 만든 객체의 prototype에는 생성자의 prototype이 할당된다.
+
그리고 생성자에 명시적인 return 구문이 없으면 this가 가리키는 객체를 반환한다.
+
function Foo() {
+ this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+ console.log(this.bla);
+};
+
+var test = new Foo();
+
위 코드는 new 키워드가 실행되는 시점에 Foo를 생성자로 호출하고 Foo.prototype을 새 객체의 prototype에 할당한다.
+
아래 코드와 같이 생성자에 명시적인 return 문이 있는 경우에는 반환하는 값이 객체인 경우에만 그 값을 반환한다.
test 함수 안에 있는 'foo' 변수에 var 구문을 빼버리면 Global Scope의 foo의 값을 바꿔버린다. '뭐 이게 뭐가 문제야'라고 생각될 수 있지만 수천 줄인 JavaScript 코드에서 var를 빼먹어서 생긴 버그를 해결하는 것은 정말 어렵다.
+
// Global Scope
+var items = [/* some list */];
+for(var i = 0; i < 10; i++) {
+ subLoop();
+}
+
+function subLoop() {
+ // Scope of subLoop
+ for(i = 0; i < 10; i++) { // var가 없다.
+ // 내가 for문도 해봐서 아는데...
+ }
+}
+
subLoop 함수는 전역 변수 i의 값을 변경해버리기 때문에 외부에 있는 for문은 subLoop을 한번 호출하고 나면 종료된다. 두 번째 for문에 var를 사용하여 i를 정의하면 이 문제는 생기지 않는다. 즉, 의도적으로 외부 스코프의 변수를 사용하는 것이 아니라면 var를 꼭 넣어야 한다.
이렇게 한줄로 바뀌면 log 함수가 함수를 반환할 가능성이 거의 없으므로 undefined is not a function이라는 TypeError가 발생한다.
+
결론
+
쎄미콜론은 반드시 사용해야 한다. 그리고 {}도 생략하지 않고 꼭 사용하는 것이 좋다. 한 줄밖에 안 되는 if / else 블럭에서도 꼭 사용해야 한다. 이 두 가지 규칙을 잘 지키면 JavaScript 파서가 잘못 해석하는 일을 미리 방지하고 코드도 튼튼해진다.
+
delete 연산자
간단히 말해서 전역 변수와 전역 함수 그리고 DontDelete 속성을 가진 자바스크립트 객체는 삭제할 수 없다.
+
Global 코드와 Function 코드
+
전역이나 함수 스코프에 정의한 함수나 변수는 모두 Activation 객체나 전역 객체의 프로퍼티다. 이 프로퍼티는 모두 DontDelete 속성을 가진다. 전역이나 함수 코드에 정의한 변수와 함수는 항상 DontDelete 프로퍼티로 만들어지기 때문에 삭제될 수 없다:
이 방법은 모든 주요 브라우저에서 문제없이 잘 동작하지만 ID가 항상 순차적이어야 한다고 표준에 명시된 것이 아니다. 그러므로 timeout ID를 모두 저장했다가 삭제하는 것이 가장 안전하다. 그러면 전부 깨끗하게 제거할 수 있다.
+
보이지 않게 사용되는 eval함수
+
setTimeout과 setInterval의 첫 파라미터로 문자열을 넘길 수 있다. 하지만 내부적으로 eval을 사용하는 것이기 때문에 절대 사용해서는 안된다.
+
+
function foo() {
+ // 이게 호출됨
+}
+
+function bar() {
+ function foo() {
+ // 이것은 절대 호출 안 됨
+ }
+ setTimeout('foo()', 1000);
+}
+bar();
+
이 경우 eval이 그냥(directly) 호출되는 것이 아니다. setTimeout에 인자로 넘어간 문자열은 전역 스코프에서 실행되기 때문에 bar함수 영역에 있는 지역 변수 foo가 실행되는 것이 아니라 전역 스코프에 있는 foo가 실행된다.
+
함수에 파라미터를 넘겨야 하면 스트링을 사용하지 말아야 한다.
+
function foo(a, b, c) {}
+
+// 절대 사용하면 안 됨
+setTimeout('foo(1, 2, 3)', 1000)
+
+// 대신 익명 함수를 사용하는 게 좋다.
+setTimeout(function() {
+ foo(a, b, c);
+}, 1000)
+
+
결론
+
setTimeout과 setInterval함수에 문자열 인자를 절대 사용해서는 안된다. 핸들러 함수에 인자를 넘기는 코드도 절대 좋은 코드가 아니다. 익명 함수을 사용해서 호출해야 한다.
+
그리고 setInterval은 해당 핸들러가 블럭되든 말든 상관하지 않기 때문에 되도록이면 쓰지말자.
+
\ No newline at end of file
diff --git a/pl/index.html b/pl/index.html
new file mode 100644
index 0000000..1afff47
--- /dev/null
+++ b/pl/index.html
@@ -0,0 +1,1441 @@
+JavaScript Garden
JavaScript Garden jest publikowany w ramach licencji MIT i kod źródłowy znajduje
+się na serwerze GitHub. Jeśli znajdziesz jakieś błędy lub literówki, zgłoś proszę
+problem lub rozwiąż go i zgloś pull request ze swojego repozytorium.
+Możesz nas także znaleźć w pokoju JavaScript na chacie Stack Overflow.
+
Obiekty
Wykorzystanie obiektów i ich właściwości
Wszystko w JavaScripcie zachowuje sie jak obiekt, z dwoma wyjątkami
+null oraz undefined.
Popularnym błędem jest traktowanie literałów liczbowych jak obiektu.
+Spowodowane jest to specyfiką parsera JavaScript, który interpretuje kropkę
+po literale liczbowym jako rozdzielenie części całkowitej od części ułamkowej
+liczby.
+
2.toString(); // wyrzuca błąd SyntaxError
+
Istnieje kilka rozwiązań, dzieki którym literał liczbowy będzie zachowywał się
+jak obiekt.
+
2..toString(); // druga kropka jest poprawnie rozpoznana
+2 .toString(); // zauważ, że pozostawiona jest spacja przed kropką
+(2).toString(); // 2 zostanie najpierw zewaluowane
+
Obiekty jako typy danych
+
Obiekty w języku JavaScript mogą być używana jako tablice asocjacyjne,
+ponieważ obiekty składają się głównie z mapowań pomiędzy nazwanymi właściwościami (kluczami)
+a wartościami dla tych atrybutów.
+
Używając literału obiektu - notacji {} - istnieje możliwość stworzenia obiektu prostego.
+Ten nowy obiekt bedzie dziedziczył z Object.prototype oraz
+nie bedzie posiadał żadnych własnych właściwości.
+
var foo = {}; // nowy, pusty obiekt
+
+// nowy obiekt z właściwością test o wartości 12
+var bar = {test: 12};
+
Dostęp do właściwości
+
Właściwości obiektu można uzyskać na dwa sposoby - poprzez notację z kropką
+lub z nawiasami kwadratowymi.
Obie notacje są identyczne w swoim działaniu, z tą tylko różnicą, że notacja z nawiasami
+kwadratowymi pozwala na dynamiczne dodawanie właściwości i nie prowadzi do wyrzucenia
+błędu podczas odczytu nieistniejącej właściwości.
+
Usuwanie właściwości
+
Jedynym sposobem na faktycze usunięcie własności z obiektu jest użycie operatora
+delete. Ustawienie własności na undefined lub null usunie tylko wartość
+związaną z własnością, ale nie usunie to klucza (nazwy własności) z obiektu.
+
var obj = {
+ bar: 1,
+ foo: 2,
+ baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ console.log(i, '' + obj[i]);
+ }
+}
+
Powyższy kod wypisuje dwie linie - bar undefined i foo null. Tylko własność baz
+została usunięta i dlatego nie została wypisana.
+
Notacja właściwości
+
var test = {
+ 'case': 'jestem słowem kluczowym, więc muszę być w cudzysłowie',
+ delete: 'tak samo jak ja' // wyrzuca błąd SyntaxError
+};
+
Nazwy właściwości obiektu mogą być zarówno zapisane jako tekst (bez cudzysłowów
+lub apostrofów) lub jako string (w cudzisłowach lub apostrofach).
+Ze względu na kolejne niedociągnięcie w parserze JavaScript,
+powyższy kod wyrzuci błąd SyntaxError dla implementacji JavaScript ponizej ECMAScript 5.
+
Ten błąd wynika z faktu, że delete jest słowem kluczowym, dlatego musi zostać
+zapisany jako string (z cudzysłowami lub apostrofami), aby zapewnić, że zostanie
+to poprawnie zinterpretowane przez starsze silniki języka JavaScript.
+
Prototyp
JavaScript nie posiada klasycznego modelu dziedziczenia. Zamiast tego
+dziedziczenie jest realizowane poprzez prototypy.
+
Choć jest to często uważane za jedną ze słabości języka JavaScript,
+prototypowy model dziedziczenia, jest w rzeczywistości potężniejszy od klasycznego
+modelu. Na przykład stworzenia klasycznego modelu na podstawie modelu prototypowego
+jest dość proste, podczas gdy zrobienie odwrotnego przekształcenie to o wiele trudniejsze zadanie.
+
Ze względu na fakt, że w JavaScript jest w zasadzie jedynym powszechnie stosowanym
+językiem, któy posiada prototypowy model dziedziczenia, dostosowanie się do różnic pomiędzy
+tymi dwoma modelami wymaga trochę czasu.
+
Pierwszą znaczącą różnicą jest to, że dziedziczenie w JavaScript odbywa się za pomocą
+tak zwanych łańcuchów prototypów.
+
+
function Foo() {
+ this.value = 42;
+}
+Foo.prototype = {
+ method: function() {}
+};
+
+function Bar() {}
+
+// Ustawienie prototypu Bar na nową instancję Foo
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Hello World';
+
+// Upewniamy się, że Bar jest ustawiony jako rzeczywisty konstruktor
+Bar.prototype.constructor = Bar;
+
+var test = new Bar() // tworzymy nową instancję Bar
+
+// The resulting prototype chain
+test [instance of Bar]
+ Bar.prototype [instance of Foo]
+ { foo: 'Hello World' }
+ Foo.prototype
+ { method: ... }
+ Object.prototype
+ { toString: ... /* etc. */ }
+
W powyższym przykładzie obiekt test będzie dziedziczył z obydwu, tj.
+Bar.prototyp i Foo.prototyp, stąd będzie miał dostęp do funkcji method,
+która była zdefiniowana w Foo. Ponadto obiekt będzie miał dostęp do
+właściwości value, która jest jednyną instancją Foo i stała się jego prototypem.
+Należy pamiętać, że new Barnie tworzy nowej instancji Foo,
+tylko wykorzystuje instancję, która jest przypisana do własności prototype.
+Zatem Wszystkie instancje Bar będą dzieliły tą samą własność value.
+
+
Wyszukiwanie własności
+
Podczas dostępu do właściwości obiektu JavaScript przejdzie w górę łańcucha
+prototypów, dopóki nie znajdzie właściwości bez nazwy.
+
Gdy przeszukiwanie dotrze do końca (szczytu) łańcucha, mianowicie Object.prototype
+i nadal nie znajdzie określonej właściwości, to zwróci wartość
+undefined.
+
Właściwość prototype
+
Podczas gdy właściwość prototype jest używana przez język do budowania łańcucha
+prototypów, istnieje możliwość przypisania do niej dowolnej wartości. Jednakże
+prymitywne typy będą po prostu ignorowanie, jeżeli zostaną ustawione jako prototype.
+
function Foo() {}
+Foo.prototype = 1; // nie ma wpływu
+
Przypisywanie obiektów, jak pokazano w powyższym przykładzie, zadziała i pozwala
+na dynamiczne tworzenie łańcuchów prototypów.
+
Wydajność
+
Czas wyszukiwania właściwości, które są na końcu łańcucha prototypów może mieć
+negatywny wpływ na wydajność krytycznych części kodu. Dodatkowo, próba dostępu
+do nieistniejącej właściwości zawsze spowoduje przeszukanie całego łańcucha prototypów.
+
Również podczas iteracji po właściwościach obiektu
+każda właściwość, która znajduje się w łańcuchu prototypów (niezależnie
+na jakim znajduje się poziomie) zostanie wyliczona.
+
Rozszerzanie natywnych prototypów
+
Rozszerzanie Object.prototype lub innego prototypu wbudowanych typów jest jednym z
+najczęściej nadużywanej częsci języka JavaScript.
+
Technika ta nazywana jest monkey patching i łamie zasady enkapsulacji.
+Mimo to jest szeroko rozpowszechniona w frameworkach takich jak Prototype.
+Nie ma jednak dobrego powodu, aby zaśmiecać wbudowane typy poprzez wzbogacanie ich o
+niestandardowe funkcjonalności.
+
Jedynym dobrym powodem do rozszerzania wbudowanych prototypów jest portowanie funkcjonalności znajdujących sie w nowszych silnikach JavaScript, np. Array.forEach
+
Wnioski
+
Zanim przystąpi się do pisania skomplikowanego kodu korzystającego z dziedziczania, należy całkowicie zrozumieć prototypowy model dziedziczenia. Ponadto trzeba uważać
+na długość łańcucha prototypów i w razie potrzeby zmniejszać ilość dziedziczeń,
+aby uniknąć problemów z wydajnością. Natywne prototypy nigdy nie powinny być
+rozszerzane, chyba że ze względu na wprowadzanie kompatybilności z nowszymi silnikami
+JavaScript.
+
hasOwnProperty
W celu sprawdzenia, czy dana właściwość została zdefiniowana w tym obiekcie, a nie
+w łańcuchu prototypów, niezbędne jest skorzystanie z metody
+hasOwnProperty, której wszystkie obiekty dziedziczą z Object.prototype.
+
+
hasOwnProperty jest jedyną metodą w języku JavaScript, która operuje na właściwościach
+i nie przegląda całego łańcucha prototypów.
Tylko hasOwnProperty da prawidłowy i oczekiwany rezultat. Jest to istotne podczas
+iteracji po właściwościach obiektu. Nie ma innego sposobu na ominięcie
+właściwości, która nie została zdefiniowana przez ten konkretny obiekt,
+ale gdzieś indziej w łańcuchu prototypów.
+
hasOwnProperty jako właściwość
+
JavaScript nie chroni właściwości o nazwie hasOwnProperty, zatem istnieje
+możliwość, że obiekt będzie posiadać tak nazwaną właściwość. Konieczne jest użycie
+zewnętrznegohasOwnProperty, aby otrzymać poprawne rezultaty.
+
var foo = {
+ hasOwnProperty: function() {
+ return false;
+ },
+ bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // zawsze zwraca false
+
+// Została użyta metoda innego obiektu i wywołana z konkekstem
+// `this` ustawionym na foo
+({}).hasOwnProperty.call(foo, 'bar'); // true
+
Wnioski
+
Jedyną metodą służącą do sprawdzenia istnienia jakiejś właściwości w konkretnym
+obiekcie jest metoda hasOwnProperty. Zaleca się korzystać z hasOwnProperty w
+każdejpętli for in. Pozwoli to uniknąć błędów pochodzących
+z rozszerzonych natywnych prototypów.
+
Pętla for in
Podobnie jak operator in, pętla for in przeszukuje łańcuch prototypów
+podczas iteracji po właściwościach obiektu.
+
+
// Zatrucie Object.prototype
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+ console.log(i); // wyświetla obie właściwości: bar i moo
+}
+
Ponieważ zmiana zachowania pętli for in nie jest możliwa, niezbędne
+jest odfiltrowanie niechcianych właściwości wewnątrz ciała pętli, korzystając
+z metody hasOwnProperty z Object.prototype.
+
+
Filtrowania przy użyciu hasOwnProperty
+
// foo z przykładu powyżej
+for(var i in foo) {
+ if (foo.hasOwnProperty(i)) {
+ console.log(i);
+ }
+}
+
To jest jedyna poprawna wersja, której należy używać. Ze względu na użycie
+hasOwnProperty zostanie wypisane jedyniemoo. Gdy opuścimy hasOwnProperty,
+kod będzie podatny na błędy, gdy natywne prototypy (np. Object.prototype)
+zostaną rozszerzone.
+
Prototype jest jednym z popularniejszych frameworków, które dokonują
+takiego rozszerzenia. Używanie tego frameworku oraz nie stosowanie w pętli for in
+metody hasOwnProperty gwarantuje błędy w wykonaniu.
+
Wnioski
+
Zaleca się, aby zawsze używać metody hasOwnProperty. Nigdy nie powinno się dokonywać
+żadnych założeń na temat środowiska, w którym kod będzie wykonywany ani tego, czy
+natywne prototypy zostały rozszerzone, czy nie.
+
Funkcje
Deklaracje funkcji i wyrażenia funkcyjne
Funcje w języku JavaScript są typami pierwszoklasowymi, co oznacza, że mogą
+być przekazywane jak każda inna wartość. Jednym z typowych zastosowań tej cechy
+jest przekazywanie anonimowej funkcji jako callback do innej, prawdopodobnie
+asynchronicznej funkcji.
+
Deklaracja funkcji
+
function foo() {}
+
Powyższa funkcja zostaje wyniesiona zanim program wystartuje. Dzięki temu
+jest dostępna wszędzie w ramach zasięgu, w którym została zadeklarowana,
+nawet, jeżeli ta funkcja została wywołana przed faktyczną definicją w kodzie źródłowym.
+
foo(); // Działa ponieważ definicja funkcji została wyniesiona
+ // na początek zasięgu przed uruchomieniem kodu
+function foo() {}
+
Wyrażenie funkcyjne
+
var foo = function() {};
+
Ten przykład przypisuje nienazwaną i anonimową funkcję do zmiennej foo.
Ze względu na fakt, że deklaracja var wynosi zmienną foo na początek zasięgu
+zanim kod faktycznie zostanie uruchomiony, foo będzie zdefiniowane kiedy skrypt
+będzie wykonywany.
+
Ale ponieważ przypisania robione są dopiero podczas wykonania, wartość foo będzie
+ustawiona na domyślną wartość undefined zanim powyższy kod
+zostanie uruchomiony.
+
Nazwane wyrażenia funkcyjne
+
Kolejnym specjalnym przypadkiem jest przypisanie nazwanej funkcji.
+
var foo = function bar() {
+ bar(); // Działa
+}
+bar(); // wyrzuca ReferenceError
+
W zewnętrznym zakresie bar nie będzie dostępna, ponieważ funkcja zostaje
+przypisana do foo, jednakże w wewnętrznym zakresie bar będzie dostępna.
+Jest to spowodowane tym, jak działa rozwiązywanie nazw
+w języku JavaScript. Nazwa funkcji jest zawsze dostępna w lokalnym
+zakresie tej funkcji.
+
Jak działa this
JavaScript posiada inną koncepcję odnośnie tego na co wskazuje słowo kluczowe
+this, niż większość innych języków programowania. Istnieje dokładnie
+pięć różnych sytuacji, w których wartość this jest przypisana w języku JavaScript.
+
Zasięg globalny
+
this;
+
Używanie this w globalnym zasięgu, zwróci po prostu referencję do obiektu global.
+
Wywołanie funkcji
+
foo();
+
Tutaj this również będzie wkazywało na obiekt global
+
+
Wywoływanie metody
+
test.foo();
+
W tym przypadku this będzie wskazywało na test.
+
Wywołanie konstruktora
+
new foo();
+
Wywołanie funkcji, które jest poprzedzone słowem kluczowym new, zachowuje się
+jak konstruktor. Wewnątrz funkcji this będzie
+wskazywało na nowo utworzony obiekt.
+
Jawne ustawienie this
+
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // tablica zostanie zamieniona w to co poniżej
+foo.call(bar, 1, 2, 3); // rezultat a = 1, b = 2, c = 3
+
Używając metod call lub apply z prototypu Function.prototype, wartość this
+wewnątrz wołanej funkcji zostanie jawnie ustawiona na pierwszy argument przekazany
+podczas wywołania tych metod.
+
Zatem w powyższym przykładzie przypadek Wywoływanie metody nie będzie miał
+miejsca i this wewnątrz foo będzie wskazywać na bar.
+
+
Częste pułapki
+
Mimo iż Większość z tych przypadków ma sens, to pierwszy przypadek powinien być
+traktorany jako błąd podczas projektowania języka i nigdy nie wykorzystywany
+w praktyce.
+
Foo.method = function() {
+ function test() {
+ // wewnątrz tej funkcji this wskazuje na obiekt global
+ }
+ test();
+}
+
Powszechnym błędem jest myślenie, że this wewnątrz test wskazuje na Foo,
+podczas gdy w rzeczywistości tak nie jest.
+
Aby uzyskać dostęp do Foo wewnątrz test, niezbędne jest stworzenie wewnątrz
+metody lokalnej zmiennej, która będzie wskazywała na Foo.
+
Foo.method = function() {
+ var that = this;
+ function test() {
+ // Należy używać that zamiast this wewnątrz tej funkcji
+ }
+ test();
+}
+
that jest zwykłą zmienną, ale jest to powszechnie stosowana konwencja otrzymywania wartości zewnętrznego this. W połączeniu z domknięciami(closures),
+jest to sposób na przekazywanie wartości this wokół.
+
Metody przypisywania
+
Kolejną rzeczą, która nie działa w języku JavaScript, jest nadawanie aliasów
+funkcjom, co oznacza przypisanie metody do zmiennej.
+
var test = someObject.methodTest;
+test();
+
Podobnie jak w pierwszym przypadku test zachowuje się jak wywołanie zwykłej
+funkcji, a zatem wewnątrz funkcji this już nie będzie wskazywało someObject.
+
Podczas gdy późne wiązanie this może się na początku wydawać złym pomysłem,
+to w rzeczywistości jest to rzecz, która sprawia, że
+dziedziczenie prototypowe działa.
Kiedy metoda method zostanie wywołana na instancji Bar, this będzie
+wskazywało właśnie tę instancję.
+
Domknięcia i referencje
Jedną z najpotężniejszych funkcjonalności języka JavaScript są domknięcia.
+Oznacza to że zasięg zawsze posiada dostęp do zewnętrznego zasięgu, w którym
+został zdefiniowany. Ponieważ zasięg w JavaScript można definiować tylko poprzez
+funkcję, wszystkie funkcje domyślnie zachowują się jak domknięcia.
Tutaj Counter zwraca dwa domknięcia: funkcję increment oraz funkcję get.
+Obie te funkcje trzymają referencję do zasięgu Counter, a co za tym idzie
+zawsze posiadają dostęp do zmiennej count tak, jakby ta zmienna była zdefiniowana
+w zasięgu tych funkcji.
+
Dlaczego zmienne prywatne działają?
+
Ponieważ nie ma możliwości wskazania lub przypisania zasięgu w JavaScript,
+nie istnieje sposób, aby uzyskać dostęp do zmiennej count z zewnątrz.
+Wykorzystanie tych dwóch domknięć jest jedynym sposobem na interakcję z tą zmienną.
+
var foo = new Counter(4);
+foo.hack = function() {
+ count = 1337;
+};
+
Powyższy kod nie zmieni wartości zmiennej count wewnątrz zasięgu Counter,
+ponieważ foo.hack nie została zadeklarowana wewnątrz tego konkretnego zasięgu.
+Zamiast tego funkcja utworzy lub nadpisze globalną zmienną count.
+
Domknięcia wewnątrz pętli
+
Jednym z częstrzych błędów jest wykorzystywanie domknięć wewnątrz pętli,
+aby wartość zmiennej po której odbywa się iteracja była kopiowana do
+wewnętrznej funkcji.
+
for(var i = 0; i < 10; i++) {
+ setTimeout(function() {
+ console.log(i);
+ }, 1000);
+}
+
Powyższy kod nie wypisze numerów od 0 do 9, ale wypisze
+dziesięć razy liczbę 10.
+
Anonimowa funkcja trzyma wskaźnik do zmiennej i i podczas uruchomienia
+console.log, pętla for już zakończyła działanie i wartość zmiennej i
+została ustawiona na 10.
+
Aby otrzymać zamierzony efekt, niezbędne jest skopiowanie wartości
+zmiennej i.
+
Unikanie problemu z referencją
+
Aby skopiować wartość zmiennej, po której iterujemy w pętli, należy skorzystać
+z anonimowego wrappera.
Zewnętrzna anonimowa funkcja zostanie wywołana od razu z parametrem i
+jako pierwszym argumentem oraz otrzyma kopię wartości zmiennej i jako
+zmienną e.
+
Anonimowa funkcja która zostaje przekazana do setTimeout teraz posiada
+referencję do zmiennej e, która nie zostanie zmieniona przez pętle for.
+
Istnieje jeszcze jeden sposób na osiągnięcie tego samego efektu. Należy zwrócic
+fukcję z anonimowego wrappera, wówczas kod będzie zachowywał się jak ten
+wcześniejszy.
Każdy zasięg funkcyjny w języku JavaScript ma dostęp do specjalnej zmiennej arguments.
+Ta zmienna trzyma listę wszystkich argumentów przekazanych do funkcji.
+
+
Obiekt argumentsnie jest typu Array. Mimo że posiada pewne cechy
+semantyki tablic - właściwość length - to w rzeczywistości nie dziedziczy
+on z Array.prototype, tylko z Object.
+
Ze względu na to, na obiekcie argumentsnie można używać standardowych dla tablic metod,
+takich jak push, pop czy slice. Mimo że iteracja przy pomocy
+pętli for działa dobrze, to aby skorzystać ze standardowych metod tablicowych
+należy skonwertować arguments do prawdziwego obiekt Array.
+
Konwersja do tablicy
+
Poniższy kod zwróci nowy obiekt Array zawierający wszystkie elementy
+obiektu arguments.
+
Array.prototype.slice.call(arguments);
+
Jednakże konwersja ta jest wolna i nie jest zalecana w sekcjach,
+które mają duży wpływ na wydajność.
+
Przekazywanie argumentów
+
Zalecany sposób przekazywania argumentów z jednej funkcji do następnej
+wyglada następująco:
+
function foo() {
+ bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+ // do stuff here
+}
+
Kolejną sztuczką jest użycie razem call i apply w celu stworzenia
+szybkich i nieograniczonych wrapperów.
+
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+ console.log(this, a, b, c);
+};
+
+// Stworzenie nieograniczoną wersję metody "method"
+// która przyjmuje parametry: this, arg1, arg2...argN
+Foo.method = function() {
+
+ // Rezultat: Foo.prototype.method.call(this, arg1, arg2... argN)
+ Function.call.apply(Foo.prototype.method, arguments);
+};
+
Parametry formalne i indeksy argumentów
+
Obiekt arguments tworzy funkcje getter i setter nie tylko dla swoich
+właściwości, ale również dla parametrów formalnych funkcji.
+
W rezultacie zmiana wartości parametru formalnego zmieni również wartość
+odpowiadającemu mu wpisowi w obiekcie arguments. Zachodzi to również w drugą stronę.
+
function foo(a, b, c) {
+ arguments[0] = 2;
+ a; // 2
+
+ b = 4;
+ arguments[1]; // 4
+
+ var d = c;
+ d = 9;
+ c; // 3
+}
+foo(1, 2, 3);
+
Mity i prawdy o wydajności
+
Obiekt arguments jest tworzony zawsze, z wyjątkiem dwóch przypadków, gdy
+zmienna o takiej nazwie jest zdefiniowana wewnątrz funkcji lub jeden z parametrów
+formalnych funkcji ma taką nazwę. Nie ma znaczenia czy obiekt arguments jest
+używany czy nie.
+
Zarówno gettery jak i settery są zawsze tworzone, zatem używanie ich nie ma
+praktycznie żadnego wpływu na wydajność. Zwłaszcza w rzeczywistym kodzie, który
+wykorzystuje coś więcej niż tylko prosty dostęp do właściwości obiektu arguments.
+
+
Jednakże, istnieje jeden przypadek w którym wydajność drastycznie spada w
+nowoczesnych silnikach JavaScript. Ten przypadek to wykorzystanie
+arguments.callee.
+
function foo() {
+ arguments.callee; // operowanie na obiekcie funkcji
+ arguments.callee.caller; // i obiekcie funkcji wywołującej
+}
+
+function bigLoop() {
+ for(var i = 0; i < 100000; i++) {
+ foo(); // Normalnie zostałaby wykorzystana metoda inline
+ }
+}
+
W powyższym przykładzie foo nie może zostać wykorzystana metoda inline
+ponieważ potrzebne są nie tylko informacje na własny temat ale również
+na temat funkcji wywołującej. Takie użycie nie tylko uniemożliwia
+inlining i korzyści z niego wynikające, ale też łamie zasady enkapsulacji,
+ponieważ ta funkcja jest zależna od kontekstu w jakim została wywołana.
+
Mocno zalecane jest aby nigdy nie korzystać z arguments.callee
+i żadnej jej własności.
+
+
Konstruktory
Konstruktory w JavaScript również wyglądają inaczej niż innych językach. Każde
+wywołanie funkcji, które jest poprzedone słowem kluczowym new, zachowuje się
+jak konstruktor.
+
Wewnątrz konstruktora - wywoływanej fukcji - wartość this wskazuje na
+nowo utworzony obiekt Object. Prototyp prototype tego
+nowego obiektu będzie wskazywał na prototyp prototype obiektu fukcji,
+która została wywołana jako konstruktor.
+
Jeżeli wywołana funkcja nie posiada jawnej deklaracji return, wówczas
+fukcja domyślnie zwraca wartość this - nowy obiekt.
+
function Foo() {
+ this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+ console.log(this.bla);
+};
+
+var test = new Foo();
+
Powyżej wywołana została funkcja Foo jako konstruktor oraz ustawia
+nowo utworzonemu obiektowi właściwość prototype na Foo.prototype.
+
W tym przypadku jawna deklaracja return w funkcji zwraca wartość
+ustawioną w deklaracji, ale tylko jeżeli zwracaną wartością jest
+obiekt Object.
+
function Bar() {
+ return 2;
+}
+new Bar(); // nowy obiekt
+
+function Test() {
+ this.value = 2;
+
+ return {
+ foo: 1
+ };
+}
+new Test(); // zwrócony obiekt
+
Jeżeli słowo kluczowe new zostanie pominięte, funkcja nie zwróci nowego
+obiektu.
+
function Foo() {
+ this.bla = 1; // zostanie ustawiona w obiekcie global
+}
+Foo(); // undefined
+
Mimo że powyższy kod może zadziałać w pewnych przypadkach, w związku
+z działaniem this w języku JavaScript, to jako
+wartość this zostanie wykorzystany obiekt global.
+
Fabryki
+
Aby móc ominąć słowo kluczowe new, konstruktor musi jawnie zwracać wartość.
Oba wywołania Bar zwrócą tę samą rzecz, nowo utworzony obiekt, który posiada
+właściwość nazwaną method i dla którego Bar jest Domknięciem.
+
Należy również pamiętać, że wywołanie new Bar()nie ma wpływu na
+prototyp zwróconego obiektu (prototypem będzie object.prototype a nie Bar.prototype).
+Kiedy prototyp zostanie przypisany do nowo utworzonego obiektu, Bar nidgy
+nie zwróci tego nowego obiektu Bar, tylko literał obiektu, który jest po
+słowie kluczowym return.
+
W powyższym przykładzie nie ma żadnej różnicy w działaniu pomiędzy użyciem
+i nieużyciem słowa kluczowego new.
+
Tworzenie nowych obiektów korzystając z fabryk
+
Często zaleca się nie korzystać z operatora new, ponieważ zapominanie
+o jego stosowaniu może prowadzić do błędów.
+
W celu stworzenia nowego obiektu, powinno się używać fabryki i konstruować
+nowy obiekt wewnątrz tej fabryki.
Mimo że powyższy kod jest odporny na brak słowa kluczowego new i ułatwia
+korzystanie ze zmiennych prywatnych, to posiada
+pewne wady.
+
+
Zużywa więcej pamięci, ponieważ tworzony obiekt nie współdzieli metod
+poprzez prototyp.
+
Aby móc dziedziczyć fabryka musi skopiować wszystkie metody z dziedziczonego
+obiektu lub przypisać ten obiekt, z którego się dziedziczy, jako prototyp
+do nowo utworzonego obiektu.
+
Porzucenie łańcucha prototypów tylko ze względu na opuszczone słowo kluczowe
+new jest sprzeczne z duchem języka.
+
+
Wnioski
+
Pominięcie słowa kluczowego new może prowadzić do błędów, ale na pewno nie
+powinno to być powodem odrzucenia używania prototypów w ogóle. Sprowadza się to
+do wyboru rozwiązania, które bardziej pasuje do potrzeb aplikacji. Szczególnie
+ważne jest, aby wybrać określony styl tworzenia obiektów i trzymać się go.
+
Zasięg zmiennych i przestrzenie nazw
Mimo że JavaScript radzi sobie dobrze ze składnią opisującą dwa pasujące
+nawiasy klamrowe jako blok, to jednak nie wspiera zasięgu blokowego.
+Jedynym zasięgiem jaki istnieje w JavaScript jest zasięg funkcyjny.
+
function test() { // definiuje zasięg (scope)
+ for(var i = 0; i < 10; i++) { // nie definiuje zasięgu (scope)
+ // count
+ }
+ console.log(i); // 10
+}
+
+
W JavaScripcie nie ma również przestrzeni nazw, co oznacza, że wszystko jest
+definiowane w jednej globalnie współdzielonej przestrzeni nazw.
+
Z każdym odwołaniem do zmiennej, JavaScript przeszukuje w górę wszystkie zasięgi
+dopóki nie znajdzie tej zmiennej. W przypadku, gdy przeszukiwanie dotrze do globalnego
+zasięgu i nadal nie znajdzie żądanej nazwy, to wyrzuca błąd ReferenceError.
+
Zmora globalnych zmiennych
+
// script A
+foo = '42';
+
+// script B
+var foo = '42'
+
Powyższe dwa skrypty nie dają tego samego efektu. Skrypt A definiuje zmienną
+nazwaną foo w globalnym zasięgu, natomiast skrypt B definiuje foo
+w aktualnym zasięgu.
+
Jeszcze raz, to wcale nie daje tego samego efektu. Nie użycie var może mieć
+poważne konsekwencje.
Pominięcie słowa var w deklaracji wewnątrz funkcji test nadpisze wartość
+zmiennej globalnej foo. Mimo że nie wygląda to na początku na duży problem,
+posiadanie wielu tysięcy linii kodu w JavaScript i nie korzystanie z var
+wprowadzi straszne i trudne do wyśledzenia błędy.
+
// globalny zasięg
+var items = [/* jakaś lista */];
+for(var i = 0; i < 10; i++) {
+ subLoop();
+}
+
+function subLoop() {
+ // scope of subLoop
+ for(i = 0; i < 10; i++) { // brakuje słowa var w deklaracji
+ // do amazing stuff!
+ }
+}
+
Zewnętrzna pętla zakończy działanie po pierwszym wywołaniu subLoop, ponieważ
+subLoop nadpisuje wartość globalnej zmiennej i. Użycie var w drugiej pętli
+for pozwoliłoby łatwo uniknąć problemu. Słowo kluczowe var nie powinno być
+nigdy pominięte w deklaracji, chyba że pożądanym skutkiem jest wpłynięcie na
+zewnętrzny zasięg.
+
Lokalne zmienne
+
Jedynym źródłem zmiennych lokalnych w JavaScripcie są parametry funkcji
+oraz zmienne zadeklarowane poprzez deklaracje var wewnątrz funkcji.
+
// globalny zasięg
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+ // lokalny zasięg fukcji test
+ i = 5;
+
+ var foo = 3;
+ bar = 4;
+}
+test(10);
+
Zmienne foo oraz i są lokalnymi zmiennymi wewnątrz zasiegu funkcji test,
+natomiast przypisanie wartości do bar nadpisze zmienną globalną o tej samej nazwie.
+
"Hoisting" - wywindowanie, podnoszenie
+
JavaScript winduje deklaracje. Oznacza to, że zarówno deklaracja ze słowem
+kluczowym var jak i deklaracje funkcji function zostaną przeniesione na
+początek otaczającego zasięgu.
+
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+ if (false) {
+ goo = 1;
+
+ } else {
+ var goo = 2;
+ }
+ for(var i = 0; i < 100; i++) {
+ var e = data[i];
+ }
+}
+
Powyższy kod zostanie przekształcony przed rozpoczęciem wykonania. JavaScript
+przeniesie deklarację zmiennej var oraz deklarację funkcji function na szczyt
+najbliższego zasięgu.
+
// deklaracje var zostaną przeniesione tutaj
+var bar, someValue; // ustawione domyślnie na 'undefined'
+
+// deklaracje funkcji zostaną również przeniesione na górę
+function test(data) {
+ var goo, i, e; // brak blokowego zasięgu spowoduje przeniesienie tutaj
+ if (false) {
+ goo = 1;
+
+ } else {
+ goo = 2;
+ }
+ for(i = 0; i < 100; i++) {
+ e = data[i];
+ }
+}
+
+bar(); // powoduje błąd TypeError ponieważ bar jest nadal 'undefined'
+someValue = 42; // przypisania nie zostają zmienione przez 'hoisting'
+bar = function() {};
+
+test();
+
Brak blokowego zasięgu nie tylko przeniesie deklaracje var poza ciało pętli,
+ale również spowoduje, że niektóre porównania if staną się nieintuicyjne.
+
W oryginalnym kodzie instrukcja warunkowa if zdaje się modyfikować zmienną
+globalnągoo, podczas gdy faktycznie modyfikuje ona zmienną lokalną - po tym
+jak zostało zastosowane windowanie (hoisting).
+
Bez wiedzy na temat podnoszenia (hoistingu), poniższy kod może sprawiać wrażenie,
+że zobaczymy błąd ReferenceError.
+
// sprawdz czy SomeImportantThing zostało zainicjalizowane
+if (!SomeImportantThing) {
+ var SomeImportantThing = {};
+}
+
Oczywiście powyższy kod działa ze względu na fakt, że deklaracja var zostanie
+przeniesiona na początek globalnego zasięgu.
+
var SomeImportantThing;
+
+// inny kod który może ale nie musi zainicjalizować SomeImportantThing
+
+// upewnienie sie, że SomeImportantThing zostało zainicjalizowane
+if (!SomeImportantThing) {
+ SomeImportantThing = {};
+}
+
Kolejność rozwiązywania nazw
+
Wszystkie zasięgi w JavaScripcie, włączając globalny zasięg, posiadają
+zdefiniowaną wewnątrz specjalną nazwę this, która wskazuje
+na aktualny obiekt.
+
Zasięg funkcyjny posiada również zdefiniowaną wewnętrznie nazwę
+arguments, która zawiera listę argumentów przekazaną do
+funkcji.
+
Na przykład, kiedy próbujemy odczytać zmienną foo wewnątrz zasięgu funkcji,
+JavaScript będzie szukać nazwy w określonej kolejności:
+
+
Jeżeli wewnątrz aktualnego zasięgu znajduje się deklaracja var foo skorzystaj z niej.
+
Jeżeli jeden z parametrów fukcji został nazwany foo użyj go.
+
Jeżeli fukcja została nazwana foo skorzystaj z tego.
+
Przejdz do zewnętrznego zasięgu i przejdz do kroku #1.
+
+
+
Przestrzenie nazw
+
Powszechnym problemem posiadania tylko jednej globalnej przestrzeni nazw jest
+prawdopodobieństwo wystąpienia kolizji nazw. W JavaScripcie, można łatwo uniknąć
+tego problemu korzystając z anonimowych wrapperów.
Anonimowe funkcje są rozpoznane jako wyrażenia, więc
+aby mogły zostać wywołane muszą zostać zewaluowane.
+
( // zewaluowanie funkcji znajdującej się wewnątrz nawiasów
+function() {}
+) // zwrócenie obiektu funkcji
+() // wywołanie rezultatu ewaluacji
+
Istnieją inne sposoby aby zewaluować i wykonać wyrażenie funkcyjne. Mimo że
+mają inną składnię, zachowują się dokładnie tak samo.
+
// Trzy inne sposoby
+!function(){}();
++function(){}();
+(function(){}());
+
Wnioski
+
Zaleca się, aby zawsze używać anonimowych wrapperów do hermetyzacji kodu wewnątrz
+jego własnej przestrzeni nazw. To nie tylko chroni kod przed kolizją nazw, ale
+również wprowadza lepszą modularyzację programów.
+
Ponadto, stosowanie zmiennych globalnych jest uznawane za złą praktykę.
+Wykorzystanie zmiennych globalnych wskazuje na źle napisany kod, który
+jest podatny na błędy i trudny do utrzymania.
+
Tablice
Iterowanie po tablicach oraz właściwościach tablic
Mimo że tablice w JavaScript są obiektami, nie ma dobrych powodów aby używać
+pętli for in do iteracji po nich. W rzeczywstości istnieje
+wiele dobrych powodów przeciwko wykorzystaniu for in na tablicach.
+
+
Ponieważ pętla for in wylicza wszystkie właściwości, które są wewnątrz
+łańcucha prototypów i jedynym sposobem aby wykluczyć te właściwości jest użycie
+hasOwnProperty, ale wówczas pętla staje się
+dwadzieście razy wolniejsza od normalnej pętli for.
+
Iteracja
+
W celu osiągnięcia najlepszej wydajności podczas iteracji po tablicach należy
+użyć klasycznej pętli for.
+
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+ console.log(list[i]);
+}
+
W powyższym przykładzie jest jeszcze jeden dodatkowy haczyk. Jest to zbuforowanie
+długości tablicy poprzez l = list.length.
+
Mimo że właściwość length jest zdefiniowana wewnątrz tablicy, istnieje nadal
+dodatkowy koszt na wyszukiwanie tej właściwości przy każdej iteracji w pętli.
+Chociaż najnowsze silniki JavaScript mogą zastosować w tym
+przypadku optymalizację. Nie ma jednak możliwość ustalenia czy kod będzie wykonywany w jednym
+z tych nowych silników, czy też nie.
+
W rzeczywistości pominięcie buforowania długości tablicy może spowodować, że pętla
+będzie tylko w połowie tak szybka jak ta z buforowaniem długości.
+
Właściwość length
+
Mimo, że getter właściwości length zwraca po prostu liczbę elementów, które są
+zawarte w tablicy, to setter może być użyty do skracania tablicy.
Przypisanie mniejszej długości spowoduje skrócenie tablicy, ale zwiększenie wartości
+length nie ma żadnego wpływu na tablicę.
+
Wnioski
+
Aby uzyskać najlepszą wydajność zaleca się, aby zawsze używać zwykłej pętli for
+i zbuforowanie właściwości length. Korzystanie z pętli for in na tablicy jest
+oznaką źle napisanego kodu, który jest podatny na błędy i ma słabą wydajność.
+
Konstruktor Array
Zaleca się zawsze korzystać z literału tablicy - notacja [] - podczas tworzenia
+nowych tablic, ponieważ konstruktor Array niejednoznacznie interpretuje
+przekazane do niego parametry.
W przypadku gdy tylko jeden argument zostanie przekazany do kostruktora Array i
+ten argument jest typu Number, konstruktor zwróci nową dziwną tablicę
+z ustawioną właściwością length na wartość przekazaną jako argument. Należy
+zauważyć, że tylko właściwość length zostanie ustawiona w ten sposób.
+Rzeczywiste indeksy w tej tablicy nie zostaną zainicjalizowane.
+
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // zwraca false, indeks nie został ustawiony
+
Możliwość ustalenia z góry długości tablicy jest użyteczna tylko w kilku
+przypadkach, jak np. powtarzanie ciągu znaków, w którym unika się stosowania
+pętli for.
W miarę możliwości należy unikać używania konstruktora Array. Literały są
+zdecydowanie lepszym rozwiązaniem. Są krótsze i mają bardziej precyzyjną składnię.
+Zwiększają również czytelność kodu.
+
Typy
Równość i porównania
JavaScript posiada dwa różne sposoby równościowego porównywania obiektów.
+
Operator równości
+
Operator równości składa się z dwóch znaków "równa się": ==
+
JavaScript jest słabo typowanym językiem. Oznacza to, że operator równości
+konwertuje typy (dokonuje koercji), aby wykonać porównanie.
Powyższa tabela przedstawia wyniki koercji typów. Nieprzewidywalne wyniki
+porównania są głównym powodem, że stosowanie == jest powszechnie uważane za złą
+praktykę. Skomplikowane reguły konwersji są powodem trudnych do wyśledzenia błędów.
+
Ponadto koercja ma również wpływ na wydajność, Na przykład gdy typ String musi zostać
+przekształcony na typ Number przed porównaniem z drugą liczbą.
+
Operator ścisłej równości
+
Operator ścisłej równości składa się z trzech znaków "równa się": ===
+
Działa on dokładnie tak jak normalny operator równości, z jednym wyjątkiem - nie
+dokonuje koercji typów przed porównaniem.
Powyższe rezultaty są o wiele bardziej przejrzyste. Powoduje to "ustatycznienie"
+języka do pewnego stopnia oraz pozwala na wprowadzenie optymalizacji porównań
+obiektów o różnych typach.
+
Porównywanie obiektów
+
Mimo że oba operatory == i === nazywane są operatorami równościowymi,
+to zachowują się różnie, gdy jednym z operandów jest obiekt typu Object.
Oba operatory porównują tożsamość a nie równość, czyli będą porównywać czy
+jeden i drugi operand jest tą samą instancją obiektu (podobnie jak operator
+is w Pythonie i porównanie wskaźników w C).
+
Wnioski
+
Zaleca się, aby używać tylko operatora ścisłej równości. W sytuacjach gdy
+potrzebna jest koercja (porównanie obiektów różnych typów), konwersja powinna
+być dokonana jawnie, a nie pozostawiona trudnym regułom koercji
+obowiązującym w języku.
+
Operator typeof
Operator typeof (razem z operatorem instanceof) jest
+prawdopodobnie najwiekszą wadą konstrukcji języka JavaScript. Posiada on praktycznie same wady.
+
Mimo że instanceof ma swoje wady to nadal ma ograniczone zastosowanie w praktyce,
+natomiast typeof ma tylko jeden praktyczny przypadek użycia, który na dodatek
+nie jest związany z sprawdzaniem typu obiektu.
+
+
Tablica typów JavaScript
+
Wartość Klasa Typ
+-------------------------------------
+"foo" String string
+new String("foo") String object
+1.2 Number number
+new Number(1.2) Number object
+true Boolean boolean
+new Boolean(true) Boolean object
+new Date() Date object
+new Error() Error object
+[1,2,3] Array object
+new Array(1, 2, 3) Array object
+new Function("") Function function
+/abc/g RegExp object (function w Nitro i V8)
+new RegExp("meow") RegExp object (function w Nitro i V8)
+{} Object object
+new Object() Object object
+
W powyższej tabeli Typ odnosi się do wartości zwracanej przez operator typeof.
+Wyraźnie widać, że zwracane wartości w ogóle nie są spójne.
+
Klasa odnosi sie do wartości wewnętrznej właściwości [[Class]] obiektu.
+
+
W celu uzyskania wartości właściwości [[Class]] trzeba skorzystać z metody
+toString z Object.prototype.
+
Klasa obiektu
+
Specyfikacja zawiera dokładnie jeden sposób dostepu do wartości [[Class]],
+wykorzystując Object.prototype.toString.
Powyższy przykład wywołuje Object.prototype.toString z wartością
+this ustawioną na obiekt, dla której wartość właściwości
+[[Class]] ma zostać odczytana.
+
+
Testowanie niezdefiniowania zmiennej
+
typeof foo !== 'undefined'
+
Powyższy kod sprawdza czy foo została faktycznie zadeklarowana czy też nie.
+Próba odwołania się do zmiennej spowodowała by wyrzucenie błędu ReferenceError.
+Jest to jedyne praktyczne wykorzystanie operatora typeof.
+
Wnioski
+
W celu sprawdzenia typu obiektu zalecane jest skorzystanie z
+Object.prototype.toString, ponieważ jest to jedyny wiarygodny sposób. Jak
+pokazano w powyższej tabeli typów, niektóre wartości zwracane przez typeof nie
+są zdefiniowane w specyfikacji, co za tym idzie mogą się różnić w różnych
+implementacjach.
+
O ile nie operator typeof nie jest użyty do sprawdzania czy zmienna została
+zdefiniowana, powinien być unikany jeśli to tylko możliwe.
+
Operator instanceof
Operator instanceof porównuje konstruktory obiektów przekazanych jako operendy.
+Jest on użyteczny jedynie do porównywania obiektów utworzonych klas. Stosowanie
+go na wbudowanych typach jest praktycznie tak samo bezużyteczne, jak operatora
+typeof.
+
Porównywanie obiektów utworzonych klas
+
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// poniżej kod który przypisuje do Bar.prototype obiekt funkcji Foo
+// a nie faktyczną instancję Foo
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
Jedną ważną rzeczą, którą należy zauważyć jest to, że instanceof nie zadziała
+na obiektach, które pochodzą z różnych kontekstów JavaScript (np. z różnych
+dokumentów wewnątrz przeglądarki), ponieważ ich konstruktory nie będą tymi
+samymi obiektami.
+
Wnioski
+
Operator instanceof powinien być używany wyłącznie podczas korzystania z obiektów
+klas utworzonych, które były zdefiniowane w tym samym kontekscie JavaScriptowym.
+Podobnie jak operator typeof, należy unikać korzystania
+z tego operatora w innych sytuacjach.
+
Rzutowanie typów
JavaScript jest językiem słabo typowanym. Co za tym idzie, będzie stosować koercję
+typów gdziekolwiek jest to możliwe.
+
// te zwracają true
+new Number(10) == 10; // Number.toString() zostanie przekształcone
+ // z powrotem do liczby
+
+10 == '10'; // stringi zostaną przekształcone do typu Number
+10 == '+10 '; // kolejne wariacje
+10 == '010'; // i następne
+isNaN(null) == false; // null zostanie przekształcony do 0
+ // który oczywiście nie jest NaN
+
+// poniższe zwracają false
+10 == 010;
+10 == '-10';
+
+
Aby uniknąć powyższych problemów, należy koniecznie korzystać ze
+ściełego operatora równości. Mimo, że pozwala to uniknąć wiele
+typowych problemów to nadal istnieje wiele innych, które powstają na bazie słabego
+typowania języka JavaScript.
+
Konstruktory typów wbudowanych
+
Konstruktory typów wbudowanych, takich jak Number lub String, zachowują się
+inaczej kiedy są poprzedzone słowem kluczowym new a inaczej kiedy nie są.
+
new Number(10) === 10; // False, Object i Number
+Number(10) === 10; // True, Number i Number
+new Number(10) + 0 === 10; // True, ponieważ dokonano jawnej konwersji
+
Korzystanie z wbudowanych typów jak Number jako konstruktora tworzy nowy obiekt
+typu Number, natomiast opuszczenie słowa kluczowego new powoduje, że funkcja
+Number zachowuje się jak konwerter.
+
Ponadto, użycie literałów lub wartości nieobiektowych zaowocuje jeszcze większą
+ilością rzutowań (koercją) typów.
+
Najlepszym rozwiązaniem jest jawne rzutowanie do jednego z trzech typów.
+
Rzutowanie do typu String
+
'' + 10 === '10'; // true
+
Konkatenacja pustego stringu i wartości powoduje rzutowanie do typu String.
+
Rzutowanie do typu Number
+
+'10' === 10; // true
+
Zastosowanie unarnego operatora + spowoduje rzutowanie do typu Number.
+
Rzutowanie do typu Boolean
+
Używając dwukrotnie operatora negacji, dowolna wartość może zostać zrzutowana
+do typu Boolean
Niestaty, eval zostanie wykonana w lokalnym zasięgu tylko wtedy, gdy zostanie wywołana
+bezpośrednioi nazwa wywoływanej funkcji równa sie eval.
+
var foo = 1;
+function test() {
+ var foo = 2;
+ var bar = eval;
+ bar('foo = 3');
+ return foo;
+}
+test(); // 2
+foo; // 3
+
Należy unikać stosowania evalo ile to tylko możliwe. W 99.9% przypadków można
+osiągnąć ten sam efekt nie używając eval.
+
eval w przebraniu
+
Funkcje wykonywane po upływie czasusetTimeout i setInterval
+mogą przyjąć string jako pierwszy argument. String ten zawsze będzie wykonywany
+w globalnym zasięgu, ponieważ funkcja eval jest w tym wypadku wywoływana pośrednio.
+
Problemy z bezpieczeństwem
+
Funkcja eval jest również problematyczna od strony bezpieczeństwa, ponieważ
+wykonuje każdy kod, który zostanie do niej przekazany i nigdy nie należy
+jej używać na stringach nieznanego lub niezaufanego pochodzenia.
+
Wnioski
+
Funkcja eval nie powinna być w ogóle używana. Każdy kod, który jej używa
+powinien zostać sprawdzony pod względem działania, wydajności i bezpieczeństwa.
+W przypadku gdy użycie eval jest niezbędne do działania, wówczas taki kod
+należy ponownie przemyśleć i ulepszyć aby nie wymagał użycia eval.
+
undefined i null
JavaScript ma dwie różne wartości dla pustych wartości, bardziej użyteczną
+z tych dwóch jest undefined.
+
Wartość undefined
+
undefined jest typem z dokładnie jedną wartością: undefined.
+
Język również definiuje globalną zmienną, która ma wartość undefined - zmienna
+ta jest nazwana undefined. Jednakże jest to zmienna a nie stała, czy słowo
+kluczowe. Oznacza to, że możliwe jest nadpisanie wartości tej zmiennej.
+
+
Kilka przykładów kiedy wartość undefined jest zwracana:
+
+
dostęp do (niemodyfikowalnej) zmiennej globalnej undefined,
+
wyjście z funkcji, która nie ma deklaracji return,
+
deklaracja return, która nic jawnie nie zwraca,
+
poszukiwanie nieistniejącej właściwości,
+
parametr funkcji, który nie został jawnie przekazany podczas wywołania funkcji,
+
wszystko czemu została przypisana wartość undefined.
+
+
Obsługa przypadku zmiany wartości undefined
+
Ponieważ globalna zmienna undefined zawiera tylko kopię prawdziwej wartości typu
+undefined, przypisanie nowej wartości do tej zmiennej nie zmienia wartości
+typuundefined.
+
Jednak aby porównać coś z wartością undefined, trzeba odczytać wartość undefined.
+
Aby uchronić swój kod przed możliwym nadpisaniem zmiennej undefined, korzysta
+się z powszechnej techniki dodania dodatkowego parametru do
+anonimowego wrappera, do którego nie zostanie przekazany
+argument.
+
var undefined = 123;
+(function(something, foo, undefined) {
+ // undefined o lokalnym zasięgu znowu
+ // odnosi się do poprawnej wartości
+
+})('Hello World', 42);
+
Kolejnym sposobem na osiągnięcie tego samego efektu jest użycie deklaracji zmiennej
+wewnątrz wrappera.
+
var undefined = 123;
+(function(something, foo) {
+ var undefined;
+ ...
+
+})('Hello World', 42);
+
Jedyną różnicą pomiędzy tymi sposobami są dodatkowe 4 bajty przeznaczone na słowo
+kluczowe var i spację po nim.
+
Zastosowanie null
+
Podczas gdy undefined w kontekście języka jest używany jak null w sensie
+tradycyjnych języków, null w JavaScript (jako literał i jako typ) jest po
+prostu kolejnym typem danych.
+
Jest wykorzystywany we wnętrzu JavaScript (np. deklaracji końca łańcucha prototypów
+poprzez ustawienie Foo.prototype = null), ale prawie w każdym przypadku można go
+zastąpić przez undefined.
+
Automatyczne wstawianie średnika
Mimo że JavaScript ma składnię podobną do języka C, to nie wymusza stosowania
+średników w kodzie źródłowym. Istnieje możliwość ich pominięcia.
+
JavaScript nie jest językiem bez średników, tak na prawdę potrzebuje
+średników aby zinterpretować kod źródłowy. Jednakże parser JavaScript
+automatycznie wstawia średniki o ile napotka błąd parsowania związany z
+brakiem średnika.
+
var foo = function() {
+} // błąd parsowania, oczekiwany był w tym miejscu średnik
+test()
+
Parser dodaje średnik, i próbuje jeszcze raz sparsować skrypt.
+
var foo = function() {
+}; // bez błędu parser kontynuuje
+test()
+
Automatyczne wstawianie średników jest uważane za jeden z największych błędów
+konstrukcji języka, ponieważ może ono zmienić zachowanie kodu.
+
Jak działa wstawianie
+
Kod poniżej nie ma żadnych średników, więc parser zdecyduje, w których miejscach
+je wstawi.
Jest bardzo prawdopodobne, że lognie zwróci fukcji. Co za tym idzie
+powyższy kod wyrzuci błąd TypeError oznajmując, że undefined is not a
+function - undefined nie jest funkcją.
+
Wnioski
+
Zaleca się, aby nigdy nie pomijać średników, pozostawiać nawias otwierający
+w tej samej linii co odpowiadająca mu definicja i nigdy nie pozostawiać deklaracji
+if / else bez nawiasów - nawet, jeżeli są jednolinijkowe. Wszystkie te uwagi nie
+tylko pomagają poprawić spójność kodu, ale też zapobiegają zmianie działania
+kodu przez parser JavaScript.
+
Inne
setTimeout i setInterval
Ponieważ JavaScript jest asynchroniczny, istnieje możliwość zaplanowania wykonania
+funkcji przy użyciu funkcji setTimeout i setInterval.
+
+
function foo() {}
+var id = setTimeout(foo, 1000); // zwraca liczbę typu Number > 0
+
Powyższe wywołanie setTimeout zwraca ID budzika i planuje wywołanie foo za
+około tysiąc milisekund. foo zostanie wykonana dokładnie jeden raz.
+
Nie ma pewności, że kod zaplanowany do wykonania wykona się dokładnie po
+upłynięciu zadanego czasu podanego jako parametr do setTimeout, ponieważ zależy
+to od dokładności zegara w silniku JavaScript, który wykonuje kod oraz od tego,
+że inny kawałek kodu może zablokować wątek, ponieważ JavaScript jest tylko
+jednowątkowy.
+
Funkcja, która została przekazana jako pierwszy parametr zostanie wykonana w
+globalnym zasięgu, co oznacza, że this wewnątrz tej funkcji
+będzie wskazywać na obiekt global.
+
function Foo() {
+ this.value = 42;
+ this.method = function() {
+ // this wskazuje na obiekt global
+ console.log(this.value); // wypisze undefined
+ };
+ setTimeout(this.method, 500);
+}
+new Foo();
+
+
Kolejkowanie wywołań z setInterval
+
Podczas gdy setTimeout wywołuje podaną funkcję tylko raz, setInterval -
+jak wskazuje nazwa - będzie wykonywać funkcję w odstępach czasowych co X
+milisekund. Jednakże korzystanie z tej funkcji jest odradzane.
+
Kiedy wykonywany kod zablokuje możliwość uruchomienia zaplanowanej funkcji,
+setInterval będzie próbować uruchamiać daną funkcję, co będzie powodować
+kolejkowanie wykonania tej samej funkcji kilkukrotnie. Może się to zdażyć
+szczególnie przy krótkim interwale.
+
function foo(){
+ // coś co blokuje wykonanie na 1 sekundę
+}
+setInterval(foo, 1000);
+
W powyższym kodzie kod foo zostanie wywołany tylko raz i zablokuje wywołanie na
+jedną sekundę.
+
Podczas, gdy funkcja foo blokuje wykonanie, setInterval będzie planować kolejne
+wywołania foo. W momencie, gdy pierwsze wywołanie foo się zakończy,
+w kolejce do wywołania będzie już czekało kolejne dziesięć wywołań tej funkcji.
+
Radzenie sobie z możliwymi blokadami
+
Najprostszą, jak również najbardziej kontrolowaną sytuacją, jest użycie setTimeout
+wewnątrz wywoływanej funkcji.
+
function foo(){
+ // coś co blokuje wykonanie na 1 sekundę
+ setTimeout(foo, 1000);
+}
+foo();
+
Powyższy kod nie tylko hermetyzuje wywołanie setTimeout, ale też zapobiega
+kolejkowaniu wywołań funkcji i daje dodatkową kontrolę. W tym przypadku funkcja
+foo może zdecydować czy powinna się wywołać ponownie, czy też nie.
+
Ręczne usuwanie budzików
+
Usuwanie budzików i interwałów dokonywane jest przez przekazanie odpowiedniego ID
+do clearTimeout lub clearInterval, w zależności z jakiej funkcji zostało
+zwrócone ID.
+
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
Usuwanie wszystkich budzików
+
Ponieważ nie istnieje wbudowana metoda usuwania wszystkich budzików i/lub
+interwałów, do osiągnięcia tego efektu konieczne jest użycie metody 'brute force'.
+
// usunięcie "wszystkich" budzików
+for(var i = 1; i < 1000; i++) {
+ clearTimeout(i);
+}
+
Nadal mogą istnieć jakieś budziki, na które powyższy kawałek kodu nie zadziała.
+Ponieważ ID było z innego przedziału, zamiast korzystania z metody brute force,
+zaleca się śledzić wszystkie numery ID budzików, aby można je było usunąć.
+
Ukryte wykorzystanie eval
+
Do setTimeout i setInterval można również przekazać string jako pierwszy
+parametr zamiast obiektu funkcji, jednakże nigdy nie należy korzystać z tej
+możliwości, ponieważ wewnętrznie setTimeout i setInterval wykorzystują eval.
+
+
function foo() {
+ // zostanie wykonane
+}
+
+function bar() {
+ function foo() {
+ // nigdy nie zostanie wywołane
+ }
+ setTimeout('foo()', 1000);
+}
+bar();
+
Ponieważ eval nie zostało wywołane w tym przypadku wprost, to
+string przekazany do setTimeout zostanie uruchomiony w zasięgu globalnym.
+Co za tym idzie, lokalna zmienna foo z zasięgu bar nie zostanie użyta.
+
Kolejnym zaleceniem jest niestosowanie stringów do przekazywania argumentów
+do funkcji, która ma zostać wywołana przez budzik.
+
function foo(a, b, c) {}
+
+// NIGDY nie należy tak robić
+setTimeout('foo(1,2, 3)', 1000)
+
+// zamiast tego należy skorzystać z anonimowej funkcji
+setTimeout(function() {
+ foo(a, b, c);
+}, 1000)
+
+
Wnioski
+
Nigdy nie należy przekazywać stringu jako parametru do setTimeout lub
+setInterval. Jest to wyraźną oznaką bardzo złego kodu. Jeżeli potrzebne jest
+przekazanie argumentów do funkcji, należy skorzystać z anonimowej funkcji i
+wewnątrz niej dokonać przekazania argumentów.
+
Ponadto, należy unikać korzystania z setInterval, ponieważ planista może
+zablokować wykonanie JavaScriptu.
+
\ No newline at end of file
diff --git a/ru/index.html b/ru/index.html
new file mode 100644
index 0000000..480e9a3
--- /dev/null
+++ b/ru/index.html
@@ -0,0 +1,1062 @@
+JavaScript Гарден
JavaScript Гарден распространяется под лицензией MIT и располагается на GitHub. Если вы найдёте ошибку или опечатку, пожалуйста сообщите нам о ней или запросите права на загрузку в репозиторий. Кроме того, вы можете найти нас в комнате JavaScript среди чатов Stack Overflow.
+
Объекты
Объекты и их свойства
В JavaScript всё ведет себя, как объект, лишь за двумя исключениями — null и undefined.
Неверно считать, что числовые литералы нельзя использовать в качестве объектов — это распространённое заблуждение. Его причиной является упущение в парсере JavaScript, благодаря которому применение точечной нотации к числу воспринимается им как литерал числа с плавающей точкой.
+
2.toString(); // вызывает SyntaxError
+
Есть несколько способов обойти этот недостаток и любой из них можно использовать для того, чтобы работать с числами, как с объектами:
+
2..toString(); // вторая точка распознаётся корректно
+2 .toString(); // обратите внимание на пробел перед точкой
+(2).toString(); // двойка вычисляется заранее
+
Объекты как тип данных
+
Объекты в JavaScript могут использоваться как хеш-таблицы: подавляющей частью состоят из именованных свойств (ключей), привязанных к значениям.
+
Используя объектный литерал — нотацию {} — можно создать простой объект. Новый объект наследуется от Object.prototype и не имеет собственных свойств.
+
var foo = {}; // новый пустой объект
+
+// новый объект со свойством 'test', имеющим значение 12
+var bar = {test: 12};
+
Доступ к свойствам
+
Получить доступ к свойствам объекта можно двумя способами: используя либо точечную нотацию, либо запись квадратными скобками.
+
var foo = {name: 'kitten'}
+foo.name; // kitten
+foo['name']; // kitten
+
+var get = 'name';
+foo[get]; // kitten
+
+foo.1234; // SyntaxError
+foo['1234']; // работает
+
Обе нотации идентичны по принципу работы — одна лишь разница в том, что использование квадратных скобок позволяет устанавливать свойства динамически и использовать такие имена свойств, какие в других случаях могли бы привести к синтаксической ошибке.
+
Удаление свойств
+
Единственный способ удалить свойство у объекта — использовать оператор delete; устанавливая свойство в undefined или null, вы только заменяете связанное с ним значение, но не удаляете ключ.
+
+
var obj = {
+ bar: 1,
+ foo: 2,
+ baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ console.log(i, '' + obj[i]);
+ }
+}
+
Приведённый код выведет две строки: bar undefined и foo null — на самом деле удалено было только свойство baz и посему только оно будет отсутствовать в выводе.
+
Запись ключей
+
var test = {
+ 'case': 'Я — ключевое слово, поэтому меня надо записывать строкой',
+ delete: 'Я тоже ключевое слово, так что я' // бросаю SyntaxError
+};
+
Свойства объектов могут записываться как явно символами, так и в виде закавыченных строк. В связи с другим упущением в парсере JavaScript, этот код выбросит SyntaxError во всех версиях ранее ECMAScript 5.
+
Источником ошибки является факт, что delete — это ключевое слово и поэтому его необходимо записывать как строчный литерал: ради уверенности в том, что оно будет корректно опознано более старыми движками JavaScript.
+
От перев.: И еще один пример в пользу строковой нотации, это относится к JSON:
+
// валидный JavaScript и валидный JSON
+{
+ "foo": "oof",
+ "bar": "rab"
+}
+
+// валидный JavaScript и НЕвалидный JSON
+{
+ foo: "oof",
+ bar: "rab"
+}
+
Великий Прототип
В JavaScript отсутствует классическая модель наследования — вместо неё используется прототипная модель.
+
Хотя её часто расценивают как один из недостатков JavaScript, на самом деле прототипная модель наследования намного мощнее классической. К примеру, поверх неё можно предельно легко реализовать классическое наследование, а попытки совершить обратное вынудят вас попотеть.
+
Из-за того, что JavaScript — практически единственный широко используемый язык с прототипным наследованием, придётся потратить некоторое время на осознание различий между этими двумя моделями.
+
Первое важное отличие заключается в том, что наследование в JavaScript выполняется с использованием так называемых цепочек прототипов.
+
+
function Foo() {
+ this.value = 42;
+}
+Foo.prototype = {
+ method: function() {}
+};
+
+function Bar() {}
+
+// Установим значением прототипа Bar новый экземпляр Foo
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Hello World';
+
+// Убедимся, что Bar является действующим конструктором
+Bar.prototype.constructor = Bar;
+
+var test = new Bar() // создадим новый экземпляр bar
+
+// Цепочка прототипов, которая получится в результате
+test [instance of Bar]
+ Bar.prototype [instance of Foo]
+ { foo: 'Hello World' }
+ Foo.prototype
+ { method: ... }
+ Object.prototype
+ { toString: ... /* и т.д. */ }
+
В приведённом коде объект test наследует оба прототипа: Bar.prototype и Foo.prototype; следовательно, он имеет доступ к функции method которую мы определили в прототипе Foo. Также у него есть доступ к свойству valueодного уникального экземпляра Foo, который является его прототипом. Важно заметить, что код new Bar()не создаёт новый экземпляр Foo, а повторно вызывает функцию, которая была назначен его прототипом: таким образом все новые экземпляры Bar будут иметь одинаковое свойство value.
+
+
Поиск свойств
+
При обращении к какому-либо свойству объекта, JavaScript проходит вверх по цепочке прототипов этого объекта, пока не найдет свойство c запрашиваемым именем.
+
Если он достигнет верхушки этой цепочки (Object.prototype) и при этом так и не найдёт указанное свойство, вместо него вернётся значение undefined.
+
Свойство prototype
+
То, что свойство prototype используется языком для построения цепочек прототипов, даёт нам возможность присвоить любое значение этому свойству. Однако обычные примитивы, если назначать их в качестве прототипа, будут просто-напросто игнорироваться.
+
function Foo() {}
+Foo.prototype = 1; // ничего не произойдёт
+Foo.prototype = {
+ "foo":"bar"
+};
+
При этом присвоение объектов, как в примере выше, позволит вам динамически создавать цепочки прототипов.
+
Производительность
+
Поиск свойств, располагающихся относительно высоко по цепочке прототипов, может негативно сказаться на производительности, особенно в критических местах кода. Если же мы попытаемся найти несуществующее свойство, то поиск будет осуществлён вообще по всей цепочке, со всеми вытекающими последствиями.
+
Вдобавок, при циклическом переборе свойств объекта, будет обработано каждое свойство, существующее в цепочке прототипов.
+
Расширение встроенных прототипов
+
Часто встречается неверное применение прототипов — расширение прототипа Object.prototype или прототипов одного из встроенных объектов JavaScript.
+
Подобная практика нарушает принцип инкапсуляции и имеет соответствующее название — monkey patching. К сожалению, в основу многих широко распространенных фреймворков, например Prototype, положен принцип изменения базовых прототипов. Вам же стоит запомнить — от хорошей жизни прототипы встроенных объектов не меняют.
+
Единственным оправданием для расширения встроенных прототипов может быть только воссоздание возможностей более новых движков JavaScript, например функции Array.forEach, которая появилась в версии 1.6.
+
Заключение
+
Перед тем, как вы приступите к разработке сложных приложений на JavaScript, вы должны полностью осознать как работают прототипы, и как организовывать наследование на их основе. Также, помните о зависимости между длиной цепочек прототипов и производительностью — разрывайте их при необходимости. Кроме того — никогда не расширяйте прототипы встроенных объектов (ну, если только для совместимости с новыми возможностями Javascript).
+
Функция hasOwnProperty
Если вам необходимо проверить, определено ли свойство у самого объекта, а не в его цепочке прототипов, вы можете использовать метод hasOwnProperty, который все объекты наследуют от Object.prototype.
+
+
hasOwnProperty — единственная функция в JavaScript, которая позволяет получить свойства объекта без обращения к цепочке его прототипов.
Только используя `hasOwnProperty` можно гарантировать правильный результат при переборе свойств объекта. И нет иного способа для определения свойств, которые определены в самом объекте, а не где-то в цепочке его прототипов.
+
hasOwnProperty как свойство
+
JavaScript не резервирует свойство с именем hasOwnProperty. Так что, если есть потенциальная возможность, что объект может содержать свойство с таким именем, требуется использовать внешний вариант функции hasOwnProperty чтобы получить корректные результаты.
+
var foo = {
+ hasOwnProperty: function() {
+ return false;
+ },
+ bar: 'Да прилетят драконы'
+};
+
+foo.hasOwnProperty('bar'); // всегда возвращает false
+
+// Используем метод hasOwnProperty пустого объекта
+// и передаём foo в качестве this
+({}).hasOwnProperty.call(foo, 'bar'); // true
+
Заключение
+
Единственным способом проверить существование свойства у объекта является использование метода hasOwnProperty. При этом рекомендуется использовать этот метод в каждомцикле for in вашего проекта, чтобы избежать возможных ошибок с ошибочным заимствованием свойств из прототипов родительских объектов. Также вы можете использовать конструкцию {}.hasOwnProperty.call(...) на случай, если кто-то вздумает расширить прототипы встроенных объектов.
+
Цикл for in
Как и оператор in, цикл for in проходит по всей цепочке прототипов, обходя свойства объекта.
+
+
// Испортим Object.prototype
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+ console.log(i); // печатает и bar и moo
+}
+
Так как изменить поведение цикла for in как такового не представляется возможным, то для фильтрации нежелательных свойств объекта внутри этого цикла используют метод hasOwnProperty из Object.prototype.
+
+
Использование hasOwnProperty в качестве фильтра
+
// возьмём foo из примера выше
+for(var i in foo) {
+ if (foo.hasOwnProperty(i)) {
+ console.log(i);
+ }
+}
+
Это единственная версия правильного использования цикла. Благодаря использованию hasOwnPropery будет выведено только свойство moo. Если же убрать hasOwnProperty, код становится нестабилен и могут возникнуть ошибки, особенно если кто-то изменил встроенные прототипы, такие как Object.prototype.
+
Один из самых популярных фреймворков Prototype как раз этим и славится, и если вы его подключаете, то не забудьте использовать hasOwnProperty внутри цикла for in, иначе у вас гарантированно возникнут проблемы.
+
Рекомендации
+
Рекомендация одна — всегда используйте hasOwnProperty. Пишите код, который будет в наименьшей мере зависеть от окружения, в котором он будет запущен — не стоит гадать, расширял кто-то прототипы или нет и используется ли в ней та или иная библиотека.
+
Функции
Выражения и объявление функций
Функции в JavaScript тоже являются объектами (шок, сенсация) — следовательно, их можно передавать и присваивать точно так же, как и любой другой объект. Одним из вариантов использования такой возможности является передача анонимной функции как функции обратного вызова в другую функцию — к примеру, для асинхронных вызовов.
+
Объявление function
+
// всё просто и привычно
+function foo() {}
+
В следующем примере описанная функция резервируется перед запуском всего скрипта; за счёт этого она доступна в любом месте кода, вне зависимости от того, где она определена — даже если функция вызывается до её фактического объявления в коде.
+
foo(); // сработает, т.к. функция будет создана до выполнения кода
+function foo() {}
+
function как выражение
+
var foo = function() {};
+
В этом примере безымянная и анонимная функция присваивается переменной foo.
Так как в данном примере выражение var — это определение функции, переменная с именем foo будет заранее зарезервирована перед запуском скрипта (таким образом, foo уже будет определена во время его работы).
+
Но поскольку присвоения исполняются непосредственно во время работы кода, foo по умолчанию будет присвоено значение undefined (до обработки строки с определением функции):
Существует еще нюанс, касающийся именованных функций создающихся через присваивание:
+
var foo = function bar() {
+ bar(); // работает
+}
+bar(); // получим ReferenceError
+
Здесь объект bar не доступен во внешней области, так как имя bar используется только для присвоения переменной foo; однако bar можно вызвать внутри функции. Такое поведение связано с особенностью работы JavaScript с пространствами имен - имя функции всегда доступно в локальной области видимости самой функции.
+
Как работает this
В JavaScript область ответственности специальной переменной this концептуально отличается от того, за что отвечает this в других языках программирования. Различают ровно пять вариантов того, к чему привязывается this в языке.
+
1. Глобальная область видимости
+
this;
+
Когда мы используем this в глобальной области, она будет просто ссылаться на глобальный объект.
+
2. Вызов функции
+
foo();
+
Тут this также ссылается на глобальный объект.
+
+
3. Вызов метода
+
test.foo();
+
В данном примере this ссылается на test.
+
4. Вызов конструктора
+
new foo();
+
Если перед вызовом функции присутствует ключевое слово new, то данная функция будет действовать как конструктор. Внутри такой функции this будет указывать на новосозданныйObject.
+
5. Переопределение this
+
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // массив развернётся в a = 1, b = 2, c = 3
+foo.call(bar, 1, 2, 3); // аналогично
+
Когда мы используем методы call или apply из Function.prototype, то внутри вызываемой функции thisявным образом будет присвоено значение первого передаваемого параметра.
+
Исходя из этого, в предыдущем примере (строка с apply) правило #3 вызов методане будет применёно, и this внутри foo будет присвоено bar.
+
+
Наиболее распространенные ошибки
+
Хотя большинство из примеров ниже наполнены глубоким смыслом, первый из них можно считать ещё одним упущением в самом языке, поскольку он вообще не имеет практического применения.
+
Foo.method = function() {
+ function test() {
+ // this ссылается на глобальный объект
+ }
+ test();
+}
+
Распространенным заблуждением будет то, что this внутри test ссылается на Foo, но это не так.
+
Для того, чтобы получить доступ к Foo внутри функции test, необходимо создать локальную переменную внутри method, которая и будет ссылаться на Foo.
+
Foo.method = function() {
+ var that = this;
+ function test() {
+ // Здесь используем that вместо this
+ }
+ test();
+}
+
Подходящее имя для переменной - that, его часто используют для ссылки на внешний this. В комбинации с замыканиямиthis можно пробрасывать в глобальную область или в любой другой объект.
+
+
Назначение методов
+
Еще одной фичей, которая не работает в JavaScript, является создание псевдонимов для методов, т.е. присвоение метода объекта переменной.
+
var test = someObject.methodTest;
+test();
+
Следуя первому правилу test вызывается как обычная функция; следовательно this внутри него больше не ссылается на someObject.
+
Хотя позднее связывание this на первый взгляд может показаться плохой идеей, но на самом деле именно благодаря этому работает наследование прототипов.
В момент, когда будет вызван method нового экземпляра Bar, this будет ссылаться на этот самый экземпляр.
+
Замыкания и ссылки
Одним из самых мощных инструментов JavaScript'а считаются возможность создавать замыкания — это такой приём, когда наша область видимости всегда имеет доступ к внешней области, в которой она была объявлена. Собственно, единственный механизм работы с областями видимости в JavaScript — это функции: т.о. объявляя функцию, вы автоматически реализуете замыкания.
В данном примере Counter возвращает два замыкания: функции increment и get. Обе эти функции сохраняют ссылку на область видимости Counter и, соответственно, имеют доступ к переменной count из этой самой области.
+
Как это работает
+
Поскольку в JavaScript нельзя присваивать или ссылаться на области видимости, заполучить count извне не представляется возможным. Единственным способом взаимодействовать с ним остается использование двух замыканий.
+
var foo = new Counter(4);
+foo.hack = function() {
+ count = 1337;
+};
+
В приведенном примере мы не изменяем переменную count в области видимости Counter, т.к. foo.hack не объявлен в данной области. Вместо этого будет создана или перезаписана глобальная переменная count;
+
Замыкания внутри циклов
+
Часто встречается ошибка, когда замыкания используют внутри циклов, передавая переменную индекса внутрь.
+
for(var i = 0; i < 10; i++) {
+ setTimeout(function() {
+ console.log(i);
+ }, 1000);
+}
+
Данный код не будет выводить числа с 0 до 9, вместо этого число 10 будет выведено десять раз.
+
Анонимная функция сохраняет ссылку на i и, когда будет вызвана функция console.log, цикл for уже закончит свою работу, а в i будет содержаться 10.
+
Для получения желаемого результата необходимо создать копию переменной i.
+
Во избежание ошибок
+
Для того, чтобы скопировать значение индекса из цикла, лучше всего использовать анонимную функцию как обёртку.
Анонимная функция-обертка будет вызвана сразу же, и в качестве первого аргумента получит i, значение которой будет скопировано в параметр e.
+
Анонимная функция, которая передается в setTimeout, теперь содержит ссылку на e, значение которой не изменяется циклом.
+
Еще одним способом реализации является возврат функции из анонимной функции-обертки, поведение этого кода будет таким же, как и в коде из предыдущего примера.
В области видимости любой функции в JavaScript есть доступ к специальной переменной arguments. Эта переменная содержит в себе список всех аргументов, переданных данной функции.
+
+
Объект argumentsне является наследником Array. Он, конечно же, очень похож на массив и даже содержит свойство length — но он не наследует Array.prototype, а представляет собой Object.
+
По этой причине, у объекта argumentsотсутствуют стандартные методы массивов, такие как push, pop или slice. Хотя итерация с использованием обычного цикла for по аргументам работает вполне корректно, вам придётся конвертировать этот объект в настоящий массив типа Array, чтобы применять к нему стандартные методы массивов.
+
Конвертация в массив
+
Указанный код вернёт новый массив типа Array, содержащий все элементы объекта arguments.
+
Array.prototype.slice.call(arguments);
+
Эта конвертация занимает много времени и использовать её в критических частях кода не рекомендуется.
+
Передача аргументов
+
Ниже представлен рекомендуемый способ передачи аргументов из одной функции в другую.
+
function foo() {
+ bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+ // делаем здесь что-нибудь
+}
+
Другой трюк — использовать и call и apply вместе, чтобы быстро создать несвязанную обёртку:
+
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+ console.log(this, a, b, c);
+};
+
+// Создаём несвязанную версию "method"
+// Она принимает параметры: this, arg1, arg2...argN
+Foo.method = function() {
+
+ // Результат: Foo.prototype.method.call(this, arg1, arg2... argN)
+ Function.call.apply(Foo.prototype.method, arguments);
+
+};
+
Формальные аргументы и индексы аргументов
+
Объект arguments создаёт по геттеру и сеттеру и для всех своих свойств и для формальных параметров функции.
+
В результате, изменение формального параметра также изменит значение соответствующего свойства объекта arguments и наоборот.
+
function foo(a, b, c) {
+ arguments[0] = 2;
+ a; // 2
+
+ b = 4;
+ arguments[1]; // 4
+
+ var d = c;
+ d = 9;
+ c; // 3
+}
+foo(1, 2, 3);
+
Мифы и правда о производительности
+
Объект arguments создаётся во всех случаях, лишь за двумя исключениями — когда он переопределён внутри функции (по имени) или когда одним из её параметров является переменная с таким именем. Неважно, используется при этом сам объект или нет.
+
Геттеры и сеттеры создаются всегда; так что их использование практически никак не влияет на производительность.
+
+
Однако, есть один момент, который может радикально понизить производительность современных движков JavaScript. Этот момент — использование arguments.callee.
+
function foo() {
+ arguments.callee; // сделать что-либо с этим объектом функции
+ arguments.callee.caller; // и с вызвавшим его объектом функции
+}
+
+function bigLoop() {
+ for(var i = 0; i < 100000; i++) {
+ foo(); // При обычных условиях должна бы была быть развёрнута...
+ }
+}
+
В коде выше, функция foo не может быть развёрнута (а могла бы), потому что для корректной работы ей необходима ссылка и на себя и на вызвавший её объект. Это не только кладёт на лопатки механизм развёртывания, но и нарушает принцип инкапсуляции, поскольку функция становится зависима от конкретного контекста вызова.
+
Крайне не рекомендуется использовать arguments.callee или какое-либо из его свойств. Никогда.
+
+
Конструктор
Создание конструкторов в JavaScript также отличается от большинства других языков. Любая функция, вызванная с использованием ключевого слова new, будет конструктором.
+
Внутри конструктора (вызываемой функции) this будет указывать на новосозданный Object. Прототипом этого нового объекта будет prototype функции, которая была вызвана в качестве конструктора.
+
Если вызываемая функция не имеет явного возврата посредством return, то вернётся this — этот новый объект.
+
function Foo() {
+ this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+ console.log(this.bla);
+};
+
+var test = new Foo();
+
В этом примере Foo вызывается в виде конструктора, следовательно прототип созданного объекта будет привязан к Foo.prototype.
+
В случае, когда функция в явном виде возвращает некое значение используя return, то в результате выполнения конструктора мы получим именно его, но только если возвращаемое значение представляет собой Object.
+
function Bar() {
+ return 2;
+}
+new Bar(); // новый объект
+
+function Test() {
+ this.value = 2;
+
+ return {
+ foo: 1
+ };
+}
+new Test(); // возвращённый объект
+
Если же опустить ключевое слово new, то функция не будет возвращать никаких объектов.
Этот пример в некоторых случаях всё-таки может сработать: это связано с поведением this в JavaScript — он будет восприниматься парсером как глобальный объект.
+
Фабрики
+
Если хотите избавится от необходимости использования new, напишите конструктор, возвращающий значение посредством return.
В обоих случаях при вызове Bar мы получим один и тот же результат — новый объект со свойством method (спасибо замыканию за это).
+
Также следует заметить, что вызов new Bar() никак не связан с прототипом возвращаемого объекта. Хоть прототип и назначается всем новосозданным объектам, но Bar никогда не возвращает этот новый объект.
+
В предыдущем примере нет функциональных отличий между вызовом конструктора с оператором new или без него.
+
Создание объектов с использованием фабрик
+
Часто рекомендуют не использовать new, поскольку если вы его забудете, это может привести к ошибкам.
+
Чтобы создать новый объект, лучше использовать фабрику и создать новый объект внутри этой фабрики.
Хотя данный пример и сработает, если вы забыли ключевое слово new и благодаря ему легче работать с приватными переменными, у него есть несколько недостатков
+
+
Он использует больше памяти, поскольку созданные объекты не хранят методы в прототипе и соответственно для каждого нового объекта создаётся копия каждого метода.
+
Чтобы эмулировать наследование, фабрике нужно скопировать все методы из другого объекта или установить прототипом нового объекта старый.
+
Разрыв цепочки прототипов просто по причине забытого ключевого слова new идёт в разрез с духом языка.
+
+
Заключение
+
Хотя забытое ключевое слово new и может привести к багам, это точно не причина отказываться от использования прототипов. В конце концов, полезнее решить какой из способов лучше совпадает с требованиями приложения: очень важно выбрать один из стилей создания объектов и после этого не изменять ему.
+
Области видимости и пространства имён
Хотя JavaScript нормально понимает синтаксис двух фигурных скобок, окружающих блок, он не поддерживает блочную область видимости; всё что остаётся на этот случай в языке — область видимости функций.
+
function test() { // область видимости
+ for(var i = 0; i < 10; i++) { // не область видимости
+ // считаем
+ }
+ console.log(i); // 10
+}
+
+
Также JavaScript не знает ничего о различиях в пространствах имён: всё определяется в глобально доступном пространстве имён.
+
Каждый раз, когда JavaScript обнаруживает ссылку на переменную, он будет искать её всё выше и выше по областям видимости, пока не найдёт её. В случае, если он достигнет глобальной области видимости и не найдет запрошенное имя и там тоже, он ругнётся ReferenceError.
+
Проклятие глобальных переменных
+
// скрипт A
+foo = '42';
+
+// скрипт B
+var foo = '42'
+
Вышеприведённые два скрипта не приводят к одному результату. Скрипт A определяет переменную по имени foo в глобальной области видимости, а скрипт B определяет foo в текущей области видимости.
+
Повторимся, это вообще нетот же самый эффект. Если вы не используете var — то вы в большой опасности.
Из-за того что оператор var опущен внутри функции, фунция test перезапишет значение foo. Это поначалу может показаться не такой уж и большой проблемой, но если у вас имеется тысяча строк JavaScript-кода и вы не используете var, то вам на пути встретятся страшные и трудноотлаживаемые ошибки — и это не шутка.
+
// глобальная область видимости
+var items = [/* какой-то список */];
+for(var i = 0; i < 10; i++) {
+ subLoop();
+}
+
+function subLoop() {
+ // область видимости subLoop
+ for(i = 0; i < 10; i++) { // пропущенный оператор var
+ // делаем волшебные вещи!
+ }
+}
+
Внешний цикл прекратит работу сразу после первого вызова subLoop, поскольку subLoop перезаписывает глобальное значение переменной i. Использование var во втором цикле for могло бы вас легко избавить от этой ошибки. Никогда не забывайте использовать var, если только влияние на внешнюю область видимости не является тем, что вы намерены получить.
+
Локальные переменные
+
Единственный источник локальных переменных в JavaScript - это параметры функций и переменные, объявленные с использованием оператора var.
+
// глобальная область видимости
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+ // локальная область видимости для функции test
+ i = 5;
+
+ var foo = 3;
+ bar = 4;
+}
+test(10);
+
В то время как foo и i — локальные переменные в области видимости функции test, присвоение bar переопределит значение одноимённой глобальной переменной.
+
Высасывание
+
JavaScript высасывает определения. Это значит, что оба определения с использованием var и определение function будут перенесены наверх заключающей их области видимости.
+
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+ if (false) {
+ goo = 1;
+
+ } else {
+ var goo = 2;
+ }
+ for(var i = 0; i < 100; i++) {
+ var e = data[i];
+ }
+}
+
Этот код трансформируется ещё перед исполнением. JavaScript перемещает операторы var и определение function наверх ближайшей оборачивающей области видимости.
+
// выражения с var переместились сюда
+var bar, someValue; // по умолчанию - 'undefined'
+
+// определение функции тоже переместилось
+function test(data) {
+ var goo, i, e; // потерянная блочная область видимости
+ // переместилась сюда
+ if (false) {
+ goo = 1;
+
+ } else {
+ goo = 2;
+ }
+ for(i = 0; i < 100; i++) {
+ e = data[i];
+ }
+}
+
+bar(); // вылетает с ошибкой TypeError,
+ // поскольку bar всё ещё 'undefined'
+someValue = 42; // присвоения не подвержены высасыванию
+bar = function() {};
+
+test();
+
Потерянная область видимости блока не только переместит операторы var вовне циклов и их тел, но и сделает результаты некоторых конструкций с if неинтуитивными.
+
В исходном коде оператор if изменял глобальную переменнуюgoo, когда, как оказалось, он изменяет локальную переменную — в результате работы высасывания.
+
Если вы не знакомы с высасываниями, то можете посчитать, что нижеприведённый код должен породить
+ReferenceError.
+
// проверить, проинициализована ли SomeImportantThing
+if (!SomeImportantThing) {
+ var SomeImportantThing = {};
+}
+
Но, конечно же, этот код работает: из-за того, что оператор var был перемещён наверх глобальной области видимости
+
var SomeImportantThing;
+
+// другой код может инициализировать здесь переменную SomeImportantThing,
+// а может и нет
+
+// убедиться, что она всё ещё здесь
+if (!SomeImportantThing) {
+ SomeImportantThing = {};
+}
+
Порядок разрешения имён
+
Все области видимости в JavaScript, включая глобальную области видимости, содержат специальную, определённую внутри них, переменную this, которая ссылается на текущий объект.
+
Области видимости функций также содержат внутри себя переменную arguments, которая содержит аргументы, переданные в функцию.
+
Например, когда JavaScript пытается получить доступ к переменной foo в области видимости функции, он будет искать её по имени в такой последовательности:
+
+
Если в текущей области видимости есть выражение var foo, использовать его.
+
Если один из параметров функции называется foo, использовать его.
+
Если функция сама называется foo, использовать её.
+
Перейти на одну область видимости выше и начать с п. 1
+
+
+
Пространства имён
+
Нередкое последствие наличия только одного глобального пространства имён — проблемы с перекрытием имён переменных. В JavaScript эту проблему легко избежать, используя анонимные обёртки.
+
(function() {
+ // самостоятельно созданное "пространство имён"
+
+ window.foo = function() {
+ // открытое замыкание
+ };
+
+})(); // сразу же выполнить функцию
+
Безымянные функции являются выражениями; поэтому, чтобы вы имели возможность их выполнить, они сперва должны быть разобраны.
+
( // разобрать функцию внутри скобок
+function() {}
+) // и вернуть объект функции
+() // вызвать результат разбора
+
Есть другие способы разбора и последующего вызова выражения с функцией; они, хоть и различаются в синтаксисе, но действуют одинаково.
+
// Два других способа
++function(){}();
+(function(){}());
+
Заключение
+
Рекомендуется всегда использовать анонимную обёртку для заключения кода в его собственное пространство имён. Это не только защищает код от совпадений имён, но и позволяет создавать более модульные программы.
+
Важно добавить, что использование глобальных переменных считается плохой практикой. Любое их использование демонстрирует плохое качество кода и может привести к трудноуловимым ошибкам.
+
Массивы
Итерации по массивам и свойства
Несмотря на то, что массивы в JavaScript являются объектами, нет достаточных оснований для использования цикла for in для итерации по элементам массива. Фактически, существует несколько весомых причин против использования for in в массивах.
+
+
Во время выполнения for in циклически перебираются все свойства объекта, находящиеся в цепочке прототипов. Единственный способ исключить ненужные свойства — использовать hasOwnProperty, а это в 20 раз медленнее обычного цикла for.
+
Итерирование
+
Для достижения лучшей производительности при итерации по массивам, лучше всего использовать обычный цикл for.
+
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+ console.log(list[i]);
+}
+
В примере выше есть один дополнительный приём, с помощью которого кэшируется величина длины массива: l = list.length.
+
Несмотря на то, что свойство length определено в самом массиве, поиск этого свойства накладывает дополнительные расходы на каждой итерации цикла. Пусть в этом случае новые движки JavaScript теоретически могут применить оптимизацию, но нет никакого способа узнать, будет оптимизирован код на новом движке или нет.
+
Фактически, отсутствие кэширования может привести к выполнению цикла в два раза медленнее, чем при кэшировании длины
+
Свойство length
+
Хотя геттер свойства length просто возвращает количество элементов содержащихся в массиве, сеттер можно использовать для обрезания массива.
Присвоение свойству length меньшей величины урезает массив, однако присвоение большего значения не даст никакого эффекта.
+
Заключение
+
Для оптимальной работы кода рекомендуется всегда использовать простой цикл for и кэшировать свойство length. Использование for in с массивами является признаком плохого кода, обладающего предпосылками к ошибкам и может привести к низкой скорости его выполнения.
+
Конструктор Array
Так как в конструкторе Array есть некоторая двусмысленность, касающаяся его параметров, настоятельно рекомендуется при создании массивов всегда использовать синтаксис литеральной нотации — [].
В случае, когда в конструктор Array передаётся один аргумент и этот аргумент имеет тип Number, конструктор возвращает новый, заполненный случайными значениями, массив, имеющий длину равную значению переданного аргумента. Стоит заметить, что в этом случае будет установлено только свойство length нового массива, индексы массива фактически не будут проинициализированы.
+
var arr = new Array(3);
+arr[1]; // не определён, undefined
+1 in arr; // false, индекс не был установлен
+
Поведение, которое позволяет изначально установить только размер массива, может пригодиться лишь в нескольких случаях, таких как повторение строк, за счёт чего избегается использование цикла for loop.
+
new Array(count + 1).join(stringToRepeat);
+
Заключение
+
Использование конструктора Array нужно избегать, насколько это возможно. Литералы определённо предпочтительнее — это краткая запись и она имеет более понятный синтаксис, так что при этом даже улучшается читабельность кода.
+
Типы
Равенство и сравнение
JavaScript имеет 2 различных способа сравнения значений объектов на равенство.
+
Оператор сравнения
+
Оператор сравнения состоит из двух символов равенства: ==
+
Слабая типизированность языка JavaScript подразумевает приведение обеих переменных к одному типу для того, чтобы произвести сравнение.
В таблице выше показаны результаты приведения типов и это главная причина, почему использование == повсеместно считается плохой практикой: оно приводит к трудностям в отслеживании ошибок из-за сложных правил преобразования типов.
+
Кроме того, приведение типов во время сравнения также влияет на производительность; например, строка должна быть преобразована в число перед сравнением с другим числом.
+
Оператор строгого равенства
+
Оператор строгого равенства состоит из трёх символов равенства: ===
+
В отличие от обычного оператора равенства, оператор строгого равенства не выполняет приведение типов между операндами.
Результаты выше более понятны и позволяют быстрее выявлять ошибки в коде. Это в определённой степени улучшает код, а также дает прирост производительности в случае, если операнды имеют различные типы.
+
Сравнение объектов
+
Хотя оба оператора == и === заявлены как операторы равенства, они ведут себя по-разному, когда хотя бы один из операндов является Object.
Здесь оба операнда сравниваются на идентичность, а не на равенство; то есть будет проверяться, являются ли операнды одним экземпляром объекта, так же как делает is в Python и сравниваются указатели в С.
+
Заключение
+
Крайне рекомендуется использовать только операторы строгого равенства. В случае, когда намечается преобразование типов, нужно сделать явное приведение и не оставлять их на совести языковых хитростей с преобразованиями.
+
Оператор typeof
Оператор typeof (вместе с instanceof) — это, вероятно, самая большая недоделка в JavaScript, поскольку, похоже, он поломан более, чем полностью.
+
Хотя instanceof еще имеет ограниченное применение, typeof на самом деле имеет только один практический случай применения, который при всём при этом не является проверкой типа объекта.
+
+
Таблица типов JavaScript
+
Значение Класс Тип
+-------------------------------------
+"foo" String string
+new String("foo") String object
+1.2 Number number
+new Number(1.2) Number object
+true Boolean boolean
+new Boolean(true) Boolean object
+new Date() Date object
+new Error() Error object
+[1,2,3] Array object
+new Array(1, 2, 3) Array object
+new Function("") Function function
+/abc/g RegExp object (function в Nitro/V8)
+new RegExp("meow") RegExp object (function в Nitro/V8)
+{} Object object
+new Object() Object object
+
В таблице выше Тип представляет собой значение, возвращаемое оператором typeof. Как хорошо видно, это значение может быть абсолютно любым, но не логичным результатом.
+
Класс представляет собой значение внутреннего свойства [[Class]] объекта.
+
+
Для того, чтобы получить значение [[Class]], необходимо вызвать метод toString у Object.prototype.
+
Класс объекта
+
Спецификация предоставляет только один способ доступа к значению [[Class]] — используя Object.prototype.toString.
В примере выше Object.prototype.toString вызывается со значением this, являющимся объектом, значение [[Class]] которого нужно получить.
+
+
Проверка переменных на определённость
+
typeof foo !== 'undefined'
+
Выше проверяется, было ли foo действительно объявлено или нет; просто обращение к переменной приведёт к ReferenceError. Это единственное, чем на самом деле полезен typeof.
+
Заключение
+
Для проверки типа объекта настоятельно рекомендуется использоватьObject.prototype.toString — это единственный надежный способ. Как показано выше в таблице типов, некоторые возвращаемые typeof значения не определены в спецификации: таким образом, они могут отличаться в различных реализациях.
+
Кроме случая проверки, была ли определена переменная, typeof следует избегать во что бы то ни стало.
+
Оператор instanceof
Оператор instanceof сравнивает конструкторы двух операндов. Это полезно только когда сравниваются пользовательские объекты. Использование на встроенных типах почти так же бесполезно, как и оператор typeof.
+
Сравнение пользовательских объектов
+
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// Всего лишь присваиваем Bar.prototype объект функции Foo,
+// но не экземпляра Foo
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
Здесь надо отметить одну важную вещь: instanceof не работает на объектах, которые происходят из разных контекстов JavaScript (например, из различных документов в web-браузере), так как их конструкторы и правда не будут конструкторами тех самых объектов.
+
Заключение
+
Оператор instanceof должен использоваться только при обращении к пользовательским объектам, происходящим из одного контекста JavaScript. Так же, как и в случае оператора typeof, любое другое использование необходимо избегать.
+
Приведение типов
JavaScript — слабо типизированный язык, поэтому преобразование типов будет применяться везде, где возможно.
+
// Эти равенства — истинны
+new Number(10) == 10; // Number.toString() преобразуется
+ // обратно в число
+
+10 == '10'; // Strings преобразуется в Number
+10 == '+10 '; // Ещё чуток строко-безумия
+10 == '010'; // и ещё
+isNaN(null) == false; // null преобразуется в 0,
+ // который конечно же NaN
+
+// Эти равенства — ложь
+10 == 010;
+10 == '-10';
+
+
Для того, чтобы избежать этого, настоятельно рекомендуется использовать оператор строгого равенства. Впрочем, хотя это и позволяет избежать многих распространенных ошибок, существует ещё много дополнительных вопросов, которые возникают из-за слабости типизации JavaScript.
+
Конструкторы встроенных типов
+
Конструкторы встроенных типов, например, Number и String ведут себя различным образом, в зависимости от того, вызываются они с ключевым словом new или без.
+
new Number(10) === 10; // False, Object и Number
+Number(10) === 10; // True, Number и Number
+new Number(10) + 0 === 10; // True, из-за неявного преобразования
+
Использование встроенных типов, например, Number, с конструктором создаёт новый экземпляр объекта Number, но использование без ключевого слова new создаст функцию Number, которая будет вести себя, как конвертер.
+
Кроме того, присутствие литералов или переменных, которые не являются объектами, приведет к еще большему насилию над типами.
+
Лучший вариант — это явное приведение к одному из трех возможных типов.
+
Приведение к строке
+
'' + 10 === '10'; // true
+
Путём добавления в начале пустой строки, значение легко приводится к строке.
+
Приведение к числовому типу
+
+'10' === 10; // true
+
Используя унарный оператор плюс можно преобразовать значение в число.
+
Приведение к булеву типу
+
Используя оператор not (!) дважды, значение может быть приведено к логическому (булеву) типу.
Но eval исполняется в локальной области видимости только тогда, когда он вызывается напрямуюи при этом имя вызываемой функции именно eval.
+
var foo = 1;
+function test() {
+ var foo = 2;
+ var bar = eval;
+ bar('foo = 3');
+ return foo;
+}
+test(); // 2
+foo; // 3
+
Любой ценой избегайте использования функции eval. 99.9% случаев её "использования" могут достигаться без её участия.
+
eval под прикрытием
+
Обе функции работы с интервалами времениsetTimeout и setInterval могут принимать строку в качестве первого аргумента. Эта строка всегда будет выполняться в глобальной области видимости, поскольку eval в этом случае вызывается не напрямую.
+
Проблемы с безопасностью
+
Кроме всего прочего, функция eval — это проблема в безопасности, поскольку исполняется любой переданный в неё код; никогда не следует использовать её со строками из неизвестных или недоверительных источников.
+
Заключение
+
Никогда не стоит использовать eval: любое применение такого кода поднимает вопросы о качестве его работы, производительности и безопасности. Если вдруг для работы вам необходим eval, эта часть должна тут же ставиться под сомнение и не должна использоваться в первую очередь — необходимо найти лучший способ , которому не требуются вызовы eval.
+
undefined и null
В JavaScript есть два отдельных типа для представления ничего, при этом более полезным из них является undefined.
+
Тип undefined
+
undefined — это тип с единственным возможным значением: undefined.
+
Кроме этого, в языке определена глобальная переменная со значением undefined, и эта переменная так и называется — undefined. Не являясь константой, она не является и ключевым словом. Из этого следует, что её значение можно с лёгкостью переопределить.
+
+
Несколько случаев, когда возвращается undefined:
+
+
При попытке доступа к глобальной переменной undefined (если она не изменена).
+
Неявный возврат из функции при отсутствии в ней оператора return.
+
Из операторов return, которые ничего не возвращают.
+
В результате поиска несуществующего свойства у объекта (и доступа к нему).
+
Параметры, которые не были переданы в функцию явно.
+
При доступе ко всему, чьим значением является undefined.
+
+
Обработка изменений значения undefined
+
Поскольку глобальная переменная undefined содержит копию настоящего значенияundefined, присвоение этой переменной нового значения не изменяет значения типаundefined.
+
Но при этом, чтобы сравнить что-либо со значениемundefined, прежде нужно получить значение самой переменнойundefined.
+
Чтобы защитить код от переопределения переменной undefined, часто используется техника анонимной обёртки, которая использует отсутствующий аргумент.
+
var undefined = 123;
+(function(something, foo, undefined) {
+ // в локальной области видимости `undefined`
+ // снова ссылается на правильное значене.
+
+})('Hello World', 42);
+
Другой способ достичь того же эффекта — использовать определение внутри обёртки.
+
var undefined = 123;
+(function(something, foo) {
+ var undefined;
+ ...
+
+})('Hello World', 42);
+
Единственная разница между этими вариантами в том, что последняя версия будет больше на 4 байта при минификации, а в первом случае внутри анонимной обёртки нет дополнительного оператора var.
+
Использование null
+
Хотя undefined в контексте языка JavaScript чаще используется в качестве традиционного null, настоящий null (и тип и литерал) является в большей или меньшей степени просто другим типом данных.
+
Он используется во внутренних механизмах JavaScript (например для определения конца цепочки прототипов за счёт присваивания Foo.prototype = null). Но в большинстве случаев тип null может быть заменён на undefined.
+
Автоматическая вставка точек с запятой
Хоть JavaScript и имеет синтаксис, подобный языкам семейства C, он при этом не принуждает вас ставить точки с запятой в исходном коде — вы всегда можете их опустить.
+
При этом JavaScript — не язык без точек с запятой, они на самом деле нужны ему, чтобы он мог разобраться в вашем коде. Поэтому парсер JavaScript автоматически вставляет их в те места, где сталкивается с ошибкой парсинга из-за их отсутствия.
+
var foo = function() {
+} // ошибка разбора, ожидается точка с запятой
+test()
+
Происходит вставка и парсер пытается снова.
+
var foo = function() {
+}; // ошибки нет, парсер продолжает
+test()
+
Автоматическая вставка точек с запятой считается одним из наибольших упущений в проекте языка, поскольку она может изменить поведение кода.
+
Как это работает
+
Приведённый код не содержит точек с запятой, так что места для их вставки остаются на совести парсера:
Чрезвычайно высоки шансы, что log возвращает не функцию; таким образом, эта строка вызовет TypeError с сообщением о том, что undefined не является функцией.
+
Заключение
+
Настоятельно рекомендуем никогда не забывать ставить точку с запятой; также рекомендуется оставлять скобки на одной строке с соответствующим оператором и никогда не опускать их для выражений с использованием if / else. Оба этих совета не только повысят читабельность вашего кода, но и предотвратят от изменения поведения кода, произведённого парсером втихую.
+
Другое
setTimeout и setInterval
Поскольку JavaScript поддерживает асинхронность, есть возможность запланировать выполнение функции, используя функции setTimeout и setInterval.
+
+
function foo() {}
+var id = setTimeout(foo, 1000); // возвращает число > 0
+
Функция setTimeout возвращает идентификатор таймаута и планирует вызвать foo через, примерно, тысячу миллисекунд. Функция foo при этом будет вызвана ровно один раз.
+
В зависимости от разрешения таймера в используемом для запуска кода движке JavaScript, а также с учётом того, что JavaScript является однопоточным языком и посторонний код может заблокировать выполнение потока, нет никакой гарантии, что переданный код будет выполнен ровно через указанное в вызове setTimeout время.
+
Переданная первым параметром функция будет вызвана как глобальный объект — это значит, что оператор this в вызываемой функции будет ссылаться на этот самый объект.
+
function Foo() {
+ this.value = 42;
+ this.method = function() {
+ // this ссылается на глобальный объект
+ console.log(this.value); // выведет в лог undefined
+ };
+ setTimeout(this.method, 500);
+}
+new Foo();
+
+
Поочерёдные вызовы с использованием setInterval
+
setTimeout вызывает функцию единожды; setInterval — как и предполагает название — вызывает функцию каждыеX миллисекунд. И его использование не рекомендуется.
+
В то время, когда исполняющийся код будет блокироваться во время вызова с таймаутом, setInterval будет продолжать планировать последующие вызовы переданной функции. Это может (особенно в случае небольших интервалов) повлечь за собой выстраивание вызовов функций в очередь.
+
function foo(){
+ // что-то, что выполняется одну секунду
+}
+setInterval(foo, 1000);
+
В приведённом коде foo выполнится один раз и заблокирует этим главный поток на одну секунду.
+
Пока foo блокирует код, setInterval продолжает планировать последующие её вызовы. Теперь, когда первая foo закончила выполнение, в очереди будут уже десять ожидающих выполнения вызовов foo.
+
Разбираемся с потенциальной блокировкой кода
+
Самый простой и контролируемый способ — использовать setTimeout внутри самой функции.
+
function foo(){
+ // что-то, выполняющееся одну секунду
+ setTimeout(foo, 1000);
+}
+foo();
+
Такой способ не только инкапсулирует вызов setTimeout, но и предотвращает от очередей блокирующих вызовов и при этом обеспечивает дополнительный контроль. Сама функция foo теперь принимает решение, хочет ли она запускаться ещё раз или нет.
+
Очистка таймаутов вручную
+
Удаление таймаутов и интервалов работает через передачу соответствующего идентификатора либо в функцию clearTimeout, либо в функцию clearInterval — в зависимости от того, какая функция set... использовалась для его получения.
+
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
Очистка всех таймаутов
+
Из-за того, что встроенного метода для удаления всех таймаутов и/или интервалов не существует, для достижения этой цели приходится использовать брутфорс.
+
// удаляем "все" таймауты
+for(var i = 1; i < 1000; i++) {
+ clearTimeout(i);
+}
+
Вполне могут остаться таймауты, которые не будут захвачены этим произвольным числом; так что всё же рекомендуется следить за идентификаторами всех создающихся таймаутов, за счёт чего их можно будет удалять индивидуально.
+
Скрытое использование eval
+
setTimeout и setInterval могут принимать строку в качестве первого параметра. Эту возможность не следует использовать никогда, поскольку изнутри при этом производится скрытый вызов eval.
+
+
function foo() {
+ // будет вызвана
+}
+
+function bar() {
+ function foo() {
+ // никогда не будет вызывана
+ }
+ setTimeout('foo()', 1000);
+}
+bar();
+
Поскольку eval в этом случае не вызывается напрямую, переданная в setTimeout строка будет выполнена в глобальной области видимости; так что локальная переменная foo из области видимости bar не будет выполнена.
+
По этим же причинам рекомендуется не использовать строку для передачи аргументов в функцию, которая должна быть вызвана из одной из двух функций, работающих с таймаутами.
+
function foo(a, b, c) {}
+
+// НИКОГДА не делайте такого
+setTimeout('foo(1,2, 3)', 1000)
+
+// Вместо этого используйте анонимную функцию
+setTimeout(function() {
+ foo(a, b, c);
+}, 1000)
+
+
Заключение
+
Никогда не используйте строки как параметры setTimeout или setInterval. Это явный признак действительно плохого кода. Если вызываемой функции необходимо передавать аргументы, лучше передавать анонимную функцию, которая самостоятельно будет отвечать за сам вызов.
+
Кроме того, избегайте использования setInterval в случаях, когда его планировщик может блокировать выполнение JavaScript.
+
Пояснения
От переводчиков
Авторы этой документации требуют от читателя не совершать каких-либо ошибок и постоянно следить за качеством пишущегося кода. Мы, как переводчики и опытные программисты на JavaScript, рекомендуем прислушиваться к этим советам, но при этом не делать из этого крайность. Опыт — сын ошибок трудных, и иногда в борьбе с ошибками зарождается намного более детальное понимание предмета. Да, нужно избегать ошибок, но допускать их неосознанно — вполне нормально.
+
К примеру, в статье про сравнение объектов авторы настоятельно рекомендуют использовать только оператор строгого неравенства ===. Но мы считаем, что если вы уверены и осознали, что оба сравниваемых операнда имеют один тип, вы имеете право опустить последний символ =. Вы вольны применять строгое неравенство только в случаях, когда вы не уверены в типах операндов (!== undefined — это полезный приём). Так в вашем коде будут опасные и безопасные области, но при этом по коду будет явно видно, где вы рассчитываете на переменные одинаковых типов, а где позволяете пользователю вольности.
+
Функцию setInterval тоже можно использовать, если вы стопроцентно уверены, что код внутри неё будет исполняться как минимум в три раза быстрее переданного ей интервала.
+
С другой стороны, использование var и грамотная расстановка точек с запятой — обязательные вещи, халатное отношение к которым никак не может быть оправдано — в осознанном пропуске var (если только вы не переопределяете глобальный объект браузера... хотя зачем?) или точки с запятой нет никакого смысла.
+
Относитесь с мудростью к тому, что вы пишете — важно знать как работает именно ваш код и как это соответствует приведённым в статье тезисам — и уже из этого вы сможете делать вывод, подходит ли вам тот или иной подход или нет. Важно знать как работает прототипное наследование, но это не так необходимо, если вы используете функциональный подход или пользуетесь какой-либо сторонней библиотекой. Важно помнить о том, что у вас недостаёт какого-либо конкретного знания и что пробел следует заполнить, но если вы не используете в работе эту часть, вы всё равно можете писать хороший код — ну, если у вас есть талант.
+
Гонка за оптимизацией — это драматично и правильно, но лучше написать работающий и понятный вам код, а потом уже его оптимизировать и искать узкие места, при необходимости. Оптимизацию необходимо делать, если вы видите явные неудобства для пользователя в тех или иных браузерах или у вас есть супер-крупный проект, которым никогда не мешает оптимизация, или вы работаете с какой-либо сверхтребовательной технологией типа WebGL. Данная документация очень поможет вам в определении этих узких мест.
+
\ No newline at end of file
diff --git a/site/style/garden.css b/style/garden.css
similarity index 100%
rename from site/style/garden.css
rename to style/garden.css
diff --git a/site/style/print.css b/style/print.css
similarity index 100%
rename from site/style/print.css
rename to style/print.css
diff --git a/tr/index.html b/tr/index.html
new file mode 100644
index 0000000..fad12b7
--- /dev/null
+++ b/tr/index.html
@@ -0,0 +1,1508 @@
+JavaScript Garden
Giriş
Giriş
JavaScript Garden JavaScript programlama dilinin acayiplikleri üzerine
+derlenmiş bir döküman koleksiyonudur. Henüz ustalaşmamış JavaScript
+programcılarının sıkça yaptığı yanlışlar, dile has incelikler ve performans
+sorunlarına karşı tavsiyeler içerir.
+
JavaScript Garden'ın amacı size JavaScript öğretmek değildir. Bu rehberde
+anlatılan konuları anlamak için JavaScript dilini önceden biliyor olmanız
+gerekir. Eğer JavaScript dilinin temellerini öğrenmek istiyorsanız, lütfen
+Mozilla Programcı Ağı'nda bulunan mükemmel rehbere başvurun.
JavaScript Garden MIT lisansı altında yayınlanmıştır ve GitHub
+üzerinde bulunmaktadır. Eğer rehberde yanlışlıklar veya yazım hatalarına
+rastlarsanız lütfen sorunu bize bildirin veya bir pull request gönderin.
+Bizi ayrıca Stack Overflow'da JavaScript sohbet odasında da
+bulabilirsiniz.
+
Nesneler
Nesne Kullanımı ve Özellikleri
JavaScript'te iki istisna dışında her şey bir nesne olarak davranır;
+bu istisnalar da null ve undefined
+'dır.
Sık düşülen bir yanılgı sayı sabitlerinin nesne olarak kullanılamayacağıdır. Bu
+yanılgının sebebi de JavaScript çözümleyicisinin nokta notasyonu ile girilen
+sayıları bir reel sayı olarak algılama hatasıdır.
+
2.toString(); // SyntaxError hatası verir
+
Bu hatayı aşıp sayı sabitlerinin de nesne olarak davranmasını sağlamak için
+uygulanabilecek bazı çözümler vardır.
+
2..toString(); // ikinci nokta doğru şekilde algılanır
+2 .toString(); // noktanın solundaki boşluğa dikkat edin
+(2).toString(); // ilk önce 2 değerlendirilir
+
Bir veri türü olarak nesneler
+
JavaScript nesneleri aynı zamanda bir Hashmap olarak da kullanılabilir,
+nesneler temelde isimli özellikler ve bunlara karşılık gelen değerlerden
+ibarettir.
+
Nesne sabiti ({} notasyonu) ile düz bir nesne yaratmak mümkündür. Bu yeni
+nesne kalıtım ile Object.prototype 'dan türüyecektir ve
+hiçbir baz özelliğe sahip olmayacaktır.
+
var foo = {}; // yeni bir boş nesne
+
+// adı 'test' ve değeri 12 olan bir özelliği sahip yeni bir nesne
+var bar = {test: 12};
+
Özelliklere erişmek
+
Bir nesnenin özelliklerine iki yolla erişilebilir, ya nokta notasyonu ile veya
+köşeli parantez notasyonu ile.
Her iki notasyon da aynı şekilde çalışır, tek fark köşeli parantez notasyonunun
+özelliklerin dinamik olarak oluşturulmasına ve normalde bir yazım hatasına yol
+açabilecek özellik isimlerinin kullanılmasına izin vermesidir.
+
Özellikleri silmek
+
Bir nesnenin özelliklerinden birini silmenin tek yolu delete operatörünü
+kullanmaktır; özelliğe undefined veya null değerlerini atamak sadece
+özelliğin değerini kaldırır, anahtarı değil.
+
var obj = {
+ bar: 1,
+ foo: 2,
+ baz: 3
+};
+obj.bar = undefined;
+obj.foo = null;
+delete obj.baz;
+
+for(var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ console.log(i, '' + obj[i]);
+ }
+}
+
Yukarıdaki örnek sonuç olarak hem bar undefined hem de foo null yazacaktır.
+Sadece baz özelliği kaldırılmış olacak ve çıktıda görünmeyecektir.
+
Anahtar notasyonu
+
var test = {
+ 'case': 'anahtar kelime olduğu için katar olarak girildi',
+ delete: 'yine bir anahtar kelime' // SyntaxError hatası
+};
+
Nesne özellikleri düz karakterler olarak da katar notasyonu ile de
+tanımlanabilir. Fakat JavaScript çözümleyicisinin bir başka tasarım hatası
+yüzünden, yukarıdaki örnek ECMAScript 5 öncesinde bir SyntaxError hatası
+verecektir.
+
Bu hata delete 'in bir anahtar kelime olmasından kaynaklanır, bu nedenle
+eski JavaScript motorlarının bu örneği doğru algılaması için karakter katarı
+notasyonu ile girilmelidir.
+
Prototip
JavaScript klasik bir kalıtım modeli değil prototip modeli kullanır.
+
Çoğu zaman bu modelin JavaScript'in zayıf yönlerinden biri olduğu söylense de,
+aslında prototip model klasik modelden daha güçlüdür. Mesela prototip model
+temel alınarak klasik kalıtım modeli oluşturulabilir, fakat bunun tersini yapmak
+çok daha zordur.
+
Prototip kalıtım modeli kullanan tek popüler dil JavaScript olduğu için iki
+model arasındaki farklılıklara alışmak biraz zaman alır.
+
İlk büyük farklılık JavaScript'te kalıtımın prototip zincirleri ile
+yapılmasıdır.
+
+
function Foo() {
+ this.value = 42;
+}
+Foo.prototype = {
+ method: function() {}
+};
+
+function Bar() {}
+
+// Bar nesnesinin prototipi olarak yeni bir Foo nesnesini ata
+Bar.prototype = new Foo();
+Bar.prototype.foo = 'Hello World';
+
+// Nesne oluşturucusunun Bar olmasını sağla
+Bar.prototype.constructor = Bar;
+
+var test = new Bar() // yeni bir Bar oluştur
+
+// Sonuçta ortaya çıkan prototip zinciri
+test [bir Bar sınıfı nesnesi]
+ Bar.prototype [bir Foo sınıfı nesnesi]
+ { foo: 'Hello World' }
+ Foo.prototype
+ { method: ... }
+ Object.prototype
+ { toString: ... /* vs. */ }
+
Yukarıda, test nesnesi hem Bar.prototype hem de Foo.prototype 'dan
+türeyecektir; bu nedenle Foo 'da tanımlanmış olan method fonksiyonuna
+da erişebilir. Ayrıca, prototipi olan tekFoo nesnesinin value
+özelliğine de erişebilir. Dikkat edilmesi gereken bir nokta, new Bar()
+ifadesinin yeni bir Foo nesnesi yaratmayıp, prototipine atanmış olan
+nesneyi kullanmasıdır; bu nedenle, tüm Bar nesneleri aynıvalue
+özelliğine sahip olacaktır.
+
+
Özelliklere bulmak
+
Bir nesnenin özelliklerine erişildiğinde, JavaScript, istenen isimdeki özelliği
+bulana kadar prototip zincirinde yukarı doğru dolaşır.
+
Zincirin en üstüne ulaştığında (yani Object.prototype) ve hala istenen özelliği
+bulamamışsa sonuç olarak undefined verecektir.
+
prototype özelliği
+
prototype özelliği dil tarafından prototip zincirleri oluşturmak için
+kullanılsa da, bu özelliğe herhangi bir değer atamak mümkündür. Fakat
+prototip olarak atanan ilkel nesne türleri göz ardı edilecektir.
+
function Foo() {}
+Foo.prototype = 1; // hiç bir etkisi olmaz
+
Bir önceki örnekte gösterildiği gibi, prototip olarak nesneler atanabilir, bu da
+prototip zincirlerinin dinamik olarak oluşturulabilmesini sağlar.
+
Performans
+
Prototip zincirinin yukarısındaki özellikleri aramanın performansı kritik olan
+programlarda olumsuz etkileri olabilir. Ek olarak, mevcut olmayan özelliklere
+erişmeye çalışmak da tüm prototip zincirinin baştan sona taranmasına neden
+olacaktır.
+
Ayrıca, bir nesnenin özellikleri üzerinde iterasyon
+yapıldığında da prototip zinciri üzerindeki tüm özelliklere bakılacaktır.
+
Temel prototiplerin genişletilmesi
+
Sıklıkla yapılan bir hata Object.prototype 'ı veya diğer baz prototipleri
+genişletmektir.
+
Bu tekniğe monkey patching denir ve kapsüllemeyi bozar. Bu teknik
+Prototype gibi bazı popüler sistemlerde kullanılsa bile, temel nesne
+türlerine standart olmayan özellikler eklenmesinin geçerli iyi bir nedeni
+yoktur.
+
Temel prototipleri genişletmenin tek bir geçerli nedeni vardır, o da daha
+yeni JavaScript motorlarında bulunan özelliklerin eski motorlara getirilmesidir;
+mesela Array.forEach.
+
Sonuç
+
Prototip kalıtım modeli kullanan karmaşık programlar yazmadan önce bu modelin
+tamamen anlaşılması şarttır. Ayrıca, prototip zincirinin uzunluğuna dikkat
+edilmeli ve çok uzaması durumunda performans sorunları yaşamamak için parçalara
+bölünmelidir. Bundan başka, temel prototipler yeni JavaScript motorları ile
+uyumluluk sağlamak dışında bir nedenle asla genişletilmemelidir.
+
hasOwnProperty
Bir özelliğin nesnenin prototip zinciri üzerinde bir yerde
+değil, kendisi üzerinde tanımlandığını belirlemek için, Object.prototype
+kalıtımı ile tüm nesnelerin sahip olduğu hasOwnProperty metodunun kullanılması
+gerekir.
+
+
hasOwnProperty JavaScript'te nesne özellikleri üzerinde çalışıp prototip
+zincirinin tümünü dolaşmayan tek şeydir.
+
// Object.prototype'a bar özelliğini ekle
+Object.prototype.bar = 1;
+var foo = {goo: undefined};
+
+foo.bar; // 1
+'bar' in foo; // true
+
+foo.hasOwnProperty('bar'); // false
+foo.hasOwnProperty('goo'); // true
+
Sadece hasOwnProperty beklenen doğru sonucu verecektir, nesne özellikleri
+üzerinde iterasyon yaparken bu çok önemlidir. Bir nesnenin kendisi üzerinde
+değil de protip zinciri üzerinde bir yerde tanımlanmış olan özelliklerini
+çıkarmanın başka hiçbir yolu yoktur.
+
hasOwnProperty özelliği
+
JavaScript hasOwnProperty adının bir özellik olarak kullanılmasını engellemez;
+bu nedenle bir nesnenin bu isimde bir özelliğe sahip olması ihtimali varsa,
+doğru sonuç alabilmek için hasOwnPropertyharicen kullanılmalıdır.
+
var foo = {
+ hasOwnProperty: function() {
+ return false;
+ },
+ bar: 'Here be dragons'
+};
+
+foo.hasOwnProperty('bar'); // her zaman false verir
+
+// hasOwnProperty başka bir nesne üzerinde
+// kullanıldığında 'this' foo olur
+({}).hasOwnProperty.call(foo, 'bar'); // true
+
Sonuç
+
Bir nesnenin bir özelliği sahip olup olmadığını kontrol etmek için
+kullanılabilecek tek yöntem hasOwnProperty 'dir. Aynı zamanda, nesne
+prototiplerinin genişletilmesinden kaynaklanabilecek
+hataların önüne geçmek için, tümfor in döngüleri ile
+hasOwnProperty kullanılması tavsiye olunur.
+
for in Döngüsü
Tıpkı in operatörü gibi for in döngüsü de bir nesnenin özellikleri üzerinde
+iterasyon yaparken prototip zincirini dolaşır.
+
+
// Object.prototype'a bar özelliğini ekle
+Object.prototype.bar = 1;
+
+var foo = {moo: 2};
+for(var i in foo) {
+ console.log(i); // hem bar hem de moo yazar
+}
+
for in döngüsünün davranışını değiştirmek mümkün olmadığı için, istenmeyen
+özelliklerin döngünün içinde filtrelenmesi gerekir, bu da Object.prototype
+nesnesinin hasOwnProperty metodu ile yapılır.
+
+
hasOwnProperty kullanarak filtrelemek
+
// yukarıdaki örnekteki foo nesnesi
+for(var i in foo) {
+ if (foo.hasOwnProperty(i)) {
+ console.log(i);
+ }
+}
+
Doğru kullanım bu yeni versiyonda gösterildiği gibidir. hasOwnProperty kontrol
+edildiği için sadecemoo yazacaktır. hasOwnProperty kullanılmaz ise ve
+Object.prototype 'ın baz özellikleri değiştirilmişse, program bazı hatalara
+yatkın olabilir.
+
Bunu yapan ve yaygın olarak kullanılan bir JavaScript sistemi Prototype
+'dır. Bu sistemde hasOwnProperty kullanmayan for in döngüleri kesinlikle
+hatalı sonuç verecektir.
+
Sonuç
+
hasOwnPropertyher zaman kontrol edilmelidir. Programın içinde çalıştığı
+ortam için, nesnelerin baz özelliklerinin değiştirilip değiştirilmediğine dair
+hiçbir kabul yapılmamalıdır.
+
Fonksiyonlar
Fonksiyon Tanımlaması ve Fonksiyon İfadesi
Fonksiyonlar JavaScript'te birinci sınıf nesnelerdir, yani sıradan bir değer
+gibi kullanılabilirler. Bu özellik sıklıkla bir isimsiz fonksiyonu başka bir
+fonksiyona - ki bu muhtemelen asenkron bir fonksiyondur - callback olarak
+geçirmekte kullanılır.
+
function tanımlaması
+
function foo() {}
+
Yukarıdaki fonksiyon tanımlaması program çalışmadan önce
+yukarı taşınır ve böylece tanımlandığı kapsam içinde
+her yerde (hatta tanımlanmadan önce bile) kullanılabilir.
+
foo(); // foo bu satır çalışmadan önce oluşturuldu
+function foo() {}
+
function ifadesi
+
var foo = function() {};
+
Bu örnekte isimsiz fonksiyonfoo değişkenine atanır.
+
foo; // 'undefined'
+foo(); // Bu satır bir TypeError hatasına neden olur
+var foo = function() {};
+
Yukarıdaki var anahtar kelimesi bir bildirim olduğu için foo değişkeni
+program çalışmadan önce yukarı alınır, program çalıştığında foo tanımlanmştır.
+
Fakat değer atamaları sadece program çalışırken gerçekleşeceği için, ilgili
+satır çalıştığında, foo değişkeninin değeri varsayılan olarak
+undefined olacaktır.
+
İsimli fonksiyon ifadesi
+
Bir başka özel durum isimli fonksiyon ifadesidir.
+
var foo = function bar() {
+ bar(); // Çalışır
+}
+bar(); // ReferenceError hatası verir
+
Burada bar fonksiyonuna dış kapsamdan ulaşılamaz, çünkü sadece foo
+değişkenine atanmıştır; fakat iç kapsamda bar fonksiyonuna erişilebilir.
+Bunun nedeni JavaScript'te isim çözümlemenin çalışma
+şeklidir, fonksiyonun adına fonksiyonun içinden her zaman erişilebilir.
+
this Nasıl Çalışır
JavaScript'te this özel kelimesinin anlamı diğer programlama dillerinden
+farklıdır. this kelimesinin birbirinden farklı anlamlar yüklendiği tam
+beş durum vardır.
+
Genel kapsam
+
this;
+
this kelimesi genel kapsamda kullanıldığında global nesneye işaret eder.
+
Bir fonksiyon çağırma
+
foo();
+
Burada this yine global nesneye işaret eder.
+
+
Bir metod çağırma
+
test.foo();
+
Bu örnekte this kelimesi test 'e işaret edecektir.
+
Bir nesne oluşturucu çağırma
+
new foo();
+
Bir fonksiyon başında new anahtar kelimesi ile birlikte çağrılırsa bir
+nesne oluşturucu olarak davranır. Bu fonksiyonun
+içinde this kelimesi yeni oluşturulanObject 'e işaret eder.
+
this kelimesinin atanması
+
function foo(a, b, c) {}
+
+var bar = {};
+foo.apply(bar, [1, 2, 3]); // dizi aşağıdaki gibi açılır
+foo.call(bar, 1, 2, 3); // sonuç: a = 1, b = 2, c = 3
+
Function.prototype 'ın call veya apply metodları kullanıldığında, çağrılan
+fonksiyonun içinde this 'in değeri ilk argümanın değeri olarak atanır.
+
Sonuç olarak, yukarıdaki örnekte metod çağırma durumu geçerli olmayacak,
+bunun yerine foo fonksiyonu içinde this 'in değeri bar olacaktır.
+
+
Sık düşülen yanılgılar
+
Yukarıdaki durumların çoğu mantıklı görünse bile, ilk durum dilin tasarım
+hatalarından biri olarak değerlendirilmelidir çünkü hiçbir pratik
+kullanılımı yoktur.
+
Foo.method = function() {
+ function test() {
+ // this genel nesneye işaret eder
+ }
+ test();
+}
+
Bir başka yanılgı test fonksiyonunun içinde this 'in Foo 'ya işaret
+edeceğinin sanılmasıdır, ama bu doğru değildir.
+
test fonksiyonu içinden Foo 'ya erişmenin yolu method içinde bir lokal
+değişken oluşturmaktır.
+
Foo.method = function() {
+ var that = this;
+ function test() {
+ // Burada this yerine that kullanın
+ }
+ test();
+}
+
that kelimesinin dilde özel bir anlamı yoktur, ama sıklıkla dış kapsamdaki
+this 'e işaret etmek için kullanılır. Bu yöntem closure
+kavramı ile birlikte kullanıldığında this değerini program içinde taşımaya da
+yarar.
+
Metodları değişkenlere atamak
+
JavaScript'te mevcut olmayan bir başka özellik de fonksiyon isimlendirmedir,
+başka bir deyişle bir metodu bir değişkene atamak.
+
var test = someObject.methodTest;
+test();
+
İlk durum nedeniyle test artık sıradan bir fonksiyon olarak davranacaktır; bu
+nedenle test fonksiyonu içinde this artık someObject 'e işaret
+etmeyecektir.
+
this kelimesinin geç bağlanması ilk bakışta yanlış görünse de, aslında
+prototipsel kalıtımı mümkün kılan şey budur.
Yukarıda Bar sınıfına ait bir nesnenin method 'u çağrıldığında this bu
+nesneye işaret edecektir.
+
Closure ve Referanslar
JavaScript'in en güçlü özelliklerinden biri de closure 'lara sahip olmasıdır.
+Bunun anlamı her hangi bir kapsamın her zaman kendisini içeren kapsama
+erişebilmesidir. JavaScript'te tek kapsam fonksiyon kapsamı
+olduğu için temelde tüm fonksiyonlar closure 'durlar.
Burada, Counterikiclosure verir: increment fonksiyonu ve get
+fonksiyonu. Bu iki fonksiyon da Counter fonksiyonun kapsamına ait bir
+referans 'a sahiptir, ve bu nedenle söz konusu kapsamda tanımlanmış olan
+count değişkenine erişebilirler.
+
Private değişkenler nasıl işler
+
JavaScript'te kapsam referanslarına erişmek yada atama yapmak mümkün olmadığı
+için, dış kapsamdan count değişkenine ulaşmak mümkün değildir. Bu
+değişkene ulaşmanın tek yolu yukarıdaki iki closure 'dur.
+
var foo = new Counter(4);
+foo.hack = function() {
+ count = 1337;
+};
+
Bu program parçası Counter fonksiyonun kapsamındaki count değişkeninin
+değerini değiştirmez, çünkü foo.hackbu kapsamda tanımlanmamıştır.
+Bunun yerine global kapsamda yeni bir değişen oluşturur (yada mevcut bir
+değişkeni değiştirir).
+
Döngü içinde closure
+
Sık yapılan bir hata, döngü içinde closure kullanıp döngünün indeks değişkeninin
+değerinin kopyalanacağını varsaymaktır.
+
for(var i = 0; i < 10; i++) {
+ setTimeout(function() {
+ console.log(i);
+ }, 1000);
+}
+
Yukarıdaki örnek çıktı olarak 0 - 9 arası sayıları vermek yerine, 10
+sayısını on kez yazacaktır.
+
İçteki isimsiz fonksiyon i değişkeninin değerine değil referansına sahiptir
+ve console.log çağrıldığında, for döngüsü çoktan tamamlanmış ve i
+değişkeninin değeri 10 olmuştur.
+
İstenen davranışı elde etmek için i değişkeninin değerinin kopyalanması
+gerekir.
+
Referans probleminin çözümü
+
Döngünün indeks değişkeninin değerini kopyalamanın en iyi yolu bir
+isimsiz fonksiyon kullanmaktır.
Dıştaki isimsiz fonksiyon her adımda çağrılacak ve e parametresi olarak
+i 'nin değerinin bir kopyası verilecektir.
+
setTimeOut fonksiyonuna verilen isimsiz fonksiyon artık e 'ye ait bir
+referansa sahip olacaktır, ve referansın değeri döngü tarafından
+değiştirilmeyecektir.
+
Bu davranışı başka bir yolla da elde etmek mümkündür; isimsiz fonksiyondan başka
+bir fonksiyon döndürmek. Bu durumda yukarıdaki ile aynı davranış elde
+edilecektir.
JavaScript'te her fonksiyon kapsamında arguments adlı özel bir nesne
+tanımlıdır. Bu nesne fonksiyon çağrılırken verilen argümanların listesini
+içerir.
+
+
arguments nesnesi bir Arraydeğildir. Bir dizinin özelliklerinin bir
+kısmına sahip olsa da (length özelliği) Array.prototype sınıfından
+türetilmemiştir, aslında bir Object bile değildir.
+
Bu nedenle, arguments nesnesi üzerinde push, pop ve slice gibi standart
+dizi metotlarını kullanmak mümkün değildir. Klasik for döngüsü arguments
+nesnesi ile kullanılabilir, ancak standart dizi metotlarını kullanmak için
+gerçek bir diziye dönüştürmek gerekir.
+
Diziye dönüştürmek
+
Aşağıdaki program parçası arguments nesnesinin tüm elemanlarına sahip yeni bir
+dizi verecektir.
+
Array.prototype.slice.call(arguments);
+
Bu dönüşüm yavaştır, ve performansın belirleyici olduğu durumlarda
+kullanılması tavsiye olunmaz.
+
Argümanların geçirilmesi
+
Aşağıdaki örnekte, argümanların bir fonksiyondan diğerine geçirilmesi
+için önerilen yöntem gösterilmiştir.
+
function foo() {
+ bar.apply(null, arguments);
+}
+function bar(a, b, c) {
+ // do stuff here
+}
+
Bir başka püf noktası da call ve apply 'ı birlikte kullanarak hızlı,
+ilişkisiz fonksiyonlar yaratmaktır.
+
function Foo() {}
+
+Foo.prototype.method = function(a, b, c) {
+ console.log(this, a, b, c);
+};
+
+// "method" 'un ilişkisiz bir versiyonunu yarat
+// Aldığı parametreler: this, arg1, arg2...argN
+Foo.method = function() {
+
+ // Sonuç: Foo.prototype.method.call(this, arg1, arg2... argN)
+ Function.call.apply(Foo.prototype.method, arguments);
+};
+
Tanımlı parametreler ve argüman indisleri
+
arguments nesnesi her iki özelliği ve fonksiyonun tanımlı parametreleri için
+getter ve setter fonksiyonlar oluşturur.
+
Sonuç olarak, bir tanımlı parametrenin değerini değiştirmek arguments
+nesnesindeki karşılık gelen özelliğin değerini de değiştirecektir.
+
function foo(a, b, c) {
+ arguments[0] = 2;
+ a; // 2
+
+ b = 4;
+ arguments[1]; // 4
+
+ var d = c;
+ d = 9;
+ c; // 3
+}
+foo(1, 2, 3);
+
Performans mitleri ve gerçekler
+
arguments nesnesi fonksiyon kapsamında bir değişken veya tanımlı parametre
+olarak kullanılmış olması durumları dışında her zaman oluşturulur. Kullanılıp
+kullanılmaması fark etmez.
+
getter ve setter fonksiyonlar her zaman oluşturulur; dolayısıyla
+arguments nesnesini kullanmanın performans üzerinde olumsuz bir etkisi yoktur,
+özellikle de sadece arguments nesnesinin özelliklerine erişmekten ibaret
+olmayan gerçek programlarda.
+
+
Fakat, modern JavaScript motorlarının performansını ciddi bir şekilde etkileyen
+bir durum vardır. Bu durum arguments.callee nesnesinin kullanılmasıdır.
+
function foo() {
+ arguments.callee; // içinde olduğumuz fonksiyon nesnesi
+ arguments.callee.caller; // ve çağıran fonksiyon nesnesi
+}
+
+function bigLoop() {
+ for(var i = 0; i < 100000; i++) {
+ foo(); // Normalde inline edilirdi...
+ }
+}
+
Yukarıdaki program parçasında, foo fonksiyonuna inlining uygulanması
+mümkün değildir çünkü fonksiyonun hem kendisini ve kendisini çağıran fonksiyonu
+bilmesi gerekmektedir. Bu yüzden hem inlining yapılamadığı için bir performans
+artışı sağlanamamış hem de kapsüllenme bozulmuş olmaktadır, çünkü fonksiyon
+artık kendisini çağıran kapsama bağımlı hale gelmiş olabilir.
+
arguments.callee ve özelliklerinin asla kullanılmaması
+şiddetle tavsiye olunur.
+
+
Nesne Oluşturucular
JavaScript'te oluşturucular diğer dillerden farklıdır. Başında new bulunan
+her fonksiyon çağrısı bir oluşturucudur.
+
Oluşturucunun (çağrılan fonksiyonun) içinde this 'in değeri yeni yaratılan
+Object 'dir. Bu yeni nesnenin prototipi oluşturucu
+olarak çağrılan fonksiyon nesnesinin prototipidir.
+
Çağrılan fonksiyonda bir return ifadesi yoksa, this (yani yeni nesneyi)
+döndürür.
+
function Foo() {
+ this.bla = 1;
+}
+
+Foo.prototype.test = function() {
+ console.log(this.bla);
+};
+
+var test = new Foo();
+
Yukarıdaki program Foo oluşturucusunu çağırır ve yeni yaratılan nesnenin
+prototipiniFoo.prototype olarak belirler.
+
Oluşturucunun içinde bir return ifadesi bulunması durumunda, ve sadece
+bu değer bir Object ise oluşturucu fonksiyon verilen değeri döndürür.
+
function Bar() {
+ return 2;
+}
+new Bar(); // yeni bir Bar nesnesi
+
+function Test() {
+ this.value = 2;
+
+ return {
+ foo: 1
+ };
+}
+new Test(); // döndürülen nesne
+
new anahtar kelimesi ihmal edilirse, fonksiyon yeni bir nesne döndürmez.
+
function Foo() {
+ this.bla = 1; // global nesnenin özelliğini değiştirir
+}
+Foo(); // undefined
+
Yukarıdaki örnek bazı durumlarda doğru çalışıyor gibi görünebilir, ama
+JavaScript'te this 'in çalışma şeklinden dolayı this
+'in değeri global nesne olacaktır.
+
Nesne fabrikaları
+
new anahtar kelimesini ihmal edebilmek için oluşturucu fonksiyonun bir değer
+döndürmesi gerekir.
Yukarıda Bar fonksiyonunu çağıran her iki ifade de aynı şeyi döndürecektir:
+method adında bir closure özelliği olan yeni yaratılmış
+bir nesne.
+
Başka bir nokta da new Bar() fonksiyonunun döndürülen nesnenin prototipini
+etkilememesidir. Yeni nesnenin prototipi oluşturulacaktır ancak Bar bu
+nesneyi döndürmez.
+
Yukarıdaki örnekte new anahtar kelimesini kullanmakla kullanmamak arasında
+hiçbir bir fark yoktur.
+
Fabrikalar ile yeni nesneler oluşturmak
+
new anahtar kelimesinin kullanılmaması tavsiye edilir, çünkü unutulması
+durumu hatalara sebep olabilir.
+
Bunun yerine yeni bir nesne oluşturmak için bir fabrika kullanılmalıdır.
Yukarıdaki örnek hem new anahtar kelimesinin unutulmasından etkilenmez hem de
+private değikenlerin kullanılmasını kolaylaştırır, ama
+bazı dezavantajları da vardır.
+
+
Oluşturulan nesneler bir prototip üzerinde metotlarını paylaşmadıkları
+için daha fazla hafıza kullanılır.
+
Başka bir sınıf türetmek için fabrikanın tüm metotları başka bir nesneden
+kopyalaması veya bu nesneyi yeni nesnenin prototipine yerleştirmesi gerekir.
+
Sadece new anahtar kelimesinin ihmal edilmesinden kaynaklanacak sorunları
+gidermek için prototip zincirinden vazgeçmek dilin ruhuna aykırıdır.
+
+
Sonuç
+
new anahtar kelimesini ihmal etmek hatalara neden olabilir, fakat bu
+kesinlikle prototip zincirinden vazgeçmek için bir neden olamaz. Hangi
+çözümün belirli bir programa uygun olduğu kararını verirken, en önemli nokta
+nesne oluşturmak için belirli bir yöntemi seçip bu çözüme bağlı kalmaktır.
+
Kapsamlar ve İsim Uzayları
JavaScript'te birbiri ile eşleşen ayraçlar kullanılmasına karşın blok
+kapsamı bulunmaz; bu nedenle, dilde sadece fonksiyon kapsamı mevcuttur.
+
function test() { // fonksiyon kapsamı
+ for(var i = 0; i < 10; i++) { // kapsam değil
+ // sayaç
+ }
+ console.log(i); // 10
+}
+
+
JavaScript'te isim uzayları kavramı da bulunmaz, tanımlanan herşey
+genel olarak paylaşılmış tek bir isim uzayının içindedir.
+
Bir değişkene erişildiğinde, JavaScript değişkenin tanımını bulana dek yukarıya
+doğru tüm kapsamlara bakar. Genel kapsama ulaşıldığı halde hala değişkenin
+tanımı bulanamamışsa bir ReferenceError hatası oluşur.
+
Genel değişkenler felaketi
+
// A programı
+foo = '42';
+
+// B programı
+var foo = '42'
+
Yukarıdaki iki program birbirinden farklıdır. A programında genel kapsamda
+bir foo değişkeni tanımlanmıştır, B programındaki foo değişkeni ise mevcut
+kapsamda tanımlanmıştır.
+
Bu iki tanımlamanın birbirinden farklıetkileri olacaktır, var anahtar
+kelimesini kullanmamanın önemli sonuçları olabilir.
test fonksiyonun içinde var anahtar kelimesinin atlanması genel kapsamdaki
+foo değişkeninin değerini değiştirecektir. İlk bakışta bu önemsiz gibi görünse
+de, binlerce satırlık bir programda var kullanılmaması korkunç ve takibi güç
+hatalara neden olacaktır.
+
// genel kapsam
+var items = [/* bir dizi */];
+for(var i = 0; i < 10; i++) {
+ subLoop();
+}
+
+function subLoop() {
+ // subLoop fonksiyonun kapsamı
+ for(i = 0; i < 10; i++) { // var kullanılmamış
+ // do amazing stuff!
+ }
+}
+
Dışarıdaki döngüden subLoop fonksiyonu bir kez çağrıldıktan sonra çıkılacaktır,
+çünkü subLoopi değişkeninin dış kapsamdaki değerini değiştirir. İkinci
+for döngüsünde de var kullanılması bu hatayı kolayca engelleyecektir.
+Bilinçli olarak dış kapsama erişilmek istenmiyorsa var ifadesi asla
+atlanmamalıdır.
+
Lokal değişkenler
+
JavaScript'te lokal değişkenler sadece fonksiyon
+parametreleri ve var ifadesi ile tanımlanan değişkenlerdir.
+
// genel kapsam
+var foo = 1;
+var bar = 2;
+var i = 2;
+
+function test(i) {
+ // test fonksiyonunun lokal kapsamı
+ i = 5;
+
+ var foo = 3;
+ bar = 4;
+}
+test(10);
+
test fonksiyonun içinde foo ve i lokal değişkenlerdir, bar değişkenine
+değer atanması ise genel kapsamdaki aynı isimdeki değişkenin değerini
+değiştirecektir.
+
Yukarı taşıma
+
JavaScript'te tanımlamalar yukarı taşınır. Yani hem var ifadesi hem de
+function bildirimleri içindeki bulundukları kapsamın en üstüne taşınırlar.
+
bar();
+var bar = function() {};
+var someValue = 42;
+
+test();
+function test(data) {
+ if (false) {
+ goo = 1;
+
+ } else {
+ var goo = 2;
+ }
+ for(var i = 0; i < 100; i++) {
+ var e = data[i];
+ }
+}
+
Program çalışmadan önce yukarıdaki kod dönüştürülür. JavaScript, var
+ifadelerini ve function bildirimlerini içinde bulundukları kapsamın en üstüne
+taşır.
+
// var ifadeleri buraya taşınır
+var bar, someValue; // varsayılan değerleri 'undefined' olur
+
+// function bildirimi de yukarı taşınır
+function test(data) {
+ var goo, i, e; // blok kapsamı olmadığı için buraya taşınır
+ if (false) {
+ goo = 1;
+
+ } else {
+ goo = 2;
+ }
+ for(i = 0; i < 100; i++) {
+ e = data[i];
+ }
+}
+
+bar(); // bir TypeError hatası oluşur çünkü bar hala 'undefined'
+someValue = 42; // değer atamaları etkilenmez
+bar = function() {};
+
+test();
+
Blok kapsamının bulunmaması nedeniyle hem var ifadeleri döngülerin dışına
+taşınır hem de bazı if ifadeleri anlaşılmaz sonuçlar verebilir.
+
Orijinal programda if ifadesi goo isimli genel değişkeni değiştiriyor gibi
+görünüyordu, fakat yukarı taşımadan sonra anlaşıldığı gini aslında
+lokal değişkeni değiştiriyor.
+
Yukarı taşıma dikkate alınmadığında aşağıdaki programın bir ReferenceError
+oluşturacağı sanılabilir.
+
// SomeImportantThing değişkenine değer atanmış mı, kontrol et
+if (!SomeImportantThing) {
+ var SomeImportantThing = {};
+}
+
Fakat var değişkeni genel kapsamın en üstüne taşınacağı için bu program
+çalışacaktır.
+
var SomeImportantThing;
+
+// SomeImportantThing arada bir yerde atanmış olabilir
+
+// Değer atandığından emin ol
+if (!SomeImportantThing) {
+ SomeImportantThing = {};
+}
+
İsim çözümleme
+
JavaScript'te genel kapsam da dahil tüm kapsamlarda this
+adında bir özel değişken tanımlanmıştır, bu değişken geçerli nesneyi gösterir.
+
Fonksiyon kapsamlarında aynı zamanda arguments adında
+bir değişken tanımlanmıştır ve fonksiyonun argümanlarını içerir.
+
Örnek olarak bir fonksiyon kapsamında foo değişkenine eriğildiğinde JavaScript
+isim çözümlemeyi aşağıdaki sıra ile yapacaktır:
+
+
Geçerli kapsamda bir var foo ifadesi mevcutsa bu kullanılır.
+
Fonksiyonun parametrelerinden birinin adı foo ise bu kullanılır.
+
Fonksiyonun kendisinin adı foo ise bu kullanılır.
+
Bir dıştaki kapsama geçilir ve yeniden 1 adımına dönülür.
+
+
+
İsim uzayları
+
Tek bir genel isim uzayının bulunmasının yol açtığı yaygın sonuç isim
+çakışmasıdır. JavaScript'te bu sorun isimsiz fonksiyonlar ile kolayca
+önlenebilir.
+
(function() {
+ // bir "isim uzayı"
+
+ window.foo = function() {
+ // korunmasız bir closure
+ };
+
+})(); // fonksiyonu hemen çalıştır
+
İsimsiz fonksiyonlar ifade olarak değerlendirilir;
+bu nedenle çağrılabilmeleri için önce değerlendirilmeleri gerekir.
+
( // parantezin içindeki fonksiyonu değerlendir
+function() {}
+) // ve fonksiyon nesnesini döndür
+() // değerlendirmenin sonucu fonksiyon nesnesini çağır
+
Bir fonksiyon ifadesini değerlendirip çağırmanın başka yolları da vardır ve
+yukarıdaki ile aynı sonucu verirler.
+
// İki farklı yöntem
++function(){}();
+(function(){}());
+
Sonuç
+
Programı kendi isim uzayı ile kapsamak için her zaman isimsiz fonksiyonların
+kullanılması tavsiye edilir. Böylece hem isim çakışmalarından korunulmuş olunur,
+hem de programlar daha modüler halde yazılmış olur.
+
Ayrıca, genel değişkenlerin kullanılması kötü bir uygulamadır. Genel
+değişkenlerin herhangi bir şekilde kullanılmış olması programın kötü yazılmış
+olduğuna, hatalara eğilimli olduğuna ve sürdürülmesinin zor olacağına işaret
+eder.
+
Diziler
Dizi İterasyonu ve Özellikleri
Diziler JavaScript nesneleri olmalarına rağmen, iterasyon yapmak için
+for in döngüsü kullanmak için bir neden yoktur.
+Aslında dizilerde for in kullanılmasına karşı bazı iyi nedenler
+vardır.
+
+
for in döngüsü prototip zincirindeki tüm özellikleri dolaştığı için ve bunu
+engellemenin tek yolu hasOwnProperty kullanmak
+olduğu için for in döngüsü sıradan bir for döngüsünden yirmi kata kadar
+daha yavaştır.
+
İterasyon
+
Dizilerde iterasyon yaparken en iyi performansı elde etmenin en iyi yolu klasik
+for döngüsünü kullanmaktır.
+
var list = [1, 2, 3, 4, 5, ...... 100000000];
+for(var i = 0, l = list.length; i < l; i++) {
+ console.log(list[i]);
+}
+
Yukarıdaki örnekte bir optimizasyon var, o da dizinin uzunluğun iterasyonun
+başında l = list.length ile saklanmış olması.
+
length özelliği dizinin kendisinde tariflenmiş olmasına rağmen, her adımda
+bu özelliği okumanın yine de bir maliyeti vardır. Modern JavaScript motorları
+bu tür durumlar için muhtemelen optimizasyon yapıyor olsa bile, programın
+her zaman modern bir motorda çalışacağından emin olmak mümkün değildir.
+
Aslında, yukarıdaki örnekteki optimizasyonu uygulamamak döngünün
+iki kat daha yavaş çalışmasına neden olabilir.
+
length özelliği
+
length özelliğine değer atanarak diziyi kısaltmak için kullanılabilir.
Daha küçük bir uzunluk atanması diziyi kısaltır, fakat daha büyük bir uzunluk
+atanmasının dizi üzerinde bir etkisi yoktur.
+
Sonuç
+
En iyi performans için her zaman sıradan for döngüsü kullanılmalı ve
+length özelliği saklanmalıdır. Dizilerde for in döngüsünün kullanılmış
+olması hatalara meyilli kötü yazılmış bir programa işaret eder.
+
Array Oluşturucusu
Array oluşturucusunun parametrelerini nasıl değerlendirdiği belirsiz olduğu
+için, yeni diziler oluşturulurken her zaman dizi sabitlerinin ([]
+notasyonu) kullanılması tavsiye olunur.
Array oluşturucusuna tek bir argüman verildiğinde, ve bu argümanın türü
+Number ise, oluşacak boş dizinin length özelliği argümanın
+değerine eşit olacaktır. Bu şekilde oluşturulan bir dizinin sadece
+length özelliği belirlenmiş olup dizi indisleri tanımsız olacaktır.
+
var arr = new Array(3);
+arr[1]; // undefined
+1 in arr; // false, indisler atanmadı
+
Dizinin uzunluğunu bu şekilde önceden belirlemek sadece bir iki durumda
+kullanışlıdır. Bunlardan birisi bir döngüye gerek olmadan bir karakter
+katarını tekrarlamaktır.
+
new Array(count + 1).join(stringToRepeat);
+
Sonuç
+
Array oluşturucusunun kullanılmasından mümkün olduğu kadar kaçınılmalıdır.
+Bunun yerine her zaman dizi sabitleri tercih edilmelidir. Hem daha kısadırlar
+hem de daha anlaşılır bir sentaksa sahiptirler; bu nedenle programın
+okunabilirliğini de artırırlar.
+
Nesne Tipleri
Eşitlik ve Karşılaştırmalar
JavaScript'de nesnelerin değerlerinin eşitliğini kontrol etmenin iki farklı yolu
+vardır.
+
Eşitlik operatörü
+
Eşitlik operatörü iki adet eşittir işaretinden oluşur: ==
+
JavaScript weakly typed bir dildir. Bu nedenle, eşitlik operatörü ile
+değişkenleri karşılaştırırken tip dönüşümü yapar.
Yukarıdaki tablo tip dönüşümünün sonuçlarını verir, ve == kullanımının kötü
+bir uygulama olarak değerlendirilmesinin başlıca sebebidir. Bu karmaşık dönüşüm
+kuralları tespit edilmesi zor hatalara neden olur.
+
Ayrıca tip dönüşümü işin içine girdiğinde performans üzerinde de olumsuz etkisi
+olur; mesela, bir katarın bir sayı ile karşılaştırılabilmesi için önce bir
+sayıya dönüştürülmesi gerekir.
+
Kesin eşitlik operatörü
+
Kesin eşitlik operatörü üç adet eşittir işaretinden oluşur: ===
Yukarıdaki sonuçlar hem daha anlaşılırdır, hem de progamdaki hataların erkenden
+ortaya çıkmasını sağlar. Bu programı bir miktar sağlamlaştırır ve ayrıca
+karşılaştırılan değerlerin farklı tiplerden olması durumunda performansı da
+artırır.
+
Nesneleri karşılaştırmak
+
Hem == hem de === operatörlerinin eşitlik operatörü olarak
+adlandırılmasına rağmen, değerlerden en azından birinin bir Object olması
+durumunda farklı davranış gösterirler.
Bu durumda her iki operatör de eşitlik değilaynılık karşılaştırması
+yapar; yani, terimlerin aynı nesnenin örnekleri olup olmadığını kontrol
+ederler, tıpkı Python dilindeki is ve C dilindeki gösterici karşılaştırması
+gibi.
+
Sonuç
+
Sadece kesin eşitlik operatörünün kullanılması şiddetle tavsiye edilir.
+Tip dönüşümü yapılmasının gerekli olduğu durumlarda, bu açıkça
+yapılmalıdır ve dilin karmaşık dönüşüm kurallarına bırakılmamalıdır.
+
typeof Operatörü
typeof operatörü (instanceof ile birlikte)
+herhalde JavaScript'in en büyük tasarım hatalarından biridir, çünkü neredeyse
+tamamen arızalıdır.
+
instanceof operatörünün sınırlı kullanımı olsa da, typeof operatörünün
+gerçekte tek bir pratik kullanımı vardır, ve bunun da bir nesnenin tipini
+kontrol etmekle ilgili yoktur.
+
+
JavaScript tip tablosu
+
Değer Sınıf Tip
+-------------------------------------
+"foo" String string
+new String("foo") String object
+1.2 Number number
+new Number(1.2) Number object
+true Boolean boolean
+new Boolean(true) Boolean object
+new Date() Date object
+new Error() Error object
+[1,2,3] Array object
+new Array(1, 2, 3) Array object
+new Function("") Function function
+/abc/g RegExp object (function in Nitro/V8)
+new RegExp("meow") RegExp object (function in Nitro/V8)
+{} Object object
+new Object() Object object
+
Yukarıdaki tabloda Tip sütunu typeof operatörünün verdiği sonucu gösterir.
+Açıkça görülebileceği gibi, bu sonuç tutarlı olmaktan çok uzaktır.
+
Sınıf sütunu bir nesnenin dahili [[Class]] özelliğini gösterir.
+
+
[[Class]] özelliğinin değerini almak için Object.prototype 'ın toString
+metodu kullanılmalıdır.
+
Bir nesnenin sınıfı
+
Spesifikasyona göre [[Class]] değerine erişmenin tek yolu
+Object.prototype.toString kullanmaktır.
Yukarıdaki örnekte, Object.prototype.toString çağrıldığında
+this 'in değeri [[Class]] değeri aranan nesne olarak
+atanmış olmaktadır.
+
+
Bir değişkenin tanımlandığını kontrol etmek
+
typeof foo !== 'undefined'
+
Yukarıdaki satır foo değişkeninin tanımlanıp tanımlanmadığını belirler;
+tanımlanmamış bir değişkene erişmek bir ReferenceError hatası oluştur.
+typeof operatörünün tek kullanışlı olduğu şey işte budur.
+
Sonuç
+
Bir nesnenin tipini kontrol etmek için Object.prototype.toString 'in
+kullanılması şiddetle tavsiye edilir; çünkü bunu yapmanın tek güvenilir yoludur.
+Yukarıdaki tip tablosunda gösterildiği gibi, typeof operatörünün bazı
+sonuçları spesifikasyonda tanımlanmamıştır; bu nedenle, çeşitli platformlarda
+farklılık gösterebilirler.
+
Bir değişkenin tanımlandığını kontrol etmek dışında, typeof operatörün
+kullanımından her ne pahasına olursa olsun kaçınılmalıdır.
+
instanceof Operatörü
instanceof operatörü verilen iki terimin nesne oluşturucularını karşılaştırır.
+Kullanışlı olduğu tek durum özel nesnelerin karşılaştırılmasıdır. Temel nesneler
+üzerinde kullanıldığında neredeyse typeof operatörü kadar
+yararsızdır.
+
Özel nesneleri karşılaştırmak
+
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// Bu satır sadece Bar.prototype'a Foo fonksiyon nesnesinin atar
+// Bir Foo sınıfı nesnesine değil
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
Dikkat edilmesi gereken ilginç bir nokta, instanceof operatörünün farklı
+JavaScript kaynaklarından gelen nesneler üzerinde çalışmamasıdır (mesela bir
+internet tarayıcısının farklı dökümanları), çünkü bu durumda nesne
+oluşturucuları aynı olmayacaktır.
+
Sonuç
+
instanceof operatörü sadece aynı JavaScript kaynağından gelen özel
+nesneler ile kullanılmalıdır. Tıpkı typeof operatöründe
+olduğu gibi, bunun dışındaki tüm kullanımlarından kaçınılmalıdır.
+
Tip Dönüşümleri
JavaScript weakly typed bir dildir, bu yüzden mümkün olan yerlerde
+tip dönüşümü uygular.
+
// Bunlar true verir
+new Number(10) == 10; // Number.toString() tekrar sayıya
+ // dönüştürülür
+
+10 == '10'; // Katarlar sayıya dönüştürülür
+10 == '+10 '; // Bir başka katar çılgınlığı
+10 == '010'; // Ve bir tane daha
+isNaN(null) == false; // null 0'a dönüştürülür
+ // tabii 0 NaN değildir
+
+// Bunlar false verir
+10 == 010;
+10 == '-10';
+
+
Yukarıdakilerden kaçınmak için, kesin eşitlik operatörünün
+kullanılması şiddetle tavsiye edilir. Böylece yaygın hataların çoğundan
+kaçınılabilir, yine de JavaScript'in weak typing sisteminden kaynaklanan başka
+sorunlar da vadır.
+
Temel tiplerin nesne oluşturucuları
+
Number ve String gibi temel tiplerin nesne oluşturucuları new anahtar
+kelimesi ile kullanılıp kullanılmamalarına göre farklı davranış gösterir.
+
new Number(10) === 10; // False, Object ve Number
+Number(10) === 10; // True, Number ve Number
+new Number(10) + 0 === 10; // True, tip dönüşümü nedeniyle
+
Number gibi bir temel tipin nesne oluşturucusunu kullanmak yeni bir Number
+nesnesi yaratacaktır, fakat new kelimesi kullanılmazsa Number fonksiyonu
+bir dönüştürücü olarak davranacaktır.
+
Ayrıca, sabitler ve nesne olmayan değerler kullanılması durumunda başka tür
+dönüşümler de söz konusu olacaktır.
+
En iyi seçenek üç olası tipten birine açıkça dönüşüm yapılmasıdır.
+
Karakter katarına dönüştürmek
+
'' + 10 === '10'; // true
+
Bir değerin başına boş bir katar eklenerek kolayca katara dönüştürülebilir.
+
Sayıya dönüştürmek
+
+'10' === 10; // true
+
Tek terimli toplama operatörü kullanılarak bir değer sayıya dönüştürülebilir.
+
Mantıksal değişken tipine dönüştürmek
+
Değil operatörü iki kez üst üste kullanılarak bir değer mantıksal değişken
+tipine dönüştürülebilir.
Fakat eval sadece direkt olarak çağrıldığında ve çağrılan fonksiyonun
+adı eval ise lokal kapsamda çalışır.
+
var foo = 1;
+function test() {
+ var foo = 2;
+ var bar = eval;
+ bar('foo = 3');
+ return foo;
+}
+test(); // 2
+foo; // 3
+
eval fonksiyonu asla kullanılmamalıdır. Kullanıldığı durumların %99.9'unda
+evalkullanılmadan da istenen sonuç elde edilebilir.
+
Gizli eval
+
Zamanlama fonksiyonlarısetTimeout ve setInterval'ın her
+ikisinin de ilk argümanları bir karakter katarıdır. Bu durumda eval dolaylı
+olarak çağrıldığı için bu argüman her zaman genel kapsamda yürütülecektir.
+
Güvenlik sorunları
+
eval kendisine verilen her kodu işlettiği için aynı zamanda bir güvenlik
+sorunudur ve asla kaynağı bilinmeyen yada güvenilir olmayan karakter
+katarları ile kullanılmamalıdır.
+
Sonuç
+
eval asla kullanılmamalıdır, kullanan programlar ise doğruluk, performans ve
+güvenlik açılarından sorgulanmalıdır. eval kullanımı gerekli görülmüşse,
+programın tasarımı sorgulanmalı ve kullanılmamalı, bunun yerine eval
+gerektirmeyen daha iyi bir tasarım kullanılmalıdır.
+
undefined ve null
JavaScript'te tanımsız anlamına gelen iki değer vardır, ve bunlardan
+undefined daha kullanışlıdır.
+
undefined değeri
+
undefined bir değişken türüdür ve tek bir değere sahip olabilir: undefined.
+
JavaScript'te ayrıca değeri undefined olan bir de genel kapsam değişkeni
+tanımlanmıştır ve bu değişkenin adı da undefined'dır. Fakat bu değişken
+bir sabit yada dilin anahtar kelimelerinden biri değildir. Yani bu
+değişkenin değeri kolayca değiştirilebilir.
+
+
undefined değerinin verildiği durumlara bazı örnekler:
+
+
Genel kapsamlı undefined değişkeninin (değiştirilmedi ise) değeri
+
return ifadesi içermeyen fonksiyonların verdiği değer
+
Bir değer döndürmeyen return ifadeleri
+
Mevcut olmayan nesne özellikleri
+
Değer atanmamış fonksiyon parametreleri
+
Değeri undefined olarak atanmış değişkenler
+
+
undefined değerinin değiştirilmesi durumu
+
Genel kapsamdaki undefined değişkeni asıl undefineddeğerinin kopyasını
+tuttuğu için, bu değeri değiştirmek undefineddeğişken türünün değerini
+değiştirmez.
+
Fakat, bir şeyi undefined ile karşılaştırmak için önce undefined'ın değerini
+geri almak gerekir.
+
Programı undefined değişkeninin değiştirilmesi olasılığına karşı korumak için
+uygulanan yaygın bir yöntem isimsiz bir fonksiyona
+kullanılmayan bir parametre eklemektir.
+
var undefined = 123;
+(function(something, foo, undefined) {
+ // lokal kapsamda undefined değişkeni
+ // yine undefined değerine sahip
+
+})('Hello World', 42);
+
Benzer bir yöntem yine isimsiz fonksiyonun içinde değer atanmamış bir değişken
+deklare etmektir.
+
var undefined = 123;
+(function(something, foo) {
+ var undefined;
+ ...
+
+})('Hello World', 42);
+
Buradaki tek fark program sıkıştırılırsa ortaya çıkacaktır, eğer fonksiyonun
+başka bir yerinde var ifadesi kullanılmıyorsa fazladan 4 bayt kullanılmış
+olacaktır.
+
null kullanımı
+
JavaScript dilinde undefined geleneksel null yerine kullanılmaktadır, asıl
+null (hem null değişmezi hem de değişken türü) ise kabaca başka bir
+veri türüdür.
+
null JavaScript içinde kapalı olarak kullanılır (mesela prototip zincirinin
+sonuna gelindiği Foo.prototype = null ile belirtilir), fakat hemen her durumda
+bunun yerine undefined kullanılabilir.
+
Otomatik Noktalı Virgül İlavesi
JavaScript sentaksı C'ye benzese de, noktalı virgül kullanılması
+zorunlu değildir.
+
Fakat JavaScript noktalı virgül kullanmayan bir dil değildir, hatta
+programı anlayabilmek için noktalı virgüllere ihtiyaç duyar. Bu yüzden
+JavaScript gramer çözümleyicisi eksik bir noktalı virgül yüzünden bir
+hata ile karşılaştığında otomatik olarak eksik noktalı virgülleri
+ekler.
var foo = function() {
+}; // hata ortadan kalktı, çözümleme devam edebilir
+test()
+
Noktalı virgüllerin bu şekilde otomatik olarak eklenmesi JavaScript'in
+en büyük tasarım hatalarından biri olarak kabul edilir, çünkü programın
+davranışını değiştirmesi mümkündür.
+
Ekleme nasıl olur
+
Aşağıdaki örnekte hiç noktalı virgül yok, bu yüzden nereye noktalı virgül
+eklenmesi gerektiğini gramer çözümleyicinin karar vermesi gerekiyor.
Büyük ihtimalle yukarıdaki log bir fonksiyon döndürmüyordur;
+bu nedenle, yukarıdaki satır undefined is not a function hata mesajı ile bir
+TypeError oluştumasına neden olacaktır.
+
Sonuç
+
Noktalı virgüllerin hiç bir zaman ihmal edilmemesi tavsiye edilir, ayrıca
+ayraçların kendilerinden önceki ifade ile aynı satırda tutulması ve tek satırlık
+if ve else ifadelerinde bile ayraçların ihmal edilmemesi önerilir. Her iki
+önlem de hem programın tutarlılığını artıracak, hem de JavaScript
+çözümleyicisinin programın davranışını değiştirmesini engelleyecektir.
+
delete Operatörü
Kısacası, genel kapsamda tanımlanmış değişkenleri, fonksiyonları ve DontDelete
+niteliğine sahip bazı başka şeyleri silmek imkansızdır.
+
Genel kapsam ve fonksiyon kapsamı
+
Bir değişken veya fonksiyon genel kapsamda veya
+fonksiyon kapsamında tanımlandığında aktivasyon nesnesinin
+veya global nesnenin bir özelliği olacaktır. Bu tür özelliklerin bir takım
+nitelikleri vardır ve bunlardan biri DontDelete niteliğidir. Genel kapsamda ve
+fonksiyon kapsamında tanımlanan değişkenler ve fonksiyonlar yaratıldıklarında
+her zaman DontDelete niteliğine sahip olacaktır, ve bu nedenle silinemezler.
+
// genel kapsam değişkeni:
+var a = 1; // DontDelete niteliğine sahip
+delete a; // false
+a; // 1
+
+// normal bir fonksiyon:
+function f() {} // DontDelete niteliğine sahip
+delete f; // false
+typeof f; // "function"
+
+// başka bir değişkene atamak işe yaramaz:
+f = 1;
+delete f; // false
+f; // 1
Burada a'yı silmek için bir hile kullanıyoruz. this
+burada genel nesneye işaret ediyor ve a değişkenini onun özelliği olarak
+atıyoruz, ve böylece onu silebiliyoruz.
+
IE (en azından 6-8) bazı hatalar içerdiğinden yukarıdaki örnek çalışmayacaktır.
+
Fonksiyon argümanları ve önceden tanımlı özellikler
+
Fonksiyonlara verilen argümanlar, arguments nesnesi
+ve önceden tanımlı özellikler de DontDelete niteliğine sahiptir.
Host nesneler üzerinde kullanıldığında delete operatörünün davranışı belirsiz
+olabilir. Standarda göre host nesneler istedikleri davranışı uygulayabilirler.
+
Sonuç
+
delete operatörünün davranışı genellikle belirsizdir ve güvenle kullanılabileceği
+tek yer sıradanan nesneler üzerinde açıkça tanımlanan özelliklerdir.
+
Diğer
setTimeout ve setInterval
JavaScript asenkron olduğu için setTimeout ve setInterval kullanarak bir
+fonksiyonun ileri bir zamanda çalışmasını sağlamak mümkündür.
+
+
function foo() {}
+var id = setTimeout(foo, 1000); // 0'dan büyük bir sayı verir
+
Yukarıdaki örnekte setTimeout fonksiyonu çağrıldığında, oluşturulan
+zamanlayıcı tanımlayan bir ID sayısı verir ve foo fonksiyonu yaklaşık
+bin milisaniye sonra çalıştırılmak üzere programlanır. foo fonksiyonu
+tam olarak bir kez çağrılacaktır.
+
Kullanılan JavaScript motorunun zamanlayıcı hassasiyetine bağlı olarak, ve
+ayrıca JavaScript tek thread ile çalıştığı ve çalışan başka program
+parçaları bu tek thread 'i bloke edeceği için, setTimeout ile belirlenen
+erteleme süresinin tam olarak gerçekleşeceği hiçbir şekilde garanti
+edilemez.
+
İlk argüman olarak verilen fonksiyon global nesne tarafından çağrılacaktır,
+yani çağrılan fonksiyonun içinde this bu nesneye işaret
+edecektir.
+
function Foo() {
+ this.value = 42;
+ this.method = function() {
+ // this global nesneye işaret eder
+ console.log(this.value); // undefined yazar
+ };
+ setTimeout(this.method, 500);
+}
+new Foo();
+
+
setInterval ile fonksiyon çağrılarının yığılması
+
setTimeout verilen fonksiyonu bir kez çağırırken, setInterval (adından da
+anlaşılacağı gibi) verilen fonksiyonu herX milisaniyede bir çağırır.
+Fakat kullanılması önerilmez.
+
Mevcut program parçası çalışırken zamanlama bloke olduğu halde, setInterval
+verilen fonksiyonu çağırmaya devam edecektir. Bu da, özellikle küçük aralıklarla
+kullanıldığında, fonksiyon çağrılarının istiflenmesine neden olur.
+
function foo(){
+ // 1 saniye süren bir işlem
+}
+setInterval(foo, 1000);
+
Yukarıdaki örnekte foo fonksiyonu bir kez çağrılıp bir saniye boyunca bloke
+edecektir.
+
foo programı bloke etmişken, setInterval fonksiyon çağrılarını zamanlamaya
+devam edecektir. foo tamamlandığında, çalıştırılmayı bekleyen on çağrı
+daha olacaktır.
+
Bloke eden programlarla başa çıkmak
+
En kolay ve kontrol edilebilir çözüm, setTimeout 'u fonksiyonun içinde
+kullanmaktır.
+
function foo(){
+ // 1 saniye süren bir işlem
+ setTimeout(foo, 1000);
+}
+foo();
+
Bu örnekte hem setTimeout çağrısı fonksiyonun kendisi içinde kapsanmış olmakta,
+hem de fonksiyon çağrılarının istiflenmesinin önüne geçilerek daha fazla kontrol
+sağlanmaktadır. Artık foo fonksiyonunun kendisi tekrar çalışmak isteyip
+istemediğine karar verebilir.
+
Zamanlayıcıları iptal etmek
+
Zamanlayıcıları iptal etmek için ilgili ID sayıları ile kullanılan zamanlayıcı
+fonksiyonuna karşılık gelen clearTimeout ve clearInterval fonksiyonlarından
+biri kullanılır.
+
var id = setTimeout(foo, 1000);
+clearTimeout(id);
+
Tüm zamanlayıcıları iptal etmek
+
Tüm zamanlayıcıları iptal etmenin dahili bir yolu olmadığı için, bu amaca
+ancak kaba kuvvetle ulaşılabilir.
+
// "tüm" zamanlayıcıları iptal et
+for(var i = 1; i < 1000; i++) {
+ clearTimeout(i);
+}
+
Bu rastgele seçilmiş sayıdan etkilenmeyen zamanlayıcılar kalabilir; bu yüzden
+tüm zamanlayıcı ID'lerinin saklanarak, teker teker iptal edilmeleri tavsiye
+edilir.
+
eval fonksiyonun gizli kullanımı
+
setTimeout ve setInterval fonksiyonları ilk parametreleri olarak bir katar
+da kabul eder. Bu özellik asla kullanılmamalıdır, çünkü bu durumda dahili
+olarak eval kullanılır.
+
+
function foo() {
+ // setTimeOut ile bu fonksiyon çağrılacaktır
+}
+
+function bar() {
+ function foo() {
+ // bu fonksiyon çağrılmayacaktır
+ }
+ setTimeout('foo()', 1000);
+}
+bar();
+
Bu durumda evaldirekt olarak çağrılmadığı için, setTimeout
+fonksiyonuna verilen katar genel kapsamda çalıştırılacaktır; bu nedenle,
+bar fonksiyonu kapsamındaki lokal foo değişkenini kullanmayacaktır.
+
Zamanlama fonksiyonlarına verilen fonksiyona argüman sağlamak için de bir katar
+kullanılması tavsiye edilmez.
+
function foo(a, b, c) {}
+
+// ASLA bu şekilde kullanılmamalı
+setTimeout('foo(1, 2, 3)', 1000)
+
+// Bunu yerine isimsiz bir fonksiyon kullanın
+setTimeout(function() {
+ foo(a, b, c);
+}, 1000)
+
+
Sonuç
+
setTimeout veya setInterval fonksiyonlarına asla bir katar parametre
+verilmemelidir. Bu kullanım çok kötü bir programa işaret eder. Çağrılan
+fonksiyona argümanlar verilmesinin gerektiği durumlarda gerçek çağrıyı içinde
+bulunduran bir isimsiz fonksiyon kullanılmalıdır.
+
Ayrıca, setInterval fonksiyonu çalışan JavaScript programı tarafından bloke
+olmadığı için tercih edilmemelidir.
+
\ No newline at end of file
diff --git a/zh/index.html b/zh/index.html
new file mode 100644
index 0000000..7c62a22
--- /dev/null
+++ b/zh/index.html
@@ -0,0 +1,1255 @@
+JavaScript 秘密花园
虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大。
+实现传统的类继承模型是很简单,但是实现 JavaScript 中的原型继承则要困难的多。
+(It is for example fairly trivial to build a classic model on top of it, while the
+other way around is a far more difficult task.)
function foo() {
+ arguments.callee; // do something with this function object
+ arguments.callee.caller; // and the calling function object
+}
+
+function bigLoop() {
+ for(var i = 0; i < 100000; i++) {
+ foo(); // Would normally be inlined...
+ }
+}
\ No newline at end of file
diff --git a/zhtw/index.html b/zhtw/index.html
new file mode 100644
index 0000000..753f38e
--- /dev/null
+++ b/zhtw/index.html
@@ -0,0 +1,1197 @@
+JavaScript 庭院
As a result, in the above example the method case does not apply, and this
+inside of foo will be set to bar.
+
+
常見誤解
+
While most of these cases make sense, the first can be considered another
+mis-design of the language because it never has any practical use.
+
Foo.method = function() {
+ function test() {
+ // this is set to the global object
+ }
+ test();
+}
+
A common misconception is that this inside of test refers to Foo; while in
+fact, it does not.
+
In order to gain access to Foo from within test, it is necessary to create a
+local variable inside of method that refers to Foo.
+
Foo.method = function() {
+ var that = this;
+ function test() {
+ // Use that instead of this here
+ }
+ test();
+}
+
that is just a normal variable name, but it is commonly used for the reference to an
+outer this. In combination with closures, it can also
+be used to pass this values around.
+
Assigning Methods
+
Another thing that does not work in JavaScript is function aliasing, which is
+assigning a method to a variable.
+
var test = someObject.methodTest;
+test();
+
Due to the first case, test now acts like a plain function call; therefore,
+this inside it will no longer refer to someObject.
+
While the late binding of this might seem like a bad idea at first, in
+fact, it is what makes prototypal inheritance work.
匿名外部的函數被呼叫,並把 i 作為它第一個參數,此時函數內 e 變數就擁有了一個 i 的拷貝。
+當傳遞給 setTimeout 這個匿名函數執行時,它就擁有了對 e 的引用,而這個值 不會 被循環改變。
+另外有一個方法也可以完成這樣的工作,那就是在匿名函數中返回一個函數,這和上面的程式碼有同樣的效果。
function Foo() {}
+function Bar() {}
+Bar.prototype = new Foo();
+
+new Bar() instanceof Bar; // true
+new Bar() instanceof Foo; // true
+
+// This just sets Bar.prototype to the function object Foo,
+// but not to an actual instance of Foo
+Bar.prototype = Foo;
+new Bar() instanceof Foo; // false
// 這些都是真
+new Number(10) == 10; // Number.toString() is converted
+ // back to a number
+
+10 == '10'; // Strings gets converted to Number
+10 == '+10 '; // More string madness
+10 == '010'; // And more
+isNaN(null) == false; // null converts to 0
+ // which of course is not NaN
+
+// 下面都假
+10 == 010;
+10 == '-10';
new Number(10) === 10; // False, Object and Number
+Number(10) === 10; // True, Number and Number
+new Number(10) + 0 === 10; // True, due to implicit conversion
+
使用內置類型 Number 作為建構函式會建造一個新的 Number 物件,而在不使用 new 關鍵字的 Number 函式更像是一個數字轉換器。
setTimeout and setInterval can also take a string as their first parameter.
+This feature should never be used because it internally makes use of eval.
+
+
function foo() {
+ // will get called
+}
+
+function bar() {
+ function foo() {
+ // never gets called
+ }
+ setTimeout('foo()', 1000);
+}
+bar();
+
Since eval is not getting called directly in this case, the string
+passed to setTimeout will be executed in the global scope; thus, it will
+not use the local variable foo from the scope of bar.
+
It is further recommended to not use a string to pass arguments to the
+function that will get called by either of the timeout functions.
+
function foo(a, b, c) {}
+
+// NEVER use this
+setTimeout('foo(1, 2, 3)', 1000)
+
+// Instead use an anonymous function
+setTimeout(function() {
+ foo(a, b, c);
+}, 1000)
+
+
In Conclusion
+
A string should never be used as the parameter of setTimeout or
+setInterval. It is a clear sign of really bad code, when arguments need
+to be supplied to the function that gets called. An anonymous function should
+be passed that then takes care of the actual call.
+
Furthermore, the use of setInterval should be avoided because its scheduler is not
+blocked by executing JavaScript.