Давайте вместе изучим объекты JavaScript

внешний интерфейс GitHub JavaScript

Давайте вместе изучим объекты JavaScript

Объект представляет собой динамический набор свойств, у него есть скрытое свойство, связанное с прототипом (примечание:__proto__).

У свойства есть ключ и значение.

ключ свойства

Ключ свойства представляет собой уникальную строку.

Доступ к свойствам можно получить двумя способами: запись через точку и запись в квадратных скобках. При использовании записи через точку ключ свойства должен быть допустимым идентификатором.

let obj = {
  message : "A message"
}
obj.message //"A message"
obj["message"] //"A message"

Доступ к несуществующему свойству не приведет к ошибке, но вернетundefined.

obj.otherProperty //undefined

При использовании скобочной нотации ключ свойства не обязательно должен быть допустимым идентификатором — это может быть любое значение.

let french = {};
french["thank you very much"] = "merci beaucoup";

french["thank you very much"]; //"merci beaucoup"

Когда ключ свойства не является строковым значением, он преобразуется в строку с помощью метода toString() (если он доступен).

let obj = {};
//Number
obj[1] = "Number 1";
obj[1] === obj["1"]; //true
//Object
let number1 = {
  toString : function() { return "1"; }
}
obj[number1] === obj["1"]; //true

В приведенном выше примере объектnumber1используется как ключ. Он преобразуется в строку, а результат преобразования «1» используется в качестве ключа свойства.

стоимость имущества

Значением свойства может быть любой базовый тип данных, объект или функция.

Объект

Объекты могут быть вложены в другие объекты.см. пример ниже:

let book = {
  title : "The Good Parts",
  author : {
    firstName : "Douglas",
    lastName : "Crockford"
  }
}
book.author.firstName; //"Douglas"

Таким образом, мы можем создать пространство имен:

let app = {};
app.authorService = { getAuthors : function() {} };
app.bookService = { getBooks : function() {} };

функционировать как значение

Когда функция используется в качестве значения свойства, она обычно становится методом. В методеthisКлючевое слово представляет текущий объект.

this, будут иметь разные значения в зависимости от того, как вызывается функция. Узнать больше оthisПроблему потери контекста можно посмотретьЧто делать, когда «это» теряет контекст.

динамизм

Объекты по своей природе динамичны. Свойства могут быть добавлены или удалены по желанию.

let obj = {};
obj.message = "This is a message"; //add new property
obj.otherMessage = "A new message"; //add new property
delete obj.otherMessage; //delete property

Map

Мы можем думать об объекте как о Карте. Ключ Карты является свойством объекта.

Доступ к ключу не требует сканирования всех свойств. Временная сложность доступа равна o(1).

прототип

Объекты имеют «скрытое» свойство, связанное с объектом-прототипом.__proto__, объект наследует свойства от этого объекта-прототипа.

Например, объекты, созданные с использованием литералов объектов, имеют указатель наObject.prototypeссылка на:

var obj = {};
obj.__proto__ === Object.prototype; //true

Сеть прототипов

Объект-прототип имеет свой собственный прототип. Когда происходит доступ к свойству, которое не содержится в текущем объекте, JavaScript выполняет поиск по цепочке прототипов до тех пор, пока не найдет свойство, к которому осуществляется доступ, или не достигнетnullдо.

только чтение

Прототипы используются только для чтения значений. Когда объект изменяется, это влияет только на текущий объект и не влияет на прототип объекта, даже если на прототипе есть свойство с таким же именем.

пустой объект

Как мы видим, пустой объект{}на самом деле не пустой, потому что он содержит указатели наObject.prototypeссылка на. Чтобы создать действительно пустой объект, мы можем использоватьObject.create(null). Он создаст объект без каких-либо свойств. Обычно это используется для создания карты.

Примитивные значения и объекты-обертки

Разрешая доступ к свойствам, JavaScript описывает примитивные значения как объекты. Конечно, примитивные значения не являются объектами.

(1.23).toFixed(1); //"1.2"
"text".toUpperCase(); //"TEXT"
true.toString(); //"true"

Чтобы разрешить доступ к свойствам с примитивными значениями, JavaScript создает объект-оболочку, а затем уничтожает его. Движок JavaScript оптимизирует процесс создания и уничтожения объектов-оболочек.

Числовые, строковые и логические значения имеют эквивалентные объекты-оболочки. Не будь:Number,String,Boolean.

nullа такжеundefinedПримитивные значения не имеют соответствующих объектов-оболочек и не предоставляют методов.

Встроенный прототип

Числа наследуются отNumber.prototype,Number.prototypeунаследовано отObject.prototype.

var no = 1;
no.__proto__ === Number.prototype; //true
no.__proto__.__proto__ === Object.prototype; //true

Строки наследуют отString.prototype. Булевы значения наследуются отBoolean.prototype

Функции являются объектами и наследуются отFunction.prototype. функция имеетbind(),apply()а такжеcall()и другие методы.

Все объекты, функции и примитивные значения (кромеnullа такжеundefined) оба изObject.prototypeУнаследованные свойства. у них у всех естьtoString()метод.

Дополнение встроенных объектов полифиллами

JavaScript может легко дополнять встроенные объекты новыми функциями.

Полифилл — это фрагмент кода, который реализует функцию в браузерах, которые ее не поддерживают.

Утилиты

Например, этоObject.assign()написаноpolyfill, если он недоступен, то вObjectДобавьте новый метод выше.

дляArray.from()написал что-то вродеpolyfill, если он недоступен, просто вArrayДобавьте новый метод выше.

прототип

К прототипу можно добавлять новые методы.

Например,String.prototype.trim() polyfillсделать все строки доступнымиtrim()метод.

let text = "   A text  ";
text.trim(); //"A text"

Array.prototype.find() polyfillсделать все массивы доступнымиfind()метод.polyfillТо же самое справедливо.

let arr = ["A", "B", "C", "D", "E"];
arr.indexOf("C"); //2

единоличное наследство

Object.create()Создайте новый объект с определенным объектом-прототипом. Он используется для одиночного наследования.Рассмотрим следующий пример:

let bookPrototype = {
  getFullTitle : function(){
    return this.title + " by " + this.author;
  }
}
let book = Object.create(bookPrototype);
book.title = "JavaScript: The Good Parts";
book.author = "Douglas Crockford";
book.getFullTitle();//JavaScript: The Good Parts by Douglas Crockford

множественное наследование

Object.assign()Копирует свойства одного или нескольких объектов в целевой объект. Он используется для множественного наследования.см. пример ниже:

let authorDataService = { getAuthors : function() {} };
let bookDataService = { getBooks : function() {} };
let userDataService = { getUsers : function() {} };
let dataService = Object.assign({},
 authorDataService,
 bookDataService,
 userDataService
);
dataService.getAuthors();
dataService.getBooks();
dataService.getUsers();

неизменяемый объект

Object.freeze()Заморозить объект. Атрибуты нельзя добавлять, удалять или изменять. Объекты становятся неизменяемыми.

"use strict";
let book = Object.freeze({
  title : "Functional-Light JavaScript",
  author : "Kyle Simpson"
});
book.title = "Other title";//Cannot assign to read only property 'title'

Object.freeze()Выполните неглубокую заморозку. Для глубокой заморозки вам необходимо рекурсивно заморозить каждое свойство объекта.

копировать

Object.assign()используется как объект копирования.

let book = Object.freeze({
  title : "JavaScript Allongé",
  author : "Reginald Braithwaite"
});
let clone = Object.assign({}, book);

Object.assign()Выполняйте поверхностную копию, а не глубокую. Он копирует свойства первого уровня объекта. Вложенные объекты совместно используются оригиналом и копией.

литерал объекта

Литералы объектов обеспечивают простой и элегантный способ создания объектов.

let timer = {
  fn : null,
  start : function(callback) { this.fn = callback; },
  stop : function() {},
}

Однако этот синтаксис имеет некоторые недостатки. Все свойства являются общедоступными, методы можно переопределять, и один и тот же метод нельзя использовать в новом экземпляре.

timer.fn;//null 
timer.start = function() { console.log("New implementation"); }

Object.create()

Object.create()а такжеObject.freeze()Вместе мы сможем решить две последние проблемы.

Сначала я хочу создать замороженный прототип со всеми методамиtimerPrototype, а затем создайте объекты для наследования от него.

let timerPrototype = Object.freeze({
  start : function() {},
  stop : function() {}
});
let timer = Object.create(timerPrototype);
timer.__proto__ === timerPrototype; //true

Когда прототип заморожен, объекты, унаследованные от него, не могут изменить свои свойства. Сейчас,start()а такжеstop()Методы не могут быть переопределены.

"use strict";
timer.start = function() { console.log("New implementation"); } //Cannot assign to read only property 'start' of object

Object.create(timerPrototype)Может использоваться для создания большего количества объектов с использованием одного и того же прототипа.

Конструктор

Первоначально язык JavaScript предлагал конструкторы в качестве синтаксического сахара для них.см. код ниже:

function Timer(callback){
  this.fn = callback;
}
Timer.prototype = {
  start : function() {},
  stop : function() {}
}
function getTodos() {}
let timer = new Timer(getTodos);

все сfunctionФункции, определенные ключевым словом, могут использоваться как конструкторы. Функция использования конструктораnewпередача. Новый объект имеет прототип, установленный наFunctionConstructor.prototype.

let timer = new Timer();
timer.__proto__ === Timer.prototype;

Точно так же нам нужно заморозить прототип, чтобы предотвратить переопределение методов.

Timer.prototype = Object.freeze({
  start : function() {},
  stop : function() {}
});

новый оператор

при исполненииnew Timer(), это так же, как функцияnewTimer()Тот же эффект:

function newTimer(){
  let newObj = Object.create(Timer.prototype);
  let returnObj = Timer.call(newObj, arguments);
  if(returnObj) return returnObj;
    
  return newObj;
}

использоватьTimer.prototypeВ качестве прототипа создается новый объект. затем выполнитьTimerфункцию и задайте поля свойств для нового объекта.

Добрый

ES2015 привносит во все это лучший синтаксический сахар.см. пример ниже:

class Timer{
  constructor(callback){
    this.fn = callback;
  }
  
  start() {}
  stop() {}  
}
Object.freeze(Timer.prototype);

Объекты, созданные с помощью класса, имеют прототип, установленный наClassName.prototype. При создании объекта с классом необходимо использоватьnewоператор.

let timer= new Timer();
timer.__proto__ === Timer.prototype;

Синтаксис класса не замораживает прототип, поэтому нам нужно сделать это позже.

Object.freeze(Timer.prototype);

Наследование на основе прототипа

В JavaScript объекты наследуются от объектов.

Конструкторы и классы — это синтаксический сахар для всех методов, используемых для создания объектов-прототипов. Затем он создает новый объект, который наследуется от объекта-прототипа, и устанавливает поля данных для нового объекта. Преимущество наследования на основе прототипа заключается в сохранении памяти. Прототипы создаются только один раз и используются всеми экземплярами.

нет пакета

Шаблон наследования на основе прототипов не имеет конфиденциальности. Все свойства объекта являются общедоступными.

Object.keys()Возвращает массив, содержащий все ключи свойств. Его можно использовать для перебора всех свойств объекта.

function logProperty(name){
  console.log(name); //property name
  console.log(obj[name]); //property value
}
Object.keys(obj).forEach(logProperty);

Смоделированный частный режим содержит использование_чтобы пометить частные свойства, чтобы другие не использовали их:

class Timer{
  constructor(callback){
    this._fn = callback;
    this._timerId = 0;
  }
}

заводской узор

JavaScript предоставляет новый способ создания инкапсулированных объектов с использованием фабричного шаблона.

function TodoStore(callback){
    let fn = callback;
    
    function start() {},
    function stop() {}
    
    return Object.freeze({
       start,
       stop
    });
}

fnПеременные являются частными. Толькоstart()а такжеstop()Методы общедоступны.start()а такжеstop()Методы не могут быть изменены посторонними. здесь не используетсяthis, то нетthisПроблема потери контекста.

Литералы объектов по-прежнему используются для возврата объектов, но на этот раз они содержат только функции. Что еще более важно, эти функции являются замыканиями, которые имеют одно и то же частное состояние.Object.freeze()Используется для замораживания общедоступных API.

Полную реализацию объекта Timer см.Практические объекты JavaScript с возможностями инкапсуляции.

В заключение

JavaScript обрабатывает примитивные значения, объекты и функции как объекты.

Объекты являются динамическими по своей природе и могут использоваться в качестве карт.

Объекты наследуют от других объектов. Конструкторы и классы — это синтаксический сахар для создания объектов, наследуемых от других объектов-прототипов.

Object.create()может использоваться для одиночного наследования,Object.assign()Используется для множественного наследования.

Фабричные функции могут создавать инкапсулированные объекты.

Для получения дополнительной информации о возможностях JavaScript см.:

Discover the power of first class functions

How point-free composition will make you a better functional programmer

Here are a few function decorators you can write from scratch

Why you should give the Closure function another chance

Make your code easier to read with Functional Programming