基本的なタイプの文字列がメソッドを直接呼び出すことができるのはなぜですか

これが突然勤勉でドキュメントを更新し続けているZhuoです。

起因

昨日コンソールでコードをデバッグしていたとき、そのようなコードの文字列を見ました。

let tool = {
    handleString (str) {
        return str.slice(0, 10)
    }
}

// 正常使用
tool.handleString('这里是Zhuoooo,点个关注不迷路~')
复制代码

string.png

このとき、突然質問がありtool.handleStringました。オブジェクトであり、演算子を使用してメソッドを呼び出すことができます。ただ、ベーシックタイプなのに、どうしてこんな風に使えるの?また、これは常にこのように使用されており、リテラルを介して属性タイプを直接定義しているため、キーワード。小さな頭は大きな疑問に満ちています:それはまたオブジェクトか?tool.strnewString

bujiandan.jpg

実際には、それもStringオブジェクト。

string2.png

この動作は、使いやすくするためでもあります。(考えてみてください。使用するたびに、最初newに呼び出してから、値を取得したときにもう一度呼び出す必要がありvalueOfます。これはtoString非常に面倒です。)次に、言語の特性が動作と一致しません。論理的に考えて、それが舞台裏でそれ自体で何かをしていることを示します。

文字列として出力されますが、.演算子。次に、Stringオブジェクトの結果を返し、最後にオブジェクトを破棄する場合があります。

バグ?いいえ、機能です!

後で、JavaScriptの高度なプログラミング言語設計(第4版)で完全な答えを見つけました。5.3プリミティブ値ラッパータイプ(113ページ)を参照してください。

元のコンテンツ:

为了方便操作原始值,ECMAScript 提供了3 种特殊的引用类型:Boolean、Number 和String。这些类型具有本章介绍的其他引用类型一样的特点,但也具有与各自原始类型对应的特殊行为。每当用到某个原始值的方法或属性时,后台都会创建一个相应原始包装类型的对象,从而暴露出操作原始值的各种方法。来看下面的例子:

let s1 = "some text";
let s2 = s1.substring(2);
复制代码

在这里,s1 是一个包含字符串的变量,它是一个原始值。第二行紧接着在s1 上调用了substring()方法,并把结果保存在s2 中。我们知道,原始值本身不是对象,因此逻辑上不应该有方法。而实际上这个例子又确实按照预期运行了。这是因为后台进行了很多处理,从而实现了上述操作。具体来说,当第二行访问s1 时,是以读模式访问的,也就是要从内存中读取变量保存的值。在以读模式访问字符串值的任何时候,后台都会执行以下3 步:

  1. 创建一个String 类型的实例;
  2. 调用实例上的特定方法;
  3. 销毁实例。

可以把这3 步想象成执行了如下3 行ECMAScript 代码:

let s1 = new String("some text");
let s2 = s1.substring(2);
s1 = null;
复制代码

这种行为可以让原始值拥有对象的行为。对布尔值和数值而言,以上3 步也会在后台发生,只不过使用的是BooleanNumber 包装类型而已。引用类型与原始值包装类型的主要区别在于对象的生命周期。在通过new 实例化引用类型后,得到的实例会在离开作用域时被销毁,而自动创建的原始值包装对象则只存在于访问它的那行代码执行期间。这意味着不能在运行时给原始值添加属性和方法。比如下面的例子:

let s1 = "some text";
s1.color = "red";
console.log(s1.color); // undefined
复制代码

ここでのコードの2行目は、文字列にプロパティをs1追加しようとしています。colorただし、コードの3行目がプロパティにアクセスすると、colorプロパティは失われます。その理由は、コードの2行目が実行されるとStringオブジェクトが一時的に作成され、コードの3行目が実行されるとオブジェクトが破棄されているためです。実際、ここでのコードの3行目は独自のStringオブジェクトを作成しますが、このオブジェクトにはcolorプロパティがありません。プリミティブ値ラッパーオブジェクトは、コンストラクターを使用して明示的に作成できますBoolean、Number 和Stringただし、これは本当に必要な場合にのみ行う必要があります。そうしないと、開発者を混乱させやすくなり、それらがプリミティブ値なのか参照値なのかが明確になりません。プリミティブラッパー型のインスタンスを呼び出すと、typeofが返さ"object"れ、すべてのプリミティブラッパーオブジェクトがブール値に変換されtrueます。さらに、Objectコンストラクターはファクトリメソッドとして機能し、渡された値のタイプに応じて、対応するプリミティブ値ラッパータイプのインスタンスを返すことができます。例えば:

let obj = new Object("some text");
console.log(obj instanceof String); // true
复制代码

に渡されるObjectのが文字列の場合、のStringインスタンスが作成されます。数値の場合、Numberのインスタンスが作成されます。ブール値はBooleanのインスタンスを取得します。** newを使用してプリミティブラップ型のコンストラクターを呼び出すことは、同じ名前のキャスト関数を呼び出すことと同じではないことに注意してください。**例えば:

let value = "25";
let number = Number(value); // 转型函数
console.log(typeof number); // "number"

let obj = new Number(value); // 构造函数
console.log(typeof obj); // "object"
复制代码

この例では、変数numberは25のプリミティブ値を保持し、変数objはのNumberインスタンスを保持します。プリミティブ値ラッパータイプのインスタンスを明示的に作成することはお勧めしませんが、プリミティブ値を操作する機能にとって重要です。各プリミティブ値ラッパータイプには、データ操作を容易にするための対応するメソッドのセットがあります。

最後に

この小さな知識は実際のコーディングにはあまり役に立たないかもしれません。著者はまた、それが面白いと思い、それを要約することを考えました。おもしろいと思ったら、いいねしてください。見てくれてありがとう〜

end.gif

おすすめ

転載: juejin.im/post/7079677967529410596