this
. Это отличается от правил лексического окружения, которые применяются к обычным переменным в Javascript. То, на что ссылается this
часто может не относиться к лексическому окружению функции. Чтобы c этим можно было работать обычно используют похожий трюк:function blah(){ var that = this; somethingThatRebindsThings( function(){ that.whatever(); }); }
Каждый, кто много писал на Javascript прошел через это. А представьте себе жизнь без that
. Как это возможно? Один из способов — это никогда не использовать this
. Звучит абсурдно? Давайте посмотрим.
Почему this
?
Причина по которой мы используем this
обычно связана с одними из наиболее полезных абстракций в ОО парадигме: состояние и поведение. Точнее, объекты с переменными и методами. Вы могли подумать, что мы потеряем эту мощную абстракцию если прекратим использовать this
. Как могут методы обращаться к переменными именно своего объекта без this
? Возможно, вы уже знаете правильный ответ: замыкания.
Вы можете открыть для себя, что замыкания это еще один способ определить состояние и поведение объекта. Вот пример замены this
-кода на код, основанный на замыканиях:
function Car(numberOfDoors){ this.numberOfDoors = numberOfDoors; this.numberOfWheels = 4; this.describe = function(){ return "I have " + this.numberOfWheels + " wheels and " + this.numberOfDoors + " doors."; } } var sportsCar = new Car(2); console.log( sportsCar.describe() );
Вот как мы сделаем то же самое используя замыкания:
function createCar(numberOfDoors){ var numberOfWheels = 4; function describe(){ return "I have " + numberOfWheels + " wheels and " + numberOfDoors + " doors."; } return { describe: describe }; } var suv = createCar(4); console.log( suv.describe() );
Я написал функцию-конструктор createCar
. Она описывает все состояния и поведения моего класса Car
и возвращает объект который предоставляет только публичный метод. Все остальное, что определенно в функции-конструкторе, недоступно внешнему миру, но, благодаря замыканиям, все объявленое в пределах функции-конструктора может продолжать взаимодействовать друг с другом. Каждый вызов функции-конструктора создает новое замыкание, новую маленькую сумочку со своими переменными и методами.
Наследование
А что же наследование? Обычно мы реализовали бы его через прототипное наследование, что означало бы использование this
. Мы не используем this
и поэтому давайте будем креативными и используем другой способ наследования:
function createMiniVan(capacity){ var car = createCar(4); car.capacity = function(){ return "I have room for " + capacity + " passengers."; }; return car; } var miniVan = createMiniVan(7); console.log( miniVan.describe() ); console.log( miniVan.capacity() );
В этом выдуманном примере я создал новый класс MiniVan
, который наследует все публичные методы класса Car
и потом добавляет новый функционал индикации вместимости. Это похоже на миксыны используемые в CLOS и Ruby. Недостаток этого подхода в том что это не позволит дочерним или родительским классам получить доступ к внутреннему состоянию или поведению — другими словами нет концепции защищенной видимости. Впрочем, лично я редко встречал случаи когда защищенный доступ был бы полезен. Я бы отметил, что почти во всех случаях вы можете достичь тех же целей более модной «композицией» чем «наследованием».
Композиция
Вот как можно добавить классу новую функцию с помощью композиции:
function createOdometer(){ var mileage = 0; function increment(numberOfMiles){ mileage += numberOfMiles; } function report(){ return mileage; } return { increment: increment, report: report } } function createCarWithOdometer(numberOfDoors){ var odometer = createOdometer(); var car = createCar(numberOfDoors); сar.drive = function(numberOfMiles){ odometer.increment(numberOfMiles); } car.mileage = function(){ return "car has driven " + odometer.report() + " miles"; } return car; }
Внутри моей функции-конструктора createCartWithOdometer
я создаю odometer
, чтобы использовать его функционал. Потом я создаю объект Car
и добавляю этому объекту новую функцию — и мы имеем новый тип, который расширяет Car
используя функционал odometer
. Все это без прототипного наследованиея и this
.
Неужели?
Да, действительно. Я работал в команде которая разработала довольно большое Javascript приложение таким способом. Мы использовали this
всего раз 10 в мульти-тысячной кодобазе и мы счастливы.
Почему мы считаем этот подход успешым? Во-первых, это позволило нам обойти подводные камни, связанных с тем, как this
работает в JavaScript. К примеру, больше нет путанницы с тем, что jQuery переопределяет this
на объект который итерирует. Во-вторых, возможность композиции собственных классов внутри функции-конструктора оказалась очень мощной. Это дает полезную часть множественного наследования без проблем с зависимостями. Наконец, самое большое преимущество было в контроле над API каждого класса, возможности спрятать приватные методы в замыканиях функции-конструктора. Я уверен, что это было значительная причина нашего успеха в работе в команде над огромной кодовой базой.
ссылка на оригинал статьи http://habrahabr.ru/post/215043/
Добавить комментарий