Управляемое чтение
Переменные и типы изучаютсяJavaScript
Первое, с чем вы соприкасаетесь, но часто самые простые вещи часто скрывают много знаний, которые вы не понимаете или склонны делать ошибки, например, следующие вопросы:
-
JavaScript
Какова конкретная форма хранения переменных в памяти? -
0.1+0.2
почему не равно0.3
?Какова конкретная причина ошибки десятичного вычисления? -
Symbol
характеристики и каковы реальные сценарии применения? -
[] == ![]
,[undefined] == false
почему равноtrue
• Когда в коде происходят неявные преобразования типов? Каковы правила конвертации? - Как точно определить тип переменной?
Если вы не можете хорошо ответить на вышеперечисленные вопросы, значит, вы не до конца усвоили эту часть знаний, то, пожалуйста, внимательно прочитайте следующие статьи.
В этой статье подробно рассказывается от основного принципа до практического применения.JavaScript
Знание переменных и типов в .
1. Типы данных JavaScript
Стандарт ECMAScriptпредусмотренный7
тип данных, который обрабатывает это7
Существует два типа типов данных: примитивные типы и объектные типы.
примитивный тип
-
Null
: содержит только одно значение:null
-
Undefined
: содержит только одно значение:undefined
-
Boolean
: содержит два значения:true
иfalse
-
Number
: целое число или число с плавающей запятой, а также некоторые специальные значения (-Infinity
,+Infinity
,NaN
) -
String
: последовательность символов, представляющая текстовое значение -
Symbol
: Экземпляр — это уникальный и неизменяемый тип данных.
(существуетes10
Добавлен седьмой примитивный тип.BigInt
, сейчас обновленоChrome
служба поддержки)
тип объекта
-
Object
: Не так уж сложно отнести себя к одной категории, за исключением часто используемых.Object
,Array
,Function
специальные объекты
Во-вторых, зачем различать примитивные типы и типы объектов?
2.1 Неизменность
Примитивные типы, упомянутые выше, вECMAScript
стандарт, они определяются какprimitive values
, то есть исходное значение, а это значит, что само значение изменить нельзя.
Возьмем строку в качестве примера, когда мы вызываем метод, который манипулирует строкой, нет метода, который может напрямую изменить строку:
var str = 'ConardLi';
str.slice(1);
str.substr(1);
str.trim(1);
str.toLowerCase(1);
str[0] = 1;
console.log(str); // ConardLi
В приведенном выше коде мы имеемstr
Вызываются несколько методов без исключения, все эти методы генерируют новую строку на основе исходной строки, а не напрямую изменяют ее.str
, что подтверждает неизменность строк.
Итак, когда мы переходим к вызову следующего кода:
str += '6'
console.log(str); // ConardLi6
ты найдешь,str
Значение строки было изменено, не нарушает ли это неизменность строки? На самом деле это не так, мы это по памяти понимаем:
существуетJavaScript
, каждой переменной требуется место в памяти для ее хранения.
Пространство памяти делится на два типа: память стека и память кучи.
стек памяти:
- Сохраняемые значения имеют фиксированный размер
- меньше места
- Сохраненными переменными можно управлять напрямую, а эффективность работы высока.
- Место для хранения выделяется системой автоматически
JavaScript
Значение примитивного типа in хранится непосредственно в стеке, и стек выделяет для него место в памяти при определении переменной.
Поскольку размер пространства памяти в стеке фиксирован, переменные, хранящиеся в стеке, должны быть неизменяемыми.
В приведенном выше коде мы выполняемstr += '6'
'ConardLi6'
, то переменнаяstr
указывает на это пространство, так что это не нарушает不可变性的
Функции.
2.2 Ссылочные типы
куча памяти:
- Сохраненное значение является неопределенным и может быть динамически изменено
- Большое пространство, низкая эффективность работы
- С его внутренней памятью нельзя напрямую манипулировать, читать по ссылочному адресу.
- Выделение места через код
По сравнению с приведенными выше неизменяемыми примитивными типами, я привык называть объекты ссылочными типами.Значение ссылочного типа фактически хранится в куче памяти.В стеке хранится только адрес фиксированной длины, который указывает на адрес в память кучи.значение.
var obj1 = {name:"ConardLi"}
var obj2 = {age:18}
var obj3 = function(){...}
var obj4 = [1,2,3,4,5,6,7,8,9]
Так как память ограничена, эти переменные не могут все время занимать ресурсы в памяти.Эта статья рекомендуется здесь.Сборка мусора и утечки памяти в JavaScript, здесь, чтобы сказать вам
JavaScript
как выполняется сборка мусора и некоторые сценарии, в которых могут возникнуть утечки памяти.
Конечно, ссылочные типы больше не имеют不可变性
, мы можем легко изменить их:
obj1.name = "ConardLi6";
obj2.age = 19;
obj4.length = 0;
console.log(obj1); //{name:"ConardLi6"}
console.log(obj2); // {age:19}
console.log(obj4); // []
Возьмите массив в качестве примера, многие из его методов могут видоизменяться.
-
pop()
Удалить последний элемент массива, если массив пуст, массив не будет изменен, вернуть undefined, изменить исходный массив и вернуть удаленный элемент -
push()
Добавьте один или несколько элементов в конец массива, измените исходный массив и верните длину нового массива. -
shift()
Удалить первый элемент массива, если массив пуст, ничего не делать, вернуть undefined, изменить исходный массив и вернуть значение первого элемента -
unshift()
Добавляет один или несколько элементов в начало массива, изменяет исходный массив и возвращает длину нового массива. -
reverse()
Обратный порядок элементов в массиве, изменение исходного массива и возврат массива -
sort()
Отсортируйте элементы массива, измените исходный массив и верните массив -
splice()
Добавить/удалить элементы из массива, изменить исходный массив, вернуть удаленный элемент
Давайте сравним разницу между примитивными типами и ссылочными типами через несколько операций:
2.3 Копировать
Когда мы копируем значение одной переменной в другую переменную, примитивные типы и ссылочные типы ведут себя по-разному.Давайте сначала рассмотрим примитивные типы:
var name = 'ConardLi';
var name2 = name;
name2 = 'code秘密花园';
console.log(name); // ConardLi;
В памяти есть переменнаяname
, значениеConardLi
. Начнем с переменнойname
скопировать переменнуюname2
, в памяти создается новый блок пространства для храненияConardLi
, хотя значения двух одинаковы, пространство памяти, на которое они указывают, совершенно различно, и две переменные участвуют в любой операции, не влияя друг на друга.
Скопируйте ссылочный тип:
var obj = {name:'ConardLi'};
var obj2 = obj;
obj2.name = 'code秘密花园';
console.log(obj.name); // code秘密花园
Когда мы копируем переменную ссылочного типа, мы фактически копируем адрес, хранящийся в стеке, поэтому скопированныйobj2
на самом деле иobj
указывает на тот же объект в куче. Итак, мы меняем значение любой из переменных, другая переменная будет затронута, поэтому есть глубокие и мелкие копии.
2.4 Сравнение
Когда мы сравниваем две переменные, разные типы переменных ведут себя по-разному:
var name = 'ConardLi';
var name2 = 'ConardLi';
console.log(name === name2); // true
var obj = {name:'ConardLi'};
var obj2 = {name:'ConardLi'};
console.log(obj === obj2); // false
Для примитивных типов сравнивать их значения напрямую, и если значения равны, возвращатьtrue
.
Для ссылочных типов при сравнении сравниваются их ссылочные адреса.Хотя объекты, хранящиеся в куче двух переменных, имеют одинаковые значения атрибутов, они хранятся в разных областях памяти, поэтому значение сравненияfalse
.
2.5 Передача по значению и передача по ссылке
С помощью следующего примера давайте посмотрим на какое значение пройдено, что проходит посредством ссылки:
let name = 'ConardLi';
function changeValue(name){
name = 'code秘密花园';
}
changeValue(name);
console.log(name);
Выполните приведенный выше код, если окончательный выводname
да'ConardLi'
, изменений нет, что указывает на то, что параметр функции передает значение переменной, то есть значение передается. Если окончательный отпечаток'code秘密花园'
, операция внутри функции может изменять входящую переменную, то это означает, что параметр функции передается по ссылке, то есть передается по ссылке.
Очевидно, что результатом вышеописанного выполнения является'ConardLi'
, то есть параметр функции — это только локальная переменная, копируемая входящей переменной, и изменение этой локальной переменной не повлияет на внешнюю переменную.
let obj = {name:'ConardLi'};
function changeValue(obj){
obj.name = 'code秘密花园';
}
changeValue(obj);
console.log(obj.name); // code秘密花园
Приведенный выше код может заставить вас задуматься, является ли параметр ссылочным типом или передается по ссылке?
Прежде всего, давайте проясним,ECMAScript
Все аргументы функции передаются по значению.
Аналогично, когда параметр функции является ссылочным типом, мы также копируем копию параметра в локальную переменную, но скопированная копия указывает только на адрес в куче памяти.Мы оперируем свойствами объекта внутри функции, на самом деле то же значение, что и внешняя переменная, указывает на память в куче, но это не значит, что ссылка передана, возьмем другой пример:
let obj = {};
function changeValue(obj){
obj.name = 'ConardLi';
obj = {name:'code秘密花园'};
}
changeValue(obj);
console.log(obj.name); // ConardLi
Видно, что параметры функции не являются переданными переменными.引用
, а копия копии переменной, которая является самим значением, если переменная является примитивным типом, и адресом в куче памяти, если переменная является ссылочным типом. Итак, вспомните еще раз:
ECMAScript
Все аргументы функции передаются по значению.
3. Неразличимый null и undefined
В примитивных типах есть два типаNull
иUndefined
, все они имеют одно и только одно значение,null
иundefined
, и оба они ничего и ничего не представляют, я их вообще различаю так:
null
Представляет назначенный объект, преднамеренно назначая объект какnull
, что намеренно пустое и не должно иметь ценности.
Таким образом, свойство объектаnull
нормально,null
При преобразовании в число значение равно0
.
undefined
означает "отсутствующее значение", т.е. здесь должно быть значение, но оно не определено,
Если значение свойства объектаundefined
, что ненормально, напримерobj.name=undefined
, мы не должны это писать, мы должны прямоdelete obj.name
.
undefined
При преобразовании в числовое значениеNaN
(специальное значение для нечислового значения)
JavaScript
Это язык с динамической типизацией.В дополнение к нулевому значению, представляющему существование члена, он может вообще не существовать (поскольку существование или нет известно только во время выполнения), чтоundefined
значение. заJAVA
Этот строго типизированный язык, если есть"undefined"
В этом случае компиляция напрямую не выполняется, поэтому в ней нет необходимости в таком типе.
Четыре, менее знакомые типы символов
Symbol
типES6
Добавлен новый примитивный тип.
Каждое значение символа, возвращаемое функцией Symbol(), уникально. Значение символа может быть использовано в качестве идентификатора свойства объекта, это единственная цель этого типа данных.
Давайте взглянемSymbol
Какими свойствами обладает тип.
4.1 Особенности символа
1. Уникальный
Использовать напрямуюSymbol()
создать новоеsymbol
Переменная, опционально строка для описания. Когда параметр является объектом, объектtoString()
метод.
var sym1 = Symbol(); // Symbol()
var sym2 = Symbol('ConardLi'); // Symbol(ConardLi)
var sym3 = Symbol('ConardLi'); // Symbol(ConardLi)
var sym4 = Symbol({name:'ConardLi'}); // Symbol([object Object])
console.log(sym2 === sym3); // false
Создаем два с двумя одинаковыми строкамиSymbol
переменные, они не равны, видно, что каждаяSymbol
Переменные уникальны.
Если мы хотим создать два равныхSymbol
переменная, вы можете использоватьSymbol.for(key)
.
Ищет существующий символ с заданным ключом и возвращает символ, если он найден. В противном случае в глобальном реестре символов будет создан новый символ с заданным ключом.
var sym1 = Symbol.for('ConardLi');
var sym2 = Symbol.for('ConardLi');
console.log(sym1 === sym2); // true
2. Примитивные типы
Обратите внимание, что использованиеSymbol()
создание функцииsymbol
переменные, вместо использования конструктора используйтеnew
Оператор сообщит об ошибке напрямую.
new Symbol(); // Uncaught TypeError: Symbol is not a constructor
мы можем использоватьtypeof
оператор для определенияSymbol
тип:
typeof Symbol() === 'symbol'
typeof Symbol('ConardLi') === 'symbol'
3. Не перечислить
когда используешьSymbol
При использовании в качестве атрибута объекта можно гарантировать, что объект не будет иметь тот же атрибут имени, вызовитеfor...in
Его нельзя перечислить, и другой вызовObject.getOwnPropertyNames、Object.keys()
тоже не могу получитьSymbol
Атрибуты.
Object.getOwnPropertySymbols() можно вызывать для получения конкретных свойств Symbol.
var obj = {
name:'ConardLi',
[Symbol('name2')]:'code秘密花园'
}
Object.getOwnPropertyNames(obj); // ["name"]
Object.keys(obj); // ["name"]
for (var i in obj) {
console.log(i); // name
}
Object.getOwnPropertySymbols(obj) // [Symbol(name)]
4.2 Сценарии применения Symbol
Вот несколькоSymbol
Сценарий применения в программе.
Приложение 1: предотвращение XSS
существуетReact
изReactElement
объект, есть?typeof
собственность, этоSymbol
Переменные типа:
var REACT_ELEMENT_TYPE =
(typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
0xeac7;
ReactElement.isValidElement
Функция используется для определения допустимости компонента React Ниже приведена его конкретная реализация.
ReactElement.isValidElement = function (object) {
return typeof object === 'object' && object !== null && object.?typeof === REACT_ELEMENT_TYPE;
};
видимыйReact
будет отображаться без?typeof
Идентификация и компоненты, не прошедшие проверку правил, отфильтровываются.
Если на вашем сервере есть уязвимость, которая позволяет пользователям хранить произвольныеJSON
объект, а клиентский код ожидает строку, что может стать проблемой:
// JSON
let expectedTextButGotJSON = {
type: 'div',
props: {
dangerouslySetInnerHTML: {
__html: '/* put your exploit here */'
},
},
};
let message = { text: expectedTextButGotJSON };
<p>
{message.text}
</p>
иJSON
нельзя хранить вSymbol
переменная типа, вот что мешаетXSS
запястье.
Приложение 2: Частная собственность
с помощьюSymbol
Тип не перечислимый, мы можем моделировать приватные свойства в классе и управлять чтением и записью переменных:
const privateField = Symbol();
class myClass {
constructor(){
this[privateField] = 'ConardLi';
}
getField(){
return this[privateField];
}
setField(val){
this[privateField] = val;
}
}
Применение 3: Предотвратить загрязнение имущества
В некоторых случаях мы можем захотеть добавить свойство к объекту, в это время это может вызвать покрытие свойства, использованиеSymbol
Свойство объекта гарантирует, что свойство с таким же именем никогда не появится.
Например, в следующем сценарии мы моделируем и реализуемcall
метод:
Function.prototype.myCall = function (context) {
if (typeof this !== 'function') {
return undefined; // 用于防止 Function.prototype.myCall() 直接调用
}
context = context || window;
const fn = Symbol();
context[fn] = this;
const args = [...arguments].slice(1);
const result = context[fn](...args);
delete context[fn];
return result;
}
Нам нужно временно вызвать метод для объекта, не вызывая загрязнения свойств,Symbol
хороший выбор.
Пять, нечестный числовой тип
зачем говоритьNumber
Типа не честный.Полагаю, что каждый сталкивался с проблемой неточного десятичного исчисления в разработке, типа0.1+0.2!==0.3
, давайте вернемся к первоисточнику, чтобы понять, почему происходит это явление и как его избежать.
Ниже приведена простая функция, которую я реализовал для определения точности сложения двух десятичных знаков:
function judgeFloat(n, m) {
const binaryN = n.toString(2);
const binaryM = m.toString(2);
console.log(`${n}的二进制是 ${binaryN}`);
console.log(`${m}的二进制是 ${binaryM}`);
const MN = m + n;
const accuracyMN = (m * 100 + n * 100) / 100;
const binaryMN = MN.toString(2);
const accuracyBinaryMN = accuracyMN.toString(2);
console.log(`${n}+${m}的二进制是${binaryMN}`);
console.log(`${accuracyMN}的二进制是 ${accuracyBinaryMN}`);
console.log(`${n}+${m}的二进制再转成十进制是${to10(binaryMN)}`);
console.log(`${accuracyMN}的二进制是再转成十进制是${to10(accuracyBinaryMN)}`);
console.log(`${n}+${m}在js中计算是${(to10(binaryMN) === to10(accuracyBinaryMN)) ? '' : '不'}准确的`);
}
function to10(n) {
const pre = (n.split('.')[0] - 0).toString(2);
const arr = n.split('.')[1].split('');
let i = 0;
let result = 0;
while (i < arr.length) {
result += arr[i] * Math.pow(2, -(i + 1));
i++;
}
return result;
}
judgeFloat(0.1, 0.2);
judgeFloat(0.6, 0.7);
5.1 Потеря точности
Все данные в компьютере二进制
хранятся, поэтому при вычислениях компьютер должен сначала преобразовать данные в二进制
выполнить расчет, а затем преобразовать результат расчета в十进制
.
Нетрудно увидеть из вышеприведенного кода, что при вычислении0.1+0.2
час,二进制
При расчете произошла потеря точности, что привело к обратному преобразованию в十进制
Результаты не соответствовали ожидаемым результатам.
5.2 Анализ результатов — дополнительные вопросы
0.1
и0.2
Двоичное число 1100 — это бесконечный цикл десятичных знаков. Давайте посмотрим на результаты, вычисленные JS, чтобы помочь нам один за другим:
0,1 в двоичном формате:
0.0001100110011001100110011001100110011001100110011001101
0,2 двоичный:
0.001100110011001100110011001100110011001100110011001101
Теоретически добавление приведенных выше результатов должно::
0.0100110011001100110011001100110011001100110011001100111
Двоичный код 0,1+0,2, рассчитанный фактическим JS
0.0100110011001100110011001100110011001100110011001101
Смотрите здесь, у вас могут возникнуть дополнительные вопросы:
Почему двоичный код 0,1, рассчитанный js, имеет столько бит, а не больше? ? ?
Почему двоичный результат (0,1 + 0,2), рассчитанный js, отличается от двоичного результата (0,1 + 0,2), рассчитанного нами? ? ?
Почему 0,1 в двоичном формате + 0,2 в двоичном формате! = 0,3 в двоичном формате? ? ?
5.3 Как js хранит двоичные десятичные числа
десятичный二进制
Большинство из них представляют собой бесконечные циклы,JavaScript
Как их хранить?
существуетСпецификация языка ECMAScript®можно увидеть в,ECMAScript
серединаNumber
Тип следуетIEEE 754
стандарт. Используйте 64-битную фиксированную длину для представления.
На самом деле существует множество языков с числовыми типами, которые следуют этому стандарту, напримерJAVA
, поэтому многие языки также имеют ту же проблему, что и выше.
Так что в следующий раз, когда вы столкнетесь с такой проблемой, не подходите и не распыляйтеJavaScript
...
Если вы заинтересованы, вы можете проверить этот сайт0.30000000000000004.com/, да, вы правильно прочитали, то есть0.30000000000000004.com/! ! !
5.4 IEEE 754
IEEE754
Стандарт содержит двоичное представление набора действительных чисел. Он состоит из трех частей:
-
знаковый бит
-
бит экспоненты
-
мантисса
Цифры каждой части числа с плавающей запятой трех прецизионностей следующие:
JavaScript
использует 64-битное кодирование с плавающей запятой двойной точности, поэтому его符号位
Занимать1
биты, биты экспоненты занимают11
биты, биты мантиссы занимают52
немного.
Ниже мы разберемся, что такое符号位
,指数位
,尾数位
,от0.1
Например:
Его двоичный файл:0.0001100110011001100...
В целях экономии места для хранения он выражается в экспоненциальном представлении в компьютере, то есть
1.100110011001100...
X 2-4
Если это нелегко понять, подумайте о десятичных числах:
1100
Научная запись11
X 102
так:
符号位
является знаком положительного и отрицательного,1
выражать负
,0
выражать正
;
指数位
Сохраните экспоненту в научном представлении;
尾数位
Сохраняйте значащие цифры после экспоненциальной записи;
Таким образом, двоичный файл, который мы обычно видим, на самом деле представляет собой биты мантиссы, которые на самом деле хранит компьютер.
5.5 toString(2) в js
Поскольку биты мантиссы могут храниться только52
число, которое объясняетtoString(2)
Результат выполнения:
Если на компьютере нет ограничений по объему памяти, то0.1
из二进制
должно быть:
0.00011001100110011001100110011001100110011001100110011001...
научная запись мантисса
1.1001100110011001100110011001100110011001100110011001...
Однако из-за ограничений значительное количество53
цифры и далее не сохраняются, из этого следует, что если1
просто иди вперед1
,если0
принцип отказа.
53-й бит в научной двоичной записи для 0,1 равен 1, поэтому мы имеем следующий результат:
0.0001100110011001100110011001100110011001100110011001101
0.2
У него такая же проблема, собственно именно из-за такого хранения здесь и происходит потеря точности, в результате чего0.1+0.2!=0.3
.
На самом деле расчетов с одинаковой проблемой точности много, и мы не можем их все записать, поэтому, когда в программе есть численные расчеты, нам лучше использовать библиотеки инструментов, которые помогут нам решить их. рекомендуемые библиотеки с открытым исходным кодом:
5.6 Наибольшее число, которое может представить JavaScript
по иIEEE 754
Ограничения 64-битной спецификации двойной точности:
指数位
Самое большое число, которое можно представить:1023
(десятичный)
尾数位
Наибольшее число, которое можно выразить, — это мантисса.1
Случай
Таким образом, наибольшее число, которое может представлять JavaScript, — это бит
1.111...
X 21023Этот результат, преобразованный в десятичный вид, равен1.7976931348623157e+308
, результатNumber.MAX_VALUE
.
5.7 Максимальный номер сейфа
JavaScriptNumber.MAX_SAFE_INTEGER
Представляет наибольшее безопасное число, а результат вычисления9007199254740991
, то есть потери точности (кроме десятичных знаков) в этом диапазоне чисел не будет, это число фактически1.111...
X 252.
Мы также можем использовать некоторые библиотеки с открытым исходным кодом для работы с большими целыми числами:
Собственно, этот вопрос чиновник тоже рассматривал.bigInt
введитеes10
вырос в и сейчасChrome
уже есть, используйтеbigInt
Может работать с числами, которые превышают максимальное безопасное число.
6. Какие еще типы ссылок существуют?
существует
ECMAScript
, ссылочный тип — это структура данных, используемая для совместной организации данных и функций.
То, что мы обычно называем объектом, является экземпляром определенного ссылочного типа.
существуетECMAScript
Только в определении типовObject
Тип, на самом деле, многие из переменных ссылочного типа, которые мы обычно используем, не определеныObject
построены, но обе конечные точки их цепочек прототиповObject
, которые все являются ссылочными типами.
-
Array
множество -
Date
Дата -
RegExp
Обычный -
Function
функция
6.1 Тип упаковки
Для облегчения манипулирования примитивными значениями,ECMAScript
Также предоставляется несколько специальных ссылочных типов, которые являются типами-оболочками для примитивных типов:
Boolean
Number
String
Обратите внимание на разницу между типами-оболочками и примитивными типами:
true === new Boolean(true); // false
123 === new Number(123); // false
'ConardLi' === new String('ConardLi'); // false
console.log(typeof new String('ConardLi')); // object
console.log(typeof 'ConardLi'); // string
Основное различие между ссылочными типами и типами-обертками заключается во времени жизни объекта.Экземпляры ссылочных типов, созданные с помощью оператора new, хранятся в памяти до тех пор, пока поток выполнения не покинет текущую область видимости, а самопримитивные типы существуют только в одной строке кода. .. выполняется мгновенно, а затем немедленно уничтожается, что означает, что мы не можем добавлять свойства и методы к примитивным типам во время выполнения.
var name = 'ConardLi'
name.color = 'red';
console.log(name.color); // undefined
6.2 Упаковка и распаковка
-
Преобразование упаковки: преобразование базового типа в соответствующий тип упаковки
-
Распаковка: Преобразование ссылочного типа в примитивный тип
Поскольку примитивные типы не могут расширять свойства и методы, как нам вызывать методы, использующие примитивные типы?
Всякий раз, когда мы работаем с базовым типом, в фоновом режиме автоматически создается объект типа-оболочки, что позволяет нам вызывать некоторые методы и свойства, например следующий код:
var name = "ConardLi";
var name2 = name.substring(2);
Фактически происходят следующие процессы:
- Создавать
String
экземпляр типа оболочки - вызов по инстанции
substring
метод - уничтожить экземпляр
То есть мы используем базовый тип для вызова метода, а операции упаковки и распаковки будут выполняться автоматически.Number
иBoolean
Этот процесс также происходит при типе.
Последует преобразование ссылочного типа в примитивный, то есть процесс распаковки.ECMAScript规范
УказанныйtoPrimitive
В принципе, ссылочный тип обычно называетсяvalueOf
иtoString
метод, вы также можете напрямую переопределитьtoPeimitive
метод. Вообще принципы преобразования в разные типы значений разные, например:
- преобразование ссылочного типа в
Number
напиши сначала позвониvalueOf
, затем позвонитеtoString
- преобразование ссылочного типа в
String
напиши сначала позвониtoString
, затем позвонитеvalueOf
какvalueOf
иtoString
не существует или не возвращает примитивный тип, выбросьтеTypeError
аномальный.
const obj = {
valueOf: () => { console.log('valueOf'); return 123; },
toString: () => { console.log('toString'); return 'ConardLi'; },
};
console.log(obj - 1); // valueOf 122
console.log(`${obj}ConardLi`); // toString ConardLiConardLi
const obj2 = {
[Symbol.toPrimitive]: () => { console.log('toPrimitive'); return 123; },
};
console.log(obj2 - 1); // valueOf 122
const obj3 = {
valueOf: () => { console.log('valueOf'); return {}; },
toString: () => { console.log('toString'); return {}; },
};
console.log(obj3 - 1);
// valueOf
// toString
// TypeError
Помимо автоматической распаковки и автоматической упаковки в программе, мы также можем выполнять операции распаковки и упаковки вручную. Мы можем напрямую вызвать тип оболочкиvalueOf
илиtoString
, чтобы реализовать операцию распаковки:
var num =new Number("123");
console.log( typeof num.valueOf() ); //number
console.log( typeof num.toString() ); //string
Семь, преобразование типов
так какJavaScript
Это язык со слабой типизацией, поэтому преобразование типов происходит очень часто.
Существует два типа преобразования типов: неявное преобразование — это преобразование типа, автоматически выполняемое программой, а обязательное преобразование — это преобразование типа, которое мы выполняем вручную.
Приведение здесь больше упоминаться не будет, давайте рассмотрим несколько вызывающих головную боль сценариев, в которых могут происходить неявные преобразования типов, и способы их преобразования:
7.1 Правила преобразования типов
Если происходит неявное преобразование, то различные типы преобразуются друг в друга в соответствии со следующими правилами:
7.2 Операторы if и логические операторы
существуетif
В операторах и логических операторах, если есть только одна переменная, переменная сначала преобразуется вBoolean
значение, только следующие случаи будут преобразованы вfalse
, остальные превращаются вtrue
:
null
undefined
''
NaN
0
false
7.3 Различные арифметические операторы
Мы имеем дело с различными неNumber
Типы используют математические операторы (- * /
), Затем на-Number
преобразование типа вNumber
тип;
1 - true // 0
1 - null // 1
1 * undefined // NaN
2 * ['5'] // 10
Уведомление+
является исключением, выполнить+
оператор:
- 1. Когда одна сторона
String
тип, распознается как конкатенация строк и предпочтительно преобразует другую сторону в строковый тип. - 2. Когда одна сторона
Number
тип, другая сторона является примитивным типом, затем примитивный тип преобразуется вNumber
тип. - 3. Когда одна сторона
Number
тип, другая сторона - ссылочный тип, ссылочный тип иNumber
Конкатенация после преобразования типа в строку.
123 + '123' // 123123 (规则1)
123 + null // 123 (规则2)
123 + true // 124 (规则2)
123 + {} // 123[object Object] (规则3)
7.4 ==
использовать==
, если обе стороны одного типа, результат сравнения и===
то же самое, иначе произойдет неявное преобразование, используйте==
Происходящее преобразование можно разделить на несколько разных случаев (только две стороны считаются разными типами):
- 1.NaN
NaN
Сравнение с любым другим типом всегда возвращаетfalse
(включая и себя).
NaN == NaN // false
- 2.Boolean
Boolean
по сравнению с любым другим типом,Boolean
сначала преобразуется вNumber
тип.
true == 1 // true
true == '2' // false
true == ['1'] // true
true == ['2'] // false
Обратите внимание на момент, который может сбить с толку:
undefined、null
иBoolean
сравни, однакоundefined、null
иfalse
легко представить себе как ложные значения, но результат их сравненияfalse
, Причина в том, чтоfalse
сначала преобразуется в0
:
undefined == false // false
null == false // false
- 3. Строка и число
String
иNumber
сравните сначалаString
преобразовать вNumber
тип.
123 == '123' // true
'' == 0 // true
- 4.нулевой и неопределенный
null == undefined
Результат сравненияtrue
,Кроме,null、undefined
Сравнивается с любым другим результатом, какfalse
.
null == undefined // true
null == '' // false
null == 0 // false
null == false // false
undefined == '' // false
undefined == 0 // false
undefined == false // false
- 5. Примитивные типы и ссылочные типы
При сравнении примитивных типов и ссылочных типов тип объектаToPrimitive
Правила преобразования в примитивные типы:
'[object Object]' == {} // true
'1,2,3' == [1, 2, 3] // true
Взгляните на сравнение ниже:
[] == ![] // true
!
приоритет выше, чем==
,![]
сначала будет преобразовано вfalse
, а затем по второму пункту выше,false
преобразовать вNumber
тип0
, левый[]
преобразовать в0
, обе стороны равны.
[null] == false // true
[undefined] == false // true
по массивуToPrimitive
правило, элементы массиваnull
илиundefined
, элемент рассматривается как пустая строка, поэтому[null]、[undefined]
будет преобразован в0
.
Итак, сказав так много, рекомендуется использовать===
Чтобы проверить, равны ли два значения...
7.5 Интересный вопрос интервью
Классический вопрос на собеседовании, как сделать:a == 1 && a == 2 && a == 3
.
В соответствии с приведенным выше преобразованием распаковки и==
Неявное преобразование , мы можем легко написать ответ:
const a = {
value:[3,2,1],
valueOf: function () {return this.value.pop(); },
}
Восемь, способ определить тип данных JavaScript
8.1 typeof
Применимая сцена
typeof
Оператор может точно определить, относится ли переменная к следующим примитивным типам:
typeof 'ConardLi' // string
typeof 123 // number
typeof true // boolean
typeof Symbol() // symbol
typeof undefined // undefined
Вы также можете использовать его для определения типов функций:
typeof function(){} // function
Неприменимые сценарии
когда вы используетеtypeof
Кажется, немного устал судить об эталонном типе:
typeof [] // object
typeof {} // object
typeof new Date() // object
typeof /^\d*$/; // object
Все ссылочные типы, кроме функций, оцениваются какobject
.
Кроме тогоtypeof null === 'object'
Это также может вызвать головную боль, которая находится вJavaScript
Первое издание переданоbug
, поскольку модификация вызовет большое количество проблем с совместимостью, она не исправлена...
8.2 instanceof
instanceof
Операторы могут помочь нам определить, к какому типу объекта относится ссылочный тип:
[] instanceof Array // true
new Date() instanceof Date // true
new RegExp() instanceof RegExp // true
Давайте сначала рассмотрим несколько правил цепочки прототипов:
- 1. Все ссылочные типы имеют объектные характеристики, то есть атрибуты можно свободно расширять
- 2. Все ссылочные типы имеют
__proto__
(Неявный прототип) свойство, которое является простым объектом - 3. Все функции есть
prototype
Свойство (явный прототип), также обычный объект - 4. Все типы ссылок
__proto__
value указывает на его конструкторprototype
- 5. При попытке получить свойства объекта, если сама переменная не имеет этого свойства, она перейдет к его
__proto__
найти в
[] instanceof Array
на самом деле судитьArray.prototype
Вы в[]
в цепочке прототипов.
Итак, используйтеinstanceof
для определения типа данных это будет не очень точно, это не является первоначальным замыслом его дизайна:
[] instanceof Object // true
function(){} instanceof Object // true
Кроме того, используйтеinstanceof
также не может обнаруживать примитивные типы данных, поэтомуinstanceof
Не очень хороший выбор.
8.3 toString
Выше мы упоминали об операции распаковкиtoString
функцию, которую мы можем вызывать для выполнения преобразований из ссылочных типов.
Каждый ссылочный тип имеет
toString
метод по умолчанию,toString()
метод каждогоObject
Наследование объекта. Если этот метод не переопределен в пользовательском объекте,toString()
возвращение"[object type]"
,вtype
является типом объекта.
const obj = {};
obj.toString() // [object Object]
Обратите внимание, что упомянутое выше如果此方法在自定义对象中未被覆盖
,toString
позволит достичь желаемого эффекта.На самом деле, большинство ссылочных типов, таких какArray、Date、RegExp
Это все переписаноtoString
метод.
Мы можем напрямую позвонитьObject
Раскрыто на прототипеtoString()
метод, используяcall
изменитьthis
Точка для достижения эффекта, который мы хотим.
8.4 jquery
Посмотримjquery
Как судить о типе в исходном коде:
var class2type = {};
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
function( i, name ) {
class2type[ "[object " + name + "]" ] = name.toLowerCase();
} );
type: function( obj ) {
if ( obj == null ) {
return obj + "";
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[Object.prototype.toString.call(obj) ] || "object" :
typeof obj;
}
isFunction: function( obj ) {
return jQuery.type(obj) === "function";
}
Примитивные типы используются напрямуюtypeof
, ссылочный тип используетObject.prototype.toString.call
получить тип, сclass2type
Объект отфильтровывает избыточный код строки, такой как[object function]
получитеarray
, а затем в суждении последнего типа, напримерisFunction
можно использовать напрямуюjQuery.type(obj) === "function"
такое суждение.
Ссылаться на
- woooooooo.EC-Code-international.org/EC-Code-262/9. …
- while.Dev/articles/отвратительно…
- GitHub.com/ в настоящее время имеет бриз…
- nuggets.capable/post/684490…
- nuggets.capable/post/684490…
- «Продвинутое JS-программирование»
резюме
Я надеюсь, что вы сможете достичь следующих результатов после прочтения этой статьи:
- учиться
JavaScript
Конкретная форма хранения переменных в памяти может соответствовать реальной сцене - Понять основные причины неточных десятичных вычислений
- Понимать сценарии, в которых могут происходить неявные преобразования типов, и принципы преобразования.
- общее суждение
JavaScript
Способ и основные принципы типов данных
Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, ставьте лайк и подписывайтесь.
Если вы хотите читать больше качественных статей, вы можете подписаться на меняgithub
блог, твоя звезда✨, лайки и внимание - движущая сила моего постоянного творчества!
Рекомендую обратить внимание на мой паблик WeChat [code secret garden], каждый день выкладывать качественные статьи, будем общаться и расти вместе.
Мы являемся научно-исследовательской командой ByteDance Interactive Entertainment, включая Douyin Short Video, Douyin Volcano, TikTok, Faceu, Qingyan, Jianying и т. д. По состоянию на январь 2020 года Douyin Daily Active (DAU) превысил 400 миллионов человек и продолжает поддерживать быстрый рост. . Вы будете поддерживать разработку продуктов и связанные с ними архитектурные работы, где каждая строка кода может повлиять на сотни миллионов пользователей.
Код набора в школу 2021:DRZUM5Z
Официальный сайт доставки:job.toutiao.com/s/JR8SthH