предисловие
существует«Контрольный список для самопроверки интерфейса JavaScript для начинающих и среднего уровня — 1»В этой части я вкратце прошёл со всеми основы JavaScript, а те, кто не видел, могут пересмотреть 😁
Эта серия статей - моя "внутри нашей команды"Современный JavaScript-коммандос», первое содержание обученияУчебник по современному JavaScriptсерияВторая частьВыходной контент, я надеюсь, что этот список для самопроверки поможет вам закрепить свои знания и извлечь уроки из прошлого.
Содержание этого раздела основано наОбъекты JavaScriptВ основном, в том числе следующие:
1. Объект
В JavaScript восемь типов данных, семь примитивных типов, а их значения содержат только один тип (строка, число или другое), в то время как объекты используются дляСохраняйте пары ключ-значение и более сложные объекты.Мы можем сделать это, используя необязательныйсписок свойствфигурные скобки**{...}**
Чтобы создать объект, свойство представляет собой пару ключ-значение.{"key" : "value"}
, где ключ (key
) — это строка (или имя атрибута), значение (value
) может быть любого типа.
1. Создайте объект
Мы можем создать новый объект двумя способами:
// 1. 通过“构造函数”创建
let user = new Object();
// 2. 通过“字面量”创建
let user = {};
2. Текст и свойства объекта
Когда объект создается, некоторые свойства объекта могут быть инициализированы:
let user = {
name : 'leo',
age : 18
}
Затем вы можете сделать пару свойств для этого объектаCRUDработать:
// 增加属性
user.addr = "China";
// user => {name: "leo", age: 18, addr: "China"}
// 删除属性
delete user.addr
// user => {name: "leo", age: 18}
// 修改属性
user.age = 20;
// user => {name: "leo", age: 20}
// 查找属性
user.age;
// 20
3. Использование квадратных скобок
Конечно ключ объекта(key
) также может быть атрибутом из нескольких слов, но должен быть заключен в кавычки, а при его использовании необходимо использовать квадратные скобки ([]
) гласит:
let user = {
name : 'leo',
"my interest" : ["coding", "football", "cycling"]
}
user["my interest"]; // ["coding", "football", "cycling"]
delete user["my interest"];
Мы также можем использовать переменные в квадратных скобках для получения значений свойств:
let key = "name";
let user = {
name : "leo",
age : 18
}
// ok
user[key]; // "leo"
user[key] = "pingan";
// error
user.key; // undefined
4. Вычисляемые свойства
При создании объекта вы можете использовать квадратные скобки в литерале объекта, т.е.вычисляемое свойство:
let key = "name";
let inputKey = prompt("请输入key", "age");
let user = {
[key] : "leo",
[inputKey] : 18
}
// 当用户在 prompt 上输入 "age" 时,user 变成下面样子:
// {name: "leo", age: 18}
Конечно, вычисляемые свойства также могут быть выражениями:
let key = "name";
let user = {
["my_" + key] : "leo"
}
user["my_" + key]; // "leo"
5. Аббревиатура имени атрибута
В реальной разработке одно и то же имя свойства и значение свойства могут быть сокращены до более короткого синтаксиса:
// 原本书写方式
let getUser = function(name, age){
// ...
return {
name: name,
age: age
}
}
// 简写方式
let getUser = function(name, age){
// ...
return {
name,
age
}
}
Вы также можете смешать:
// 原本书写方式
let getUser = function(name, age){
// ...
return {
name: name,
age: 18
}
}
// 简写方式
let getUser = function(name, age){
// ...
return {
name,
age: 18
}
}
6. Обнаружение наличия свойства объекта
6.1 Использование ключевого слова in
Этот метод может определитьСобственные и унаследованные свойства объектаон существует.
let user = {name: "leo"};
"name" in user; //true,自有属性存在
"age" in user; //false
"toString" in user; //true,是一个继承属性
6.2 Используйте метод объекта hasOwnProperty().
Этот метод может только судитьсобственностьсуществует, ибоУнаследованные свойствавернусьfalse
.
let user = {name: "leo"};
user.hasOwnProperty("name"); //true,自有属性中有 name
user.hasOwnProperty("age"); //false,自有属性中不存在 age
user.hasOwnProperty("toString"); //false,这是一个继承属性,但不是自有属性
6.3 Оценка с помощью undefined
Этот метод позволяет определить состояние объекта.Собственное и унаследованное имущество.
let user = {name: "leo"};
user.name !== undefined; // true
user.age !== undefined; // false
user.toString !== undefined // true
Существует проблема с этим методом, если значение свойства равноundefined
, метод не может вернуть желаемый результат:
let user = {name: undefined};
user.name !== undefined; // false,属性存在,但值是undefined
user.age !== undefined; // false
user.toString !== undefined; // true
6.4 Прямое суждение в условных утверждениях
let user = {};
if(user.name) user.name = "pingan";
//如果 name 是 undefined, null, false, " ", 0 或 NaN,它将保持不变
user; // {}
7. Обход цикла объекта
Когда нам нужно обойти каждое свойство в объекте, мы можем использоватьfor...in
заявление о реализации
7.1 Цикл for...in
for...in
оператор пересекает разделение объекта в любом порядкеSymbol
КромеисчисляемыйАтрибуты.Уведомление:for...in
Не следует применять к массиву, где важен порядок индексов.
let user = {
name : "leo",
age : 18
}
for(let k in user){
console.log(k, user[k]);
}
// name leo
// age 18
7.2 Новые методы ES7
Новое в ES7Object.values()
иObject.entries()
с предыдущимObject.keys()
Точно так же верните тип массива.
1. Object.keys()
Возвращает массив, члены которого принадлежат самому объекту параметра (без наследования)Проходные свойстваимя здоровья.
let user = { name: "leo", age: 18};
Object.keys(user); // ["name", "age"]
2. Object.values()
Возвращает массив, элементами которого являются ключи всех проходимых свойств самого объекта параметра (за исключением унаследованных).
let user = { name: "leo", age: 18};
Object.values(user); // ["leo", 18]
Если аргумент не является объектом, возвращается пустой массив:
Object.values(10); // []
Object.values(true); // []
3. Object.entries()
Возвращает массив, члены которого принадлежат самому объекту параметра (без наследования)Проходные свойстваМассив пар ключ-значение.
let user = { name: "leo", age: 18};
Object.entries(user);
// [["name","leo"],["age",18]]
Ручная реализацияObject.entries()
метод:
// Generator函数实现:
function* entries(obj){
for (let k of Object.keys(obj)){
yield [k ,obj[k]];
}
}
// 非Generator函数实现:
function entries (obj){
let arr = [];
for(let k of Object.keys(obj)){
arr.push([k, obj[k]]);
}
return arr;
}
4. Object.getOwnPropertyNames(Obj)
Этот метод возвращает массив, содержащий объектыObj
Все имущество в собственности (независимо от того, перечислимы они или нет)Название.
let user = { name: "leo", age: 18};
Object.getOwnPropertyNames(user);
// ["name", "age"]
2. Копия объекта
Справочная статья«Пожалуйста, смотрите здесь, если вы не понимаете присваивание, поверхностное копирование и глубокое копирование в JS».
1. Операция присваивания
Сначала просмотрите основные типы данных и справочные типы данных:
- базовый тип
Концепция: значения базового типа занимают фиксированный размер в памяти и хранятся в栈内存
средний (не считая闭包
переменная в ).
Общие включают: undefined, null, Boolean, String, Number, Symbol
- тип ссылки
Концепция: значением ссылочного типа является объект, хранящийся в堆内存
середина. Память стека хранит переменный идентификатор объекта и адрес хранения (ссылка) объекта в памяти кучи.Ссылочный тип данных хранит в стеке указатель, а указатель указывает на начальный адрес сущности в куче . Когда интерпретатор ищет ссылочное значение, он сначала извлекает его адрес в стеке, а затем получает сущность из кучи после получения адреса.
Общие включают: объект, массив, дату, функцию, регулярное выражение и т. д.
1.1 Присвоение базового типа данных
Когда данные в памяти стека изменяются, система автоматически присваивает новое значение новой переменной в памяти стека.Эти две переменные не зависят друг от друга и не влияют друг на друга.
let user = "leo";
let user1 = user;
user1 = "pingan";
console.log(user); // "leo"
console.log(user1); // "pingan"
1.2 Назначение типа справочных данных
В JavaScript переменные хранят не сам объект, а его «адрес в памяти», иными словами, «ссылку» на него.
как показано нижеleo
Переменная просто содержит паруuser
Соответствующая ссылка на объект:
let user = { name: "leo", age: 18};
let leo = user;
Другие переменные также могут быть указаныuser
Объект:
let leo1 = user;
let leo2 = user;
Но так как переменная содержит ссылку, когда мы модифицируем переменнуюleo
\ leo1
\ leo2
эти ценности,также изменяется на ссылочный объект user
, но когдаuser
Если вы его модифицируете, изменятся значения других переменных, которые ссылаются на объект:
leo.name = "pingan";
console.log(leo); // {name: "pingan", age: 18}
console.log(leo1); // {name: "pingan", age: 18}
console.log(leo2); // {name: "pingan", age: 18}
console.log(user); // {name: "pingan", age: 18}
user.name = "pingan8787";
console.log(leo); // {name: "pingan8787", age: 18}
console.log(leo1); // {name: "pingan8787", age: 18}
console.log(leo2); // {name: "pingan8787", age: 18}
console.log(user); // {name: "pingan8787", age: 18}
Этот процесс связан с проблемой указания указателя переменного адреса, которая здесь пока обсуждаться не будет.Заинтересованные друзья могут проверить соответствующую информацию в Интернете.
2. Сравнение объектов
Когда две переменные ссылаются на один и тот же объект, они либо==
все еще===
вернусьtrue
.
let user = { name: "leo", age: 18};
let leo = user;
let leo1 = user;
leo == leo1; // true
leo === leo1; // true
leo == user; // true
leo === user; // true
но если обе переменные являются пустыми объектами{}
, то они не равны:
let leo1 = {};
let leo2 = {};
leo1 == leo2; // false
leo1 === leo2; // false
3. Поверхностное копирование
3.1 Концепция
концепция:Новый объект копирует значения свойств, не являющихся объектами, и ссылки на свойства объектов в существующих объектах.. Его также можно понимать как:Новый объект напрямую копирует ссылку на свойства объекта существующего объекта., что является поверхностной копией.
мелкая копияКопируются только атрибуты первого слоя, когда значение атрибута первого слоя является базовым типом данных, новый объект и исходный объект не влияют друг на друга, но если значение атрибута первого слоя является сложным типом данных, то значение атрибута нового объект и исходный объект указывают на один и тот же адрес памяти.
Продемонстрируйте использование сценариев неглубокого копирования с помощью примера кода:
// 示例1 对象原始拷贝
let user = { name: "leo", skill: { JavaScript: 90, CSS: 80}};
let leo = user;
leo.name = "leo1";
leo.skill.CSS = 90;
console.log(leo.name); // "leo1"
console.log(user.name); // "leo1"
console.log(leo.skill.CSS); // 90
console.log(user.skill.CSS);// 90
// 示例2 数组原始拷贝
let user = ["leo", "pingan", {name: "pingan8787"}];
let leo = user;
leo[0] = "pingan888";
leo[2]["name"] = "pingan999";
console.log(leo[0]); // "pingan888"
console.log(user[0]); // "pingan888"
console.log(leo[2]["name"]); // "pingan999"
console.log(user[2]["name"]); // "pingan999"
Как видно из примера кода выше: Поскольку объект копируется напрямую, это эквивалентно копированиюссылочный тип данных, поэтому, когда новый объект изменяет какое-либо значение, оно изменится на исходные данные.
Затем выполните поверхностную копию, сравните следующее.
3.2 Реализация неглубокого копирования
1. Object.assign()
грамматика:Object.assign(target, ...sources)
Метод копирования объектов в ES6, первый принятый параметр — это целевая цель копии, а остальные параметры — это исходные объекты-источники копии (которых может быть несколько).
Подробности можно прочитать в документации"Объект MDN.assign".
// 示例1 对象浅拷贝
let user = { name: "leo", skill: { JavaScript: 90, CSS: 80}};
let leo = Object.assign({}, user);
leo.name = "leo1";
leo.skill.CSS = 90;
console.log(leo.name); // "leo1" ⚠️ 差异!
console.log(user.name); // "leo" ⚠️ 差异!
console.log(leo.skill.CSS); // 90
console.log(user.skill.CSS);// 90
// 示例2 数组浅拷贝
let user = ["leo", "pingan", {name: "pingan8787"}];
let leo = Object.assign({}, user);
leo[0] = "pingan888";
leo[2]["name"] = "pingan999";
console.log(leo[0]); // "pingan888" ⚠️ 差异!
console.log(user[0]); // "leo" ⚠️ 差异!
console.log(leo[2]["name"]); // "pingan999"
console.log(user[2]["name"]); // "pingan999"
Как видно из результатов печати, неглубокая копия создает новый объект только в корневом атрибуте (первом уровне объекта), но если значением атрибута является объект, копируется только тот же адрес памяти.
Object.assign()
Используйте Примечание:
- Копировать только собственные свойства исходного объекта (не копировать унаследованные свойства);
- Не копирует неперечислимые свойства объекта;
- свойство имени
Symbol
Свойство значения может быть скопировано с помощью Object.assign; -
undefined
иnull
нельзя превратить в объекты, их нельзя использовать какObject.assign
параметр, но может использоваться как исходный объект.
Object.assign(undefined); // 报错
Object.assign(null); // 报错
Object.assign({}, undefined); // {}
Object.assign({}, null); // {}
let user = {name: "leo"};
Object.assign(user, undefined) === user; // true
Object.assign(user, null) === user; // true
2. Array.prototype.slice()
грамматика:arr.slice([begin[, end]])
slice()
метод возвращает новый объект массива, который являетсяbegin
иend
Неглубокая копия определенного исходного массива (включаяbegin
, без учетаend
). Исходный массив не будет изменен.
Подробности можно прочитать в документации《Срез массива MDN》.
// 示例 数组浅拷贝
let user = ["leo", "pingan", {name: "pingan8787"}];
let leo = Array.prototype.slice.call(user);
leo[0] = "pingan888";
leo[2]["name"] = "pingan999";
console.log(leo[0]); // "pingan888" ⚠️ 差异!
console.log(user[0]); // "leo" ⚠️ 差异!
console.log(leo[2]["name"]); // "pingan999"
console.log(user[2]["name"]); // "pingan999"
3. Array.prototype.concat()
грамматика:var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])
concat()
метод используется для объединения двух или более массивов. Этот метод не изменяет существующий массив, а возвращает новый массив.
Подробности можно прочитать в документации"Объединение массивов MDN".
let user = [{name: "leo"}, {age: 18}];
let user1 = [{age: 20},{addr: "fujian"}];
let user2 = user.concat(user1);
user1[0]["age"] = 25;
console.log(user); // [{"name":"leo"},{"age":18}]
console.log(user1); // [{"age":25},{"addr":"fujian"}]
console.log(user2); // [{"name":"leo"},{"age":18},{"age":25},{"addr":"fujian"}]
Array.prototype.concat
Это также неглубокая копия, которая просто создает новый объект в корневом атрибуте (первый уровень объекта), но копирует тот же адрес памяти только в том случае, если значением атрибута является объект.
4. Оператор спреда (...)
грамматика:var cloneObj = { ...obj };
Оператор распространения также является поверхностной копией.Для свойств, значением которых является объект, его нельзя полностью скопировать в два разных объекта, но если свойства все являются значениями базовых типов, использование оператора распространения также является преимуществом и удобством. .
let user = { name: "leo", skill: { JavaScript: 90, CSS: 80}};
let leo = {...user};
leo.name = "leo1";
leo.skill.CSS = 90;
console.log(leo.name); // "leo1" ⚠️ 差异!
console.log(user.name); // "leo" ⚠️ 差异!
console.log(leo.skill.CSS); // 90
console.log(user.skill.CSS);// 90
3.3 Рукописная неглубокая копия
Принцип реализации: новый объект копирует значение атрибута, не являющегося объектом, в существующем объекте и значение атрибута объекта.Цитировать, то есть свойства объекта не копируются в память.
function cloneShallow(source) {
let target = {};
for (let key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
return target;
}
- for in
Оператор for...in перебирает собственные, унаследованные,可枚举的
, свойства, не относящиеся к символам. Для каждого отдельного атрибута выполняется инструкция.
- hasOwnProperty
Возвращаемое значение этой функции является логическим значением, и все объекты, которые наследуют Object, будут наследоваться отhasOwnProperty
Методы иin
В отличие от операторов, эта функция игнорирует свойства, унаследованные от цепочки прототипов, и свои собственные свойства.
грамматика:obj.hasOwnProperty(prop)
prop
это свойство обнаруживатьимя строкиилиSymbol
.
4. Глубокое копирование
4.1 Концепция
Скопируйте значение переменной.Для справочных данных вернитесь к основному типу, а затем скопируйте. Объект после глубокого копированияПолная изоляция от исходного объекта, не влияют друг на друга, а модификации одного объекта не влияют на другой.
4.2 Внедрение глубокого копирования
1. JSON.parse(JSON.stringify())
Принцип состоит в том, чтобы сериализовать объект в строку JSON, преобразовать содержимое объекта в строку и сохранить его на диске, а затем использоватьJSON.parse()
Десериализация превращает строку JSON в новый объект.
let user = { name: "leo", skill: { JavaScript: 90, CSS: 80}};
let leo = JSON.parse(JSON.stringify(user));
leo.name = "leo1";
leo.skill.CSS = 90;
console.log(leo.name); // "leo1" ⚠️ 差异!
console.log(user.name); // "leo" ⚠️ 差异!
console.log(leo.skill.CSS); // 90 ⚠️ 差异!
console.log(user.skill.CSS);// 80 ⚠️ 差异!
JSON.stringify()
Используйте Примечание:
- Если в значении копируемого объекта есть функция,
undefined
,symbol
затем пройти черезJSON.stringify()
`Эта пара ключ-значение исчезнет в сериализованной строке JSON; - Не может копировать неперечислимые свойства, не может копировать цепочку прототипов объекта;
- копировать
Date
Тип ссылки становится строкой; - копировать
RegExp
Ссылочные типы станут пустыми объектами; - объект содержит
NaN
,Infinity
и-Infinity
, сериализованный результат станетnull
; - Циклические приложения, которые не могут копировать объекты (т.
obj[key] = obj
).
2. Сторонние библиотеки
4.3 Рукописная глубокая копия
Основная идеярекурсия, просматривайте объекты и массивы до тех пор, пока они не станут базовыми типами данных, а затем скопируйте их, что является глубокой копией. Код реализации:
const isObject = obj => typeof obj === 'object' && obj != null;
function cloneDeep(source) {
if (!isObject(source)) return source; // 非对象返回自身
const target = Array.isArray(source) ? [] : {};
for(var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (isObject(source[key])) {
target[key] = cloneDeep(source[key]); // 注意这里
} else {
target[key] = source[key];
}
}
}
return target;
}
Недостатки этого метода: при встрече с циклической ссылкой она попадет в рекурсивный процесс в цикле, что приведет к взрыву стека. Другое письмо, вы можете прочитать«Как написать глубокий текст, который удивит интервьюера?».
5. Резюме
мелкая копия: копировать каждое свойство объекта по очереди, но когда значение свойства объекта является ссылочным типом, фактическая копия является его ссылкой, и когда значение, на которое указывает ссылка, изменяется, оно также изменится.
глубокая копия: Скопируйте значение переменной.Для справочных данных вернитесь к основному типу, а затем скопируйте. Объект после глубокого копированияПолная изоляция от исходного объекта, не влияют друг на друга, а модификации одного объекта не влияют на другой.
Глубокая копия и неглубокая копия предназначены для сложных типов данных, поверхностная копия копирует только один слой, а глубокая копия — это послойная копия.
3. Механизм сбора мусора (GC)
Сборка мусора (Garbage Collection, сокращенно GC)Это автоматический механизм управления памятью. Когда часть пространства памяти, занятого программой, больше не используется программой, программа возвращает эту часть пространства памяти операционной системе с помощью алгоритма сборки мусора. Сборщик мусора может снизить нагрузку на программиста и уменьшить количество ошибок в программе. Сборка мусора возникла на языке LISP. В настоящее время многие языки, такие как языки Smalltalk, Java, C# и D, поддерживают сборщики мусора, а известный нам JavaScript имеет механизм автоматической сборки мусора.
В JavaScript данные примитивных типов размещаются в пространстве стека, а данные ссылочного типа размещаются в пространстве кучи.
1. Сборка мусора в пространстве стека
когда функцияshowName
После завершения вызова перейдите вниз наESP (расширенный указатель стека)указатель на уничтожениеshowName
функция, при последующем вызове других функций она перезапишет старую память, сохранит контекст выполнения другой функции и реализует сборку мусора.Изображение взято из «Принцип работы и практика браузера».
2. Сборка мусора в куче
Основой стратегии сбора мусора данных в куче является:Генеральная гипотеза(Поколенческая гипотеза). который:
- Большинство объектов существует в памяти очень короткое время, и многие объекты очень быстро становятся недоступными.
- Бессмертные объекты будут жить дольше.
Эти две функции применимы не только к JavaScript, но и к большинству динамических языков, таких как Java, Python и т. д. Движок V8 делит пространство кучи наКайнозой(хранится для выживаниямало времениобъект) истарое поколение(хранится для выживаниямного времениобъекты) в двух регионах и используют разные сборщики мусора.
- Утилизация суб-отходов в основном отвечает за новое поколение мусора.
- Главный сборщик мусора в основном отвечает за сборку мусора старого поколения.
Независимо от сборщика мусора используется один и тот же процесс сборки мусора:Отметьте активные и неактивные объекты, освободите память неактивных объектов и, наконец, очистите память. **
1.1 Вторичный сборщик мусора
С помощью алгоритма Scavenge пространство новой генерации делится на две области пополам, область объектов и свободную область.Изображение взято из «Принцип работы и практика браузера».
Процесс реализации:
- существует новый объектплощадь объекта, когда область объекта будет заполнена, выполните сборку мусора;
- В процессе сборки мусора сначала помечается мусор в объектной области, а затем вспомогательный сборщик мусора копирует и упорядочивает уцелевшие объекты в свободную область, что эквивалентно завершению сортировки памяти.
- После завершения копирования область объекта и свободная область переворачиваются для завершения операции сборки мусора, что также позволяет многократно использовать две области в новом поколении.
Конечно, это также имеет некоторые проблемы: если данные операции копирования велики, это повлияет на эффективность очистки. Решение для движка JavaScript состоит в том, чтобы установить область нового поколения относительно небольшого размера и принять стратегию продвижения объектов (объекты, которые все еще живы после двух переработок, будут перемещены в область старого поколения), чтобы избежать установки выживших объектов. из-за малой площади нового поколения.Проблемы,заполняющие всю площадь.
1.2 Главный сборщик мусора
Разделен на:Алгоритм маркировки-развертки,иАлгоритм Марк-Компакт.
а) Алгоритм маркировки-развертки процесс:
- Процесс маркировки: обход всего элемента из набора корневых элементов, элементы, до которых можно добраться, являются активными объектами, и наоборот — мусорными данными;
- Процесс очистки: очистить отмеченные данные и создать много фрагментированной памяти. (Недостаток: большие объекты не могут быть выделены достаточно непрерывной памяти)
Изображение взято из «Принцип работы и практика браузера».
б) Алгоритм Марка-Компакта процесс:
- Процесс маркировки: обход всего элемента из набора корневых элементов, элементы, до которых можно добраться, являются активными объектами, и наоборот — мусорными данными;
- Завершающий процесс: переместите все уцелевшие объекты в сегмент, а затем очистите содержимое за пределами конечной границы.
Изображение взято из «Принцип работы и практика браузера».
3. Дополнительная литература
1.«Иллюстрированный механизм сборки мусора Java» 2.«Управление памятью MDN»
4. Методы объекта и это
1. Объектные методы
Подробное введение можно прочитать«Определение метода MDN».
Метод, являющийся свойством объекта, называется «методом объекта» следующим образом.user
объектsay
метод:
let user = {};
let say = function(){console.log("hello!")};
user.say = say; // 赋值到对象上
user.say(); // "hello!"
Можно использовать и более лаконичный подход:
let user = {
say: function(){}
// 简写为
say (){console.log("hello!")}
// ES8 async 方法
async say (){/.../}
}
user.say();
Конечно, имя метода объекта также поддерживает имя вычисляемого свойства в качестве имени метода:
const hello = "Hello";
let user = {
['say' + hello](){console.log("hello!")}
}
user['say' + hello](); // "hello!"
Также обратите внимание: все определения методов не являются конструкторами и вызовут исключение, если вы попытаетесь их создать.TypeError
.
let user = {
say(){};
}
new user.say; // TypeError: user.say is not a constructor
2. this
2.1 Введение в это
Когда методам объекта необходимо использовать свойства объекта, вы можете использоватьthis
Ключевые слова:
let user = {
name : 'leo',
say(){ console.log(`hello ${this.name}`)}
}
user.say(); // "hello leo"
когда кодuser.say()
Во время выполнения,this
Относитсяuser
объект. Конечно, вы также можете использовать имя переменной напрямую.user
Цитироватьsay()
метод:
let user = {
name : 'leo',
say(){ console.log(`hello ${user.name}`)}
}
user.say(); // "hello leo"
Но это небезопасно, потому чтоuser
Объекту можно присвоить другую переменную и присвоить ей другие значения.user
объект, это может вызвать ошибку:
let user = {
name : 'leo',
say(){ console.log(`hello ${user.name}`)}
}
let leo = user;
user = null;
leo.say(); // Uncaught TypeError: Cannot read property 'name' of null
но будетuser.name
изменить наthis.name
Код работает нормально.
2.2 это значение
this
Значение находится вВычисляется при выполнении кода, его значение зависит от контекста кода:
let user = { name: "leo"};
let admin = {name: "pingan"};
let say = function (){
console.log(`hello ${this.name}`)
};
user.fun = say;
admin.fun = say;
// 函数内部 this 是指“点符号前面”的对象
user.fun(); // "hello leo"
admin.fun(); // "hello pingan"
admin['fun'](); // "hello pingan"
Правило: еслиobj.fun()
называется, тоthis
существуетfun
Во время вызова функцииobj
, так что вышеthis
первыйuser
,Послеadmin
.
Но в глобальной среде, включен ли строгий режим или нет,this
оба указывают на глобальный объект
console.log(this == window); // true
let a = 10;
this.b = 10;
a === this.b; // true
2.3 Стрелочные функции не имеют собственного this
Стрелочные функции особенные и не имеют собственныхthis
, если есть ссылкиthis
Если это так, это указывает на внешнюю нормальную функцию.В следующем примереthis
направлениеuser.say()
метод:
let user = {
name : 'leo',
say : () => {
console.log(`hello ${this.name}`);
},
hello(){
let fun = () => console.log(`hello ${this.name}`);
fun();
}
}
user.say(); // hello => say() 外部函数是 window
user.hello(); // hello leo => fun() 外部函数是 hello
2.4 call / apply / bind
Вы можете подробно прочитать«Основы JS — все о вызовах, применении, связывании».
когда мы хотимthis
Чтобы привязать значение к другой среде, вы можете использоватьcall
/ apply
/ bind
Реализация метода:
var user = { name: 'leo' };
var name = 'pingan';
function fun(){
return console.log(this.name); // this 的值取决于函数调用方式
}
fun(); // "pingan"
fun.call(user); // "leo"
fun.apply(user); // "leo"
Примечание: здесьvar name = 'pingan';
Нужно использоватьvar
объявлять, использоватьlet
если,window
адмирал неname
Переменная.
Синтаксис тройки следующий:
fun.call(thisArg, param1, param2, ...)
fun.apply(thisArg, [param1,param2,...])
fun.bind(thisArg, param1, param2, ...)
5. Конструкторы и новый оператор
1. Конструктор
Функция конструктора состоит в том, чтобыРеализовать повторно используемый код создания объекта. В общем, есть два соглашения для конструкторов:
- Делайте первую букву при названии заглавной;
- использовать только
new
оператор выполняет.
new
операторСоздает экземпляр пользовательского типа объекта или экземпляр встроенного объекта с помощью конструктора.
Синтаксис следующий:
new constructor[([arguments])]
Параметры следующие:
-
constructor
Класс или функция, определяющая тип экземпляра объекта. -
arguments
один былconstructor
Список аргументов вызова.
2. Простой пример
Возьмем простой пример:
function User (name){
this.name = name;
this.isAdmin = false;
}
const leo = new User('leo');
console.log(leo.name, leo.isAdmin); // "leo" false
3. Новый процесс работы оператора
когда используется функцияnew
Когда оператор выполняется, он выполняет следующие шаги:
- Новый пустой объект создается и назначается
this
. - Выполняется тело функции. Обычно он изменяет
this
, добавьте к нему новый атрибут. - возвращение
this
значение .
доUser
Метод например:
function User(name) {
// this = {};(隐式创建)
// 添加属性到 this
this.name = name;
this.isAdmin = false;
// return this;(隐式返回)
}
const leo = new User('leo');
console.log(leo.name, leo.isAdmin); // "leo" false
когда мы выполняемnew User('leo')
, происходят следующие вещи:
- один унаследовал от
User.prototype
Создается новый объект; - вызвать конструктор с указанными параметрами
User
, и воляthis
привязать к вновь созданному объекту; - Объект, возвращаемый конструктором,
new
результат выражения. Если конструктор явно не возвращает объект, используется объект, созданный на шаге 1.
требует внимания:
- Как правило, конструкторы не возвращают значения, но разработчики могут активно возвращать объекты, чтобы переопределить обычные этапы создания объектов;
-
new User
Эквивалентноnew User()
, только без указания списка параметров, т.е.User
без параметров;
let user = new User; // <-- 没有参数
// 等同于
let user = new User();
- Любая функция может быть использована в качестве конструктора, т.е.
new
работает оператор.
4. Методы в конструкторе
В конструкторе также можно привязывать методы кthis
начальство:
function User (name){
this.name = name;
this.isAdmin = false;
this.sayHello = function(){
console.log("hello " + this.name);
}
}
const leo = new User('leo');
console.log(leo.name, leo.isAdmin); // "leo" false
leo.sayHello(); // "hello leo"
6. Необязательная цепочка "?".
Подробности можно посмотреть«Необязательный оператор цепочки MDN».
1. Введение
В реальной разработке часто возникают следующие ошибки:
// 1. 对象中不存在指定属性
const leo = {};
console.log(leo.name.toString());
// Uncaught TypeError: Cannot read property 'toString' of undefined
// 2. 使用不存在的 DOM 节点属性
const dom = document.getElementById("dom").innerHTML;
// Uncaught TypeError: Cannot read property 'innerHTML' of null
в дополнительной цепочке?.
Прежде чем появиться, мы будем использовать операцию короткого замыкания&&
Оператор для решения проблемы:
const leo = {};
console.log(leo && leo.name && leo.name.toString()); // undefined
Недостатком этого способа записи является то, чтоСлишком много проблем.
2. Дополнительное введение цепи
дополнительная цепочка?.
этоЗащищенный от ошибок способ доступа к свойствам вложенных объектов. Даже если свойство в середине не существует, ошибки не возникает.
если необязательная цепочка?.
Передняя частьundefined
илиnull
, он останавливает операцию и возвращаетundefined
.
грамматика:
obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)
** Модифицируем код предыдущего примера:
// 1. 对象中不存在指定属性
const leo = {};
console.log(leo?.name?.toString());
// undefined
// 2. 使用不存在的 DOM 节点属性
const dom = document?.getElementById("dom")?.innerHTML;
// undefined
3. Будьте осторожны
Хотя опциональное связывание легко использовать, следует помнить о нескольких вещах:
- Не злоупотребляйте необязательными цепочками;
мы должны только?.
Он используется в местах, где некоторые свойства или методы могут не существовать.В качестве примера возьмем приведенный выше пример кода:
const leo = {};
console.log(leo.name?.toString());
Было бы лучше написать это так, потому чтоleo
объект должен существовать, аname
Свойства могут не существовать.
-
дополнительная цепочка
?.
Предыдущая переменная должна быть объявлена;
в дополнительной цепочке?.
Необходимо использовать предыдущую переменнуюlet/const/var
оператора, иначе будет сообщено об ошибке:
leo?.name;
// Uncaught ReferenceError: leo is not defined
- Необязательная цепочка не может использоваться для назначения;
let object = {};
object?.property = 1;
// Uncaught SyntaxError: Invalid left-hand side in assignment
- Метод доступа к элементам массива с необязательной цепочкой;
let arrayItem = arr?.[42];
4. Другие случаи: ?.() и ?.[]
Следует отметить?.
это специальная синтаксическая конструкция, а не оператор, она также может использоваться с()
и[]
использовать вместе:
4.1 Дополнительные цепочки и вызовы функций ?.()
?.()
Используется для вызова функции, которая может не существовать, например:
let user1 = {
admin() {
alert("I am admin");
}
}
let user2 = {};
user1.admin?.(); // I am admin
user2.admin?.();
?.()
проверит его левую часть: если функция администратора существует, то вызовем ее для запуска (дляuser1
). иначе (дляuser2
) работа останавливается без ошибок.
4.2 Необязательные цепочки и выражения?.[]
?.[]
Позволяет безопасно читать свойства возможно несуществующего объекта.
let user1 = {
firstName: "John"
};
let user2 = null; // 假设,我们不能授权此用户
let key = "firstName";
alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
alert( user1?.[key]?.something?.not?.existing); // undefined
5. Дополнительная цепь?.
Краткое описание синтаксиса
дополнительная цепочка?.
Есть три формы грамматики:
-
obj?.prop
-- еслиobj
вернуть, если существуетobj.prop
, иначе возвратundefined
. -
obj?.[prop]
-- еслиobj
вернуть, если существуетobj[prop]
, иначе возвратundefined
. -
obj?.method()
-- еслиobj
звоните если естьobj.method()
, иначе возвратundefined
.
Как мы видели, эти синтаксические формы просты в использовании.?.
Проверьте, является ли левая частьnull/undefined
, если нет, продолжайте операцию.?.
Цепочки позволяют нам безопасно получать доступ к вложенным свойствам.
Семь, Символ
В спецификации указано, что свойства объектов в JavaScript могут быть толькоТип строкиилиТип символа, в конце концов, мы видели только эти два типа.
1. Введение концепции
Представлен ES6Symbol
как новыйпримитивный тип данных,Выражатьуникальныйзначение, в основном дляПредотвращение конфликтов имен свойств.
После ES6 в JavaScript появилось всего несколько типов данных:Symbol
,undefined
,null
,Boolean
,String
,Number
,Object
.Простой в использовании:
let leo = Symbol();
typeof leo; // "symbol"
Symbol поддерживает входящие параметры в виде имен Symbol, что удобно для отладки кода: **
let leo = Symbol("leo");
2. Меры предосторожности**
-
Symbol
функция не может быть использованаnew
, будет сообщено об ошибке.
так какSymbol
это примитивный тип, а не объект, поэтому вы не можете добавлять свойства, это строковый тип данных.
let leo = new Symbol()
// Uncaught TypeError: Symbol is not leo constructor
-
Symbol
не равны,даже если параметры одинаковые.
// 没有参数
let leo1 = Symbol();
let leo2 = Symbol();
leo1 === leo2; // false
// 有参数
let leo1 = Symbol('leo');
let leo2 = Symbol('leo');
leo1 === leo2; // false
-
Symbol
Невозможно вычислить с другими типами значений, будет сообщено об ошибке.
let leo = Symbol('hello');
leo + " world!"; // 报错
`${leo} world!`; // 报错
-
Symbol
Нет автоматического преобразования в строку, только явное преобразование.
let leo = Symbol('hello');
alert(leo);
// Uncaught TypeError: Cannot convert a Symbol value to a string
String(leo); // "Symbol(hello)"
leo.toString(); // "Symbol(hello)"
-
Symbol
Можно преобразовать в логическое, но не в числовое:
let a1 = Symbol();
Boolean(a1);
!a1; // false
Number(a1); // TypeError
a1 + 1 ; // TypeError
-
Symbol
Имущество не участвуетfor...in/of
цикл.
let id = Symbol("id");
let user = {
name: "Leo",
age: 30,
[id]: 123
};
for (let key in user) console.log(key); // name, age (no symbols)
// 使用 Symbol 任务直接访问
console.log( "Direct: " + user[id] );
3. Используйте Symbol в качестве имени свойства в литералах
использовать в литералах объектовSymbol
В качестве имени свойства необходимо использоватьКвадратных скобках([]
),как[leo]: "leo"
.
Преимущество: Предотвращает свойства с одинаковыми именами и предотвращает перезапись или перезапись ключей.
let leo = Symbol();
// 写法1
let user = {};
user[leo] = 'leo';
// 写法2
let user = {
[leo] : 'leo'
}
// 写法3
let user = {};
Object.defineProperty(user, leo, {value : 'leo' });
// 3种写法 结果相同
user[leo]; // 'leo'
требует внимания: когда символ используется в качестве имени свойства объекта, оператор точки не может использоваться и должен быть заключен в квадратные скобки.
let leo = Symbol();
let user = {};
// 不能用点运算
user.leo = 'leo';
user[leo] ; // undefined
user['leo'] ; // 'leo'
// 必须放在方括号内
let user = {
[leo] : function (text){
console.log(text);
}
}
user[leo]('leo'); // 'leo'
// 上面等价于 更简洁
let user = {
[leo](text){
console.log(text);
}
}
Часто также используется для создания набора констант, который гарантирует, что все значения не равны.:
let user = {};
user.list = {
AAA: Symbol('Leo'),
BBB: Symbol('Robin'),
CCC: Symbol('Pingan')
}
4. Применение: избавьтесь от волшебных строк
волшебная нить: Относится к строкам или значениям, которые появляются в коде несколько раз, сильно связаны, и их следует избегать, а вместо них следует использовать переменные с четким значением.
function fun(name){
if(name == 'leo') {
console.log('hello');
}
}
fun('leo'); // 'hello' 为魔术字符串
Переменные часто используются для исключения волшебных строк:
let obj = {
name: 'leo'
};
function fun(name){
if(name == obj.name){
console.log('hello');
}
}
fun(obj.name); // 'hello'
использоватьSymbol
Устраните сильную связь, чтобы вам не нужно было связывать конкретные значения:
let obj = {
name: Symbol()
};
function fun (name){
if(name == obj.name){
console.log('hello');
}
}
fun(obj.name); // 'hello'
5. Обход имен свойств
Символ, пройденный как имя свойства, отсутствуетfor...in
,for...of
цикл, ниObject.keys()
,Object.getOwnPropertyNames()
,JSON.stringify()
возвращение.
let leo = Symbol('leo'), robin = Symbol('robin');
let user = {
[leo]:'18', [robin]:'28'
}
for(let k of Object.values(user)){console.log(k)}
// 无输出
let user = {};
let leo = Symbol('leo');
Object.defineProperty(user, leo, {value: 'hi'});
for(let k in user){
console.log(k); // 无输出
}
Object.getOwnPropertyNames(user); // []
Object.getOwnPropertySymbols(user); // [Symbol(leo)]
Object.getOwnPropertySymbols
Метод возвращает массив, содержащий все значения Symbol, используемые в качестве имен свойств для текущего объекта.
let user = {};
let leo = Symbol('leo');
let pingan = Symbol('pingan');
user[leo] = 'hi leo';
user[pingan] = 'hi pingan';
let obj = Object.getOwnPropertySymbols(user);
obj; // [Symbol(leo), Symbol(pingan)]
В качестве альтернативы вы можете использоватьReflect.ownKeys
Метод может возвращать все типы имен ключей, включая имена обычных ключей и имена ключей символов.
let user = {
[Symbol('leo')]: 1,
age : 2,
address : 3,
}
Reflect.ownKeys(user); // ['age', 'address',Symbol('leo')]
Поскольку значение Symbol как свойство имени не проходится обычным методом, оно часто используется для определения некоторых незакрытых и внутренних методов объекта.
6. Символ.для(), Символ.keyFor()
6.1 Symbol.for()
Используется для повторного использования значения символа, получитьнитьВ качестве параметра, если есть значение Symbol с этим параметром в качестве имени, вернуть Symbol, в противном случае создать новый и вернуть значение Symbol с этим параметром в качестве имени.
let leo = Symbol.for('leo');
let pingan = Symbol.for('leo');
leo === pingan; // true
Symbol()
иSymbol.for()
разница:
Symbol.for('leo') === Symbol.for('leo'); // true
Symbol('leo') === Symbol('leo'); // false
6.2 Symbol.keyFor()
Используется для возврата использованного ключа типа Symbol:
let leo = Symbol.for('leo');
Symbol.keyFor(leo); // 'leo'
let leo = Symbol('leo');
Symbol.keyFor(leo); // undefined
7. Встроенные значения символов
ES6 предоставляет 11 встроенных значений Symbol, которые указывают на методы, используемые внутри языка:
7.1 Symbol.hasInstance
когда другие объекты используютinstanceof
оператор, этот метод вызывается при оценке того, является ли он экземпляром объекта. Например,foo instanceof Foo
Внутри языка фактический вызовFoo[Symbol.hasInstance](foo)
.
class P {
[Symbol.hasInstance](a){
return a instanceof Array;
}
}
[1, 2, 3] instanceof new P(); // true
P - это класс, new P() вернет экземпляр, экземплярSymbol.hasInstance
метод, будетinstanceof
Вызывается автоматически во время операции, чтобы определить, является ли оператор слеваArray
пример.
7.2 Symbol.isConcatSpreadable
Это логическое значение, указывающее, что объект используется дляArray.prototype.concat()
, можно ли его расширить.
let a = ['aa','bb'];
['cc','dd'].concat(a, 'ee');
// ['cc', 'dd', 'aa', 'bb', 'ee']
a[Symbol.isConcatSpreadable]; // undefined
let b = ['aa','bb'];
b[Symbol.isConcatSpreadable] = false;
['cc','dd'].concat(b, 'ee');
// ['cc', 'dd',[ 'aa', 'bb'], 'ee']
7.3 Symbol.species
Указывает на конструктор, который будет использоваться при создании производных объектов и должен использоваться при использованииget
Оценщик.
class P extends Array {
static get [Symbol.species](){
return this;
}
}
Решите следующие проблемы:
// 问题: b应该是 Array 的实例,实际上是 P 的实例
class P extends Array{}
let a = new P(1,2,3);
let b = a.map(x => x);
b instanceof Array; // true
b instanceof P; // true
// 解决: 通过使用 Symbol.species
class P extends Array {
static get [Symbol.species]() { return Array; }
}
let a = new P();
let b = a.map(x => x);
b instanceof P; // false
b instanceof Array; // true
7.4 Symbol.match
при исполненииstr.match(myObject)
, который вызывается при наличии переданного свойства и возвращает возвращаемое значение метода.
class P {
[Symbol.match](string){
return 'hello world'.indexOf(string);
}
}
'h'.match(new P()); // 0
7.5 Symbol.replace
когда объектString.prototype.replace
При вызове метода возвращается возвращаемое значение метода.
let a = {};
a[Symbol.replace] = (...s) => console.log(s);
'Hello'.replace(a , 'World') // ["Hello", "World"]
7.6 Symbol.hasInstance
когда объектString.prototype.search
При вызове метода возвращается возвращаемое значение метода.
class P {
constructor(val) {
this.val = val;
}
[Symbol.search](s){
return s.indexOf(this.val);
}
}
'hileo'.search(new P('leo')); // 2
7.7 Symbol.split
когда объектString.prototype.split
При вызове метода возвращается возвращаемое значение метода.
// 重新定义了字符串对象的split方法的行为
class P {
constructor(val) {
this.val = val;
}
[Symbol.split](s) {
let i = s.indexOf(this.val);
if(i == -1) return s;
return [
s.substr(0, i),
s.substr(i + this.val.length)
]
}
}
'helloworld'.split(new P('hello')); // ["hello", ""]
'helloworld'.split(new P('world')); // ["", "world"]
'helloworld'.split(new P('leo')); // "helloworld"
7.8 Symbol.iterator
возражать против выполненияfor...of
При цикле он будет вызыватьсяSymbol.iterator
метод, который возвращает итератор по умолчанию для этого объекта.
class P {
*[Symbol.interator]() {
let i = 0;
while(this[i] !== undefined ) {
yield this[i];
++i;
}
}
}
let a = new P();
a[0] = 1;
a[1] = 2;
for (let k of a){
console.log(k);
}
7.9.Symbol.toPrimitive
Когда объект преобразуется в значение типа-примитива, этот метод вызывается для возврата значения типа-примитива, соответствующего объекту. При вызове необходимо получить строковый параметр, в котором указан текущий режим работы.Режимы работы:
- Число: в настоящее время необходимо преобразовать в числовое значение.
- String : на этом этапе его необходимо преобразовать в строку.
- По умолчанию: в настоящее время может быть преобразовано в числовое значение или строку.
let obj = {
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return 123;
case 'string':
return 'str';
case 'default':
return 'default';
default:
throw new Error();
}
}
};
2 * obj // 246
3 + obj // '3default'
obj == 'default' // true
String(obj) // 'str'
7.10 Symbol.toStringTag
вызвать этот объектObject.prototype.toString
метод, если это свойство существует, его возвращаемое значение появится вtoString
Строка, возвращаемая методом, представляет тип объекта. То есть это свойство можно использовать для настройки[object Object
]или[object Array]
серединаobject
следующая строка.
// 例一
({[Symbol.toStringTag]: 'Foo'}.toString())
// "[object Foo]"
// 例二
class Collection {
get [Symbol.toStringTag]() {
return 'xxx';
}
}
let x = new Collection();
Object.prototype.toString.call(x) // "[object xxx]"
7.11 Symbol.unscopables
Этот объект указывает, какие свойства исключаются из среды with при использовании ключевого слова with.
// 没有 unscopables 时
class MyClass {
foo() { return 1; }
}
var foo = function () { return 2; };
with (MyClass.prototype) {
foo(); // 1
}
// 有 unscopables 时
class MyClass {
foo() { return 1; }
get [Symbol.unscopables]() {
return { foo: true };
}
}
var foo = function () { return 2; };
with (MyClass.prototype) {
foo(); // 2
}
Приведенный выше код определяетсяSymbol.unscopables
свойства такие, чтоwith
Синтаксический блок не будет искаться в текущей областиfoo
свойства, то естьfoo
Укажет на переменную во внешней области.
Восемь, преобразование исходной стоимости
Я рассмотрел преобразование строк, чисел, логических значений и т. д., но не упомянул о правилах преобразования объектов Давайте посмотрим на эту часть: . Следует помнить несколько правил:
- Все объекты в логическом контексте
true
, и нет преобразования в логическую операцию, только строковые и числовые преобразования. - Числовые преобразования происходят при вычитании объектов или при применении математических функций. как
Date
Объекты могут быть вычтены, например,date1 - date2
Результатом является разница между двумя временами. - При преобразовании строк это обычно происходит, например.
alert(obj)
эта форма.
Конечно, мы можем использовать специальные методы объекта для точной настройки строковых и числовых преобразований. Три типа (подсказка) преобразования описаны ниже:
1. object to string
Преобразование объекта в строку, когда мы выполняем операции над объектами, которые ожидают строку, например «оповещение»:
// 输出
alert(obj);
// 将对象作为属性键
anotherObj[obj] = 123;
2. object to number
Преобразование объекта в число, например, когда мы занимаемся математикой:
// 显式转换
let num = Number(obj);
// 数学运算(除了二进制加法)
let n = +obj; // 一元加法
let delta = date1 - date2;
// 小于/大于的比较
let greater = user1 > user2;
3. object to default
В редких случаяхКогда оператор «не уверен» в ожидаемом типе значения.
Например, бинарное сложение+
Может использоваться для строк (конкатенация) или чисел (сложение), поэтому оба типа подходят. Поэтому, когда бинарное сложение получает аргумент типа объекта, оно будет основано на"default"
преобразовать его.
Также, если объект используется со строками, числами или символами==
Сравнение, не очень понятно, какое преобразование следует выполнять в этот момент, поэтому используйте"default"
.
// 二元加法使用默认 hint
let total = obj1 + obj2;
// obj == number 使用默认 hint
if (user == 1) { ... };
4. Алгоритм преобразования типов
Чтобы выполнить преобразование, JavaScript пытается найти и вызвать три метода объекта:
- перечислить
obj[Symbol.toPrimitive](hint)
- с символьным ключомSymbol.toPrimitive
(системный символ) метод, если этот метод существует, - В противном случае, если подсказка
"string"
-- пытатьсяobj.toString()
иobj.valueOf()
, в зависимости от того, что существует. - В противном случае, если подсказка
"number"
или"default"
-- пытатьсяobj.valueOf()
иobj.toString()
, в зависимости от того, что существует.
5. Symbol.toPrimitive
Подробное введение можно прочитать"MDN | Символ.toPrimitive".Symbol.toPrimitive
— это встроенное значение Symbol, которое существует как свойство объекта с функциональным значением, которое вызывается, когда объект преобразуется в соответствующее примитивное значение.
Простой пример введения:
let user = {
name: "Leo",
money: 9999,
[Symbol.toPrimitive](hint) {
console.log(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.money;
}
};
alert(user); // 控制台:hint: string 弹框:{name: "John"}
alert(+user); // 控制台:hint: number 弹框:9999
alert(user + 1); // 控制台:hint: default 弹框:10000
6. toString/valueOf
toString
/ valueOf
Это два относительно ранних метода реализации преобразований. когда нетSymbol.toPrimitive
, то JavaScript попытается найти их в следующем порядке:
- Для подсказки "строка",
toString -> valueOf
. - Другие случаи,
valueOf -> toString
.
Оба метода должны возвращать примитивное значение. еслиtoString
илиvalueOf
Если возвращается объект, возвращаемое значение будет проигнорировано. По умолчанию обычные объекты имеютtoString
иvalueOf
метод:
-
toString
метод возвращает строку"[object Object]"
. -
valueOf
Метод возвращает сам объект.
Простой пример введения:
const user = {name: "Leo"};
alert(user); // [object Object]
alert(user.valueOf() === user); // true
Мы также можем комбинироватьtoString
/ valueOf
Реализовать то, что было описано в пункте 5 вышеuser
Объект:
let user = {
name: "Leo",
money: 9999,
// 对于 hint="string"
toString() {
return `{name: "${this.name}"}`;
},
// 对于 hint="number" 或 "default"
valueOf() {
return this.money;
}
};
alert(user); // 控制台:hint: string 弹框:{name: "John"}
alert(+user); // 控制台:hint: number 弹框:9999
alert(user + 1); // 控制台:hint: default 弹框:10000
Суммировать
В качестве второй части «Контрольного списка для самопроверки интерфейса JavaScript для начинающих и среднего уровня» эта статья в основном знакомит с объектами JavaScript, в том числе точками знаний, от которых у меня сияют глаза, напримерSymbol.toPrimitive
метод. Я также надеюсь, что этот список поможет вам проверить свои навыки работы с JavaScript и выяснить, чего не хватает.