暗記メーカー
ログイン
js5 関数の高度な機能 オブジェクトプロパティの設定 プロトタイプ, 継承プロトタイプ, 継承、クラス
  • 谷峻輔

  • 問題数 96 • 3/14/2024

    記憶度

    完璧

    14

    覚えた

    35

    うろ覚え

    0

    苦手

    0

    未解答

    0

    アカウント登録して、解答結果を保存しよう

    問題一覧

  • 1

    for..in ループはプロトタイプから継承したプロパティは対象となるか?

    対象になる let animal = { eats: true }; let rabbit = { jumps: true, __proto__: animal }; // Object.keys は実親のキーだけを返します alert(Object.keys(rabbit)); // jumps // for..in は実親と継承したキー両方をループします for(let prop in rabbit) alert(prop); // jumps, eats

  • 2

    this.__proto__.method で親の method が取得できるという算段で、 以下のコードを構築した。 これが無限ループになってしまう理由を説明せよ let animal = { name: "Animal", eat() { alert(this.name + " eats."); } }; let rabbit = { __proto__: animal, eat() { // ...bounce around rabbit-style 親 (animal) メソッドを呼び出す this.__proto__.eat.call(this); // (*) } }; let longEar = { __proto__: rabbit, eat() { // ...do something with long ears 親 (rabbit) メソッドを呼び出す this.__proto__.eat.call(this); // (**) } }; longEar.eat(); // Error:

    this.__proto__.eat.call(this); // (**) は、rabbit.eat.call(longEar)となる。 上記によって、以下が呼び出される eat() { // ...bounce around rabbit-style 親 (animal) メソッドを呼び出す this.__proto__.eat.call(this); // (*) } this.__proto__.eat.call(this); // (*) では、rabbit.eat.call(longEar)となる。 よって無限ループとなってしまう

  • 3

    なぜ継承したコンストラクタは親のコンストラクタを呼び出さなければならないのか? 内部的な説明をせよ

    javascript内部で、親のコンストラクタを呼び出すことを期待されているから。 またその実装は 内部プロパティによって制御されており、 newキーワードなどの振る舞いに影響を与えている。 子のコンストラクタ関数は特別な内部プロパティ [[ConstructorKind]]:"derived"   という、 特別な内部のラベルがつく このラベルがつくとnewの振る舞いが変更される。 通常の場合: 関数が new で実行される際、空のオブジェクトを作成し、 this に割り当てます。 派生コンストラクタが実行される場合: 親のコンストラクタを呼び出すことを期待します。 そのため、superは必須であり、なければ this =オブジェクトが作成されないままエラーとなる。

  • 4

    プロトタイプへのアクセス方法で __proto__は現在はやや非推奨とされている。 プロトタイプにかかわる組み込みメソッドを2つしめせ。

    Object.getPrototypeOf(obj) – obj の [[Prototype]] を返します。 Object.setPrototypeOf(obj, proto) – obj の [[Prototype]] に proto をセットします。 let animal = { eats: true }; // animal をプロトタイプとして新しいオブジェクトを作成する let rabbit = Object.create(animal); alert(rabbit.eats); // true alert(Object.getPrototypeOf(rabbit) === animal); // rabbit のプロトタイプを取得 Object.setPrototypeOf(rabbit, {}); // rabbit のプロトタイプを {} に変更

  • 5

    クラスフィールドで定義したメソッドを インスタンスメソッドというが、 アロー関数を利用した場合は アロー関数内のthisはどこを指すか? class Button { constructor(value) { this.value = value; } click = () => { alert(this.value); } } let button = new Button("hello"); setTimeout(button.click, 1000); // hello またコンストラクタ関数で登録したインスタンスメソッドのアロー関数内のthisはどこを指すか

    アロー関数はthisをもたず外側のthisを参照する。 では、上記問題の時の外側とはなにか? 普通に考えれば、外側はボタンクラスなので、ボタンクラスのthisってなんやねん。 となってしまう。 ただこれを頭の中で オブジェクトリテラルに置き換えてみてほしい。 全てのインスタンスはオブジェクトリテラルのシンタックスシュガーと考えてもよさそうだ。 class Button { constructor(value) { this.value = value; } click = () => { alert(this.value); } } これをnewキーワードでオブジェクトを作成すると下記のようなオブジェクトが生成される。 {  value:value  click:() => { alert(this.value);   } } つまり、アロー関数でのthisとは newキーワードで生成したオブジェクトを指すこととなる。 ※クラスのもちものではない! さらに、アロー関数はコーディング上の外部のレキシカルではなく、 アロー関数が評価され、関数オブジェクトとして生成されたタイミングでの外部のレキシカル環境のthisを指すともいえる。 外部レキシカルからとってきているので、 thisのクロージャみたいな感じの認識だと思う。 →バインドされていると考えることができる。 なので、その関数をどこに渡そうが thisは失われてたないのである!! これは、コンストラクタ関数で登録したインスタンスメソッドのアロー関数内のthisも同様のことがいえる。

  • 6

    クラスフィールドについて以下のような例は動作するか? class User { name = prompt("Name, please?", "John"); } let user = new User(); alert(user.name); // John

    複雑な式も動作する。 動作するタイミングはインスタンズ時となる。

  • 7

    名前付きクラス式とはなにか?

    クラス内部で、該当クラスを表現する際に利用する 名前付き関数と同様、クラスも名前を持つことができます。 クラス式に名前がある場合、そのクラス内部でのみ見えます: // "名前付きクラス式" // (スペックにはこのような用語はありませんが、名前付き関数式と同じです) let User = class MyClass { sayHi() { alert(MyClass); // MyClass の名前はクラスの内部でのみ見えます } }; new User().sayHi(); // 動作します, MyClass の定義を表示 alert(MyClass); // error, MyClass の名前はクラスの外からは見えません

  • 8

    親クラスで定義したメソッドを子クラスで別途同名で定義することをなんていうか

    メソッドのオーバーライド

  • 9

    Object.keys, Object.values などのほとんどの他のキー/バリュー取得メソッドについて、プロトタイプ継承したプロパティを対象となるか?、

    ならない。 Object.keys, Object.values などのほとんどの他のキー/バリュー取得メソッドは、継承したプロパティを無視します。 これらはオブジェクト自身の操作のみ行います。プロトタイプからのプロパティは考慮され ません。

  • 10

    コンストラクタをオーバーライドする際の考慮事項をあげよ

    オーバーライドしたコンストラクタは、 thisを利用する前に親側のコンストラクタを呼び出さなければならない。 つまり、1番最初に親側をつくれってこと なので以下はエラーとなる class Animal { constructor(name) { this.speed = 0; this.name = name; } // ... } class Rabbit extends Animal { constructor(name, earLength) { this.speed = 0; this.name = name; this.earLength = earLength; } // ... } // 動作しません! let rabbit = new Rabbit("White Rabbit", 10); // Error: this は定義されていません ただしくは以下の通り class Rabbit extends Animal { constructor(name, earLength) { super(name); this.earLength = earLength; } // ... } // 今は問題ありません let rabbit = new Rabbit("White Rabbit", 10); alert(rabbit.name); // White Rabbit alert(rabbit.earLength); // 10

  • 11

    親側のコンストラクタを呼び出すには?

    super(...) は親のコンストラクタを呼び出します(コンストラクタの内側でのみ)。

  • 12

    プロトタイプからゲッターやセッター呼び出しを行った例である コメント部分の値はどうなるか? let user = { name: "John", surname: "Smith", set fullName(value) { [this.name, this.surname] = value.split(" "); }, get fullName() { return `${this.name} ${this.surname}`; } }; let admin = { __proto__: user, isAdmin: true }; alert(admin.fullName); // John Smith (*) // setter がトリガします! admin.fullName = "Alice Cooper"; // (**) alert(admin.fullName); // なにか? alert(user.fullName); // なにか?

    admin.fullName = "Alice Cooper"; によって、プロトタイプからセッターメソッドが動く。 セッターメソッドの内容は、 set fullName(value) { [this.name, this.surname] = value.split(" "); }, なので、adminオブジェクトのプロパティにname surnameが追加される! よって、 alert(admin.fullName); でもゲッター関数が呼ばれ、 get fullName() { return `${this.name} ${this.surname}`; } }; Alice Cooper"が出力される alert(user.fullName); はJohn Smithが出力される

  • 13

    bindの利用用途として、thisの固定化だけでなく、他に何があるか?

    this だけでなく、引数もバインドすることが可能です。これはめったにされませんが、便利な場合があります。 bind の完全な構文は次の通りです: let bound = func.bind(context, [arg1], [arg2], ...); context を this とし、関数の開始引数をバインドすることができます。 function mul(a, b) { return a * b; } let double = mul.bind(null, 2); alert( double(3) ); // = mul(2, 3) = 6 alert( double(4) ); // = mul(2, 4) = 8 alert( double(5) ); // = mul(2, 5) = 10 カリー化!! これは 部分関数アプリケーション と呼ばれ、既存のパラメータのいくつかを固定にすることで新しい関数を作成します

  • 14

    クラスフィールドをオーバライドする際の注意事項をあげよ また対策をあげよ

    子供のインスタンス生成時であっても、 親側の処理を先におこなうため、 親側のコンストラクタ処理では、 子側のフィールドはまだ評価されておらず、 当然オーバーライドもできていない。 子供側のフィールドを 子クラスをインスタンスする際の 実行順は以下の通り 子のコンストラクタで親コンストラクタsuper()を実行 →親のクラスフィールド評価→親のコンストラクタ評価→子のクラスフィールド評価→ 子のコンストラクタの続きを実行 これはjavascriptのみの言語仕様! クラスフィールドのオーバライドは極力おこなわず、プロトタイプのもちものであるセッターやゲッターを利用すれば、 子側でアクセサメソッドを定義していても親コンストラクタ処理時にアクセサメソッドは優先的に処理される。   

  • 15

    プロトタイプを指定してオブジェクト作成するにはどうするか?

    Object.create(proto[, descriptors]) – 与えられた proto を [[Prototype]] として、また任意のプロパティディスクリプタで空のオブジェクトを作ります。 let animal = { eats: true }; let rabbit = Object.create(animal, { jumps: { value: true } }); alert(rabbit.jumps); // true

  • 16

    プロパティ [[Prototype]]を明示的にセットする関数を利用しない方法は?

    __proto__ を使う方法です: let animal = { eats: true }; let rabbit = { jumps: true }; rabbit.__proto__ = animal; // (*) // 今、rabbit で両方のプロパティを見つけることができます: alert( rabbit.eats ); // true (**) alert( rabbit.jumps ); // true __proto__はそもそもプロトタイプのもちもののアクセサメソッドなのでセットされた場合は、プロトタイプのsetterが呼ばれ、[[prototype]]内部プロパティを変更してくれるっぽい?

  • 17

    bindで部分関数化したいが、thisは固定化したくない場合はどうするか? だとえばオブジェクトメソッドで、部分関数として使いたいが どのコンテキストから実行するか不定なので、thisは固定化したくないという場合。

    bindは第一引数のcontextは必須なので、 使えない! クロージャを利用することによって、部分関数化を行う。 function partial(func, ...argsBound) { return function(...args) { // (*) return func.call(this, ...argsBound, ...args); } } // Usage: let user = { firstName: "John", say(time, phrase) { alert(`[${time}] ${this.firstName}: ${phrase}!`); } }; // 固定時間で部分メソッドを追加 user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes()); user.sayNow("Hello"); // Something like: // [10:00] John: Hello!

  • 18

    extends で任意の式は指定できるか?

    任意の式は指定できる! ただ返却値はクラスとかコンストラクタ関数である必要がある。 例えば、親クラスを生成する関数呼び出します: function f(phrase) { return class { sayHi() { alert(phrase) } } } ↑関数でクラスをかえすことで、クロージャみたいなことができる!! class User extends f("Hello") {} new User().sayHi(); // Hello ここでは、 class User は f("Hello") の結果を継承しています。

  • 19

    非常にシンプルなオブジェクトに対して、 __proto__プロパティを設定するとどうなるか?

    ただのキーとして扱われる! なぜならプロトタイプが存在しないということは、アクセサメソッドである__proto__も存在しないため セッターがないから、キーとして作れられる

  • 20

    Object.createを利用した、 オブジェクトのクローンの方法を記載せよ またその特徴も説明せよ

    コピー対象のプロトタイプとディスクリプタを取得しオブジェクト作成する。 let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); これは継承もプロパティフラグもシンボルもコピーされる

  • 21

    プロトタイプは組み込みメソッドを、 保有在しているが、オブジェクト自身は何を保有しているか?

    オブジェクト自身はデータのみ。配列アイテム、オブジェクトプロパティ、日付 インスタンスはデータのみという考え方でよさそう

  • 22

    nameプロパティをもつUserクラスを作成し、sayHiメソッド(nameをアラートする) をサポートせよ

    class User { constructor(name) { this.name = name; } sayHi() { alert(this.name); } } // 使い方はこんなかんじ let user = new User("John"); user.sayHi();

  • 23

    オブジェクトからプロパティを参照した時に見つからない場合はどこを参照して探すか?

    object からプロパティを読んだときに存在しない場合、JavaScriptは自動的にプロトタイプからそれを取得します。プログラミングではこのようなことを “プロトタイプ継承” と呼びます。 プロパティはプロトタイプチェーンから! 変数はレキシカルのチェーンから!

  • 24

    Array, Date, Function のような、他の組み込みのオブジェクトは、どこに組み込み関数を有しているか?

    それぞれのプロトタイプに組み込み関数を有している。 例えば、配列 [1, 2, 3] を作るとき、デフォルトの new Array() コンストラクタが内部で使われます。なので、配列データは新しいオブジェクトに書き込まれ、Array.prototype はそのプロトタイプとなり、メソッドを提供します。これは非常にメモリ効率が良いです。 スペックでは、すべての組み込みのプロトタイプは先頭に Object.prototype を持っています。なので、“すべてはオブジェクトを継承している” という人もいます。 let arr = [1, 2, 3]; // Array.prototype から継承している? alert( arr.__proto__ === Array.prototype ); // true // 次に Object.prototype からは継承している? alert( arr.__proto__.__proto__ === Object.prototype ); // true // そしてトップの null alert( arr.__proto__.__proto__.__proto__ ); // null

  • 25

    obj.__proto__の正体について説明せよ

    __proto__ はオブジェクトのプロパティではないです。 Object.prototype のアクセサプロパティです。: なので、もし obj.__proto__ が読み込まれたり代入された場合、該当の getter/setter がそのプロトタイプから呼ばれ、それは [[Prototype]] の取得/設定をします。 最初に言ったとおり、__proto__ は [[Prototype]] にアクセスする方法であり、[[Prototype]] 自身ではありません。

  • 26

    以下????はなにか? また理由ものべよ class Animal { name = 'animal'; constructor() { alert(this.name); // (*) } } class Rabbit extends Animal { name = 'rabbit'; } new Animal(); // ???? new Rabbit(); // ????

    両方ともanimalが出力される! 前問の説明などが全て。 以下は前問の説明を見ていない時の思考となる。 Rabbit には自身のコンストラクタはないので、Animal コンストラクタが呼ばれます。 そのとき親コンストラクタは常にオーバーライドされたものではなく、自身のフィールド値を利用します。 thisだからrabbitオブジェクトのnameが参照されそうにみえるのになぜ。。 以下のようなメソッドのオーバーライドではちゃんとrabbitオブジェクトのメソッドが動く!。 class Animal { showName() { // this.name = 'animal' の代わり alert('animal'); } constructor() { this.showName(); // alert(this.name); の代わり } } class Rabbit extends Animal { showName() { alert('rabbit'); } } new Animal(); // animal new Rabbit(); // rabbit なぜ。。。

  • 27

    非常にシンプルなオブジェクトはどのように利用できるか?

    連想配列のオブジェクトとしてシンプルに運用したい場合 ほとんどのオブジェクトに関連したメソッドは Object.keys(obj) のように Object.something(...) であることに注目してください。それらはプロトタイプにはないので、このようなオブジェクトで機能し続けます。: let chineseDictionary = Object.create(null); chineseDictionary.hello = "ni hao"; chineseDictionary.bye = "zai jian"; alert(Object.keys(chineseDictionary)); // hello,bye toStrngとかはつかえなくなるので影響はどんなもんかわからん

  • 28

    関数式と同様にクラス式も存在するか?

    する。 関数と同じように、クラスも別の式の中で定義し、渡したり、返却したり代入することができます。 これはクラス式の例です。: let User = class { sayHi() { alert("Hello"); } };

  • 29

    すべての関数のプロトタイプにメソッド defer(ms) を追加してください。それはラッパーを返し、ms ミリ秒呼び出しを遅延します。 これは、どのように動作すべきか、の例です: function f(a, b) { alert( a + b ); } f.defer(1000)(1, 2); // 1秒後に 3 が表示される 引数をオリジナルの関数に渡す必要があることに注意してください。

    任意引数のラッパー関数を返す。 ラッパーかんすうでは、ms秒待機したあと、実行コンテキスト(f)の関数を実行したい。 また実行方法に着目すると f.defer(1000)(1, 2); // shows 3 after 1 sec f.defer(1000)の部分まではメソッド実行で その帰ってきた関数に対し(1, 2)で、関数実行を行っていることに注目する。 そのため、返却する関数内ではthisの扱いがundefinedになることを認識する。 よって以下のように定義できる。 Function.prototype.defer = function(ms) { //返却する関数では実行コンテキストの関数をthisで参照できないので、外部変数にとっておく。 let f = this; return function(...args) { // 外部変数にとっておいた関数を実行する。f(…args)でも問題なく動くが、メソッド実行にも対応するためapplyメソッドを利用する setTimeout(() => f.apply(this, args), ms); } }; // check it function f(a, b) { alert( a + b ); } f.defer(1000)(1, 2); // shows 3 after 1 sec f.defer(1000)で帰ってきた関数にたいして (1, 2)で実行している。 そのとき関数実行となっておりthisは失われている。 今回はthisは失われててもよい。 ただの関数を実行したいだけなので。 オブジェクトメソッドとして実行する場合は、f.defer(1000);を実行した後、戻り値の関数をオブジェクトのプロパティに登録し、そこから呼ぶとオブジェクトメソッドとしてちゃんと動く設計になっている。

  • 30

    クラスはgetter setterをサポートしている。 このようなアクセサメソッドは内部的にどこで管理されるか? また、以下例について、コンストラクタでの設定はアクセサメソッドを通すか? class User { constructor(name) { //?? this.name = name; } get name() { return this._name; } set name(value) { if (value.length < 4) { alert("Name too short."); return; } this._name = value; } } let user = new User("John"); alert(user.name); // John user = new User(""); // Name too short.

    アクセサメソッドは User.prototype に getter / setter を作成することで機能している。 そのため また、コンストラクタでの設定に関しても インスタンスにnameプロパティは存在しないので、プロトタイプからセッターが探され、 セッターが起動する this.name = name;の処理はセッターをキックするだけなので注意。 結果的にはインスタンスにはnameプロパティではなく _nameプロパティが設定されることになる!

  • 31

    自分が定義したオブジェクトに組み込み関数を取り入れるにはどうするか? 例えば、配列ライクなオブジェクトを作成している場合、そこにいくつかの Array メソッドをそこにコピーしたい場合があります。 2パターンあげよ

    1 プロトタイプのメソッドをオブジェクトのプロパティへ代入する。 let obj = { 0: "Hello", 1: "world!", length: 2, }; obj.join = Array.prototype.join; alert( obj.join(',') ); // Hello,world! 組み込みの join メソッドの内部アルゴリズムは、正しいインデックスと length プロパティのみを考慮するため、これは機能します。オブジェクトがたしかに配列かどうかはチェックしません。多くの組み込みメソッドはこのようになっています。 2 オブジェクトのプロトタイプをArray.prototypeに設定する。 ですが、obj が既に別のオブジェクトを継承していた場合にはこれは不可能です。一度に1つのオブジェクトしか継承できません。

  • 32

    オブジェクトがもつ特殊な隠しプロパティで継承にかかわるやつをあげよ

    オブジェクトは、[Prototype]] を持っており、それは null または別のオブジェクトを参照します。 そのオブジェクトは “プロトタイプ” と呼ばれます。

  • 33

    アロー関数はコンストラクタ(newを利用)として利用できるか?

    できない。 this を持たないことによる制限らしい。

  • 34

    アクセサメソッドの定義について、 オブジェクトリテラル コンストラクタ関数で作成するオブジェクト クラスで作成するオブジェクト での方法をこたえよ

    リテラル const a= { firstName: firstName, lastName: lastName, get fullName() { return this.firstName + " " + this.lastName; }, }; コンストラクタ関数 Object.definePropertyを使うしかない! // コンストラクタ関数 function Person3(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; Object.defineProperty( this, "fullName", { get: function () { return this.firstName + " " + this.lastName; } }); } クラス いつものかんじ

  • 35

    前問について状況をまとめるとどういうことがおこっているか?

    孫→子→親のといった継承時に、 thisは実行コンテキストを示すので、this.__proto__の参照先は孫も子も変わらない。 孫側で実行すると子を示し 子側で実行すると子のままで親は示さないといった状況になってしまう。 これが無限ループを生んでしまう。 よって、正確に親のプロトタイプを参照できないという問題点がある。 つまり、このままではsuperの実装がうまくできない!!

  • 36

    組み込みのsuperの実装を行うため、どのような仕組みとなったか?

    JavaScriptはもう1つ関数のための特別な内部プロパティを追加しています: [[HomeObject]] です。 [[HomeObject]] プロパティは 関数がクラスメソッドまたはオブジェクトメソッドとして指定されたときに設定され、そのオブジェクトそのものになります。 let longEar = { __proto__: rabbit, name: "Long Ear", eat() { // [[HomeObject]] == longEar super.eat(); } }; そして、super はこれを使って親のプロトタイプとメソッドメソッドを解決しました。 longEar.eat のようなメソッドは、その [[HomeObject]] を知っており、そのプロトタイプから親のメソッドを取得します。this を使用することなく。 結構前の問題であったとおり // Unexpected super setTimeout(function() { super.stop() }, 1000); でsuperが参照できないのは、 ホームオブジェクトが失われるからだと思う

  • 37

    Object.prototypeとはなにか?

    オブジェクトのプロトタイプ。 ほぼ全てのオブジェクトのプロトタイプの元祖。 toStringなどのオブジェクトの組み込みメソッドはこのプロトタイプが有している。 ちなみにObject.__proto__は空のメソッドで非推奨らしい。 インスタンスされたobj.__proto__と同一。 let obj = {}; alert(obj.__proto__ === Object.prototype); // true alert(obj.toString === obj.__proto__.toString); //true alert(obj.toString === Object.prototype.toString); //true

  • 38

    アロー関数とbindの違いは?

    bindは関数のバインドしたバージョンを作成する アロー関数はバインドを作成せず、ただthisをもたないだけ

  • 39

    obj.hasOwnProperty(key)はどのオブジェクトから継承されてきたか? またなぜ列挙されないか?

    チェーンをみることで、メソッドは Object.prototype.hasOwnProperty で提供されていることがわかります。つまり、継承されたものです。 …ですが、なぜ hasOwnProperty は継承したプロパティもリストする for..in ループで eats や jumps のように登場しないのでしょう。 答えはシンプルです。これは列挙不可だからです。Object.prototype の他のすべてのプロパティと同様に、enumerable:false フラグを持ちます。そして、for..in は列挙可能なプロパティのみをリストします。そのため、Object.prototype のプロパティは列挙されません。

  • 40

    オブジェクトのプロパティに関する情報で、 valueの他に、 プロパティフラグと呼ばれているものを3つあげよ

    writable – true の場合、変更可能です。それ以外の場合は読み取り専用です。 enumerable – true の場合、ループで列挙されます。それ以外の場合は列挙されません。 configurable – true の場合、プロパティを削除したり属性の変更ができます。

  • 41

    プロトタイプチェーンに関する制限事項を二つあげよ

    参照を循環させることはできません。JavaScriptは、循環するように __proto__ を割り当てようとするとエラーになります。 __proto__ の値はオブジェクトまたは null になります。プリミティブのような、それ以外のすべての値は無視されます。 明白かもしれませんが、1つの [[Prototype]] しか存在しません。 オブジェクトは2つの他のものから継承することはできません。

  • 42

    アクセサプロパティのディスクリプタは、 オブジェクトプロパティのディスクリプタと比べてどう異なるか?

    アクセサプロパティには、value も writable もありませんが、代わりに、get と set があります。 したがって、アクセサディスクリプタには次のものがあります: get – 引数なしの関数で、プロパティが読まれたときに動作します。 set – 1つの引数をもつ関数で、プロパティがセットされたときに呼ばれます。 enumerable – データプロパティと同じです。 configurable – データプロパティと同じです。 let user = { name: "John", surname: "Smith" }; Object.defineProperty(user, 'fullName', { get() { return `${this.name} ${this.surname}`; }, set(value) { [this.name, this.surname] = value.split(" "); } }); alert(user.fullName); // John Smith for(let key in user) alert(key); // name, surname ↑enumerableがfalseで作られたので列挙されない!

  • 43

    クラスフィールドとはなにか?

    “クラスフィールド” は任意のプロパティが追加できる構文です。 変数ではないことに留意!! また書き方もオブジェクトリテラルとは違い key:value的な書き方ではないことに注意! 例えば、class User に name プロパティを追加しましょう。 class User { name = "John"; sayHi() { alert(`Hello, ${this.name}!`); } } new User().sayHi(); // Hello, John! つまり、宣言の中で、" = " と記述するだけです。

  • 44

    クラス構文の全容を示せ

    class MyClass { prop = value; // プロパティ constructor(...) { // コンストラクタ // ... } method(...) {} // メソッド get something(...) {} // getter set something(...) {} // setter [Symbol.iterator]() {} // 計算された名前のメソッド (ここではシンボル) // ... }

  • 45

    私たちは2匹のハムスターを持っています: speedy と lazy は一般的な hamster オブジェクトを継承しています。 そのうちの1匹に餌をやるとき、もう1匹もお腹一杯になります。なぜでしょう?どのように修正しますか? let hamster = { stomach: [], eat(food) { this.stomach.push(food); } }; let speedy = { __proto__: hamster }; let lazy = { __proto__: hamster }; // 一方が食べ物を見つけました speedy.eat("apple"); alert( speedy.stomach ); // apple // もう一方も持っています。なぜでしょう?修正してください。 alert( lazy.stomach ); // apple

    実行コンテキストはspeedyそのものだが、プロパティを所持していないので、プロトタイプチェーンにより、親プロトタイプのstomachを参照し、更新してしまう。 つまり、ハムスター両者は同じ胃袋を共有している。 これを避けるためには、 ハムスター両方にstomachプロパティを各自作成してやるか、 eatメソッドの中で、以下のように代入すれば、各オブジェクトのプロパティが作られるので、避けることができる。 // this.stomach.push の代わりに this.stomach に代入する this.stomach = [food];

  • 46

    [[HomeObject]] が設定される条件は? また、考慮事項をあげよ

    クラスと単純なオブジェクト両方で定義されたメソッドに対して定義されています。 しかし、オブジェクトの場合、メソッドは指定された方法で正確に指定されなければなりません。: "method: function()" ではなく method() として指定する必要があります。 クラスのときクラスフィールドとして定義したインスタンスメソッドについては多分ホームオブジェクトの設定はされなさそー 我々にとってこの違いは本質的ではないかもしれませんが、JavaScript にとっては重要です。 下の例では、上の例との比較のために非メソッド構文を使っています。[[HomeObject]] プロパティはセットされず、継承は動作しません。: let animal = { eat: function() { // 短縮構文: eat() {...} にする必要があります // ... } }; let rabbit = { __proto__: animal, //このかきかただと、[[homeObject]]が設定されない!! eat: function() { super.eat(); } }; rabbit.eat(); // super 呼び出しエラー([[HomeObject]] が無いため)

  • 47

    クラスとはなにか?

    クラスは、クラス内で定義したコンストラクタを含め、 コンストラクタ関数そのものとなる。 また、 クラス.prototypeのもちものとなり、コンストラクタとして登録される。 ↑の動作は通常のコンストラクタ関数であっても、コンストラクタ関数名.prototypeにコンストラクタとして登録されている。 クラスはつまり関数オブジェクトである。 クラスで定義したメソッドは そのクラス.prototypeへのメソッド設定を行う!

  • 48

    オブジェクトのプロパティをログで出すのに最適なのは?

    for inなら プロトタイプチェーンも辿ってプロパティを出してくれる。 ただし列挙不可や、シンボルプロパティとかはとれない。 クラスで定義したクラスメソッド(プロトタイプメソッド)は列挙不可に設定されるので、微妙か。。? Object.getOwnPropertyDescriptors(obj)はシンボルや列挙不可も含めてプロパティのディスクリプタを返してくれる。 プロトタイプチェーンは辿らない。 Reflect.ownKeys(obj)てのもある この返値は、Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)) と同じです。 このどっちかかなぁ。。 組み合わせて使うか? https://qiita.com/Quest_love33/items/ef16f7e6bfcd4ac3f4b3

  • 49

    プロトタイプの操作で要考慮事項とされている操作はなにか?

    速度が重要な場合は、既存のオブジェクトの [[Prototype]] を変更しないでください 通常はオブジェクト作成時に一度だけ設定を行い、変更はしません。rabbit は animal から継承しており、それは変更しません。 また、JavaScriptエンジンは高度に最適化されています。Object.setPrototypeOf または obj.__proto__= で “その場で” プロトタイプを変更することは、可能ですがとても遅い操作になります。 そのため、何をしようとしているかわかっている場合、または JavaScript の速度が全く問題にならない場合以外は避けてください。

  • 50

    function sayHi() { alert( this.name ); } sayHi.test = 5; let bound = sayHi.bind({ name: "John" }); alert( bound.test ); // 何が出力されるでしょう? それはなぜでしょう?

    解答: undefined. bind の結果は別のオブジェクトです。それは test プロパティを持っていません。 バインドして返される関数はラップされるのでカスタムプロパティはアクセスできなくなる。

  • 51

    非常にシンプルなオブジェクトであれば問題ないが、 非常にシンプルでないオブジェクトの脆弱性をひとつあげよ

    ユーザがキーとして "__proto__" を入力する可能性があり、その場合エラーが発生し、通常は予期せぬ結果になるでしょう。 以下例のように、キー情報に__proto__が指定されてしまい、プリミティブ値を設定したとしても、その処理はシステムにより無視されてしまうのでバグを生みやすいらしい let obj = {}; let key = prompt("What's the key?", "__proto__"); obj[key] = "some value"; alert(obj[key]); // [object Object], "some value" ではありません! ここでは、ユーザが __proto__ を入力した場合、その代入は無視されます! それは驚くことではありません。__proto__ プロパティは特別です: それはオブジェクトまたは null であり、文字列はプロトタイプにはなれません。 しかし、このような振る舞いを実装するつもりはありませんでした。私たちはキー/値ペアを格納したいですが、キー名が "__proto__" の場合は正しく保存されませんでした。なので、これはバグです。 ようわからんがプロトタイプのアクセサメソッドが動いた結果、プリミティブは弾かれるかんじか?

  • 52

    オブジェクトプロパティ  writeableがfalseの時の説明をせよ

    書き込みするとエラーになる。 let user = { name: "John" }; Object.defineProperty(user, "name", { writable: false }); user.name = "Pete"; // Error: Cannot assign to read only property 'name'...

  • 53

    プロトタイプ継承したプロパティはfor...inの対象となり、それを処理対象外としたい場合はどうするか?

    組み込みのメソッド obj.hasOwnProperty(key)が利用できます。これは obj が key という名前のプロパティをもつ場合(継承したものは含みません)、true を返します。 従って、継承したプロパティをフィルタする(あるいはそれらについて何かをする)ことが可能です。: let animal = { eats: true }; let rabbit = { jumps: true, __proto__: animal }; for(let prop in rabbit) { let isOwn = rabbit.hasOwnProperty(prop); if (isOwn) { alert(`Our: ${prop}`); // Our: jumps } else { alert(`Inherited: ${prop}`); // Inherited: eats } } ちなみにin演算子は継承したやつも含める

  • 54

    ゲッターやセッターの使い道はなに?、

    Getter/setter は、“実際の” プロパティ値のラッパーとして使用することで、それらをより詳細に制御することができます。 例えば、user で短すぎる名前を禁止したい場合、name を特別なプロパティ _name に格納することができます。そして、setter で値をフィルタします。: let user = { get name() { return this._name; }, set name(value) { if (value.length < 4) { alert("Name is too short, need at least 4 characters"); return; } this._name = value; } }; user.name = "Pete"; alert(user.name); // Pete user.name = ""; // Name is too short...

  • 55

    extendsしたとき、メソッドやコンストラクタ以外に何がオーバライドできるか?

    クラスフィールドもオーバライドできる。 ちなみに親側のプロパティとか子側のプロパティとかの概念はない。 インスタンス生成したら、親側も子側も 一緒くたにされる。 なのでクラスフィールドのオーバーライドはただ上塗りしたるだけ

  • 56

    オブジェクトプロパティのプロパティフラグを更新するには?

    Object.defineProperty でフラグの変更ができます。 構文: Object.defineProperty(obj, propertyName, descriptor) obj, propertyName 処理するオブジェクトとプロパティです。 descriptor 適用するプロパティディスクリプタです。 プロパティが存在する場合、defineProperty はそのフラグを更新します。存在しない場合は指定された値とフラグでプロパティを作ります。 その場合に、もしフラグが指定されていなければ false とみなされます。 例えば、ここではプロパティ name はすべて false のフラグで作られます。: let user = {}; Object.defineProperty(user, "name", { value: "John" }); let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); alert( JSON.stringify(descriptor, null, 2 ) ); /* { "value": "John", "writable": false, "enumerable": false, "configurable": false } */ “通常の方法で” 作成された user.name と上記を比較してください。今すべてのフラグは false です。このようにしたくなければ、descriptor で true をセットするのがよいでしょう。

  • 57

    ネイティブプロトタイプにプロパティを追加するとどうなる? また、利用は推奨されているか?

    ネイティブプロトタイプは変更することができます。例えば、もしあるメソッドを String.prototype に追加した場合、それはすべての文字列で利用可能になります。: String.prototype.show = function() { alert(this); }; "BOOM!".show(); // BOOM! ただ推奨されていない。 要約すると、ネイティブプロトタイプの変更が恒常化されると、複数のライブラリ間でコンフリクトが起きやすいからとのこと。

  • 58

    非常にシンプルなオブジェクトとはなにか?

    プロトタイプがnullで作成されたオブジェクトのこと。 let obj = Object.create(null); alert(obj); // Error (no toString) もちろん組み込み関数は使えない。

  • 59

    自身で作ったコンストラクタ関数でインスタンスした複数のオブジェクトについて、 同じメソッドをサポートさせるにはどうするか?

    コンストラクタ関数のprototypeプロパティを参照し、prototypeに対しプロパティを追加する。 function Rabbit(name) { this.name = name; } Rabbit.prototype.sayHi = function() { alert(this.name); }; let rabbit = new Rabbit("Rabbit"); インスタンスのプロトタイプ、 つまりRabbit.prototype にはインスタンスメソッドを追加できる。

  • 60

    下記の例のようにプロパティをディスクリプタで定義する理由は? function User(name, birthday) { this.name = name; this.birthday = birthday; // age は現在の日付と誕生日から計算されます Object.defineProperty(this, "age", { get() { let todayYear = new Date().getFullYear(); return todayYear - this.birthday.getFullYear(); } }); } let john = new User("John", new Date(1992, 6, 1)); alert( john.birthday ); // birthday は利用可能です alert( john.age ); // ...age も同様です

    プロパティフラグがfalseで生成されるため、裏プロパティ見たいな意味あいになる。

  • 61

    オブジェクトプロパティ  configurableがfalseの時の説明をせよ

    変更できないプロパティは削除したり変更することができません 例えば、Math.PI は書き込み不可で、列挙不可であり、変更不可です: let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI'); alert( JSON.stringify(descriptor, null, 2 ) ); /* { "value": 3.141592653589793, "writable": false, "enumerable": false, "configurable": false } */ したがって、プログラマは Math.PI の値を変えることも上書きすることもできません。 Math.PI = 3; // Error // delete Math.PI もまた動作しません Math.PI を再度 writable にすることもできません。: // Error, configurable: false なので Object.defineProperty(Math, "PI", { writable: true }); Math.PI についてはは何もできません。 変更不可なプロパティの作成は一方通行であり、それを defineProperty 戻すことはできません。 注意: configurable: false はプロパティフラグの変更や削除を禁止しますが、値を変更することは可能です

  • 62

    オブジェクトプロパティ  enumerableがfalseの時の説明をせよ

    組み込みの関数同様、for..in ループで列挙されなくなります。 例えば、以下のようにtoStringメソッドを定義した場合、for inループでtoStringも出力対象となってしまう。 列挙されたくなければ、toStringプロパティに対してenumerableをfalseで設定してやると良い let user = { name: "John", toString() { return this.name; } }; Object.defineProperty(user, "toString", { enumerable: false }); // これで toString は消えました: for (let key in user) alert(key); // name 列挙可能でないプロパティは Object.keys からも除外されます。: alert(Object.keys(user)); // name

  • 63

    以下のようなthisを失う状況において、クラスならではの解決法を示せ class Button { constructor(value) { this.value = value; } click() { alert(this.value); } } let button = new Button("hello"); setTimeout(button.click, 1000); // undefined

    現在の構文だとクラスメソッドとして扱われ、プロトタイプに登録される。 以下のように書き換えると、クラスフィールドとして扱われる! click = () => { alert(this.value); } クラスフィールドはそれぞれのオブジェクト(インスタンス)のプロパティとなることから インスタンス生成時にアロー関数が評価され関数オブジェクトが生成される。 そのタイミングで、外側のthisとは、 外部のレキシカルであることから、 つまりインスタンスそのものを指す。 →詳しくはこの問題集の最後の問題を参照 よって以下のようなクラスフィールドを定義し、渡すとthisを失わない クラスフィールドは別の、すばらしい構文を提供します: class Button { constructor(value) { this.value = value; } click = () => { alert(this.value); } } let button = new Button("hello"); setTimeout(button.click, 1000); // hello

  • 64

    アロー関数のthisやargumentsは具体的に、アロー関数外側の何を参照しているか?

    外部のレキシカル環境からthisが参照されている

  • 65

    つまり、superをもつメソッドに関してどのような考慮事項があるか?

    コピーした時に、[[homeObject]]がバインドされた状態なので、superがコピー後の実行コンテキスト通りの振舞いにならず、バインドされた親を参照してしまうので、 コピーに気をつける

  • 66

    setTimeoutなどで、関数を渡す際、 その渡す関数の処理で親側のクラスを呼び出すとき、どうなるか?、 class Rabbit extends Animal { stop() { setTimeout(function() { super.stop() }, 1000); } }

    superはクラスメソッドである関数を評価時にホームオブジェクトを関数にバインドしており、そこから辿ることで実現している。 しかしながら、上記問題の通りクラスメソッドの中の関数に関しては、ホームオブジェクトがundefinedであるので、superを辿れずエラーとなってしまう。 アロー関数ではホームオブジェクトをアロー関数外側のレキシカルから取得するので、 以下のようにすれば問題なく動く class Rabbit extends Animal { stop() { setTimeout(() => super.stop(), 1000); // 1秒後、親の stop を実行 } }

  • 67

    [[HomeObject]] の存在はJavaScriptの思想とマッチしていない。 どういうことか?

    javascriptのオブジェクトは、バインドされない、つまり束縛されない=自由であるという原則がある。 自由であるということは、オブジェクトのコピーや渡し捜査が容易であるということ。 しかし[[HomeObject]] はメソッドそのものに束縛されるため、その原則に反する [[HomeObject]] は変更できないため、このバインドは永遠です。 [[HomeObject]] が使用される言語での唯一の場所が super です。 したがって、メソッドが super を使用しない場合、メソッドは以前として自由とみなせ、オブジェクト間でコピーできます。

  • 68

    Object.getOwnPropertyDescriptorsで取得するプロパティの特記事項をあげよ

    シンボリックプロパティもとれる

  • 69

    Objectとnew Objectや{}で作成した オブジェクトの違いは?

    Objectはコンストラクタ関数そのもの。 今はクラスともいう。 new Objectや{}で作成したオブジェクトはObjectクラスのインスタンス。 Objectコンストラクタ関数から作成されている。 であってるかな。。?

  • 70

    フラグを意識したオブジェクトのコピーを作成するにはどうするか?

    Object.getOwnPropertyDescriptors(obj) と Object.defineProperties と合わせて、“フラグを意識して” オブジェクトをクローンする方法として使うことができます。: let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj)); 通常、オブジェクトをクローンするとき、次のようにプロパティをコピーするために代入を使います。: for (let key in user) { clone[key] = user[key] } …ですが、これはフラグはコピーしません

  • 71

    以下の処理について実際にやってることを説明せよ class User { constructor(name) { this.name = name; } sayHi() { alert(this.name); } }

    クラス宣言の結果となる User と言う名前の関数を作成します。 関数の中身は constructor メソッドです(メソッドがない場合は空と想定します)。 // 証拠: User は function です alert(typeof User); // function User.prototype に、sayHi などのクラスメソッドを格納します。

  • 72

    コンストラクタのオーバーライドしない場合の考慮事項をあげよ

    クラスが別のクラスを拡張し、constructor を持たない場合、次のような constructor 暗黙的に生成されます。 class Rabbit extends Animal { // 独自のコンストラクタを持たないクラスを拡張するために生成されます constructor(...args) { super(...args); } }

  • 73

    前問で謎だった クラスフィールドのオーバライド時に、親コンストラクタで親のクラスフィールドを参照してしまう理由をのべよ

    親コンストラクタを処理しているタイミングで子クラスのクラスフィールドが初期化されてないため。 子クラスのクラスフィールドは次のタイミングで初期化されます: 子クラスのコンストラクタでsuper()を読んだ直後 ちなみに親クラスのクラスフィールドはコンストラクタの前に初期化される。

  • 74

    オブジェクトのプロパティの定義の仕方についてまとめよ 1 オブジェクトリテラルの中 2 コンストラクタ関数で作成するインスタンス 3 クラスから作成されるインスタンス

    1 オブジェクトリテラルの中は key:value, ←カンマ区切り みたいな構文のみ受け付けている。 トップレベルでは変数宣言など、処理はかけない。 関数宣言はできず、関数式のみ、value部分に定義できる。 bio() {...}みたいな構文は bio:function() {…} の略である。 例 const person = { name: ["Bob", "Smith"], age: 32, bio() { console.log(`${this.name[0]} ${this.name[1]} is ${this.age} years old.`); }, introduceSelf() { console.log(`Hi! I'm ${this.name[0]}.`); }, }; 2 コンストラクタ関数で作成するインスタンス これは関数内なので処理がかける。 つまり、関数宣言も可能→関数内関数 ただ、コンストラクタ関数の中で関数宣言しても結局は、this.key = 関数名としてプロパティ登録することの利用用途しかなさそう。 コンストラクタ関数はthisオブジェクトを作りそれに対し設定してthisを返す。 なのでthis.key = value;でインスタンスのプロパティを作成する。 関数をインスタンスのプロパティとして登録する場合は、 this.key = 関数式って感じ。 function User(name) { // this = {}; (暗黙) // this へプロパティを追加 this.name = name; this.isAdmin = false; // return this; (暗黙) } 3 クラスから作成されるインスタンス クラスフィールドで、プロパティ作成する prop = value; また、コンストラクタ内でも同様に this.key = value;で可能。 クラスフィールド→コンストラクタの順で動くので、 クラスフィールドでは初期値などの初期化処理 また、初期化を行わないでも、明示的な宣言としても使える。 コンストラクタでは、初期化処理はかかず、引数をうけとった時の設定作業を行う。 コンストラクタ含むクラスメソッドに関しては プロトタイプのもちものとなる。 またクラスフィールドとしてもメソッドを登録できるので、それはインスタンスの持ち物となる。 class Person { name; //明示的な宣言として使える constructor(name) { this.name = name; } // クラスフィールドに定義したメソッド = インスタンスメソッド greeting = () => { console.log(`${this.name}さん、こんにちは`) } // クラスに定義したメソッド = プロトタイプメソッド callName() { console.log(this.name); } }

  • 75

    クラスで宣言時に、動的な名前でメソッド名を作成するには?

    これは括弧 [...] を使用した計算されたメソッド名の例です。 class User { ['say' + 'Hi']() { alert("Hello"); } } new User().sayHi(); このような特徴は、リテラルオブジェクトに似ているので、覚えやすいと思います。