Pull to refresh

Comments 71

Сильно! Спасибо вам, читаем и ждем продолжения!
Javascript с неявными геттерами и сеттерами уже не выглядит как java, это фишка как в Object Pascal, ну или как операторы в C++.

Утром решил перед работать прочитать интересную статью на хабре, пока пью кофе… эх затянулось, горите сроки синем пламенем!

Побольше бы таких статей, а не рекламы и холивара.

P.S. «Продолжение следует...» хех
Теперь всех кто пытается нагородить классов в Javascript, буду тыкать носом в эту статью
простите за небольшой флуд, но в личке с тов. andybel произошла интересная дискуссия. Процитирую:
andybel:
> Вот у меня есть имя, отчество и фамилия. У испанцев часто по нескольку имён. У казахов — имя и, кажется — отчество.
> Поэтому идея классов — бредовая по определению. Она ничего кроме надуманной сложности не приносит, как в С++, так и в JS.
> Я думаю, должно быть так
> Испанец.имя = «Вася»
> Испанец.имя2 = «Петя»
> Испанец.имя3 = «Коля»
> Язык должен позволять добавлять свойства вместе с присвоением значения.

я:

> По вашему примеру можно сделать свойство name массивом:
> Испанец.name[0] = «Хуан»;
> Испанец.name[1] = «Педрович»;
> Испанец.name[2] = «Санта-Мария»;
> Испанец.name[3] = «Гомес»;

> И ходить по массиву циклом если что.

andybel:
> Можно и так и так, на то он и великий JS, разницы аж никакой.
> Просто самое передовое там, где молодёжь. А в С++ и JAVA — одни пердуны с засохшими мозгами.
> Не вина JS, что он вырастает из дерьма клиентских програмулин для сайтегов.
Только действительно недалекий человек может так резко отзываться о программистах на C++ и Java.
Ну капец, и мне в итоге досталось((
Всех, кто против оберток для создания «классов», давно тыкают носом в эту статью. Данная статья, кстати, ничем им не противоречит.
Ну да, сахарок, не более. Я же говорю про случаи когда пытаются портировать наследование из других языков.
Я конечно понимаю, что это перевод но сначала Вы пишите:
: Однако, в JavaScript нет концепции класса. К примеру, объект с свойствами {name: Linda, age: 21} не является экземпляром какого-либо класса или класса Object.

А далее по тексту:
: Литеральный объект определяет новый объект, родитель которого Object.prototype (о родителях поговорим немного позже).

Дак все-таки является ли литеральный объект экземпляром какого-либо класса?
Отвечу сам с примером.
Базовым классам литерального объекта является Object.prototype: это можно легко проверить:

Иначе откуда тогда появились методы .toString(), .hasOwnProperty(), valueOf() и т.д.

Спасибо за перевод.
Особо приглянулось разжевывание поведения «this». Распечатаю и раздам коллегам-кодерам, которые «ой, да вообще он странный, ваш этот JS, особенно эта магия с this».
Отдельное спасибо за ссылку на «Understanding delete»
Писал когда то статью про this в javascript — habrahabr.ru/post/149516/
Там есть и возможность проверить свои навыки и потренироваться в угадывании результата
>Всё, включая Strings, Arrays, Numbers, Functions, и, очевидно, т.н. Object — это примитивы, но они конвертируются в объекты, когда вы пытаетесь оперировать ими.

Подавился чаем и полез в спецификацию.
Мм… Ну, мне сложно с этим поспорить. Однако это лишь перевод статьи. В оригинале написано следующим образом: «This includes Strings, Arrays, Numbers, Functions, and, obviously, the so-called Object — there are primitives, but they're converted to an object when you need to operate upon them. „
Речь про элементарные типы вероятно, но в любом случае автор неправ. Массивы и функции ну никак к ним не относятся. Это больше похоже на легкую степень бреда.
Более детально можно почитать здесь.
Я после этих слов стал читать статью по диагонали.
Я после этого вообще прочтение отложил в долгий ящик…
Всё, с чем вы работаете в JavaScript является объектами. Всё, включая Strings, Arrays, Numbers, Functions — это примитивы, но они конвертируются в объекты, когда вы пытаетесь оперировать ими.

Примитивные значения не являются объектами.

var x=1; 

У глобального объекта window появится свойство x, которое будет иметь значение 1, а не ссылку на объект Number.

x+=1; 

Тут будут присходить математические операции с примитивными значениями, а не с объектами, хотя мы и оперируем ими.
У глобального объекта window появится свойство x, которое будет иметь значение 1, а не ссылку на объект Number.


Да?
А если вызвать x.toString()? Отработает ведь? И что? Не объект, все равно?
Выведите в консоли x.constructor.prototype и успокойтесь.

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

Все равно не объект! Когда вы напишете x.toString() будет вызван конструктор Number, который создаст объект-обертку, который после выполнения будет уничтожен. И да x.constructor.prototype выведет то что вы ожидаете, но после вывода объект снова будет уничтожен.
Но если вы все еще считаете что от этого число становится объектом, то просто попробуйте добавить ему новое свойство и затем выведите это свойство в консоль. Думаю вас ждет большой сюрприз:

var a = 1;
console.log(a.constructor.prototype);
a.color='green';
console.log(a.color); // OMG! undefined!!!


P.S. надесь вы распознали скрытый тэг <сарказм>
Грёбаный автобоксинг :-)
А вот так объект:

var numberObject = new Number(1602);
Давайте начнем с прочтения спецификации

4.2 Language Overview

Properties are containers that hold other objects, primitive values, or functions. A primitive value is a member of one of the following built-in types: Undefined, Null, Boolean, Number, and String; an object is a member of the remaining built-in type Object; and a function is a callable object.

Итак, свойствами могут быть объекты, примитивные значения или функции.

Далее, смотрим 11.2.1 Property Accessors
8. Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.

Cвойства могут быть только у типа Reference (что логично). Для этого нужно примитивное значение предварительно превратить в объект, что и описано в 8.7.1 GetValue (V)
" — there are primitives, but..." переводится не как «это примитивы», а скорее как " — существуют (также) примитивы, но..." т.е. автор не виновен в том, в чём вы его обвиняете.
По просьбам трудящихся внёс небольшие корректировки в текст, чтобы больше никто не возмущался.
В чем не виноват автор? В том что утверждает что массивы и функции являются примитивами но они конвертируются в объекты при обращении к ним?
Я видел много разных классификаций, но это вообще бред сивой кобылы. Почитайте спецификацию:
Функция (function) # Ⓣ
– элемент типа Object, являющийся экземпляром стандартного встроенного конструктора Function, который может быть вызван в качестве подпрограммы.


Убрал это предложение вообще.
Понимаете, здесь вопрос не в том что неверно одно предложение. Вопрос в том можно ли вообще доверять автору который пишет такое (речь про автора оригинальной статьи)?
Согласен. Но от ошибок никто не застрахован. Однако, данную статью в целом я нахожу достаточно полезной. Если автор не совсем правильно написал про некоторый факт, то это не значит, что можно считать неверной всё статью.
Примитив или нет проверить довольно просто и без спецификации. Достаточно попробовать присвоить «объекту» новый член:

var x = 1;
x.hello = 'world';
console.log(x.hello); // undefined


Значит примитив. То же самое будет и со строками. И с Boolean. А вот регулярные выражения, объекты и массивы это объекты, а не примитивы, хоть и могут задаваться литералами.

И, кстати, интересный факт, и число, и строка, и булевское значение может быть объектом, а не примитивом, но только в случае если оно создано как объект:

> var x = new Number(1)
undefined
> x
{}
> x.toString()
'1'
> x.ha = 1
1
> x
{ ha: 1 }
> typeof x
'object'
> x instanceof Number
true
> isNaN(x)
false
> x.toNumber
undefined
> Object.getOwnPropertyNames(x)
[ 'ha' ]
> Object.getOwnPropertyNames(x.__proto__)
[ 'constructor',
  'toLocaleString',
  'valueOf',
  'toString',
  'toPrecision',
  'toFixed',
  'toExponential' ]
> x.toFixed()
'1'
> x.toPrecision()
'1'
> x.toExponential()
'1e+0'
> x.valueOf()
1
Спасибо, Кэп, я в курсе:)
Я бы хотел почитать про инкапсуляцию в js. Вроде как это один из пунктов ООП. Ждем от Вас еще статей.
По-моему основная проблема инкапсуляции в том, что её неправильно понимают.
Издревне вбивалось в голову, что инкапсуляция — это возможность скрытия переменных через «приват» и всё такое.
Но блин, инкапсуляция, как принцип ООП — это нифига не синтаксический сахар для ограничения доступа. Ограничение доступа вообще никак не влияет на архитектуру и на объектность.
Имхо, инкапсуляция — это сокрытие реализации. То есть мы обращаемся к методу, функции и чему угодно, используем её публичный интерфейс и получаем результат. Не обращая внимание на её внутренности и ветвления. Это и есть инкапсуляция.
Минусую только за «Всё, с чем вы работаете в JavaScript является объектами.». Прекратите вводить людей в заблуждение.
чОрт, в конце злополучное «Продолжение следует»…
Об оригинале статьи уже где-то писал, повторюсь:
Статейка неплохая, но либо на будущее, либо для тех, кто может полностью забить на браузер, только нода — ES5 по полной.
В IE младше 9 недоступно Object.defineProperty, не эмулировать с «writable»,«enumerable»,«configurable», это главное.
Без defineProperty Object.create можно эмулировать, но только с первым параметром.
Ну и вообще в ней довольно странный подход к ООП.
Кстати, я тут подумал, тут бы оплачиваемый контент, который обсуждается в соседней теме о гугле… я бы заплатил не жалея ни копейки)
1. Очень хотелось бы увидеть пояснения, в каких браузерах работают описанные возможности. Насколько я знаю в IE Object.defineProperty и Function.bind работают только с 9 версии.

2. Вы рписываете наследование с использованием Object.create. Поясните, пожалуйста, отличие наследования с применением Object.create от наследования с использованием конструкторов:

function A() {
    this.x = 10; 
}
A.prototype = {
    constructor: A,
    y: 100
};
var a = new A();
a.x; // 10
a.y // 100 (из прототипа)


На практике встречаю только способ наследования через конструкторы.

3. Вы пишете:
>>> Пустой объект — это объект без родителя, без свойств. Посмотрим на синтакс создания такого объекта в JavaScript:
>>> var mikhail = Object.create(null)
Что вижу я в Firebug:

Можете пояснить подобное поведение?
К сожалению, я не автор этого текста, а только автор перевода. Я не являюсь гуру JavaScript. Сам много нового узнал из этой статьи — вот и решил поделиться с сообществом. Боюсь, что не смогу полно и правильно ответить на ваши вопросы.
Про конструкторы — в 4ой части оригинала, соответственно, наверное, в продолжении перевода:)
Огромное спасибо за таблицу совместимости. Да, про эмуляцию bind даже на хабре нашел.

А можете 3-й пункт прояснить? То что создается с помощью Object.create ( []{} ) не похоже на обычный объект ( {} ). А typeof говорит что объект.
var x = Object.create(null);
typeof x   // "object"

Object.create(null) создает объект, у которого в __proto__ будет null, соответственно от не будет иметь методов из Object.prototype, в отличии от {}.
То есть чтобы с помощью Object.create создать объект, аналогичный {}, нужно написать следующим образом?

var x = Object.create(Object.prototype)
Да. Вот только зачем?) Как по мне — он хорош либо в связке со втором параметром, либо для наследование, либо для клонирования.
Просто теоретический вопрос :)
Спасибо за пояснения.
По третьему пункту:

Когда создается объект через Object.create(use_as_proto), то в свойство __proto__ записывается ссылка на use_as_proto. При создании объекта через литерал в его __proto__ записывается Object.prototype. А Object.prototype содежит в себе методы типа toString, valueOf ну или типа того. При создании объекта с null ссылкой в прототипе, этих методов нет. А фаербаг использует их для вывода информации в консоль.

В вашем коде схематически цепочки прототипов выглядят так:
mikhail -> null
mikhail2 -> Object.prototype -> null
Есть сценарии, в которых действительно удобно так делать, но стоит помнить, что такой механизм работает очень медленно.


Запустив этот тест 20 раз, я получил 20 разных результатов на одном и том же браузере. Причем четыре раза именно этот способ и оказывался самым быстрым
а почему комментарии в коде не переведены?
К сожалению, объектная система JS чрезмерно запутана, но при этом не содержит совершенно очевидных и необходимых вещей. Например, почему язык не даёт простой возможности реализовать перегрузку методов? Вот, например, в C++ я могу сделать несколько конструкторов с одинаковыми именами. В JS же мне придётся изощряться — createFromHtml, createFromXml, createFromSql и т.д.
Где возможность перегрузки операторов? Почему, работая с WebGL, до сих пор надо городить конструкции типа mulMatrices(m1,addVector(m2,v3)), вместо того, чтобы перегрузить операторы +,-,*?
Где, наконец, нормальная модульность и пространства имён? Почему до сих пор private и protected-поля объектов приходится реализовывать через разнообразные костыли типа замыканий?
Да и вообще любая JS-библиотека содержит такое количество вложенных скобок, что невольно вспоминаешь о Лиспе.
Могу посоветовать вам TypeScript — он избавит вас от вышеперечисленных проблем.
Вот, например, в C++ я могу сделать несколько конструкторов с одинаковыми именами.


Улыбнуло про одинаковые имена — Как будто можно с разными сделать. :-)

Это две стороны одной медали. В Си нет RTTI, нет рефлексии, потому что код генерируемый компилятором — это инструкции для конкретной железки, и потому он быстр.

А в javascript как и в java есть instanceof, с его помощью можно сделать аналог override.
Вы встали не на тот путь. В классическом понимании перегрузку функция в JS осуществить нельзя, но не кто вам не мешает менять реализацию функции в зависимости от ваших желаний (в том числе и в зависимости от типов аргументов). Но этот недостаток с лихвой компенсируется динамической типизацией а так же тем что количество аргументов функции может быть произвольным! Скажем так, вы просто не можете отказаться от своих стереотипов организации перегрузки, поэтому и не видите других очевидных плюсов JS.

Ну а что касается замыканий, кто вам сказал что они являются костылями? Вы просто не умеете их готовить ©.

Мне вот интересно, зачем вы приходите в топик о JS и начинаете делать подобные вбросы?
но не кто вам не мешает

Вот это даже меня, далеко не закоренелого граммар-наци, уже убивает, я секунд 20 размышлял, что вы хотите сказать, пришлось предложение до конца пару раз прочитать даже.
Это уже совсем другая тема. Хотите об этом поговорить? Я скажу только одно: возможно русский язык я знаю хуже чем некоторые другие, ну и что?
Да, вы мыслите стереотипами C++.
Ещё скажите, что динамическая типизация — зло, в JS нужна статическая, а там её нету, потому он плохой.

В C++ есть перегрузка функций. В JS её нету, но там она и не нужна. Давно используются другие приёмы. На то она и динамическая типизация, что количество параметров и их тип может быть любой. Посмотрите на jQuery:
> element.attr('title'); // аналог getAttribute
> element.attr('title', 'bar'); // аналог setAttribute
> element.attr({ title: 'bar', src: 'baz.jpg' }); // аналог setAttributes

Перегрузки операторов — порой не хватает, да. Но во-первых, её вполне могут добавить в будущих версиях ECMAScript. А уже есть такой интересный объект, как Proxy. Во-вторых, кто-нибудь обязательно додумается перегрузить операторы для стандартных классов (а подобная возможность, готов поспорить, будет), например Number, и получит кучу проблем ;).

Далее. Замыкания — это не костыли. А, наоборот, важное достоинство языка. Ими и реализуется пространство имён.
Заканчивается 2012 год, а на хабре всё еще пытаются понять ООП в JS. Пичаль-пичаль.
Моя дочка начнет понимать ООП в JS только лет через 10-15 (если она конечно решить заняться программированием), в чем пичаль то? :)
В том, что раз в полгода на хабре появляется очередная публикация подобного рода. Это сродни публикации «Земля — круглая» на каком-нить nat-geo.ru. Мы же не на дошкольном ресурсе, где актуально рассказывать про основы мироздания, правда?
Так расскажите нам чего-нибудь не дошкольного.
Раз в полгода? По-моему, каждые две недели)
На заметку: все имена свойств в конечном счёте конвертируются в строку, т.е. записи object[1], object[[1]], object['1'] и object[variable] (где значение variable равно 1) эквивалентны.


object[[1]] — а вот так вот вообще никогда не делайте, вообще даже в одну линейку с object[1] и object['1'], при сравнении синтаксиса, ставить не стоит.
Объяснение простое — массив с одним элементом привелся к выражению со значением первого элемента.
Но такой код обычно является причиной головной боли.
Статья выглядит хорошей. Но наличие недочетов или недосказанностей делает ее не безопасной. Не рассказана история цепочки прототипов, про скрытое (по стандарту) __proto__. Перемешаны физическое (непосредственно код) и абстрактное (упоминание [[prototype]]) устройство объектов. Из-за таких статей новичики вытаются сделать что-то вроде my_object.prototype.

По составу статьи: половина — документация, пересказанная своими словами, половина — теория (без четкого, как я уже упомянул, разделения).
Доскажите недосказанное, буду благодарен :)
Отличная статья, благодарю за перевод.
Узнал много полезного для себя.

Из пожеланий — думаю будет полезно в статье проставить ссылки на такие понятия как "литеральный синтаксис" и "сущности первого класса" и "анонимные функции", для того чтобы менее опытным читателям было проще разобраться в статье, не застревая в терминах.
Не понравилось то, что автор уделил слишком мало внимания литеральной форме записи объектов, больше внимания ES5, получается в итоге нечто оторванное от практики из-за неважной поддержки в еще не слишком старых браузерах. Кроме того, если статья рассчитана на новичков, зачем их учить не ставить точки с запятыми? Это может привести к проблемам в некоторых случаях. Ну и ошибки то ли в самой статье, то ли в переводе, о которых говорили выше.
UFO just landed and posted this here
Sign up to leave a comment.