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

Коллекция Javascript

Извините за вопрос noobie. Можете ли вы объяснить, пожалуйста, в чем разница между:

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

2. var a = {};
   a['b'] = 1;

Я не мог найти статью в Интернете, поэтому написал здесь.

4b9b3361

Ответ 1

литералов

[] и {} называются соответственно литералами массива и объектов.

var x = [] не подходит для var x = new Array();

и var y = {} не хватает для var y = new Object();

Массивы

Массивы представляют собой структуры с свойством length. Вы можете получить доступ к значениям через их числовой индекс.

var x = [] or var x = new Array();
x[0] = 'b';
x[1] = 'c';

И если вы хотите перечислить все свойства, которые вы выполняете:

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

Трюки производительности и gotchas

1. Внутреннее кэширование свойства длины

Итерация стандартного массива:

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

Малоизвестный факт: в приведенном выше сценарии свойство arr.length считывается на каждом этапе цикла for. Как и любая функция, которую вы вызываете там:

for (var i = 0; i < getStopIndex(); i++) {
     // do stuff
};

Это снижает производительность без причины. Внутреннее кэширование на помощь:

for (var i = 0, len = arr.length; i < len; i++) {
     // enter code here
};

Вот подтверждение выше.

2. Не указывайте длину массива в конструкторе.

// 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.

Доступны тестовые примеры для определения массива здесь.

3. Избегайте использования Array.prototype.push(arr.push)

Если вы имеете дело с большими коллекциями, прямое назначение выполняется быстрее, чем с помощью метода Array.prototype.push();.

myArray[i] = 0; быстрее, чем myArray.push(0);, согласно jsPerf.com тестовые примеры.

4. Неправильно использовать массивы для ассоциативных назначений.

Единственная причина, по которой это работает, состоит в том, что Array расширяет класс Object внутри ядра языка JS. Например, вы можете использовать объект Date(); или RegEx();. Это не изменит ситуацию. x['property'] = someValue ДОЛЖЕН всегда использовать Объекты.

Массивы должны иметь только числовые индексы. SEE ЭТО, рекомендации Google JS по разработке! Избегайте циклов for (x in arr) или arr['key'] = 5;.

Это можно легко скопировать, посмотрите ЗДЕСЬ для примера.

var x = [];
console.log(x.prototype.toString.call);

выведет: [object Array]

Это показывает шаблон наследования класса "класс".

var x = new String();
console.log(x.prototype.toString.call);

выводит [object String].

5. Получение минимума и максимума из массива.

Немного известный, но действительно мощный трюк:

function arrayMax(arr) {
    return Math.max.apply(Math, arr);
};

соответственно:

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

Объекты

С помощью объекта вы можете делать только:

var y = {} или var y = new Object();

y['first'] = 'firstValue' совпадает с y.first = 'firstValue', который вы не можете сделать с массивом. Объекты предназначены для ассоциативного доступа с помощью клавиш String.

Итерация выглядит примерно так:

for (var property in y) {
    if (y.hasOwnProperty(property)) {
        console.log(y.property);
    };
};

Трюки производительности и gotchas

1. Проверка наличия свойства объекта.

Большинство людей используют Object.prototype.hasOwnProperty. К сожалению, это часто приводит к ошибочным результатам, приводящим к неожиданным ошибкам.

Вот хороший способ сделать это:

function containsKey(obj, key) {
    return typeof obj[key] !== 'undefined';
};

2. Замена операторов switch.

Один из простых, но эффективных трюков JS - замена switch.

switch (someVar) {
    case 'a':
        doSomething();
        break;
    case 'b':
        doSomethingElse();
        break;
    default:
        doMagic();
        break;
};

В большинстве двигателей JS вышеупомянутое мучительно медленное. Когда вы смотрите на три возможных результата, это не имеет никакого значения, но что, если у вас было десятки или сотни?

Вышеприведенное может быть легко заменено объектом. Не добавляйте завершающий (), это не выполняет функции, а просто хранит ссылки на них:

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

Вместо switch:

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

3. Глубокое клонирование стало проще.

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;
  };
};

ЗДЕСЬ - это функция глубокого клонирования в действии.

Auto-бокс

Как динамически типизированный язык, JavaScript ограничен с точки зрения собственных типов объектов:

  • Объект
  • Массив
  • Количество
  • Boolean
  • Дата
  • RegEx
  • Ошибка

Null не является типом, typeof null является объектом.

Какой улов? Существует сильное различие между примитивными и не примитивными объектами.

var s = "str";
var s2 = new String("str");

Они делают то же самое, вы можете вызывать все строковые методы на s и s2. Тем не менее:

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

Вы можете услышать в JS все, что есть Object. Это не совсем верно, хотя это очень простая ошибка.

В действительности есть 2 типа, примитивы и объекты, а при вызове s.indexOf("c") механизм JS автоматически преобразует s в его непримитивный тип обертки, в этом случае object String, где все методы определены на String.prototype.

Это называется auto-boxing. Метод Object.prototype.valueOf(obj) - это способ заставить бросок от примитивного к непримитивному. Это то же поведение, что и язык, такой как Java, представляет для многих из его собственных примитивов, в частности пары: int - Integer, double - Double, float - Float и т.д.

Зачем вам это нужно?

Простой:

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

Итак, если s2 был создан с помощью var s2 = new String("test"), вы получаете ложный отрицательный результат даже для простой, по-видимому, простой проверки типа. Более сложные объекты также приносят с собой тяжелую производительность.

Микро-оптимизация, как некоторые скажут, но результаты действительно замечательны даже для чрезвычайно простых вещей, таких как инициализация строк. Позвольте сравнить следующие два с точки зрения производительности:

var s1 = "this_is_a_test" 

и

var s2 = new String("this_is_a_test")

Вероятно, вы ожидаете соответствия производительности по всем направлениям, но, что удивительно, последнее утверждение, использующее new String, на 92% медленнее первого, как доказано здесь.

Функции

1. Параметры по умолчанию

Оператор || является самым простым способом дефолта. Почему это работает? Из-за правдивых и ложных значений.

При оценке в логическом состоянии значения undefined и null будут автозагружаться до false.

Простой пример (код ЗДЕСЬ):

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

2. OO JS

Самое главное понять, что объект JavaScript this не является неизменным. Это просто ссылка, которую можно легко изменить.

В OO JS мы полагаемся на ключевое слово new, чтобы гарантировать неявный охват во всех членах класса JS. Тем не менее, вы можете легко изменить область действия, используя Function.prototype.call и Function.prototype.apply.

Еще одна важная вещь - Object.prototype. Непримитивные значения, вложенные в прототип объектов, являются общими, а примитивными - нет.

Код с примерами ЗДЕСЬ.

Простейшее определение класса:

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

Простой класс размера с двумя членами this.width и this.height.

В определении класса, независимо от того, что есть this перед ним, создаст новую ссылку для каждого экземпляра Size.

Добавление методов в классы и почему шаблон "закрытия" и другой "узор фантазийных имен" являются чистой фантастикой

Возможно, здесь обнаружены самые вредоносные анти-шаблоны JavaScript.

Мы можем добавить метод к нашему классу Size двумя способами.

Size.prototype.area = function() {
   return this.width * this.height;
};

Или:

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

Метод area Size2 создается для каждого экземпляра. Это совершенно бесполезно и медленно, ЛОТЬ медленнее. 89%, если быть точным. Посмотрите ЗДЕСЬ.

Вышеприведенный оператор действителен для примерно 99% всех известных "модных шаблонов имен". Помните самое важное в JS, все это не что иное, как вымысел.

Существуют сильные архитектурные аргументы, которые могут быть сделаны, в основном, связаны с инкапсуляцией данных и использованием закрытий.

Такие вещи, к сожалению, абсолютно бесполезны в JavaScript, потеря производительности просто не стоит. Мы говорим о 90% и выше, это ничего, кроме незначительного.

3. Ограничения

Поскольку определения prototype разделяются между всеми экземплярами класса, вы не сможете разместить там не примитивный объект настроек.

Size.prototype.settings = {};

Почему? size.settings будет одинаковым для каждого отдельного экземпляра. Итак, что с примитивами?

Size.prototype.x = 5; // won't be shared, because it a primitive.
// see auto-boxing above for primitive vs non-primitive
// if you come from the Java world, it the same as int and Integer.

Точка:

Средний JS-парень будет писать JS следующим образом:

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

Что такое отлично (отлично = плохое качество, сложно поддерживать код), если вы понимаете, что это объект Singleton, и эти функции должны использоваться только в глобальной области без ссылок this внутри них.

Но тогда он попадает в абсолютно ужасный код:

var x = {
   width: 10,
   height: 5
}
var y = {
   width: 15,
   height: 10
}

Вы могли бы уйти с: var x = new Size(10, 5); var y = new Size(15, 5);.

Занимает больше времени, вам нужно вводить одно и то же каждый раз. И снова, это ЛОТКА. Посмотрите ЗДЕСЬ.

Плохие стандарты во всем

Это можно увидеть почти везде:

   function() {
      // some computation
      var x = 10 / 2;
      var y = 5;
      return {
         width: x,
         height: y
      }
    }

Опять с альтернативой:

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

Точка: ИСПОЛЬЗУЙТЕ КЛАССЫ, КОТОРЫЕ ДОСТУПНЫ!

Почему? Пример 1 93% медленнее. Посмотрите ЗДЕСЬ, Примеры здесь тривиальны, но они иллюстрируют, что что-то игнорируется в JS, OO.

Это правильное правило не использовать людей, которые думают, что у JS нет классов, и чтобы получить работу от рекрутеров, говорящих о "Object Orientated" JS.

Затворы

Многие люди предпочитают их выше, потому что это дает им ощущение инкапсуляции данных. Помимо резкого падения производительности на 90%, здесь что-то одинаково легко упускать из виду. Утечка памяти.

function Thing(someParam) {
   this.someFn = function() {
     return someParam;
   }
}

Вы только что создали закрытие для someParam. Почему это плохо? Во-первых, это заставляет вас определять методы класса как свойства экземпляра, что приводит к большому снижению производительности.

Во-вторых, он ест память, потому что закрытие никогда не будет разыменовано. Посмотрите здесь для подтверждения. Конечно, вы получаете некоторую фальшивую инкапсуляцию данных, но вы используете три раза память с 90% -ным снижением производительности.

Или вы можете добавить @private и получить способ с именем функции подчеркивания.

Другие очень распространенные способы создания замыканий:

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

param теперь является замыканием! Как вы избавитесь от него? Существуют различные трюки, некоторые найдены здесь. Наилучший возможный подход, хотя и более строгий, заключается в том, чтобы избежать использования анонимных функций вместе, но для этого потребовался бы способ определения областей для обратных вызовов событий.

Такой механизм доступен только в Google Closure, насколько мне известно.

Синтаксический шаблон

Итак, что мне делать для одиночек? Я не хочу хранить случайные ссылки. Здесь чудесная идея бесстыдно украдена из Google Closure base.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, но это простой и мощный трюк. Теперь вы можете:

project.some.namespace.StateManager = function() {
   this.x_ = 5;
};
project.some.namespace.prototype.getX = function() { return x; }
addSingletonGetter(project.some.namespace.StateManager);

Как это полезно? Просто. Во всех других файлах каждый раз, когда вам нужно ссылаться на project.some.namespace.StateManager, вы можете написать: project.some.namespace.StateManager.getInstance(). Это более удивительно, чем кажется.

У вас может быть глобальное состояние с преимуществами определения класса (наследование, члены с состоянием и т.д.) и без, загрязняющих глобальное пространство имен.

Шаблон одиночного экземпляра

У вас может возникнуть соблазн сделать это:

function Thing() {
   this.someMethod = function() {..}
}
// and then use it like this:
Thing.someMethod();

Это еще один большой нет-нет в JavaScript. Помните, что объект this гарантированно остается неизменным при использовании ключевого слова new. Волшебство, стоящее за вышеуказанным кодом, интересно. this на самом деле является глобальной областью, поэтому без необходимости добавлять методы к глобальному объекту. И вы догадались, что эти вещи никогда не собираются собирать мусор.

Ничего не сказано, что JavaScript использует что-то другое. A function сам по себе не имеет области. Будьте очень осторожны, что вы делаете с static свойствами. Чтобы воспроизвести цитату, которую я когда-то читал, глобальный объект JavaScript похож на общественный туалет. Иногда у вас нет выбора, кроме как идти туда, но старайтесь максимально свести к минимуму контакт с поверхностями.

Либо придерживайтесь вышеуказанного шаблона Singleton, либо используйте объект настроек, вложенный в пространство имен.

Сбор мусора в JavaScript

JavaScript - это сборник мусора, но JavaScript GC часто довольно плохо разбирается. Точка - это снова скорость. Это, возможно, слишком знакомо.

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

// more code
function someFn() {..}

Это плохой, плохой код производительности. Причина проста. JS будет мусором собирать переменную и освобождать память кучи, которую она держит, только когда эта переменная становится де-скопированной, например. в нем нет ссылок на него в памяти.

Например:

function test(someArgs) {
   var someMoreStuf = // a very big complex object;
}
test();

Три вещи:

  • Аргументы функции преобразуются в локальные определения
  • Внутренние объявления подняты.
  • Вся память кучи, выделенная для внутренних переменных, освобождается, когда функция завершает выполнение.

Почему? Потому что они больше не принадлежат к "текущему" охвату. Они создаются, используются и уничтожаются. Также нет никаких замыканий, поэтому вся используемая вами память освобождается через сборку мусора.

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

var x = 5;
var y = {..}; //etc;

Хорошо, что теперь?

Namespaces.

JS не имеет пространств имен для каждого слова, так что это не совсем эквивалент Java, но с точки зрения администрирования кода вы получаете то, что хотите.

var myProject = {};
myProject.settings = {};
myProject.controllers = {};
myProject.controlls.MainController = function() {
    // some class definition here
}

Красивая. Одна глобальная переменная. Надлежащая структура проекта. С фазой сборки вы можете разделить проект на файлы и получить надлежащую среду разработки.

Здесь нет предела тому, чего вы можете достичь.

Подсчитайте свои библиотеки

Получив удовольствие от работы с бесчисленными кодовыми базами, последний и самый важный аргумент должен быть очень внимательным к вашим зависимостям кода. Я видел, как программисты случайно добавляли jQuery в смесь стека для простого анимационного эффекта и т.д.

Зависимость и управление пакетами - это то, что в мире JavaScript не было адресов в течение самого долгого времени, до создания таких инструментов, как Bower. Браузеры все еще несколько медленны, и даже когда они работают быстро, интернет-соединения медленны.

В мире Google, например, они просматривают длины написания целых компиляторов только для сохранения байтов, и этот подход во многом имеет правильное мышление в веб-программировании. И я поддерживаю Google в очень высоком отношении, поскольку их библиотеки JS поддерживают такие приложения, как Google Maps, которые не только безумно сложны, но и работают повсюду.

Возможно, у JavaScript есть огромное разнообразие доступных инструментов, учитывая его популярность, доступность и, в некоторой степени, очень низкий уровень качества, который экосистема в целом готова принять.

Для подписчиков Hacker News день не проходит без новой JS-библиотеки, и они, безусловно, полезны, но нельзя игнорировать тот факт, что многие из них повторно реализуют те же самые проблемы без какого-либо сильного представления о новизну или любые идеи и улучшения убийцы.

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

Если тэг <head></head> длиннее этого сообщения, вы делаете все это неправильно.

Тестирование ваших знаний JavaScript

Несколько тестов "перфекционистов":

Ответ 2

Коллекция объектов? Используйте это обозначение (массивы JavaScript):

var collection = [ {name:"object 1"} , {name:"object 2"} , {name:"object 3"} ];

Чтобы добавить новый элемент в вашу коллекцию:

collection.push( {name:"object 4"} );

Ответ 3

В JavaScript все объекты ассоциативные массивы. В первом случае вы создаете массив во втором случае, когда вы создали пустой объект, который тоже является массивом:).

Итак, в JS вы можете работать с любым объектом, как с массивом:

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

И как объект:

var a = {};
a.temp = "test";

Ответ 4

Я бы использовал массив объектов:

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

и т.д.

Ответ 5

1) Является ли массив 2) Объект

С Array все обычное, как в других языках

С объектом также. - Вы можете получить значение a.b == 1 - Но в JS вы также можете получить значение с таким синтаксисом a [ "b" ] == 1

  • Это может быть полезно, когда ключ выглядит как-то таким "некоторым ключом", в этом случае вы не можете использовать "цепочку"
  • также это полезно, если ключ - это переменная

вы можете написать вот так

    function some(f){
var Object = {name: "Boo", age: "foo"}, key;
if(f == true){
   key = "name";
}else{
   key = "age";
}
 return Object[key];
}

но я хочу использовать его как коллекцию, которую я должен выбрать?

Это зависит от того, какие данные вы хотите сохранить