В основном, потому что спецификация гласит: :
строковое значение
примитивное значение, представляющее собой конечную упорядоченную последовательность нуля или более 16-разрядных целочисленных значений без знака
Спецификация также определяет наличие объектов String, в отличие от простых строк. (Аналогично, существуют типы примитивов number
, boolean
и symbol
, а также объекты Number и Boolean и Symbol.)
Строки примитивов следуют всем правилам других примитивов. На уровне языка к ним относятся точно так же, как примитивные числа и логические значения. Для всех намерений и целей они являются примитивными значениями. Но, как вы говорите, было бы безумием a = b
буквально сделать копию строки в b
и поместить эту копию в a
. Реализации не должны делать это, потому что значения примитивной строки неизменны (точно так же как значения примитивного числа). Вы не можете изменить любые символы в строке, вы можете только создать новую строку. Если бы строки были изменяемыми, реализация должна была бы сделать копию, когда вы сделали a = b
(но если бы они были изменяемыми, spe c была бы написана по-другому).
Обратите внимание, что примитивные строки и объекты String действительно разные вещи:
const s = "hey";
const o = new String("hey");
// Here, the string `s` refers to is temporarily
// converted to a string object so we can perform an
// object operation on it (setting a property).
s.foo = "bar";
// But that temporary object is never stored anywhere,
// `s` still just contains the primitive, so getting
// the property won't find it:
console.log(s.foo); // undefined
// `o` is a String object, which means it can have properties
o.foo = "bar";
console.log(o.foo); // "bar"
Так почему же примитивные струны? Вы должны спросить Брендана Эйха (и он достаточно отзывчив в Твиттере), но я подозреваю, что это было так, что определение операторов эквивалентности (==
, ===
, !=
и !==
) не было Это должно быть либо что-то, что может быть перегружено типом объекта для его собственных целей, либо специальным регистром для строк.
Так зачем нужны строковые объекты? Наличие объектов String (и объектов Number, и логических объектов, и объектов Symbol) вместе с правилами, гласящими, что при создании временной версии примитива создается возможность определять методы для примитивов. Когда вы делаете:
console.log("example".toUpperCase());
в условиях спецификации, создается объект String (с помощью операции GetValue ), а затем свойство toUpperCase
ищется для этого объекта и (в выше) называется. Поэтому примитивные строки получают свои toUpperCase
(и другие стандартные методы) из String.prototype
и Object.prototype
. Но созданный временный объект не доступен для кода, за исключением некоторых крайних случаев, движки ¹ и JavaScript могут избежать буквального создания объекта вне этих крайних случаев. Преимущество в том, что заключается в том, что к String.prototype
можно добавлять новые методы и использовать их для примитивных строк.
¹ "Какие крайние случаи?" Я слышал, вы спрашиваете. Наиболее распространенная из них, о которой я могу подумать, это когда вы добавили свой собственный метод к String.prototype
(или аналогичному) в свободном коде режима:
Object.defineProperty(String.prototype, "example", {
value() {
console.log(`typeof this: ${typeof this}`);
console.log(`this instance of String: ${this instanceof String}`);
},
writable: true,
configurable: true
});
"foo".example();
// typeof this: object
// this instance of String: true
Там движок JavaScript был вынужден создать объект String, поскольку this
не может быть примитивом в свободном режиме.
Strict Режим позволяет избежать создания объекта, поскольку в строгом режиме this
необязательно быть типом объекта, он может быть примитивом (в данном случае, примитивной строкой):
"use strict";
Object.defineProperty(String.prototype, "example", {
value() {
console.log(`typeof this: ${typeof this}`);
console.log(`this instance of String: ${this instanceof String}`);
},
writable: true,
configurable: true
});
"foo".example();
// typeof this: string
// this instanceof String: false