Статьи

Что нового в JavaScript 1.8.5

Это прекрасное время для JavaScript. Он не только становится гораздо более уважаемым языком, но и стремительно растет — как по популярности, так и по возможностям. Поскольку все больше браузеров начинают реализовывать функции стандарта ECMAScript 5-го издания, JavaScript становится еще более мощной платформой для разработки. В этом уроке мы поговорим о новых доступных вам методах.


ECMAScript — это официальное название того, что мы все называем JavaScript. Это не значит, что мы не правы; просто имя «JavaScript» является торговой маркой Oracle; поэтому Ecma International (первоначально Европейская ассоциация производителей компьютеров — отсюда ECMA) использует термин «ECMAScript» для обозначения стандарта JavaScript. Последней версией этого стандарта является 5-е издание, и он был утвержден чуть более года назад (3 декабря 2009 г.). Он включает в себя огромное количество отличных дополнений, и некоторые из них начинают появляться в браузерах. Реализации ECMAScript 5 называются JavaScript 1.8.5.

В этом руководстве мы рассмотрим функции JavaScript 1.8.5, которые доступны нам в бета-версиях Firefox 4. Вы будете рады обнаружить, что большинство последних версий других браузеров также имеют их. , , кроме одного. На этот раз это Opera, так как IE9 включил многие из них.


Этот метод очень важен; это действительно очищает прототип наследования. Ранее (в ECMAScript 3rd edition), чтобы создать объект и установить его прототип, вы должны сделать что-то вроде этого:

01
02
03
04
05
06
07
08
09
10
11
12
function Cat(name) {
    this.name = name;
    this.paws = 4;
    this.hungry = false;
    this.eaten = [];
}
Cat.prototype = {
    constructor : Cat,
    play : function () { this.hungry = true;
    feed : function (food) { this.eaten.push(food);
    speak : function () { return «Meow» }
};

Я единственный, кто считает странным иметь прототип вне функции конструктора? И наследование становится еще сложнее. С Object.create все становится намного проще. Выше может быть закодировано так:

01
02
03
04
05
06
07
08
09
10
11
var dog = {
    name : «dog»,
    paws : 4,
    hungry : false,
    eaten : null,
    play : function () { this.hungry = true;
    feed : function (food) { if (!this.eaten) { this.eaten = [];
    speak : function () { return «Woof!»
};
 
var my_dog = Object.create(dog);

Здесь происходит следующее: я object.create , передавая ему объект для использования в качестве прототипа нового объекта, который возвращает Object.create . При использовании Object.create мне не нужно беспокоиться об определении прототипа отдельно. На самом деле, у меня гораздо больше гибкости, чтобы решить, как создавать и наследовать объекты. Например, я не могу поместить eaten массив в прототип, потому что массив является ссылочным значением, поэтому каждый объект, созданный из dog будет совместно использовать этот массив. Я решил проверить его перед использованием здесь, но если бы я хотел обернуть Object.create(dog) в функцию make_dog , я мог бы назначить его там так же легко.

Вот что хорошо в Object.create ; Вы можете выбрать, как это сделать.

Есть второй параметр, который принимает Object.create ; это объект дескриптора свойств. Это немного сложно, но это также часть следующей функции, которую мы рассмотрим, поэтому давайте проверим это.


Если у вас есть объект, для которого вы хотите определить свойство, вы, вероятно, сделаете это следующим образом:

1
my_dog.age = 2;

Это все еще отлично работает в ES5, но если вам нужен более детальный элемент управления, вы можете получить его с Object.defineProperty . Первый параметр — это объект, которому вы назначаете свойство. Второй параметр — это имя свойства в виде строки. Последнее свойство является объектом дескриптора. Вот как это работает. Это (очевидно) объект, и он может иметь комбинацию следующих свойств, которые описывают добавляемое нами свойство:

  • значение : используйте это, чтобы установить значение свойства. По умолчанию undefined .
  • доступный для записи : используйте это логическое значение, чтобы определить, является ли это переменной только для чтения. Если это доступно для записи, это true . По умолчанию false .
  • настраиваемый : используйте это логическое значение, чтобы определить, можно ли изменить тип (значение или метод) этого свойства или свойство можно удалить. Если это настраивается, это true . По умолчанию false .
  • enumerable : используйте это логическое значение, чтобы определить, включено ли это свойство при перечислении свойств объекта (цикл for-in или метод keys). По умолчанию false .
  • get : используйте это, чтобы определить пользовательский метод получения. По умолчанию undefined .
  • set : используйте это для определения пользовательского метода установки. По умолчанию undefined .

Обратите внимание, что значения по умолчанию для указанных выше логических параметров являются обратными старым стандартам obj.prop = val . Кроме того, знайте, что вы не можете определить value или writable когда определены get или set , и наоборот.

Итак, как бы вы использовали это? Попробуй это:

01
02
03
04
05
06
07
08
09
10
// assume my_dog from above
 
Object.defineProperty(my_dog, «age», {
    set : function (age) { this.human_years = age * 7;
    get : function () { return this.human_years / 7;
    enumerable : true
});
 
my_dog.age = 2;
my_dog.human_years;

Помимо того, что собачьи годы на самом деле не являются 7 человеческими годами , вы должны заметить, что мы не установили value или не writable здесь, потому что мы используем get и set . Эти функции никогда не доступны напрямую. Они «магически» запускаются за кулисами, когда вы назначаете или запрашиваете свойство. В этом примере я использую эти функции, чтобы держать age и human_years в состоянии «sync». Если вы не хотите, чтобы значение «other» было доступно, вы можете использовать анонимную, самопризывающуюся функцию, чтобы скрыть ее с помощью closure:

01
02
03
04
05
06
07
08
09
10
Object.defineProperty(my_dog, «age», (function () {
    var human_years;
 
    return {
        set : function (age) { human_years = age * 7;
        get : function () { return human_years / 7;
        enumerable : true
    };
 
}()));

Конечно, ничто не мешает вам делать что-то глупое внутри get или set , так что используйте это с умом.

Вы можете использовать форму объекта дескриптора свойства для добавления свойств к объектам с помощью Object.create . Сделайте это следующим образом:

01
02
03
04
05
06
07
08
09
10
var your_dog = Object.create(dog, {
    age : {
        get : function () { /* .
        set : function () { /* .
        enumerable: true
    },
    gender : {
        value : «female»
    }
});

Просто используйте имя свойства как свойство объекта дескриптора; затем установите атрибуты через объект в значении.


Если вы хотите определить несколько свойств одновременно, вы можете использовать объект дескрипторов свойств так же, как с Object.create чтобы определить их, используя Object.defineProperties .

01
02
03
04
05
06
07
08
09
10
Object.defineProperties(my_dog, {
    age : {
        get : function () { /* .
        set : function () { /* .
        enumerable: true
    },
    gender : {
        value : «female»
    }
});

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


Если вы когда-нибудь захотите узнать особенности свойства, вы можете использовать эту функцию Object.getOwnPropertyDescriptor . Принять к сведению «свой»; это работает только со свойствами самого объекта, а не его цепочки прототипов.

1
2
3
var person = { name : «Joe» };
 
Object.getOwnPropertyDescriptor(person, «name»);

Как видите, это работает со свойствами, установленными как старым, так и новым способом. Object.getOwnPropertyDescriptor принимает два параметра: объект и имя свойства в виде строки.


Всегда хотел получить все ключи объекта? Теперь вы можете легко сделать это с помощью Object.keys . Передайте этой функции объект, и он вернет массив всех перечисляемых свойств этого объекта. Вы также можете передать ему массив, и вы получите обратно массив индексов.

1
2
3
var horse = { name : «Ed», age : 4, job : «jumping», owner : «Jim» };
 
var horse_keys = Object.keys(horse);

Этот похож на Object.keys , за исключением того, что он включает в себя все свойства — даже те, которые не перечисляются. По более длинному названию функции вы можете сказать, что они препятствуют его использованию. Обычно вам нужны keys .


Если вы когда-либо хотели создать функцию, которая не принимает новые параметры, вы можете сделать это сейчас. Запустите ваш объект через Object.preventExtensions , и он будет отклонять все попытки добавления новых параметров. Эта функция идет рука об руку с Object.isExtensible , который возвращает true если вы можете расширить объект, и false если вы не можете.

01
02
03
04
05
06
07
08
09
10
11
var product = { name : «Foobar», rating : 3.5 };
 
   Object.isExtensible(product);
 
   Object.preventExtentions(product);
 
   Object.isExtensible(product);
 
   product.price = «$10.00»;
    
   product.price;

Следует отметить, что все свойства объекта во время запуска Object.preventExtensions все еще могут быть изменены или удалены (при условии, что их атрибуты позволяют это).


Запечатывание объекта — один шаг от предотвращения расширений. Запечатанный объект не позволит вам добавлять или удалять свойства или изменять свойства со значения (например, строки) на метод доступа (метод) или наоборот. Конечно, вы все равно сможете читать и писать свойства. Вы можете узнать, запечатан ли объект, используя Object.isSealed .

01
02
03
04
05
06
07
08
09
10
11
var pet = { name : «Browser», type : «dog» };
 
Object.seal(pet);
 
pet.name = «Oreo»;
 
pet.age = 2;
 
pet.type = function () { /**/ };
 
delete pet.name;

Заморозка это еще один шаг вперед. Замороженный объект не может быть изменен никаким образом; это только для чтения. Вы можете проверить замороженность объекта с помощью, как вы уже догадались, Object.isFrozen .

1
2
3
4
5
var obj = { greeting : «Hi!»
 
Object.freeze(obj);
 
Object.isFrozen(obj);

Можно подумать, что было бы не сложно определить, является ли данная переменная массивом. В конце концов, все остальное прекрасно работает с оператором typeof . Однако JavaScript-массивы противоречивы. Они на самом деле ближе к объектам, похожим на массивы (хотя мы обычно используем этот термин для обозначения таких вещей, как arguments и NodeList ) Эта функция дает вам 100% уверенность в том, что вы работаете с массивом. Передайте ему переменную, и она возвращает логическое значение.

1
2
3
var names = [«Collis», «Cyan»];
 
Array.isArray(names);

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


Это не так уж и много, но если вы когда-нибудь захотите сохранить даты в формате JSON, это может оказаться полезным. У объектов Date теперь есть функция toJSON , которая преобразует дату в toJSON дату JSON.

1
new Date().toJSON();

Вы, вероятно, знакомы с использованием call и apply чтобы переназначить значение this в функции .

1
2
3
4
var arr1 = [«1», «2», «3»],
    arr2 = [«4», «5», «6»];
 
Array.prototype.push.apply(arr1, arr2);

Эти методы позволяют вам изменить значение this внутри функции. Если вы хотите делать что-то подобное часто, Function.prototype.bind возвращает новую функцию с this привязкой к тому, что вы передаете, так что вы можете сохранить ее в переменной.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
var tooltip = { text: «Click here to . . . » },
    overlay = { text: «Please enter the number of attendees» };
 
function show_text () {
    // really, do something more useful here
    console.log(this.text);
}
 
tooltip.show = show_text.bind(tooltip);
tooltip.show();
 
overlay.show = show_text.bind(overlay);
 
overlay.show();

Конечно, это может быть не самый практичный пример, но он дает вам идею!


Это функции ECMAScript 5th Edition (или JavaScript 1.8.5), которые были добавлены в бета-версии Firefox 4. Есть несколько других изменений в JavaScript, которые они также реализуют, что вы можете проверить в примечаниях к выпуску .

Однако есть несколько функций ECMAScipt 5, которые уже поддерживаются в Firefox 3 и некоторых других браузерах. Вы играли с любым из них?

Примечание: они связаны с их документацией MDN.

Если вы хотите узнать, какие браузеры и версии поддерживают эти функции, вы можете проверить эту таблицу совместимости, составленную Юрием Зайцевым (Kangax) . Приятной особенностью большинства этих функций является то, что если браузер не поддерживает их, вы можете добавить поддержку, например, так:

1
2
3
4
5
6
7
8
if (typeof Object.create !== ‘function’) {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}
// Courtesy of Douglas Crockford: http://javascript.crockford.com/prototypal.html

Множество новых функций, которые мы рассмотрели здесь, на самом деле — лишь малая часть достоинства, добавленного к стандарту ECMAScript в 5-м издании. Есть ли какие-то другие функции, которые вы с нетерпением ожидаете использовать, или, может быть, даже сейчас используете? Давайте здесь это в комментариях!