|
@@ -8,7 +8,7 @@
|
|
|
function Person(){
|
|
|
}
|
|
|
|
|
|
-var person = new Person();
|
|
|
+var person = new Person()
|
|
|
```
|
|
|
|
|
|
По всеобщей договоренности, функции-конструкторы именуются с большой буквы (`Person`). Для создания нового объекта используется оператор `new`, который создает пустой объект, заносит в него
|
|
@@ -17,37 +17,37 @@ var person = new Person();
|
|
|
|
|
|
```javascript
|
|
|
function Person(name, surname){
|
|
|
- this.name = name;
|
|
|
- this.surname = surname;
|
|
|
+ this.name = name
|
|
|
+ this.surname = surname
|
|
|
}
|
|
|
|
|
|
-var person = new Person("Ivan", "Petroff");
|
|
|
+var person = new Person("Ivan", "Petroff")
|
|
|
```
|
|
|
|
|
|
Обратите внимание, что конструктор ничего не возращает, используя `return`. Считается что он возвращает новый объект, для этого достаточно просто заполнить нужные поля в `this`.
|
|
|
|
|
|
-## Методы.
|
|
|
+## Методы
|
|
|
|
|
|
Так же как данные, мы можем задать определенные методы объекту:
|
|
|
|
|
|
```javascript
|
|
|
function Person(name, surname){
|
|
|
- this.name = name;
|
|
|
- this.surname = surname;
|
|
|
+ this.name = name
|
|
|
+ this.surname = surname
|
|
|
|
|
|
this.getFullName = function(){
|
|
|
- return this.name + (this.fatherName ? " " + this.fatherName + " " : " ") + this.surname;
|
|
|
+ return this.name + (this.fatherName ? " " + this.fatherName + " " : " ") + this.surname
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-var person = new Person("Ivan", "Petroff");
|
|
|
-alert(person.getFullName());
|
|
|
+var person = new Person("Ivan", "Petroff")
|
|
|
+alert(person.getFullName())
|
|
|
|
|
|
person.fatherName = "Sydorych"
|
|
|
-alert(person.getFullName());
|
|
|
+alert(person.getFullName())
|
|
|
```
|
|
|
|
|
|
-## Приватные методы и данные
|
|
|
+## Приватные методы и данные; Замыкания.
|
|
|
|
|
|
**JS** не предоставляет обычных для Объектно-ориентированных языков программирования возможностей ограничения доступа к полям объекта (`private`, `public`, `protected`), для этого используется
|
|
|
другой подход - так называемые *замыкания*.
|
|
@@ -87,5 +87,168 @@ alert(person.getOriginalFullName())
|
|
|
```
|
|
|
|
|
|
Пример выше иллюстрирует этот подход: методы объекта `Person`, например `getOriginalFullName`, имеют доступ к области видимости уже *завершенной* функции. Код же *снаружи*, то есть определенный
|
|
|
-вне конструктора `Person` никак не может получить значения переменных, определенных внутри функции (например `originalFullName`). Обратите внимание, что `this.name` и `name` - это **разные переменные**.
|
|
|
-Одна из них является частью новосозданного объекта, вторая же - параметр функции `Person` в её области видимости.
|
|
|
+вне конструктора `Person` никак не может получить значения переменных, определенных внутри конструктора (например `originalFullName`). Обратите внимание, что `this.name` и `name` - это **разные переменные**.
|
|
|
+Одна из них является частью новосозданного объекта, вторая же - параметр функции `Person` в её области видимости. Таким образом, единственным способом работы с переменными в замыкании является *код*,
|
|
|
+который находится в одном *лексическом контексте* с переменными:
|
|
|
+
|
|
|
+### геттеры и сеттеры.
|
|
|
+
|
|
|
+```javascript
|
|
|
+function Person(name, surname){
|
|
|
+
|
|
|
+ this.name = name
|
|
|
+ this.surname = surname
|
|
|
+
|
|
|
+ var age = 0
|
|
|
+
|
|
|
+ this.setAge = function(newAge){
|
|
|
+ newAge = +newAge
|
|
|
+ if (!isNaN(newAge) && Number.isInteger(+newAge) && (newAge > 0) && (newAge < 100)){
|
|
|
+ age = newAge
|
|
|
+ }
|
|
|
+
|
|
|
+ return age
|
|
|
+ }
|
|
|
+
|
|
|
+ this.getAge = function(){
|
|
|
+ return age
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+var person = new Person("Ivan", "Petroff")
|
|
|
+
|
|
|
+alert(person.setAge(50))
|
|
|
+alert(person.setAge(125))
|
|
|
+alert(person.setAge(25))
|
|
|
+```
|
|
|
+
|
|
|
+Таким образом реализуется **паттерн getter/setter** - специальных функций, которые читают и записывают данные, защищенные от записи внешним кодом, проверяя их на правильность при записи.
|
|
|
+
|
|
|
+### Приватные функции в замыкании
|
|
|
+
|
|
|
+Таким же способом мы можем определить функции для внутреннего использования, недоступные через объект, но доступные через методы объекта:
|
|
|
+
|
|
|
+```javascript
|
|
|
+function Person(name, surname){
|
|
|
+
|
|
|
+ this.name = name
|
|
|
+ this.surname = surname
|
|
|
+
|
|
|
+ function getFullShortName(){
|
|
|
+ return this.name + " " + this.surname
|
|
|
+ }
|
|
|
+
|
|
|
+ function getFullLongName(){
|
|
|
+ return this.name + " " + this.fatherName + " " +this.surname
|
|
|
+ }
|
|
|
+
|
|
|
+ this.getFullName = function (){
|
|
|
+ if ("fatherName" in person){
|
|
|
+ return getFullLongName()
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return getFullShortName()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+var person = new Person("Ivan", "Petroff")
|
|
|
+
|
|
|
+alert(person.getFullName())
|
|
|
+```
|
|
|
+
|
|
|
+Данный пример не работает, так как `this` *не является* частью области видимости Person, а устанавливается в зависимости от *контекста вызова*. Общее правило таково: `this` равен тому, что написано до точки:
|
|
|
+
|
|
|
+```javascript
|
|
|
+alert(person.getFullName()) //this == person
|
|
|
+```
|
|
|
+
|
|
|
+```javascript
|
|
|
+...
|
|
|
+
|
|
|
+ function getFullShortName(){
|
|
|
+ return this.name + " " + this.surname //oups
|
|
|
+ }
|
|
|
+
|
|
|
+ function getFullLongName(){
|
|
|
+ return this.name + " " + this.fatherName + " " +this.surname //oups
|
|
|
+ }
|
|
|
+
|
|
|
+ this.getFullName = function (){
|
|
|
+
|
|
|
+ if ("fatherName" in person){
|
|
|
+ return getFullLongName(); //this == window или undefined в strict
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return getFullShortName(); //this == window или undefined в strict
|
|
|
+ }
|
|
|
+ }
|
|
|
+...
|
|
|
+```
|
|
|
+
|
|
|
+Для исправления ситуации воспользуемся методом `call` объекта `Function`, который позволяет "подсунуть" функции другой `this`:
|
|
|
+
|
|
|
+```javascript
|
|
|
+function Person(name, surname){
|
|
|
+
|
|
|
+ this.name = name
|
|
|
+ this.surname = surname
|
|
|
+
|
|
|
+ function getFullShortName(){
|
|
|
+ return this.name + " " + this.surname
|
|
|
+ }
|
|
|
+
|
|
|
+ function getFullLongName(){
|
|
|
+ return this.name + " " + this.fatherName + " " +this.surname
|
|
|
+ }
|
|
|
+
|
|
|
+ this.getFullName = function (){
|
|
|
+ if ("fatherName" in person){
|
|
|
+ return getFullLongName.call(this) //передаем текущий объект person как this в getFullLongName
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return getFullShortName.call(this)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+var person = new Person("Ivan", "Petroff")
|
|
|
+
|
|
|
+alert(person.getFullName())
|
|
|
+```
|
|
|
+
|
|
|
+Альтернативой этому решению является *сохранение* this в замыкании:
|
|
|
+
|
|
|
+
|
|
|
+```javascript
|
|
|
+function Person(name, surname){
|
|
|
+
|
|
|
+ this.name = name
|
|
|
+ this.surname = surname
|
|
|
+
|
|
|
+ var me = this; //теперь у нас есть ссылка на текущий объект в замыкании
|
|
|
+
|
|
|
+ function getFullShortName(){
|
|
|
+ return me.name + " " + me.surname
|
|
|
+ }
|
|
|
+
|
|
|
+ function getFullLongName(){
|
|
|
+ return me.name + " " + me.fatherName + " " + me.surname
|
|
|
+ }
|
|
|
+
|
|
|
+ this.getFullName = function (){
|
|
|
+ if ("fatherName" in person){
|
|
|
+ return getFullLongName() //передаем текущий объект person как this в getFullLongName
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return getFullShortName()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+var person = new Person("Ivan", "Petroff")
|
|
|
+
|
|
|
+alert(person.getFullName())
|
|
|
+```
|
|
|
+
|
|
|
+В отличие от `this`, переменная `me` всегда будет указывать на `this`, который был при создании объекта (если, конечно, вы не поменяете значение `me`)
|