const student = {
name: 'Billy'
};
const student = {
name: 'Billy',
getName: function () {
return this.name;
},
sleep: function () {}
};
student.getName();
// Billy
const student = {
name: 'Billy',
getName: () => {
return this.name;
},
sleep: () => {}
};
student.getName();
// ?
const student = {
name: 'Billy',
getName() {
return this.name;
},
sleep() {}
};
student.getName();
// Billy
const student = {
name: 'Billy',
getName() {
return this.name;
},
sleep() {}
};
const lecturer = {
name: 'Sergey',
getName() {
return this.name;
},
talk() {}
};
const student = {
name: 'Billy',
getName() {
return this.name;
},
sleep() {}
};
const lecturer = {
name: 'Sergey',
getName() {
return this.name;
},
talk() {}
};
const person = {
getName() {
return this.name;
}
};
Задача – научить student пользоваться общим кодом, который вынесли в person
const student = {
name: 'Billy',
};
const person = {
getName() {
return this.name;
}
};
person.getName.call(student);
// student.getName();
Для создания такой связи между объектами есть специальное внутреннее поле [[Prototype]]
const person = {
getName() {
return this.name;
}
};
const student = {
name: 'Billy',
sleep() {},
[[Prototype]]: <ссылка на person>
};
student.getName();
// Billy
Объект, на который указывает ссылка в [[Prototype]], называется прототипом
Если у объекта нет собственного метода – интерпретатор ищет его в прототипе
При вызове метода объекта в this записывается ссылка на этот объект,
а не на прототип
const person = {
getName() {
return this.name;
}
};
const student = {
name: 'Billy',
sleep() {},
[[Prototype]]: <ссылка на person>
};
student.getName();
// Billy
const person = {
getName() {
return this.name;
}
};
const student = {
name: 'Billy',
sleep() {}
};
Object.setPrototypeOf(student, person);
student.getName(); // Billy
const creature = {
getName() { return this.name; }
};
const person = {
[[Prototype]]: <сreature>
};
const student = {
name: 'Billy',
[[Prototype]]: <person>
};
student.getName();
const creature = {};
const person = {
[[Prototype]]: <сreature>
};
const student = {
[[Prototype]]: <person>
};
student.getName();
Интепретатор будет идти по цепочке прототипов в поиске поля, пока не встретит null в поле [[Prototype]]
const creature = {
[[Prototype]]: <Object.prototype>
};
const person = {
[[Prototype]]: <сreature>
};
const student = {
[[Prototype]]: <person>
};
student.getName();
Object.prototype – прототип для всех объектов по умолчанию. Cодержит общие методы для всех объектов.
Object.prototype = {
hasOwnProperty() {}
};
const student = {
name: 'Billy'
};
student.hasOwnProperty('name');
// true
student.hasOwnProperty('age');
// false
Object.prototype = { [[Prototype]]: null };
const creature = { [[Prototype]]: <Object.prototype> };
const person = { [[Prototype]]: <сreature> };
const student = { [[Prototype]]: <person> };
student.getName(); TypeError: student.getName is not a function
Array.prototype = {
concat() {},
slice() {},
splice() {},
forEach() {},
filter() {},
map() {},
[[Prototype]]: <Object.prototype>
};
Function.prototype = {
call() {},
apply() {},
bind() {},
[[Prototype]]: <Object.prototype>
};
const student = {};
const person = {};
Object.setPrototypeOf(student, person);
Object.setPrototypeOf(person, student); Error
student.getName();
TypeError: Cyclic __proto__ value
const student = {};
const person = {};
Object.setPrototypeOf(student, person);
Object.setPrototypeOf(student, null);
Object.setPrototypeOf(student, 42); Error
TypeError: Object prototype may only be an Object or null
const student = {};
const person = {};
Object.setPrototypeOf(student, person);
Object.getPrototypeOf(student) === person; // true
Object.getPrototypeOf(Object.prototype) === null; // true
const person = {
getName() {
return this.name;
}
};
const student = Object.create(person);
student.name = 'Billy';
const person = {
getName() {
return this.name;
}
};
const student = Object.create(person, {
name: { value: 'Billy' }
});
create быстрее, чем setPrototypeOf
const person = {
getName() { return this.name; }
};
const student = {
name: 'Billy',
getName() { return 'Student ' + super.getName(); }
};
Object.setPrototypeOf(student, person)
student.getName(); // Student Billy
При создании метода, его внутреннее поле [[HomeObject]] заполняется ссылкой на объект, в котором он определён
super ссылается на прототип объекта
из поля [[HomeObject]]
student.getName.[[HomeObject]] == student;
super == Object.getPrototypeOf(student.getName.[[HomeObject]]);
У обычных полей-функций
[[HomeObject]] не заполняется
const person = {
getName() { return this.name; }
};
const student = {
name: 'Billy',
getName: function() {
return 'Student ' + super.getName(); Error
}
};
SyntaxError: 'super' outside of function or class
Значение [[HomeObject]] нельзя изменить
const person = {
getName() { return 'and person ' + this.name; }
};
const student = {
name: 'Billy',
getName() { return 'Student ' + super.getName(); }
};
Object.setPrototypeOf(student, person)
const getName = student.getName; // this потеряли, но не super
getName(); // Student and person undefined
super навсегда привязывается к объекту,
в отличии от this
const student = {
name: 'Billy'
};
student.age = 21;
student['age'] = 21;
Object.defineProperty(student, 'planet', {
value: 'Earth'
});
const student = {};
Object.defineProperty(student, 'name', {
writeable: false, // Можно не указывать, по умолчанию false
value: 'Billy'
});
Object.defineProperty(student, 'age', {
writeable: true,
value: 21
});
const student = {};
Object.defineProperty(student, 'name', {
value: 'Billy'
});
Object.getOwnPropertyDescriptor(student, 'name');
// {
// value: 'Billy',
// writable: false,
// enumerable: false,
// configurable: false
// }
const person = {
planet: 'Earth'
};
const student = Object.create(person);
student.planet = 'Mars';
console.info(student.planet); // Mars
console.info(person.planet); // Earth
Работает не всегда!
Object.prototype = {
toString() {}
};
const student = {
name: 'Billy'
};
console.info('Hello, ' + student); // Hello, [object Object]
Object.prototype = {
toString() {}
};
const student = {
name: 'Billy'
};
student.toString = function {
return this.name;
}
console.info('Hello, ' + student); // Hello, Billy
const student = {};
Object.defineProperty(student, 'name', {
writable: false, // Можно не указывать, по умолчанию false
value: 'Billy'
});
student.name = 'Willy';
console.info(student.name); // Billy
Неявное поведение!
'use strict';
const student = {};
Object.defineProperty(student, 'name', {
value: 'Billy'
});
student.name = 'Willy';
console.info(student.name);
TypeError: Cannot assign to read only property 'name' of object
const person = {};
Object.defineProperty(person, 'planet', {
value: 'Earth'
});
const student = {};
Object.setPrototypeOf(student, person);
student.planet = 'Mars';
TypeError: Cannot assign to read only property
const student = {};
Object.defineProperty(student, 'contacts', {
value: {
email: 'billy@example.com',
telegram: '@billy'
}
});
student.contacts.telegram = '@willy';
console.info(student.contacts.telegram); // @willy
const student = {
name: 'Billy',
age: 21
};
for (let key in student) {
console.info(key);
} // name, age
const person = { planet: 'Earth' };
const student = {
name: 'Billy',
age: 20
};
Object.setPrototypeOf(student, person);
for (let key in student) {
console.info(key);
} // name, age, planet
const person = { planet: 'Earth' };
const student = {
name: 'Billy',
age: 21
};
Object.setPrototypeOf(student, person);
for (let key in student) {
if (student.hasOwnProperty(key)) {
console.info(key);
}
} // name, age
const person = { planet: 'Earth' };
const student = {
name: 'Billy',
age: 21
};
Object.setPrototypeOf(student, person);
Object.keys(student); // ['name', 'age']
const person = { planet: 'Earth' };
const student = {
name: 'Billy',
age: 21
};
Object.setPrototypeOf(student, person);
for (let [key, value] of Object.entries(student)) {
console.info(key);
} // name, age
const student = { name: 'Billy' };
Object.defineProperty(student, 'age', {
enumerable: false,
value: 21
});
Object.keys(student); // ['name']
JSON.stringify(student); // '{"name":"Billy"}'
Object.assign({}, student); // { name: 'Billy' }
const person = {};
Object.defineProperty(person, 'planet', {
value: 'Earth'
});
const student = { name: 'Billy' };
Object.setPrototypeOf(student, person);
for (let key in student) {
console.info(key);
} // name
Object.prototype = {
toString() {}
};
const student = { name: 'Billy' };
for (let key in student) {
console.info(key);
} // name
const student = { name: 'Billy' };
Object.defineProperty(student, 'age', {
value: 21
});
Object.getOwnPropertyNames(student);
// ['name', 'age']
let name = null; // Не будет доступна снаружи модуля
const student = {
get name() { return 'Student ' + name; }
set name(value) {
name = value;
}
};
module.exports = student;
const student = require('./student');
student.name = 'Billy';
console.info(student.name); //Student Billy;
let planet = null;
const person = {
get planet() { return planet; },
set planet(value) { planet = value; }
};
const student = {}
Object.setPrototypeOf(student, person);
student.planet = 'Mars';
student.hasOwnProperty('planet'); // false;
let name = null;
const student = {
name: 'Willy',
get name() { return 'Student ' + name; },
set name(value) { name = value;}
};
student.name = 'Billy'
console.info(student.name); // Student Billy
let name = null;
const student = {
get name() { return 'Student ' + name; },
name: 'Willy', // get становится undefined
set name(value) { name = value;}
};
student.name = 'Billy'
console.info(student.name); // undefined
let name = null;
const student = {
name: 'Willy', // get становится undefined
set name(value) { name = value;}
};
student.name = 'Billy'
console.info(student.name); // undefined
let name = null;
const student = {
get name() { return 'Student ' + name; }
};
student.name = 'Billy'
console.info(student.name); // Student null
Поле одновременно может быть либо нормальным либо геттером/сеттером
Если есть хотя бы один из методов get или set, то поле становится геттером/сеттером
let name = null;
const student = {
get name() { return 'Student ' + name; },
set name(value) { name = value;}
};
Object.getOwnPropertyDescriptor(student, 'name');
// {
// get: [Function: get],
// set: [Function: set],
// enumerable: true,
// configurable: true
// }
const student = {
name: 'Billy'
};
Object.getOwnPropertyDescriptor(student, 'name');
// {
// value: 'Billy',
// writable: true,
// enumerable: true,
// configurable: true
// }
Object.defineProperty(student, 'name', {
value: 'Billy'
});
Object.getOwnPropertyDescriptor(student, 'name');
// {
// value: 'Billy',
// writable: false,
// enumerable: false,
// configurable: false
// }
Либо set/get, либо writable/value
const student = {};
Object.defineProperty(student, 'name', {
configurable: false, // По умолчанию false
value: 'Billy'
});
delete student.name;
console.info(student.name); // Billy
configurable не контролирует изменение атрибута writable
Speaking JavaScript
Chapter 17. Objects and Inheritance.
Layer 1: Single Objects
Speaking JavaScript
Chapter 17. Objects and Inheritance
Layer 2: The Prototype Relationship Between Objects
Exploring ES6
14. New OOP features besides classes
Лекции 2015 года
Про this
Современный учебник Javascript
ООП в прототипном стиле
Прототип объекта
Современный учебник Javascript
Современные возможности ES-2015
Объекты и прототипы