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

  • 問題数 96 • 3/14/2024

    記憶度

    完璧

    14

    覚えた

    35

    うろ覚え

    0

    苦手

    0

    未解答

    0

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

    問題一覧

  • 1

    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 カリー化!! これは 部分関数アプリケーション と呼ばれ、既存のパラメータのいくつかを固定にすることで新しい関数を作成します

  • 2

    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!

  • 3

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

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

  • 4

    アロー関数のthisについて説明せよ

    アロー関数内部ではthisを持たない。 アロー関数外部と同様のthisの振る舞いとなる。 例えば、それを使ってオブジェクトメソッドの内側を反復することができます。: let group = { title: "Our Group", students: ["John", "Pete", "Alice"], showList() { this.students.forEach( student => alert(this.title + ': ' + student) ); } }; group.showList(); argumentsもおなじ

  • 5

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

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

  • 6

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

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

  • 7

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

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

  • 8

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

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

  • 9

    オブジェクトプロパティの情報を取得するには?

    メソッド Object.getOwnPropertyDescriptor で、プロパティの 完全な 情報を参照することができます。 構文は次の通りです: let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); obj 情報を取得するオブジェクトです。 propertyName プロパティ名です。 返却値はいわゆる “プロパティディスクリプタ” オブジェクトと呼ばれます。: それは値とすべてのフラグを含んでいます。 例: let user = { name: "John" }; let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); alert( JSON.stringify(descriptor, null, 2 ) ); /* プロパティディスクリプタ: { "value": "John", "writable": true, "enumerable": true, "configurable": true } */

  • 10

    通常の方法で作成されたオブジェクトプロパティのプロパティフラグはどうなってるか?

    すべてtrueで作成される。

  • 11

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

    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 をセットするのがよいでしょう。

  • 12

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

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

  • 13

    オブジェクトプロパティ  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

  • 14

    オブジェクトプロパティ  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 はプロパティフラグの変更や削除を禁止しますが、値を変更することは可能です

  • 15

    オブジェクトプロパティを一度に多くの定義ができるメソッドはなにか?

    一度に多くのプロパティが定義できるメソッド Object.defineProperties(obj, descriptors)もあります。 構文は次の通りです: Object.defineProperties(obj, { prop1: descriptor1, prop2: descriptor2 // ... }); 例えば: Object.defineProperties(user, { name: { value: "John", writable: false }, surname: { value: "Smith", writable: false }, // ... }); なので、一度に多くのプロパティをセットできます。

  • 16

    一度に全てのオブジェクトプロパティのディスクリプタを取得するメソッドは?

    一度にすべてのプロパティのディスクリプタを取得するには、Object.getOwnPropertyDescriptors(obj) が使用できます。

  • 17

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

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

  • 18

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

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

  • 19

    let user = { name: "John", surname: "Smith", }; に対してfullname (name + surname)を出力or入力するアクセサメソッドを定義せよ

    let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }, set fullName(value) { [this.name, this.surname] = value.split(" "); } }; // set fullName は指定された値で実行されます user.fullName = "Alice Cooper"; alert(user.name); // Alice alert(user.surname); // Cooper 結果、“仮想” プロパティ fullName を持っており、これは読み書き可能です。

  • 20

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

    アクセサプロパティには、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で作られたので列挙されない!

  • 21

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

    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...

  • 22

    下記の例のようにプロパティをディスクリプタで定義する理由は? 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で生成されるため、裏プロパティ見たいな意味あいになる。

  • 23

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

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

  • 24

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

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

  • 25

    プロパティ [[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]]内部プロパティを変更してくれるっぽい?

  • 26

    プロトタイプチェーンとはなにか?、

    プロトタイプの値がnullになるまで続くチェーン構造のこと

  • 27

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

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

  • 28

    __proto__ は [[Prototype]] プロパティにおけるどういう役割をもっているか?

    オブジェクトは [[Prototype]] プロパティを持っていて、それがプロトタイプ、つまり継承元となる。 __proto__プロパティはオブジェクト自身にもってるわけではなく [[Prototype]]の参照先のプロトタイプが持っているアクセサメソッドのこと。 同一ではないことに注意

  • 29

    プロトタイプからゲッターやセッター呼び出しを行った例である コメント部分の値はどうなるか? 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が出力される

  • 30

    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

  • 31

    プロトタイプ継承したプロパティは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演算子は継承したやつも含める

  • 32

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

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

  • 33

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

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

  • 34

    私たちは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];

  • 35

    オブジェクト作成で使う構文だが、別の書き方を示せ obj = {}

    obj = new Object() Object は組み込みのオブジェクトコンストラクタ関数です。

  • 36

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

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

  • 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

    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

  • 39

    文字列、数値、ブール値などのプリミティブ型はどのように組み込み関数を利用できるようしているか?

    覚えている通り、それらはオブジェクトではありません。しかし、それらのプロパティへアクセスをしようとした場合、組み込みのコンストラクタ String, Number, Boolean を使った一時的なラッパーオブジェクトが作られます。それらはメソッドを提供し、消えます。 それらのオブジェクトは我々には見えない形で作られ、ほとんどのエンジンはそれらを最適化しますが、スペックではこのように正確に説明されています。それらのオブジェクトのメソッドもまた String.prototype, Number.prototype や Boolean.prototype として利用可能なものとしてプロトタイプに存在します。

  • 40

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

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

  • 41

    現代のプログラミングでは、ネイティブプロトタイプの変更が認められたケースが1つだけあります それはなにか?

    ポリフィル ポリフィルは JavaScript のスペックには存在するが、特定のJavaScript エンジンではまだサポートされていないメソッドの代わりを用意することを指す用語です。 手動で実装し、組み込みのプロトタイプにそれを取り込むことができます。 例: if (!String.prototype.repeat) { // もしこのようなメソッドがない場合 // prototype に追加します String.prototype.repeat = function(n) { // 文字列を n 回繰り返す // 実際、このコードはこれより複雑になります // "n" の負の値に対するエラーのスロー // 完全なアルゴリズムは仕様にあります return new Array(n + 1).join(this); }; } alert( "La".repeat(3) ); // LaLaLa

  • 42

    自分が定義したオブジェクトに組み込み関数を取り入れるにはどうするか? 例えば、配列ライクなオブジェクトを作成している場合、そこにいくつかの 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つのオブジェクトしか継承できません。

  • 43

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

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

  • 44

    すべての関数のプロトタイプにメソッド 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);を実行した後、戻り値の関数をオブジェクトのプロパティに登録し、そこから呼ぶとオブジェクトメソッドとしてちゃんと動く設計になっている。

  • 45

    プロトタイプへのアクセス方法で __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 のプロトタイプを {} に変更

  • 46

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

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

  • 47

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

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

  • 48

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

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

  • 49

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

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

  • 50

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

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

  • 51

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

    連想配列のオブジェクトとしてシンプルに運用したい場合 ほとんどのオブジェクトに関連したメソッドは 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とかはつかえなくなるので影響はどんなもんかわからん

  • 52

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

    ユーザがキーとして "__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__" の場合は正しく保存されませんでした。なので、これはバグです。 ようわからんがプロトタイプのアクセサメソッドが動いた結果、プリミティブは弾かれるかんじか?

  • 53

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

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

  • 54

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

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

  • 55

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

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

  • 56

    クラスとはなにか?

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

  • 57

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

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

  • 58

    以下、アラート内容についてこたえよ class User { constructor(name) { this.name = name; } sayHi() { alert(this.name); } } alert(typeof User); // 1 / alert(User === User.prototype.constructor); // 2 alert(User.prototype.sayHi); // 3 alert(Object.getOwnPropertyNames(User.prototype)); // 4

    1 Userクラスの型はfunction 2 true 正確には User はプロトタイプの constructor メソッド 3 sayHiメソッドのコードが表示される 4 constructor, sayHi  ユーザのプロトタイプのプロパティが表示される

  • 59

    クラスは以下のように書き換え可能だが、実際のところ クラスはシンタックスシュガー(糖衣構文) だといえるか? また理由を説明せよ // 純粋な関数で User クラスを書き換え // 1. constructor 関数を作成 function User(name) { this.name = name; } // 関数 prototype は "constructor" プロパティをデフォルトで持ちます // なので、作成は不要です // 2. prototype へメソッドを追加 User.prototype.sayHi = function() { alert(this.name); }; // 使い方: let user = new User("John"); user.sayHi();

    いえない。 理由は 1 まず、class で生成された関数は特別な内部プロパティ [[IsClassConstructor]]: true でラベル付けされています このプロパティをチェックすることによって、以下の組み込み関数の動作が変更される。 クラスはnewなしで呼び出されないように制御されている。 class User { constructor() {} } alert(typeof User); // function User(); // Error: クラスのコンストラクタ User は `new` なしで呼び出せません 文字列変換するとclass…と出力されるようになる。 alert(User); // class User { ... } 2 クラス メソッドは列挙不可です クラス定義は、"prototype" にあるすべてのメソッドに対して enumerable フラグを false にセットします。 オブジェクトを for...in するとき、通常はクラスメソッドは必要ないのでこれは便利です。 3 クラスは常に use strict です クラス構造の中のコードはすべて自動で strict モードです。 などの機能が用意されているため!

  • 60

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

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

  • 61

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

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

  • 62

    クラス自体を返却することができるか?

    できる。 マジで関数と同じ扱いだとおもっておく。 function makeClass(phrase) { // クラス定義とその返却 return class { sayHi() { alert(phrase); } }; } // 新しいクラスを作成 let User = makeClass("Hello"); new User().sayHi(); // Hello

  • 63

    クラスは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プロパティが設定されることになる!

  • 64

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

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

  • 65

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

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

  • 66

    クラスフィールドは内部的にどこで管理されている?

    メソッドはプロトタイプのほうで管理されるが、 クラスフィールドはプロトタイプでは管理されず、個々のオブジェクト(インスタンス)にセットされる。

  • 67

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

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

  • 68

    以下のような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

  • 69

    クラス構文の全容を示せ

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