Colecção de Javascript

Desculpa a pergunta do novato. Pode explicar, por favor, qual é a diferença entre:

1. var a = [];
   a['b'] = 1;

2. var a = {};
   a['b'] = 1;
Não consegui encontrar um artigo na internet assim escrito aqui.

Author: karaxuna, 2012-10-19

5 answers

Literais

Os [] e {} são chamados de ordenados e literais de objectos, respectivamente.

var x = [] é diminutivo de var x = new Array();

E var y = {} é abreviatura de var y = new Object();

Arrays

As matrizes são estruturas com uma propriedade de comprimento. Você pode acessar os valores através de seu índice numérico.
var x = [] or var x = new Array();
x[0] = 'b';
x[1] = 'c';

E se quiser listar todas as propriedades que faz:

for(var i = 0; i < x.length; i++)
console.log(x[i]);// numeric index based access.

Truques de desempenho e gotchas

1. 'Cache' interior da propriedade do comprimento

A iteração padrão da matriz:

for (var i = 0; i < arr.length; i++) {
    // do stuff
};

Fato pouco conhecido: no cenário acima, a propriedade arr.length é lida em cada passo do laço for. Assim como qualquer função que você chama lá:

for (var i = 0; i < getStopIndex(); i++) {
     // do stuff
};
Isto diminui o desempenho sem motivo. Aproximação interna ao salvamento:
for (var i = 0, len = arr.length; i < len; i++) {
     // enter code here
};

Aqui está. prova do que precede.

2. Não especifique o Comprimento da matriz no construtor.

// doing this:
var a = new Array(100);
// is very pointless in JS. It will result in an array with 100 undefined values.

// not even this:
var a = new Array();
// is the best way.

var a = [];
// using the array literal is the fastest and easiest way to do things.

Existem casos de ensaio para a definição do conjunto. toma..

3. Evite usar o Array.prototipo.empurre (arr.empurre.)

Se está a lidar com grandes colecções, a atribuição directa é mais rápida do que usar o método Array.prototype.push();.

myArray[i] = 0; é mais rápido do que myArray.push(0);, de acordo com jsPerf.com casos de teste.

4. É errado usar arrays para associar atribuicao.

A única razão pela qual funciona é porque Array estende a classe Object dentro do núcleo da linguagem JS. Você pode usar um objeto Date(); ou RegEx(); por exemplo. Não fará diferença. x['property'] = someValue MUST always be used with Objects .

As matrizes só devem ter índices numéricos. Veja este, as Diretrizes de desenvolvimento do Google JS! Evite for (x in arr) loops ou arr['key'] = 5;.

Isto pode ser facilmente apoiado., olha. toma. por exemplo.
var x = [];
console.log(x.prototype.toString.call);

Irá sair: [object Array]

Isto revela o padrão hereditário da "classe" da linguagem Central.  
var x = new String();
console.log(x.prototype.toString.call);

Irá sair [object String].

5. Obter o mínimo e o máximo de uma matriz.

Um truque pouco conhecido,mas muito poderoso.
function arrayMax(arr) {
    return Math.max.apply(Math, arr);
};

, respectivamente:

function arrayMin(arr) {
    return Math.min.apply(Math, arr);
};

Objectos

Com um objecto só se pode do:

var y = {} ou var y = new Object();

y['first'] = 'firstValue' é o mesmo que y.first = 'firstValue', o que você não pode fazer com uma matriz. Os objectos são desenhados para acesso associativo com as teclas String.

E a iteração é algo assim:
for (var property in y) {
    if (y.hasOwnProperty(property)) {
        console.log(y.property);
    };
};

Truques de desempenho e gotchas

1. A verificar se um objecto tem uma propriedade.

A maioria das pessoas usa Object.prototype.hasOwnProperty. Infelizmente, isso muitas vezes dá resultados errôneos levando a inesperados percevejo.

Eis uma boa maneira de o fazer.
function containsKey(obj, key) {
    return typeof obj[key] !== 'undefined';
};

2. A substituir declarações trocadas.

Um dos truques simples mas eficientes do JS é a substituição.
switch (someVar) {
    case 'a':
        doSomething();
        break;
    case 'b':
        doSomethingElse();
        break;
    default:
        doMagic();
        break;
};
Na maioria dos motores JS, o acima é dolorosamente lento. Quando você está olhando para três resultados possíveis, não faz diferença, mas e se você tivesse dezenas ou centenas?

O acima pode ser facilmente substituído por um objecto. Não adicione o trilho. executar as funções, mas simplesmente armazenar referências a elas:

var cases = {
    'a': doSomething,
    'b': doSomethingElse,
    'c': doMagic
};

Em vez de switch:

var x = ???;
if (containsKey(cases, x)) {
    cases[x]();
} else {
    console.log("I don't know what to do!");
};

3. Clonagem profunda facilitada.

function cloneObject(obj) {
   var tmp = {};
   for (var key in obj) {
       tmp[key] = fastDeepClone(obj[key];
   };
   return tmp;
}

function cloneArr(arr) {
   var tmp = [];
   for (var i = 0, len = arr.length; i < len; i++) {
     tmp[i] = fastDeepClone(arr[i]);
   }
   return tmp;
}


function deepClone(obj) {
   return JSON.parse(JSON.stringify(obj));
};

function isArray(obj) {
   return obj instanceof Array;
}

function isObject(obj) {
  var type = typeof obj;
  return type === 'object' && obj !== null || type === 'function';
}

function fastDeepClone(obj) {
  if (isArray(obj)) {
    return cloneArr(obj);
  } else if (isObject(obj)) {
    return cloneObject(obj);
  } else {
    return obj;
  };
};

Toma. é a função de clones profundos em ação.

Auto-boxe

Como uma linguagem dinamicamente tipada, o JavaScript é limitado em termos de tipos de objectos nativos.:

  • objecto
  • matriz
  • número
  • booleano
  • Data
  • RegEx
  • erro

Null não é um tipo, typeof null é objecto.

Qual é a contrapartida? Há uma forte distinção entre objetos primitivos e não primitivos.
var s = "str";
var s2 = new String("str");

Eles fazem a mesma coisa, você pode chamar todos os métodos de string em s e s2. Ainda:

type of s == "string"; // raw data type
type of s2  == "object" // auto-boxed to non-primitive wrapper type
s2.prototype.toString.call == "[object String]";

Podes ouvir em JS tudo is an object. Isso não é exactamente verdade, embora seja um erro muito fácil de cometer.

Na realidade existem 2 tipos, primitivos e objectos, e quando se chama s.indexOf("c"), O motor JS irá converter automaticamente s para o seu tipo de invólucro não primitivo, neste caso object String, onde todos os métodos são definidos no String.prototype.

Isto chama-se auto-boxing O método de Object.prototype.valueOf(obj) é uma forma de forçar o molde de primitivo para não primitivo. É o mesmo comportamento uma linguagem como Java apresenta para muitos dos seus próprios primitivos, especificamente os pares: int Inteiro, double - Duplo, float - Float, etc. Porque haverias de te importar?

Simples:

function isString(obj) {
   return typeof obj === "string";
}
isString(s); // true
isString(s2); // false

Então se s2 foi criado com {[82] } você está recebendo um falso negativo, mesmo para uma verificação de tipo de outra forma plausivelmente simples. Objetos mais complexos também trazem consigo uma pesada penalidade de desempenho.

Uma micro-optimização, como alguns diriam, mas os resultados são verdadeiramente notável, mesmo para coisas extremamente simples como a inicialização de cordas. Vamos comparar os dois seguintes em termos de desempenho:
var s1 = "this_is_a_test" 

E

var s2 = new String("this_is_a_test")

Você provavelmente esperará um desempenho correspondente em toda a linha, mas surpreendentemente a última afirmação usando new String é 92% mais lenta do que a primeira, como provado aqui.

Funções

1. Parâmetros predefinidos

O operador é a maneira mais simples possível de falhar. Porque é que funciona? Por causa dos valores truthy e falsy.

Quando avaliado numa condição lógica, os valores de undefined e null serão autocastados para false.

Um exemplo simples(código AQUI):

function test(x) {
   var param = x || 5;
   // do stuff with x
};

2. OO JS

A coisa mais importante a entender é que o objeto JavaScript this não é imutável. É simplesmente uma referência que pode ser alterada com grande facilidade.

Em OO JS, contamos com a palavra-chave para garantir o alcance implícito em todos os membros de uma classe JS. Mesmo assim, você pode facilmente mudar o escopo, via Function.prototype.call e Function.prototype.apply.

Outra coisa muito importante é o Object.prototype. Valores não primitivos aninhados em um protótipo de objetos são compartilhados, enquanto os primitivos não são.

Código com exemplos toma..

Uma simples definição de classe:

function Size(width, height) {
    this.width = width;
    this.height = height;
};

Uma classe de tamanho simples, com dois membros, this.width e this.height.

Numa definição de classe, o que quer que tenha this à frente dela, irá criar uma nova referência para cada instância de tamanho.

Adicionar métodos às classes e por que o padrão de "fechamento" e outro padrão de "nomes chiques" são pura ficção

Talvez seja aqui que se encontram os anti-padrões JavaScript mais maliciosos. Podemos adicionar um método à nossa classe em dois. maneira.
Size.prototype.area = function() {
   return this.width * this.height;
};

Ou:

function Size2(width, height) {
   this.width = width;
   this.height = height;
   this.area = function() {
      return this.width * this.height;
   }
}

var s = new Size(5, 10);
var s2 = new Size2(5, 10);



var s3 = new Size2(5, 10);
var s4 = new Size(5, 10);
 // Looks identical, but lets use the reference equality operator to test things:
s2.area === s3.area // false
s.area === s4.area // true

O método area de Size2 é criado para cada instância. Isto é completamente inútil e lento, muito mais lento. 89% para ser exacto. Olha. toma..

A declaração acima é válida para cerca de 99% de todo o "padrão de nomes chiques"conhecido. Lembrem-se da coisa mais importante no JS, tudo isso não passa de ficção.

Há fortes argumentos arquitetônicos que podem ser feitos, a maioria girando em torno de encapsulação de dados e o uso de fechamentos. Tais coisas são, infelizmente, absolutamente inúteis em JavaScript, a perda de desempenho simplesmente não vale a pena. Estamos a falar de 90% ou mais, é tudo menos insignificante.

3. Limitações

Porque prototype as definições são compartilhadas entre todas as instâncias de uma classe, você não será capaz de colocar um objeto de configurações não primitivas lá.

Size.prototype.settings = {};
Porquê? size.settings será o mesmo para todos os casos. O que se passa com os primitivos?
Size.prototype.x = 5; // won't be shared, because it's a primitive.
// see auto-boxing above for primitive vs non-primitive
// if you come from the Java world, it's the same as int and Integer.

O ponto:

O tipo JS médio escreverá JS da seguinte forma:

var x = {
    doStuff: function(x) {
    },
    doMoreStuff: 5,
    someConstant: 10
}

Que é fine (fine = poor quality, hard to maintain code), contanto que você entenda que é um objeto Singleton, e essas funções só devem ser usadas no âmbito global sem referenciarem this dentro deles.

Mas depois chega a um código absolutamente terrível.
var x = {
   width: 10,
   height: 5
}
var y = {
   width: 15,
   height: 10
}
Podias ... escaparam com: [[103]}

Leva mais tempo para digitar, você precisa digitar a mesma coisa todas as vezes. E mais uma vez, é muito mais lento. Olha. toma..

Maus padrões ao longo de todo

Isto pode ser visto quase em qualquer lado.
   function() {
      // some computation
      var x = 10 / 2;
      var y = 5;
      return {
         width: x,
         height: y
      }
    }

Outra vez com a alternativa:

function() {
    var x = 10 / 2;
    var y = 5;
    return new Size(10, 5);
};

O Ponto: usar CLASSES sempre que apropriado!!

Porquê? O exemplo 1 é 93% mais lento . Olhar toma.. Os exemplos aqui são triviais, mas eles ilustram algo sendo ignorado em JS, OO. É uma regra sólida não empregar pessoas que acham que a JS não tem aulas e arranjar emprego de recrutadores a falar de JS orientados para objectos.

Encerramentos

Muitas pessoas preferem-nos ao acima porque lhes dá uma sensação de encapsulação de dados. Além da drástica queda de 90% de performance, aqui está algo igualmente fácil de ignorar. Fugas de memória.

function Thing(someParam) {
   this.someFn = function() {
     return someParam;
   }
}
Acabaste de criar um desfecho para o someParam. Porque é que isto é mau? Primeiro, obriga você a definir métodos de classe como propriedades de instância, resultando na grande queda de desempenho. Em segundo lugar, devora a memória, porque um encerramento nunca será dizimado. Olha. toma. como prova. Claro, você tem uma encapsulação de dados falsa, mas você usa três vezes a memória com uma queda de 90% de performance.

Ou você pode adicionar @private e obter uma maneira com um nome de função underscore.

Outras formas muito comuns de gerar encerramentos:

function bindSomething(param) {
   someDomElement.addEventListener("click", function() {
     if (param) //do something
     else // do something else
   }, false);
}

param é agora um encerramento! Como é que te livras dele? Há vários truques, alguns encontrados toma.. A melhor abordagem possível, embora mais rigorosa, é evitar o uso de funções anônimas em conjunto, mas isso exigiria uma forma de especificar âmbitos para callbacks de eventos.

Tal mecanismo só está disponível em Google Closure, tanto quanto sei.

O padrão singleton

Então, o que faço pelos singletons? Não quero guardar referências aleatórias. Eis uma ideia maravilhosa, descaradamente roubada da base do Google Closure.js
/**
 * Adds a {@code getInstance} static method that always return the same instance
 * object.
 * @param {!Function} ctor The constructor for the class to add the static
 *     method to.
 */
function addSingletonGetter(ctor) {
  ctor.getInstance = function() {
    if (ctor.instance_) {
      return ctor.instance_;
    }
    return ctor.instance_ = new ctor;
  };
};
É Java-esque, mas é um truque simples e poderoso. Você pode agora fazer:
project.some.namespace.StateManager = function() {
   this.x_ = 5;
};
project.some.namespace.prototype.getX = function() { return x; }
addSingletonGetter(project.some.namespace.StateManager);
Como é que isto é útil? Simples. Em todos os outros arquivos, cada vez que você precisa de referência project.some.namespace.StateManager, você pode escrever: project.some.namespace.StateManager.getInstance(). Isto é mais espectacular do que parece.

Você pode ter um estado global com os benefícios de uma definição de classe (herança, membros de Estado, etc.) e sem poluir o espaço de nomes global.

O padrão de instância única

Agora podes ser tentado a fazer isto:

function Thing() {
   this.someMethod = function() {..}
}
// and then use it like this:
Thing.someMethod();
Isso é outro grande Não-Não em JavaScript. Lembre-se, o objeto this só é garantido para ser imutável quando a palavra-chave new é usada. A magia por trás o código acima é interessante. this é, na verdade, o âmbito global, por isso, sem significado para si, está a adicionar métodos ao objecto global. E adivinhou, essas coisas nunca recolhem lixo. Não há nada que diga ao JavaScript para usar outra coisa. Por si só, não TEM Mira. Tenha muito cuidado com o que faz com propriedades static. Para reproduzir uma citação que li uma vez, o objeto global JavaScript é como um banheiro público. Às vezes não temos escolha a não ser ir no entanto, tente minimizar o contato com as superfícies tanto quanto possível.

Ou mantém o padrão acima Singleton ou usa um objecto de configuração aninhado sob um espaço de nomes.

Colecção de lixo em JavaScript

JavaScript é uma linguagem recolhida de lixo, mas JavaScript GC é muitas vezes bastante mal compreendido. O ponto é novamente velocidade. Isto talvez seja demasiado familiar.

// This is the top of a JavaScript file.
var a = 5;
var b = 20;
var x = {};//blabla

// more code
function someFn() {..}
Isso é Mau, Mau código de desempenho. A razão é simples. JS coletará uma variável e liberará a memória de heap que mantém apenas quando essa variável é Des-escopada, por exemplo, não há referências a ela em qualquer lugar da memória.

Por exemplo:

function test(someArgs) {
   var someMoreStuf = // a very big complex object;
}
test();
Três coisas:
  • os argumentos das funções são transformados em definições locais
  • as declarações internas são içadas.
  • toda a memória heap atribuída para variáveis internas é libertada quando a função termina execucao.
Porquê? Porque já não pertencem ao âmbito "atual". São criados, usados e destruídos. Também não há fechamentos, por isso toda a memória que usaste é libertada através da recolha de lixo. Por essa razão, nunca deves, os teus ficheiros JS nunca devem ser assim, pois o âmbito global vai manter a memória poluidora.
var x = 5;
var y = {..}; //etc;
Está bem, e agora?

Namespaces .

O JS não tem espaços de nomes. por dizer, então isso não é exatamente um equivalente Java, mas de uma perspectiva de administração de codebase você consegue o que quer.
var myProject = {};
myProject.settings = {};
myProject.controllers = {};
myProject.controlls.MainController = function() {
    // some class definition here
}
Lindo. Uma variável global. Estrutura adequada do projecto. Com uma fase de construção, você pode dividir o seu projeto em arquivos, e obter um ambiente dev adequado. Não há limite para o que podes conseguir daqui.

Contar as suas bibliotecas

Tendo tido o prazer de trabalhar em inúmeras codebases, a última e o argumento mais importante é estar muito atento às suas dependências de código. Eu vi programadores casualmente adicionando jQuery na mistura da pilha para um efeito de animação simples e assim por diante. Dependência e gestão de pacotes é algo que o mundo JavaScript não abordava há muito tempo, até a criação de ferramentas como Bower. Navegadores ainda são um pouco lentos, e mesmo quando eles são rápidos, as conexões de internet são lentas. No mundo do Google, por exemplo., eles passam pelo comprimento de escrever Compiladores inteiros apenas para salvar bytes, e essa abordagem é de muitas maneiras a mentalidade certa a ter na programação web. E eu mantenho o Google em muito alta consideração como seus aplicativos js library powers como o Google Maps, que não são apenas loucamente complexos, mas também funcionam em todos os lugares. O JavaScript tem uma imensa variedade de ferramentas disponíveis, dada a sua popularidade, acessibilidade e, em certa medida, uma barra de baixa qualidade no ecossistema como um o inteiro está disposto a aceitar. Para os assinantes do Hacker News[519], não passa um dia sem uma nova biblioteca JS por aí, e são certamente úteis, mas não se pode ignorar o fato de que muitos deles re-implementam exatamente as mesmas preocupações sem qualquer forte noção de novidade ou quaisquer ideias e melhorias assassinas. É uma regra forte resistir à necessidade de misturar todos os novos brinquedos antes de terem tempo para provar a sua novidade e utilidade para o todo o ecossistema e para distinguir fortemente entre diversão de codificação domingo e implantação de produção.

Se a tua etiqueta <head></head> é maior do que este post, estás a fazer tudo mal.

Testar o seu conhecimento de JavaScript

Alguns testes de nível "perfeccionista":
 176
Author: flavian, 2018-06-08 04:35:25
Uma colecção de objectos? Usar esta notação (matrizes de JavaScript):
var collection = [ {name:"object 1"} , {name:"object 2"} , {name:"object 3"} ];

Para colocar um novo elemento na sua colecção:

collection.push( {name:"object 4"} );
 8
Author: Juvanis, 2012-10-19 12:10:32

Em JavaScript todos os objetos são arrays associativos . No primeiro caso você cria um array no segundo caso você criou um objeto vazio que é array também :).

Então em JS você pode trabalhar com qualquer objeto como com array:

var a = {};
a["temp"] = "test";

E como objecto:

var a = {};
a.temp = "test";
 1
Author: Artem Vyshniakov, 2012-10-19 11:57:11

Eu usaria um conjunto de objectos:

collection = [ 
    { "key":"first key", "value":"first value" }, 
    { "key":"second key", "value":"second value" } 
];

Etc

 1
Author: st3inn, 2012-10-19 11:59:14

1) é um Array 2) é um objeto

Com Array tudo é normal como em outras línguas

Com objecto também. - Você pode obter valor A. b = 1 - Mas em JS você também pode obter valor com tal sintaxe a ["b"] == 1

  • isto pode ser útil quando a tecla se parece com algo, isto é "alguma chave", neste caso não se pode usar "encadeamento"
  • também esta chave Se é a variável
Podes escrever assim.
    function some(f){
var Object = {name: "Boo", age: "foo"}, key;
if(f == true){
   key = "name";
}else{
   key = "age";
}
 return Object[key];
}
Mas quero usá-lo como colecção, que tenho de escolher?

Isto depende dos dados que pretende armazenar

 1
Author: VitVad, 2012-10-19 12:03:36