新しいオペレーターの詳しい使い方紹介
この記事では、新しい演算子の使用法を詳しく紹介します。一定の参考価値があります。困っている友人は参考にしてください。お役に立てれば幸いです。
フロントエンドに触れたばかりの多くのフロントエンド パートナー、あるいは数年間働いているフロントエンド パートナーであっても、新しいオペレーターについてまだ漠然と理解していると思います。
たとえば、私は最近、勤続 2 年のフロントエンド パートナーと連絡を取りました。彼は、オブジェクトの作成には new が使用されていると言いました。それは当然です。多くの人がこれに答えるかもしれません。
それでは、この答えは間違っているのでしょうか、それとも正しいのでしょうか?
この問題について包括的に議論しましょう:
オブジェクトを取得する方法はたくさんありますが、最も一般的なのはオブジェクト リテラルです:
var obj = {}
ただし、文法の観点から見ると、これは代入ステートメントで、逆のリテラル値を変数 obj に代入します (これはあまり正確ではないかもしれませんが、実際にはオブジェクトのインスタンスがここで取得されます!!)
多くの場合、私たちがオブジェクトを作成したいと言うと、多くの友人が両手でキーボードをタッチし、数回クリックするだけでこのコードを入力します。
上で述べたように、この文は実際にはオブジェクトのインスタンスを取得するだけですが、このコードはオブジェクトの作成と同等と考えてよいでしょうか?読み続けてみましょう。
オブジェクトのインスタンスを取得するには、オブジェクト リテラルに相当する別のメソッドがコンストラクターです。
var obj = new Object()
このコードを入力すると、誰もがすぐに感心すると思います。私が今言ったこと obj
が単なるインスタンス オブジェクトであるという事実に異論はありません。そうなると、多くの友人はまた尋ねるでしょう。「これは単に新しいオブジェクトが登場するだけではないのですか!」 はい、これは確かに新しいオブジェクトの作成です。JavaScript ではすべてがオブジェクトとして説明され、obj はオブジェクトであり、new 演算子を通じて取得されるため、多くの友人がそれを確信しています。言った: new はオブジェクトの作成に使用されます。
多くの人がオブジェクトの作成とオブジェクトのインスタンス化を混同していることを説明するのは難しくありません!!
別の見方をしてみましょう: js 内のすべてのものはオブジェクトであるのに、なぜオブジェクトを作成する必要があるのでしょうか。 ? ?それ自体がオブジェクトですが、どうやって作成できるのでしょうか?では、これを
継承 の一種と考えてよいでしょうか? ここまで言って、多くのパートナーは唖然としたと思いますが、私たちの目的は 1 つです。new がいわゆるオブジェクトの作成ではなく継承に使用されることを明確にすることです。 !
継承されたインスタンス オブジェクトの特徴は何ですか?
コンストラクター内のプロパティへのアクセス- プロトタイプ チェーン上のプロパティへのアクセス
- 以下は古典的な継承です。このコードを使用してウォームアップします。楽しみはすぐに始まります:
function Person(name, age) { this.name = name this.age = age this.gender = '男' } Person.prototype.nation = '汉' Person.prototype.say = function() { console.log(`My name is ${this.age}`) } var person = new Person('小明', 25) console.log(person.name) console.log(person.age) console.log(person.gender) console.log(person.nation) person.say()
call
またはapply
function Parent() { this.name = ['A', 'B'] } function Child() { Parent.call(this) } var child = new Child() console.log(child.name) // ['A', 'B'] child.name.push('C') console.log(child.name) // ['A', 'B', 'C']
__proto__
次に、上記のウォームアップ コードを少し変更して、new を使用せずにインスタンスを作成しましょう。
function Person(name, age) { this.name = name this.age = age this.gender = '男' } Person.prototype.nation = '汉' Person.prototype.say = function() { console.log(`My name is ${this.age}`) } // var person = new Person('小明', 25) var person = New(Person, '小明', 25) console.log(person.name) console.log(person.age) console.log(person.gender) console.log(person.nation) person.say() function New() { var obj = {} Constructor = [].shift.call(arguments) // 获取arguments第一个参数:构造函数 // 注意:此时的arguments参数在shift()方法的截取后只剩下两个元素 obj.__proto__ = Constructor.prototype // 把构造函数的原型赋值给obj对象 Constructor.apply(obj, arguments) // 改变够着函数指针,指向obj,这是刚才上面说到的访问构造函数里面的属性和方法的方式 return obj }
上記のコードの New 関数は、new 演算子です。実装
主な手順:
空のオブジェクトを作成します- 引数の最初のパラメータを取得します
- コンストラクターのプロトタイプ チェーンを obj に割り当てます
- apply を使用して、コンストラクターの this ポイントを obj オブジェクトを指すように変更します。その後、obj はコンストラクター内のプロパティ、プロトタイプのプロパティとメソッドにアクセスできるようになります
- obj を返しますobject
- おそらく多くの友人がこれを見て、new がこれらのことを行うのだと思うかもしれませんが、~~
しかし、私たちが見落としていることが 1 つあります。それは、js の関数には戻り値があります。も例外ではありません。
コンストラクターでオブジェクトまたは基本値を返す場合、上記の New 関数はどうなりますか?
コードをもう一度見てみましょう。
function Person(name, age) { this.name = name this.age = age this.gender = '男' return { name: name, gender: '男' } } Person.prototype.nation = '汉' Person.prototype.say = function() { console.log(`My name is ${this.age}`) } var person = new Person('小明', 25) console.log(person.name) console.log(person.age) console.log(person.gender) console.log(person.nation) person.say()
がコードを実行すると、
name と gender
の 2 つのフィールドだけが次のように出力されることがわかります。期待されています、age
、nation
は未定義です。say()
はエラーを報告します。 コード コンストラクターのコードを変更します:
function Person(name, age) { this.name = name this.age = age this.gender = '男' // return { // name: name, // gender: '男' // } return 1 } // ...
コードを実行し、最終的にすべてのフィールドが期待どおりに出力されることを確認します。
要約は次のとおりです:
コンストラクターが参照型を返す場合、コンストラクター内のプロパティは使用できず、返されたオブジェクトのみが使用できます。- コンストラクタの場合 関数が基本型を返す場合は戻り値がない場合と同じであり、コンストラクタは影響を受けません。
- ここで、上でまとめた 2 つの機能を実現するために New 関数を変更する方法を考えてみましょう。続きを読む:
function Person(name, age) { // ... } function New() { var obj = {} Constructor = [].shift.call(arguments) obj.__proto__ = Constructor.prototype // Constructor.apply(obj, arguments) var result = Constructor.apply(obj, arguments) // return obj return typeof result === 'object' ? result : obj } var person = New(Person, '小明', 25) console.log(person.name) // ...
このコードを実行すると、上で要約した 2 つの点が達成されていることを確認できます。
解決策: 変数を使用してコンストラクターの戻り値を受け取り、New 関数で戻り値の型を決定し、型に応じて異なる値を返します。
ここを参照してください。別の友人は、「New は完全に実装されましたね」と言いました。 ! !答えは間違いなく「ノー」です。コードの一部を見てみましょう:
function Person(name, age) { this.name = name this.age = age this.gender = '男' // 返回引用类型 // return { // name: name, // gender: '男' // } // 返回基本类型 // return 1 // 例外 return null }
コードを再度実行すると、また何か問題が発生したことがわかります。 ! !
それでは、なぜこの問題が発生するのでしょうか?
基本型を返す場合、コンストラクターは影響を受けず、null が基本型であると要約したばかりではありませんか?
今この瞬間、私の心の中には一万頭の草と泥の馬が疾走しています。 ! !
解惑:null是基本类型没错,但是使用操作符typeof后我们不难发现:
typeof null === 'object' // true
特例:typeof null
返回为'object'
,因为特殊值null
被认为是一个空的对象引用
。
明白了这一点,那问题就好解决了:
function Person(name, age) { // ... } function New() { var obj = {} Constructor = [].shift.call(arguments) obj.__proto__ = Constructor.prototype // Constructor.apply(obj, arguments) var result = Constructor.apply(obj, arguments) // return obj // return typeof result === 'object' ? result : obj return typeof result === 'object' ? result || obj : obj } var person = New(Person, '小明', 25) console.log(person.name) // ...
解决方案:判断一下构造函数返回值result,如果result是一个引用(引用类型和null),就返回result,但如果此时result为false(null),就使用操作符||
之后的obj
好了,到现在应该又有小伙伴发问了,这下New函数是彻彻底底实现了吧!!!
答案是,离完成不远了!!
别急,在功能上,New函数基本完成了,但是在代码严谨度上,我们还需要做一点工作,继续往下看:
这里,我们在文章开篇做的铺垫要派上用场了:
var obj = {}
实际上等价于
var obj = new Object()
前面说了,以上两段代码其实只是获取了object对象的一个实例。再者,我们本来就是要实现new,但是我们在实现new的过程中却使用了new
!
这个问题把我们引入到了到底是先有鸡还是先有蛋的问题上!
这里,我们就要考虑到ECMAScript底层的API了————Object.create(null)
这句代码的意思才是真真切切地创建
了一个对象!!
function Person(name, age) { // ... } function New() { // var obj = {} // var obj = new Object() var obj = Object.create(null) Constructor = [].shift.call(arguments) obj.__proto__ = Constructor.prototype // Constructor.apply(obj, arguments) var result = Constructor.apply(obj, arguments) // return obj // return typeof result === 'object' ? result : obj return typeof result === 'object' ? result || obj : obj } var person = New(Person, '小明', 25) console.log(person.name) console.log(person.age) console.log(person.gender) // 这样改了之后,以下两句先注释掉,原因后面再讨论 // console.log(person.nation) // person.say()
好了好了,小伙伴常常舒了一口气,这样总算完成了!!
但是,这样写,新的问题又来了。
小伙伴:啥?还有完没完?
function Person(name, age) { this.name = name this.age = age this.gender = '男' } Person.prototype.nation = '汉' Person.prototype.say = function() { console.log(`My name is ${this.age}`) } function New() { // var obj = {} // var obj = new Object() var obj = Object.create(null) Constructor = [].shift.call(arguments) obj.__proto__ = Constructor.prototype // Constructor.apply(obj, arguments) var result = Constructor.apply(obj, arguments) // return obj // return typeof result === 'object' ? result : obj return typeof result === 'object' ? result || obj : obj } var person = New(Person, '小明', 25) console.log(person.name) console.log(person.age) console.log(person.gender) // 这里解开刚才的注释 console.log(person.nation) person.say()
别急,我们执行一下修改后的代码,发现原型链上的属性nation
和方法say()
报错,这又是为什么呢?
从上图我们可以清除地看到,Object.create(null)
创建的对象是没有原型链的,而后两个对象则是拥有__proto__
属性,拥有原型链,这也证明了后两个对象是通过继承得来的。
那既然通过Object.create(null)
创建的对象没有原型链(原型链断了),那我们在创建对象的时候把原型链加上不就行了,那怎么加呢?
function Person(name, age) { this.name = name this.age = age this.gender = '男' } Person.prototype.nation = '汉' Person.prototype.say = function() { console.log(`My name is ${this.age}`) } function New() { Constructor = [].shift.call(arguments) // var obj = {} // var obj = new Object() // var obj = Object.create(null) var obj = Object.create(Constructor.prototype) // obj.__proto__ = Constructor.prototype // Constructor.apply(obj, arguments) var result = Constructor.apply(obj, arguments) // return obj // return typeof result === 'object' ? result : obj return typeof result === 'object' ? result || obj : obj } var person = New(Person, '小明', 25) console.log(person.name) console.log(person.age) console.log(person.gender) console.log(person.nation) person.say()
这样创建的对象就拥有了它初始的原型链了,这个原型链是我们传进来的构造函数赋予它的。
也就是说,我们在创建新对象的时候,就为它指定了原型链了,新创建的对象继承自传进来的构造函数!
现在,我们来梳理下最终的New函数做了什么事,也就是本文讨论的结果————new操作符到底做了什么?
- 获取实参中的第一个参数(构造函数),就是调用New函数传进来的第一个参数,暂时记为
Constructor
; - 使用
Constructor
的原型链结合Object.create
来创建
一个对象,此时新对象的原型链为Constructor
函数的原型对象;(结合我们上面讨论的,要访问原型链上面的属性和方法,要使用实例对象的__proto__属性) - 改变
Constructor
函数的this指向,指向新创建的实例对象,然后call
方法再调用Constructor
函数,为新对象赋予属性和方法;(结合我们上面讨论的,要访问构造函数的属性和方法,要使用call或apply) - 返回新创建的对象,为
Constructor
函数的一个实例对象。
现在我,我们来回答文章开始时提出的问题,new是用来创建对象的吗?
现在我们可以勇敢的回答,new是用来做继承的,而创建对象的其实是Object.create(null)。
在new操作符的作用下,我们使用新创建的对象去继承了他的构造函数上的属性和方法、以及他的原型链上的属性和方法!
写在最后:
补充一点关于原型链的知识:
- JavaScript中的函数也是对象,而且对象除了使用字面量定义外,都需要通过函数来创建对象
- prototype属性可以给函数和对象添加可共享(继承)的方法、属性,而__proto__是查找某函数或对象的原型链方式
- prototype和__proto__都指向原型对象
- 任意一个函数(包括构造函数)都有一个prototype属性,指向该函数的原型对象
- 任意一个实例化的对象,都有一个__proto__属性,指向构造函数的原型对象。
以上が新しいオペレーターの詳しい使い方紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法 はじめに: 技術の継続的な発展により、音声認識技術は人工知能の分野の重要な部分になりました。 WebSocket と JavaScript をベースとしたオンライン音声認識システムは、低遅延、リアルタイム、クロスプラットフォームという特徴があり、広く使用されるソリューションとなっています。この記事では、WebSocket と JavaScript を使用してオンライン音声認識システムを実装する方法を紹介します。

WebSocketとJavaScript:リアルタイム監視システムを実現するためのキーテクノロジー はじめに: インターネット技術の急速な発展に伴い、リアルタイム監視システムは様々な分野で広く利用されています。リアルタイム監視を実現するための重要なテクノロジーの 1 つは、WebSocket と JavaScript の組み合わせです。この記事では、リアルタイム監視システムにおける WebSocket と JavaScript のアプリケーションを紹介し、コード例を示し、その実装原理を詳しく説明します。 1.WebSocketテクノロジー

JavaScript と WebSocket を使用してリアルタイム オンライン注文システムを実装する方法の紹介: インターネットの普及とテクノロジーの進歩に伴い、ますます多くのレストランがオンライン注文サービスを提供し始めています。リアルタイムのオンライン注文システムを実装するには、JavaScript と WebSocket テクノロジを使用できます。 WebSocket は、TCP プロトコルをベースとした全二重通信プロトコルで、クライアントとサーバー間のリアルタイム双方向通信を実現します。リアルタイムオンラインオーダーシステムにおいて、ユーザーが料理を選択して注文するとき

WebSocket と JavaScript を使用してオンライン予約システムを実装する方法 今日のデジタル時代では、ますます多くの企業やサービスがオンライン予約機能を提供する必要があります。効率的かつリアルタイムのオンライン予約システムを実装することが重要です。この記事では、WebSocket と JavaScript を使用してオンライン予約システムを実装する方法と、具体的なコード例を紹介します。 1. WebSocket とは何ですか? WebSocket は、単一の TCP 接続における全二重方式です。

JavaScript と WebSocket: 効率的なリアルタイム天気予報システムの構築 はじめに: 今日、天気予報の精度は日常生活と意思決定にとって非常に重要です。テクノロジーの発展に伴い、リアルタイムで気象データを取得することで、より正確で信頼性の高い天気予報を提供できるようになりました。この記事では、JavaScript と WebSocket テクノロジを使用して効率的なリアルタイム天気予報システムを構築する方法を学びます。この記事では、具体的なコード例を通じて実装プロセスを説明します。私たちは

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

使用法: JavaScript では、insertBefore() メソッドを使用して、DOM ツリーに新しいノードを挿入します。このメソッドには、挿入される新しいノードと参照ノード (つまり、新しいノードが挿入されるノード) の 2 つのパラメータが必要です。

JavaScript で HTTP ステータス コードを取得する方法の紹介: フロントエンド開発では、バックエンド インターフェイスとの対話を処理する必要があることが多く、HTTP ステータス コードはその非常に重要な部分です。 HTTP ステータス コードを理解して取得すると、インターフェイスから返されたデータをより適切に処理できるようになります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法と、具体的なコード例を紹介します。 1. HTTP ステータス コードとは何ですか? HTTP ステータス コードとは、ブラウザがサーバーへのリクエストを開始したときに、サービスが
