Prototype, __proto__ и оператор new

в 22:32, , рубрики: __proto__, javascript, new, prototype, Веб-разработка, метки: , , ,

В этой статье я попытаюсь кратко в примерах объяснить что такое свойства __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).

Prototype,   proto   и оператор new

__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, которое ссылается на саму функцию.

Prototype,   proto   и оператор new

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

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js