Как стать автором
Обновить

Комментарии 32

Подождите, но вы сравниваете генерацию «классов», а не экземпляров, иначе вы бы никогда не получили таких «результатов». В чем смысл такого сравнения?
А что удивительно в том, что пройтись по объекту и скопировать все свойства, дольше, чем просто задать прототип?
Да ничего… Просто хотел сказать, что если вам нужно создать экземпляр и инициализировать объект с большим количеством свойств, то значительно быстрее и удобнее это можно сделать с помощью __proto__, а не оператора new и конструктора.
Сколько в среднем вы создаете экземпляров от одного класса? 1-2? иногда сотни-тысячи, но значительно реже.
new быстрее clone в 2-5 раз, но при этом clone быстрее класса-конструктора в 20-30 раз.
Если вам нужны сотни экземпляров, можете добавить конструктор в прототип, и использовать new для инстанцирования:
var myClass = {
    constructor: function(){},
    method: function(){}
};
myClass.constructor.prototype = myClass;

var instance = new myClass.constructor;
с чего вы взяли что он deprecated? Даже в IE 11 обещают его поддержку.
Да, кажется ECMAScript 6 собираются стандартизировать это свойство, но судя по публикациям это свойство использовать не рекомендуют, потому что в ECMAScript 5 его нет.

Но мне самому интересно: насколько правильно использовать __proto__?
Мне кажется, уже можно использовать, т.к. из ECMAScript 6 его врядли выпилят, и если даже такое недоразумение случится, то функцию clone всегда можно переписать с использованием Object.create или функций-конструкторов.
Лучше всего читать последнюю версию стандарта ( html-копия ). В нём есть свойство __proto__ и, даже, функция Reflect.setPrototypeOf
Хороший пример генерирования псевдоклассов и наследования дает Typescript.
Сомнительный выигрыш в производительности при потери читаемости и лёгкости статического анализа. Например PhpStrom не может понять какие методы «класса» есть у объекта созданного с помощью вашего метода clone.
Обретите для себя es6-классы, благо, есть много имплиментаций.
es6-классы как поддерживаются IDE, например WebStorm? Потому что я уже почти созрел для перехода на них. Я даже созрел для полного перехода на ES6, но решил таки дождаться утверждения.
У Sublime есть дополнение, называется Javascript next. Классы и лямбды видит)
Нее, я ж про IDE спрашивал.
На данный момент в WebStorm/PhpStorm есть поддержка es6 стандарта, примерно, 2012 года.
Есть поддержка: классы, деструктуризация, let, rest, spread, дефолтные значения у функций, стрелочные функции, QuasiLiterals (строковые шаблоны)
Нету: объектной деструктуризации, разряженного массива как шаблона деструктуризации, дефолтных значений деструктуризации.

Это основное. Пользоваться вполне можно, но к сожалению, PhpStorm периодически зависает и вываливает сообщение о нехватке памяти — ничего серьёзного, после этого продолжает работать, просто время отнимает.
Ещё traceur встроенный в PhpStorm старой версии и я не нашел как обновить или указать свой.
Почему бы тогда сразу не задавать __proto__?

var donald = {name: "Donald", __proto__: talkingDuck$};
для совместимости с IE
Я ни в коем случае не говорю, что Ваш код или подход плохой или неправильный, просто как вариант:
Можно это дело оформить как graceful degradation:
var protohack = (function() {
	function Clone(properties) {
		for (var propName in properties){
			this[propName] = properties[propName];
		}
	}

	if ({__proto__: Clone.prototype} instanceof Clone) {
		return new Function('o', 'return o;'); // No-op, от глобального скопа
	}

	return function(o) {
		if (!o || !o.__proto__ || typeof o.__proto__ !== 'object') {
			return o; // В объекте не перекрывается __proto__, или это вообще не объект. Возвращаем обратно как есть
		}
		Clone.prototype = o.__proto__;
		delete o.__proto__;
		return new Clone(o); // Возвращаем новый объект с нужным [[prototype]]
	}
})();


Тогда вызов
var donald = protohack({name: "Donald", __proto__: talkingDuck$});
вернет одно и тоже и в ES5 и в дремучем ES3.
Странно называть функцию clone, когда она на самом деле не клонирует:

var a = { ref: HUGE_OBJECT };
var b = clone(a);
b.ref = null; 
a = null;
// а HUGE_OBJECT-то утёк


Ну и в бенчмарке у вас замер «new constructor» замеряет совсем не то, что вы думаете — совсем даже не скорость создания объекта, а скорость присвоения свойства prototype (FF, Chrome страдают от этого, Safari — нет)
Спасбо Quadratoff, статья хороша :)

Но всё же «люто плюсую» предыдущий комментарий от mraleph.

Если можете, пожалуйста — попдравьте.
Не вводите в заблуждение тех, кто не знает, что это не clone ^)

Хотя бы приписочку о, например:

jQuery.extend(true, {}, oldObject);

Подскажите пожалуйста ещё, вот, например:

clonejs
lazy shallow copy
it would not affect the cloned object (prototype)


clonejs-nano
lazy shallow copy
it would not affect the cloned object (prototype)


Вот как всё таки, используя их, мне сделать мне этот would not affect ?

Пробовал на следующем очень простом, согласитесь, объекте:

	var obj = {
		s : {
			ss: 1
		}
	};



Делал:

	
	var cln = clone (obj);
	// и
	var cln = clone.byProto (obj, {});
	// и
	var cln = clone.byConstructor (obj, {});
	// и
	var cln = clone.byObjectCreate (obj, {});



Во всех случаях после:

	
	cln.s.ss = 5;



изменялся и сам «родительский» obj, т.е. становилось:

	
	console.log ( obj.s.ss ); // 5



Попробовал после этого почитать внимательно доку и API, понимаю, что, наверное, что-то делаю не так. Использовал и:

	
	obj.$clone();
	cln.$clone();



Тоже не помогло… Наверное как-то не так использовал.

Но, всё же, может быть как-то можно что-нибудь упростить в концепции, так сказать «for the rest of us»?

Заранее спасибо :)

Ключевое слово shallow.
Не не не, привожу полную цитату с git, раз уж…

clone function produces new objects — Clones.
Clone object — this is the lazy shallow copy, i.e., it is actually not a copy, it's just a reference to the object, with one difference: if you will add/replace any of its properties, it would not affect the cloned object (prototype).
All JavaScript objects are clones of Object.prototype (except itself and objects, created by Object.create(null)).


Т.е. если в child что-то менять, то parent не должен «затрагиваться».

Вы почитали бы https://github.com/quadroid/clonejs, привёл же ссылки.
Т.е. если в child что-то менять, то parent не должен «затрагиваться».
Правильнее перевести:
Если в child добавить или заменить какое-либо его свойство, то parent не будет затрагиваться.
В обратную сторону, когда меняю родительский объект, тоже не работает.

Тестирую в Google Chrome 29.0.1547.65.
Про обратную сторону я не говорил ;)
Для решения этой проблемы в cloneJS клонируемые объекты-классы (behaviors), должны быть immutable.
Вот как всё таки, используя их, мне сделать мне этот would not affect ?

не вырывайте фразу из контекста:
if you will add/REplace any of its properties, it would not affect the cloned object (prototype).

Это не замена свойства:
 cln.s.ss = 5;

Я писал про
… lazy shallow copy
По поводу утечки — да, проблема, но для освобождения памяти наверное все-таки правильней использовать оператор delete (b.ref), и тогда эта утечка сразу выплывет наружу.
Возможно, стоит сделать метод clone.clearAllOf(a), для рекурсивного удаления всех свойств.
Оператор delete лучше вообще не использовать (за исключением очень крайних случаев), от него одно падение производительности. Еще он обладает таким интересным парадоксальным свойством (по крайней мере на V8), что после его применения объем занимаемой объектом памяти может возрасти (а не уменьшится).

Далее ведь я не обязательно хочу свойство удалять, я его допустим хочу обновить, а утечка все равно случится.

Дополнительное наблюдение, если вы пишете код в стиле:

var p = { a: 1, b: 2 };
var q = clone(p);
q.a++;
var u = clone(p);
u.b++;
some_f(q);
some_f(u);


где some_f работает с полями a / b, то some_f будет работать медленнее, чем она работала будь q и u созданы обычным конструктором, потому что они имею разные скрытые классы и как результат доступ к полям становится полиморфным.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории