Статьи

Пять полезных функций, отсутствующих в Javascript

JavaScript существует уже несколько лет, и его ядро ​​продолжает развиваться, включая новые классы и функции, помогающие программистам выполнять свою работу. Однако некоторые базовые служебные функции все еще отсутствуют и вместо этого реализованы с помощью таких библиотек, как jQuery , Prototype и MooTools . Работа с этими инструментами великолепна, но в некоторых случаях их использование чрезмерно для ваших нужд. В этой статье рассматриваются пять полезных функций, которые я всегда считал принадлежащими JavaScript.

getElementsByClassName ()

JavaScript позволяет извлекать элемент, используя его idgetElementById() Новая функция называется getElementsByClassName() К сожалению, как вы можете догадаться, он не доступен во всех версиях Internet Explorer, враг-браузер номер один для веб-дизайнеров. Только Internet Explorer 9+ поддерживает getElementsByClassName()

Лучшая функция, которую я нашел, была написана Робертом Найманом. Его реализация выпущена под лицензией MIT, и это было рекомендовано WHATWG . Он использует собственный метод getElementsByClassName()document.evaluate() Если все остальное терпит неудачу, сценарий возвращается к рекурсивному обходу DOM и сбору элементов, которые соответствуют заданным именам классов . Вы можете найти код ниже и в своем хранилище .

 var getElementsByClassName = function (className, tag, elm){
  if (document.getElementsByClassName) {
    getElementsByClassName = function (className, tag, elm) {
      elm = elm || document;
      var elements = elm.getElementsByClassName(className),
      nodeName = (tag)? new RegExp("\b" + tag + "\b", "i") : null,
      returnElements = [],
      current;
      for(var i=0, il=elements.length; i<il; i+=1){
        current = elements[i];
        if(!nodeName || nodeName.test(current.nodeName)) {
          returnElements.push(current);
        }
      }
      return returnElements;
    };
  }
  else if (document.evaluate) {
    getElementsByClassName = function (className, tag, elm) {
      tag = tag || "*";
      elm = elm || document;
      var classes = className.split(" "),
      classesToCheck = "",
      xhtmlNamespace = "http://www.w3.org/1999/xhtml",
      namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
                           returnElements = [], elements, node;
      for(var j=0, jl=classes.length; j<jl; j+=1){
        classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
      }
      try {
        elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
      }
      catch (e) {
        elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
      }
      while ((node = elements.iterateNext())) {
        returnElements.push(node);
      }
      return returnElements;
    };
  }
  else {
    getElementsByClassName = function (className, tag, elm) {
      tag = tag || "*";
      elm = elm || document;
      var classes = className.split(" "),
          classesToCheck = [],
          elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
          current,
          returnElements = [],
          match;
      for(var k=0, kl=classes.length; k<kl; k+=1){
        classesToCheck.push(new RegExp("(^|\s)" + classes[k] + "(\s|$)"));
      }
      for(var l=0, ll=elements.length; l<ll; l+=1){
        current = elements[l];
        match = false;
        for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
          match = classesToCheck[m].test(current.className);
          if (!match) {
            break;
          }
        }
        if (match) {
          returnElements.push(current);
        }
      }
      return returnElements;
    };
  }
  return getElementsByClassName(className, tag, elm);
};

расширить ()

Если вы когда-либо писали плагин, вы почти наверняка сталкивались с проблемой объединения двух или более объектов. Это часто происходит, когда у вас есть некоторые настройки по умолчанию и вы хотите, чтобы пользователь мог заменить некоторые значения по умолчанию. Если бы вы использовали jQuery, вы могли бы использовать extend() К счастью, вы можете легко построить его самостоятельно. В следующем примере показано, как создать код, который работает так же, как метод jQuery. Я добавляю наш метод extend()Object

 Object.prototype.extend = function() {
  if (arguments.length === 0)
    return this;

  for (var i = 0; i < arguments.length; i++) {
    for (var property in arguments[i]) {
      if (arguments[i].hasOwnProperty(property))
        this[property] = arguments[i][property];
    }
  }
  return this;
};

Эта функция принимает переменное количество аргументов. Это возможно благодаря использованию arguments Если вам нужна дополнительная информация об argumentsаргументы: Странность JavaScript .

равна ()

Сравнение объектов — очень распространенная операция. Хотя этот тест можно выполнить с помощью оператора строгого равенства ( === Вместо этого вы хотите знать, имеют ли два объекта одинаковые свойства с одинаковыми значениями. Код ниже делает именно это. Обратите внимание, что следующий код не мой; принадлежит пользователю с именем crazyx . Снова, equals()Object.prototype

 Object.prototype.equals = function(x) {
  var p;
  for(p in this) {
    if (typeof(x[p]) == "undefined")
      return false;
  }
  for(p in this) {
    if (this[p]) {
      switch(typeof(this[p])) {
        case "object":
          if (!this[p].equals(x[p]))
            return false;
          break;
        case "function":
          if (typeof(x[p]) == "undefined" ||
             (p != "equals" && this[p].toString() != x[p].toString()))
            return false;
          break;
        default:
          if (this[p] != x[p])
            return false;
      }
    }
    else {
      if (x[p])
        return false;
    }
  }
  for(p in x) {
    if(typeof(this[p])=="undefined")
      return false;
  }
  return true;
}

inArray ()

В JavaScript нет встроенного метода для проверки, находится ли значение в массиве. Мы напишем функцию, которая, как вы могли бы ожидать, вернет truefalse Эта функция просто сравнивает заданное значение с каждым элементом массива. Как и в предыдущих двух примерах, inArray()prototypeArray

 Array.prototype.inArray = function (value) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] === value)
      return true;
  }
  return false;
};

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

 var array = [1, 2, 3];
console.log(array.inArray(2)); // print true

var obj = {"prop": "value"};
array = [{"prop": 1}, {"prop": "a long string"}, {"prop": "value"}];
console.log(array.inArray(obj)); // print false, not true as you might expect

array = [{"prop": 1}, {"prop": "a long string"}, obj];
console.log(array.inArray(obj)); // print true

Представленная функция может быть улучшена с помощью функции equals() Таким образом, мы можем получить совпадение, если два объекта имеют одинаковые свойства и значения. Еще одно улучшение, которое мы можем сделать, — заставить функцию возвращать позицию элемента, а не просто truefalse Окончательный вариант функции показан ниже.

 Array.prototype.inArray = function (value) {
  for (var i = 0; i < this.length; i++) {
    if (typeof value === "object") {
      // If both are objects, uses the equals function
      if (typeof this[i] === "object" && value.equals(this[i]))
          return i;
    }
    else if (this[i] === value)
      return i;
  }
  return false;
};

Теперь, если вы снова запустите приведенные выше примеры, вы получите:

  1
 2
 2 

toggleClass ()

Другой метод, который часто используется в jQuery — это toggleClass() Он добавляет или удаляет класс из элемента в зависимости от того, присутствует ли имя класса или нет. Простая версия toggleClass()

 function toggleClass(id, className) {
  var element = document.getElementById(id);
  var classes = element.className.split(/s+/);
  var length = classes.length;

  for(var i = 0; i < length; i++) {
    if (classes[i] === className) {
      classes.splice(i, 1);
      break;
    }
  }
  // The className is not found
  if (length === classes.length)
    classes.push(className);

  element.className = classes.join(" ");
}

Этот код также может быть улучшен. Цикл forclasses Мы можем заменить цикл вызовом функции inArray()

 function toggleClass(id, className) {
  var element = document.getElementById(id);
  var classes = element.className.split(/s+/);
  var length = classes.length;
  var found = classes.inArray(className);
  if (found !== false)
    classes.splice(found, 1);
  // The className is not found
  if (length === classes.length)
    classes.push(className);
  element.className = classes.join(" ");
}

Вывод

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

  • Фреймворки, такие как jQuery, имеют много полезных функций, но они добавляют накладные расходы. Так что, если вам нужна только пара функций, используйте сырой JavaScript и сгруппируйте нужные методы в файле экстента.
  • Если вы использовали функцию, которая была представлена ​​в новой версии JavaScript, не избавляйтесь от нее. Оберните его условным оператором, чтобы проверить, поддерживается ли он, а если нет, используйте старый код, как указано для getElementsByClassName() Таким образом, вы продолжите поддерживать старые браузеры.
  • По возможности добавляйте функции к прототипу объекта, как показано для extend() Все экземпляры будут использовать один и тот же метод, и вы получите лучшую производительность.
  • Когда это возможно, используйте ваш код, как показано во второй версии toggleClass()