В этой статье я попытаюсь кратко в примерах объяснить что такое свойства __proto__, prototype и работу оператора new в JavaScript.
Свойство __proto__
Абсолютно любой объект в JavaScript имеет свойство __proto__. Это скрытое системное свойство, и не во всех реализациях языка оно доступно пользователю.
При обращении к любому свойству объекта, оно в первую очередь ищется в самом объекте:
var obj = {ownProperty: 1};
console.log(obj.ownProperty);// 1
Но если его там нет, поиск происходит в другом объекте, свойстве __proto__:
obj.__proto__ = {propertyOfProto: 2};
console.log(obj.propertyOfProto);// 2
Если его нет и там, оно ищется дальше по цепочке:
obj.__proto__.__proto__ = {propertyOfProtosProto: 3};
console.log(obj.propertyOfProtosProto);// 3
Эта цепочка называется цепочкой прототипов (prototype chain).
__proto__ любого значения (кроме null и undefined) ссылается на prototype соответствующего ему типу данных:
(0).__proto__ === Number.prototype &&
false.__proto__ === Boolean.prototype &&
"string".__proto__ === String.prototype &&
(new Date).__proto__ === Date.prototype
Все типы данных наследуются от Object, это означает что:
Number.prototype.__proto__ === Object.prototype
И наконец, завершение цепочки:
Object.prototype.__proto__ === null
Свойство prototype
А чем же тогда является свойство prototype? Это обычное свойство, ничем не отличающиеся от любых других свойств. За исключением двух особенностей:
1) Все функции в JavaScript имеют свойство prototype. Оно по умолчанию является объектом с единственным свойством constructor, которое ссылается на саму функцию.
2) Свойство prototype используется при создании новых объектов оператором new.
Оператор new
Этот оператор делает следущее:
1) Создает пустой объект:
var instance = {};
2) Устанавливает __proto__ этому объекту ссылкой на prototype функции-класса:
instance.__proto__ = FnClass.prototype;
3) Применяет функцию-класс к нашему новосозданному объекту:
constructorReturns = FnClass.apply(instance, arguments);
(т.е. исполняет функцию FnClass, передавая ей instance в качестве this и аргументы в виде массива arguments)
4) Возвращает экземпляр функции-класса, но если FnClass нам вернул обьект, тогда его:
return typeof(constructorReturns)=='object') ? constructorReturns : instance;
Функцией-классом я называю функцию, к которой впоследствии ожидается применение оператора new. Такие функции принято именовать с заглавной буквы.
Использование __proto__ в ваших скриптах
Т.к. свойство __proto__ является скрытым, и не описано в спецификации языка, то использование его в явном виде некорректно. Так что никогда не пишите так как я выше в примерах :) Этот код только для консоли.
Однако в последней (действующей) спецификации ECMA Script 5 наконец-то появились два метода, позволяющие манипулировать свойством __proto__, это Object.create и Object.getPrototypeOf.
Поясню их работу в двух простых примерах:
//var myObj = {__proto__: {property1OfProto: 1}}
var myObj = Object.create({property1OfProto: 1});
//myObj.__proto__.property2OfProto = 2
Object.getPrototypeOf(myObj).property2OfProto = 2;
Если вы используете более раннюю версию JavaScript, то метод Object.create можете создать самостоятельно:
if(!Object.create){
Object.create = function(proto){
var Fn = function(){};
Fn.prototype = proto;
return new Fn;
}
}
C getPrototypeOf ситуация сложнее, его можно эмулировать только для функций, и только при условии что constructor этой функции небыл изменен:
if(!Object.getPrototypeOf){
if( ({}).__proto__ !== Object.prototype ){
// may return incorrect value if fn.prototype has been modified
Function.getPrototypeOf = function(fn){
if(typeof(fn)!=='function')
throw new TypeError('Function.getPrototypeOf called on non-function');
return fn.constructor.prototype;
}
}else{
Object.getPrototypeOf = function(obj){
return obj.__proto__;
}
}
}
Автор: Quadratoff