- 投稿日:2021-01-14T17:19:05+09:00
【GAS】グローバル関数内でオブジェクト生成後に関数を持たせる場合のthisの振る舞い
この記事はノンプロ研の第4期中級GAS講座のための事前課題をするなかで、新しく知ったこと、わからなかったこと、気づいたこと、調べたことなどを発表する記事です。
仕事の事情もあり、年末に入会したノンプロ研。入会間もないですがすごく居心地が良く(社会情勢により完全オンライン移行中なのにちょっと不思議な表現ですが)、とても良い学びの場になると感じています。
会の重要なモットーでもある”教えることは2度学ぶこと”を実行するためにも、事前課題から自分の学びはアウトプットしていこうかと思いました。
この記事では、オブジェクトに関してあった発見を書こうと思います。具体的には、GASでの
this
の振る舞いについてです。
this
の理解事前課題前のわたしの
this
の理解は、インスタンスメソッドの定義でインスタンスそれ自身を指す使い方のみでした。class Person { constructor(name, age) { this.name = name; this.age = age; } isAdult() { return this.age >= 18; } } const alice = new Person('Alice', 13); alice.greet(); // "Hello! My name is Alice."インスタンスでない通常の(?)オブジェクトにもメソッドを持たせることができます(事前課題で初めて知った)。
const person = { name: 'John Doe', age: 42, isAdult() { return this.age >= 18; } }; console.log(person.isAdult()); // trueメソッドの追加
この
person
にgender
属性とisMale()
メソッドを持たせるとします。isMale()
は自身のgender
がmale
であるかどうかを返すメソッドです。person.isMale
にfunction
キーワードで代入することで実現できます。function myFunction_1() { const person = { name: 'John Doe', age: 42, isAdult() { return this.age >= 18; } }; person.gender = 'male'; person.isMale = function() { return this.gender === 'male'; } console.log(person.isMale()); // true }ところで、
person
の定義部分ではES6で導入された省略記法を使っています。const person = { ... isAdult() { ... } }後からメソッドを追加する時にも、ES6っぽい書き方ができないかな?と思いました。
ES6以降導入された、アロー関数を使って定義してみます。
const person = { ... }; person.isMale = () => { return this.gender === 'male'; }わーい今っぽい。
ところが、実際にこれでperson.isMale()
をしてみるとfalse
になってしまいました。const person = { ... }; person.gender = 'male'; person.isMale = () => { return this.gender === 'male'; } console.log(person.isAdult()); // false
this.gender
がmale
になっていないようです。
this.gender
を調べてみるとundefined
だったので、this
自体もconsole.log()
してみると、this
がグローバルオブジェクトになっていることがわかりました。※answer...というのは、事前課題の解答として自分が同一プロジェクト内に書いている関数たちです。
オブジェクト定義のその中で書かないと、
this
はそのオブジェクトを指してくれないんですね。まとめ
ということで、メソッド定義の際の
this
の扱いには注意が必要、ということを改めて発見できました。書きながら調べてみたら、タカハシさんがすでに思いっきりこのテーマを扱っていたのでたはは〜という感じでしたが、自分で書いてみることで勉強になったと思います。演習もやったという気持ちになるし、また書きたいと思っています。読んでくださった方の参考になれば幸いです。
- 投稿日:2021-01-14T17:19:05+09:00
【GAS】グローバル関数内でオブジェクトにメソッドを追加する際のthisの振る舞い
この記事はノンプロ研の第4期中級GAS講座のための事前課題をするなかで、新しく知ったこと、わからなかったこと、気づいたこと、調べたことなどを発表する記事です。
仕事の事情もあり、年末に入会したノンプロ研。入会間もないですがすごく居心地が良く(社会情勢により完全オンライン移行中なのにちょっと不思議な表現ですが)、とても良い学びの場になると感じています。
会の重要なモットーでもある”教えることは2度学ぶこと”を実行するためにも、事前課題から自分の学びはアウトプットしていこうかと思いました。
この記事では、オブジェクトに関してあった発見を書こうと思います。具体的には、GASでの
this
の振る舞いについてです。クラス定義時の
this
事前課題前のわたしの
this
の理解は、インスタンスメソッドの定義でインスタンスそれ自身を指す使い方のみでした。class Person { constructor(name, age) { this.name = name; this.age = age; } isAdult() { return this.age >= 18; } } const alice = new Person('Alice', 13); alice.greet(); // "Hello! My name is Alice."オブジェクト定義時の
this
インスタンスでない通常の(?)オブジェクトにもメソッドを持たせることができます(事前課題で初めて知った)。
const person = { name: 'John Doe', age: 42, isAdult() { return this.age >= 18; } }; console.log(person.isAdult()); // trueメソッド追加時の
this
この
person
にgender
属性とisMale()
メソッドを持たせるとします。isMale()
は自身のgender
がmale
であるかどうかを返すメソッドです。person.isMale
にfunction
キーワードで代入することで実現できます。const person = { name: 'John Doe', age: 42, isAdult() { return this.age >= 18; } }; person.gender = 'male'; person.isMale = function() { return this.gender === 'male'; } console.log(person.isMale()); // trueところで、
person
の定義部分ではES6で導入された省略記法を使っています。const person = { ... isAdult() { ... } }後からメソッドを追加する時にも、ES6っぽい書き方ができないかな?と思いました。
ES6以降導入された、アロー関数を使って定義してみます。
const person = { ... }; person.isMale = () => { return this.gender === 'male'; }わーい今っぽい。
ところが、実際にこれでperson.isMale()
をしてみるとfalse
になってしまいました。const person = { ... }; person.gender = 'male'; person.isMale = () => { return this.gender === 'male'; } console.log(person.isAdult()); // false
this.gender
がmale
になっていないようです。
this.gender
を調べてみるとundefined
だったので、this
自体もconsole.log()
してみると、this
がグローバルオブジェクトになっていることがわかりました。※answer...というのは、事前課題の解答として自分が同一プロジェクト内に書いている関数たちです。
オブジェクト定義のその中で書かないと、
this
はそのオブジェクトを指してくれないんですね。まとめ
ということで、メソッド定義の際の
this
の扱いには注意が必要、ということを改めて発見できました。書きながら調べてみたら、タカハシさんがすでに思いっきりこのテーマを扱っていたのでたはは〜という感じでしたが、自分で書いてみることで勉強になったと思います。演習もやったという気持ちになるし、また書きたいと思っています。読んでくださった方の参考になれば幸いです。
- 投稿日:2021-01-14T17:19:05+09:00
【GAS】メソッドを追加する際のthisの振る舞い
この記事はノンプロ研の第4期中級GAS講座のための事前課題をするなかで、新しく知ったこと、わからなかったこと、気づいたこと、調べたことなどを発表する記事です。
仕事の事情もあり、年末に入会したノンプロ研。入会間もないですがすごく居心地が良く(社会情勢により完全オンライン移行中なのにちょっと不思議な表現ですが)、とても良い学びの場になると感じています。
会の重要なモットーでもある”教えることは2度学ぶこと”を実行するためにも、事前課題から自分の学びはアウトプットしていこうかと思いました。
この記事では、オブジェクトに関してあった発見を書こうと思います。具体的には、GASでの
this
の振る舞いについてです。クラス定義時の
this
事前課題前のわたしの
this
の理解は、インスタンスメソッドの定義でインスタンスそれ自身を指す使い方のみでした。class Person { constructor(name, age) { this.name = name; this.age = age; } isAdult() { return this.age >= 18; } } const alice = new Person('Alice', 13); alice.greet(); // "Hello! My name is Alice."オブジェクト定義時の
this
インスタンスでない通常の(?)オブジェクトにもメソッドを持たせることができます(事前課題で初めて知った)。
const person = { name: 'John Doe', age: 42, isAdult() { return this.age >= 18; } }; console.log(person.isAdult()); // trueメソッド追加時の
this
この
person
にgender
属性とisMale()
メソッドを持たせるとします。isMale()
は自身のgender
がmale
であるかどうかを返すメソッドです。person.isMale
にfunction
キーワードで代入することで実現できます。const person = { name: 'John Doe', age: 42, isAdult() { return this.age >= 18; } }; person.gender = 'male'; person.isMale = function() { return this.gender === 'male'; } console.log(person.isMale()); // trueところで、
person
の定義部分ではES6で導入された省略記法を使っています(これも初見)。const person = { ... isAdult() { ... } }後からメソッドを追加する時にも、ES6っぽい書き方ができないかな?と思いました。
ES6以降導入された、アロー関数を使って定義してみます。
const person = { ... }; person.isMale = () => { return this.gender === 'male'; }わーい今っぽい。
ところが、実際にこれでperson.isMale()
をしてみるとfalse
になってしまいました。const person = { ... }; person.gender = 'male'; person.isMale = () => { return this.gender === 'male'; } console.log(person.isAdult()); // false
this.gender
がmale
になっていないということなのかな?と思ってthis.gender
を調べてみると、this.gender
がundefined
になっていました。そこで
this
自体もconsole.log()
してみると、this
がグローバルオブジェクトになっていることがわかりました。※answer...というのは、事前課題の解答として自分が同一プロジェクト内に書いている関数たちです。
オブジェクト定義のその中で書かないと、
this
はそのオブジェクトを指してくれないんですね。まとめ
ということで、メソッド定義の際の
this
の扱いには注意が必要、ということを改めて発見できました。書きながら調べてみたら、タカハシさんがすでに思いっきりこのテーマを扱っていたのでたはは〜という感じでしたが、自分で書いてみることで勉強になったと思います。演習もやったという気持ちになるし、また書きたいと思っています。読んでくださった方の参考になれば幸いです。
- 投稿日:2021-01-14T16:44:57+09:00
Node.jsとSeleniumを使ってChoromeのコンソールログを確認する
はじめに
Seleniumを使用してみる機会があったため,備忘録としてまとめようと思い,記事を書きました。
現在主にJavascriptを使用しているため,Node.jsを使ってSeleniumを動かそうと思います。
Node.jsの環境構築に関しては,こちらの手順で行なっています。Seleniumとは
Webブラウザの操作を自動化するためのツール。
今回は,SeleniumをNode.js内で使用して,Webブラウザを操作してみたいと思います。大まかな流れ
- 必要なパッケージ・ドライバのダウンロード
- 実行ファイル作成
- 実行
以下で詳細に説明していきます。
必要なパッケージ・ドライバのダウンロード
selenium-webdriverのインストール
$ npm install --save selenium-webdriver
--save
: package.jsonのdependenciesに追加される各ブラウザのDriverのダウンロード
主要なブラウザで動作させるには,上記のパッケージに加えて以下のドライバをダウンロードし,実行ファイルと同じパスに配置する必要があります.
ブラウザ ドライバ Chrome chromedriver(.exe) Internet Explorer IEDriverServer.exe Edge MicrosoftWebDriver.msi Firefox geckodriver(.exe) Safari safaridriver 今回はChromeを使用するので,上記表中のChromeドライバのページをクリックします.
バージョン86を使用するので,対象バージョンの欄をクリックします.
(どのバージョンのドライバをダウンロードするかはこちらのページを参照)Macのドライバ(chromedriver_mac64.zip)を選択し,ダウンロードします.
zip解凍後,Nodeの実行ファイルと同じパスに配置すれば,準備は完了です.$ cp ~/Downloads/chromedriver .実行ファイル作成
今回は,Qiitaのトップページを開き,コンソールログを取得してみます。
実行ファイルの完成形
selenium_app.js// ライブラリを呼び出す const webdriver = require("selenium-webdriver"); const chrome = require("selenium-webdriver/chrome"); const { Builder } = webdriver; const { Preferences, Type, Level } = require("selenium-webdriver/lib/logging"); // await を使うため,async function 内で処理を記述する (async function () { // オプション付きでブラウザを立ち上げる const capabilities = webdriver.Capabilities.chrome(); const logPrefs = new Preferences(); logPrefs.setLevel(Type.BROWSER, Level.ALL); capabilities.setLoggingPrefs(logPrefs); const options = new chrome.Options(capabilities); const driver = await new Builder().forBrowser("chrome").setChromeOptions(options).build(); // Qiitaのトップページへ遷移 await driver.get("https://qiita.com/"); // 5秒待機 await driver.sleep(5000); // コンソールログの取得 const consoleLogs = await driver.manage().logs().get(Type.BROWSER); for (let i = 0; i < consoleLogs.length; i++) { console.log(consoleLogs[i]); } // 終了 await driver.quit(); })();以下で詳細を説明します.
実行ファイルの作成
まずは新規ファイルを作成します
$ touch selenium_app.jswebdriverを呼び出す
公式のドキュメントを参考に,実行ファイル内でchromeのwebdriverを呼び出します。
また,ログを取得するために必要なwebdriver内のライブラリも呼び出します。selenium_app.js// ライブラリを呼び出す const webdriver = require("selenium-webdriver"); const chrome = require("selenium-webdriver/chrome"); const { Builder } = webdriver; const { Preferences, Type, Level } = require("selenium-webdriver/lib/logging");ブラウザを立ち上げる
コンソールログを取得するためのオプションを指定して,ブラウザを立ち上げます。
selenium_app.js// オプション付きでブラウザを立ち上げる const capabilities = webdriver.Capabilities.chrome(); const logPrefs = new Preferences(); logPrefs.setLevel(Type.BROWSER, Level.ALL); capabilities.setLoggingPrefs(logPrefs); const options = new chrome.Options(capabilities); const driver = await new Builder().forBrowser("chrome").setChromeOptions(options).build();
- 今回はコンソールログを取得するため,ログのタイプを
Type.BROWSER
と指定していますが,デベロッパツールのNetworkの情報を取得したい場合はこの箇所をType.PERFORMANCE
に変更すれば取得することができます。ページ遷移
操作したいページのURLを指定し,そのURLへ遷移させます。
selenium_app.js// Qiitaのトップページへ遷移 await driver.get("https://qiita.com/");コンソールログの取得
selenium_app.js// コンソールログの取得 const consoleLogs = await driver.manage().logs().get(Type.BROWSER);
- こちらも
Type.BROWSER
の箇所をType.PERFORMANCE
にすることで,デベロッパーツールのNetworkの情報を含むログ情報を取得することができます。ドライバーの終了
selenium_app.js// 終了 await driver.quit();実行
実行ファイルが存在しているディレクトリに移動し,以下のコマンドを実行します。
$ node selenium_app.js以下のような結果が表示されれば成功
Entry { level: Level { name_: 'DEBUG', value_: 700 }, message: 'https://qiita.com/ - [DOM] Input elements should have autocomplete attributes (suggested: "current-password"): (More info: https://goo.gl/9p2vKq) %o', timestamp: 1610609498344, type: '' }
参考
- 投稿日:2021-01-14T14:13:35+09:00
Googleスプレッドシートで郵便番号から住所を表示する関数 その3
概要
セルに郵便番号を入力することで、住所を表示してくれる関数があったらなぁと思って書いたプログラムが動かなくなっていたので、zipcloudさんのAPIを使って書き直してみたプログラムの出来がイマイチだった。
というわけで、今回は更にzipcloudさんのAPIを使わずに自前でAPIを作成して書き直しました。API側のサーバは無料レンタルサーバのXFREEをお借りし、郵便番号データは郵便局本家で配布している郵便番号データを成形して利用しています。
API側のプログラムの中身は内緒。
サーバは完全に無料で利用させてもらっていますので性能までは求められませんが、XFREEさんが許してくれる限りこのまま放置で使いたい人が何気に使う形でいいかなぁと思っています。使い方
下記をスクリプトエディタにコピペすれば関数を利用できるようになります。
=ZIP_ADDRESS("154-0004","address1")
みたいな感じ。利用できるデータの種類は次の通りです。
zipcode 郵便番号
prefcode 都道府県コード
address1 都道府県名
address2 市区町村名
address3 町域名function ZIP_ADDRESS(zip,part) { zip = zip.replace(/\-/g, ''); var response = UrlFetchApp.fetch('http://zipaddress.php.xdomain.jp/search.php?v=' + zip); var res = JSON.parse(response.getContentText()); if (res.status == 400) { return res.message; } if (res.results) { var address = res.results[0]; return (address[part]) ? address[part] : ""; } else { return "該当するデータがありません"; } }ただ、いつのまにかGASのセキュリティ仕様が変わっているみたいで、外部システムのデータを読み込むときに承認が必要と言われると思います。
そんなときはこちらの記事を参考に承認すれば大丈夫です!
https://qiita.com/h-sto/items/fdde0905d0a4070d18fc
- 投稿日:2021-01-14T13:37:05+09:00
css(styleタグ)をjavascriptで追加する
// styleタグを作成 var styleTag = document.createElement('style'); // styleタグに記載するcssを記入 styleTag.innerText = ` p { color:red; } `; // 作成したstyleタグを挿入 document.getElementsByTagName('head')[0].insertAdjacentElement('beforeend', styleTag);
- 投稿日:2021-01-14T13:00:22+09:00
素のECMAScriptだけで組める関数テスト
当記事では、素のECMAScriptだけで組める関数テスト用のES Modulesを書き残しています。
1. ソースコード
使い方は以下の通りです。
import { testFunction } from "./original-test-tool.mjs"; // サンプル用の関数です。 const f = (a, b) => { return a + b; }; // 試験を定義・実行します。 testFunction("f() の第1回試験", f, [1, 2], 3); testFunction("f() の第2回試験", f, [3, 4], 7); testFunction("f() の第3回試験", f, [1, 1], 999999); testFunction("f() の第4回試験", f, [1, 1], "2");実行するとコンソールにテスト結果が出力されます。
✔ f() の第1回試験 ✔ f() の第2回試験 ❌ f() の第3回試験 想定: {Number} 999999 実際: {Number} 2 ❌ f() の第4回試験 想定: {String} 2 実際: {Number} 2以下、全文です。
test.mjs/** * 引数で受け取った値のデータ型を返す関数です。 * @example * // "Number" * getType(1); * * // "String" * getType("Hello world."); * * // "Array" * getType([]); * @param {*} value 調べる対象となる値です。 * @returns {String} データ型は文字列で返します。 */ const getType = (value) => { return Object.prototype.toString.call(value).slice(8, -1); }; /** * 関数をテストする関数です。 * テスト結果に応じて、コンソールにメッセージを出力します。 * @example * const exampleFunction = (a, b) => { * return a + b; * }; * * // ✔ exampleFunction() のテスト * testFunction("exampleFunction() のテスト", exampleFunction, [1, 2], 3); * * // ❌ exampleFunction() のテスト * // 想定: {Number} 5 * // 実際: {Number} 3 * testFunction("exampleFunction() のテスト", exampleFunction, [1, 2], 5); * @param {String} testName テスト名・テスト概要を表す文字列を指定してください。 * @param {Function} testFunction テスト対象となる関数です。 * @param {Array<*>} testArguments テスト対象となる関数に渡す引数です。 * @param {*} correctTestReturnValue 想定される正しい戻り値です。undefinedは指定できません。 */ const testFunction = (testName, testFunction, testArguments, correctTestReturnValue) => { // 第1引数から第3引数までが正常であるか確認します。 const parameterTypes = ["String", "Function", "Array"]; const argumentTypes = [getType(testName), getType(testFunction), getType(testArguments)]; for (let i = 0; i < argumentTypes.length; i += 1) { if (argumentTypes[i] !== parameterTypes[i]) { throw new TypeError(`第${i + 1}引数には ${parameterTypes[i]} を指定してください。実際には ${argumentTypes[i]} が指定されています。`); } } // 第4引数が正常であるか確認します。 if (getType(correctTestReturnValue) === "Undefined") { throw new TypeError("第4引数が指定されていないか、あるいはundefinedが指定されています。第4引数に、明示的にundefinedを指定することは許可されません。"); } // 関数をテストし、その結果をコンソールに出力します。 const testReturnValue = testFunction(...testArguments); if (testReturnValue === correctTestReturnValue) { console.log(`✔ ${testName}`); } else { console.log(`❌ ${testName}\n 想定: {${getType(correctTestReturnValue)}} ${correctTestReturnValue}\n 実際: {${getType(testReturnValue)}} ${testReturnValue}`); } }; /** * ECMAScript環境だけで完全に動作するテスト用の関数・オブジェクトをまとめたモジュールです。 * @author AGadget */ export { testFunction }2. おわりに
凄まじく単純なので実用性は無いでしょうが、これくらいのクオリティのものならば意外と作れるものですね。
- 投稿日:2021-01-14T12:41:56+09:00
自宅内の見守りカメラをアップデートした:サマリー
はじめに
この記事は、私の以前の投稿「自宅内見守りカメラをOpenCV+HTML+JavaScriptで簡易に構成!」の続編です。
- RasberryPiでシステムを実働させた際の不具合修正のため、プログラム構成を変更
- 試用してみて必要と感じた機能の追加(キャリブレーション機能など)
- UIの変更
内容が多岐にわたるため、下記の通り分割して記事にしていきたいと思います。
Index
項目 内容 ハードウェア準備編 RasberryPiのカメラモジュールを使うまで お試し動作で課題にぶつかった編 パフォーマンスの課題にぶつかりました 構成を変更しよう編 自分の勉強も兼ねてGo言語を用いて作り直しをします 動作確認および機能追加編 明るさ調整機能など、必要と感じた機能を追加します
- 投稿日:2021-01-14T10:23:29+09:00
SALESFORCE 認定 JAVASCRIPT デベロッパー 試験の勉強内容まとめ
SALESFORCE 認定 JAVASCRIPT デベロッパー試験について自分の勉強したことをアウトプットを兼ねてまとめます。
試験の感想
試験には無事合格しました。
受けてきた感想としては静的メソッドなどはそこまで出ませんでした。
覚えてる範囲だとconcatとapplyの絡め技とsplice,padStartくらいでmap,filterなんかも出てこず拍子抜けです。
どちらかというとClass定義を用いた問題が多めに出てた印象で、オブジェクト作成時の動作や
プロトタイプの理解などをしっかりしておけばよいかなと思います。個人的には非同期処理とNode.jsが結構きつかったです。
非同期処理はPromiseとsetTimeout使ったときの処理の順番とかあやふやで悩みました。
また、Node.jsは範囲が広すぎて捨ててたのですが、https.createServerやserver.onでのエラー処理、コアモジュールはどれか?、次のパッチバージョンは何になるかなどが出ました。
一通りNode.jsでサーバーを立てたことがあったので何とかなりましたが触ったことない人は厳しいと思うので
勉強しておくといいと思います。あとは使い方覚えてるよっていうメソッドほどMDNのサイトで注意事項とか見直しておくとよいと思います。
記憶に残るなんじゃこりゃと思った問題は以下の3つ。
問題)
1)JSON.parse('"foo"')
;とJSON.parse("'foo'");
のどっちが動作するか(ほかにも選択肢ありました)
2)JSON.stringify([new String("false"), new Boolean("false"), undefined])
の出力結果は?
3) 以下のコードを実行したときのログの順番は?setTimeout(() => {console.log("log1");},0); console.log("log2"); Promise.resolve(1) .then(() => {console.log("log3");}) .finally(() => {console.log("log4");}); console.log("log5");答え)
1)JSON.parse('"foo"');
--> JSON.parse()はシングルコーテーションを許容しないため
2)"["false",false,null]"
--> undefinedはnullに変換されるため
3) 2 > 5 > 3 > 4 > 1の順上記の回答を調べるためにMDNの該当ページに行ったら似たような実行例が載ってました。
ここを見て理解するとよさそうですね。これ以降は試験のちょっとした内容と勉強した内容を記載していきます。
個人的な理解を基に記載しているのであまり鵜呑みにせず自分でも調べてください。
ざーっと書いているので誤字脱字・これは違うとかあってもご容赦頂ければ・・・試験情報
公式ガイド:https://tandc.salesforce.com/examguide_cert_javascript_developer.pdf
以下、公式ガイドから抜粋していきます
内容: 多肢選択/複数選択方式の 60 問
試験の所要時間: 105 分
合格点: 65%
試験範囲
・変数、データ型、コレクション: 23%
・オブジェクト、関数、クラス: 25%
・ブラウザとイベント: 17%
・デバッグとエラーハンドリング: 7%
・非同期プログラミング: 13%
・サーバーサイド JAVASCRIPT: 8%
・テスト: 7%60問の65%なので39問以上正解が必要ですね。
文法いろいろ
JavascriptはUnicode 文字セットを使用している
宣言let Früh = "foobar"文が単独の行で書かれている場合、文の後にセミコロンは必要ない。
しかし、行の中に複数の文が必要な場合は、セミコロンで区切る必要がある。
- var
- 関数スコープまたはグローバルスコープ.
- 巻き上げといって現れる場所に関係なくコード実行前に処理される
- 値は、実際には代入文に到達したときに代入され、初期値はundefined
- let
- ブロックスコープのローカル変数を宣言
- let宣言前に変数を使用するとReferenceErrorとなる
- const
- letと同じくブロックスコープ
- 初期化が必要
- オブジェクトや配列の上書きはエラーになるがプロパティや要素の変更はエラーにならない
- Object.freeze()を使えばプロパティの変更も阻止できる
変数はアンダースコア、ドル記号、文字から始まる必要がある
分割代入
オブジェクトや配列は各変数に分割して代入することができる
分割代入const foo = {bar:10,baz:12}; let {bar} = foo; console.log(bar); //10 let a, b; ({a, b} = {a: 1, b: 2}); //ここはかっこでくくらないとブロックスコープとして解釈されてしまう const o = {p: 42, q: true}; const {p: foo, q: bar} = o; console.log(foo); // 42 console.log(bar); // true const x = [1, 2, 3, 4, 5]; let [a,b,c,d,e] = x; console.log(a); //1 console.log(b); //2 console.log(c); //3 //配列から取り出した値が undefined だった場合に使用される既定値を指定できる let a, b; [a=5, b=7] = [1]; console.log(a); // 1 console.log(b); // 7データ型
Javascriptの型は8種類
※DateはObjectに分類される
- String
- Number
- BigInt
- Boolean
- null
- undefined
- object
- Symbol
String
文字列の検索方法
indexOf,search,includesなどを使う
searchは正規表現を使用するstr.indexOf(searchValue[, fromIndex])
searchValueがstrに存在するか検索し、最初にヒットしたインデックスを返す。
存在しない場合-1を返す。indexOfconst phrase = "cyan magenta yellow"; console.log(phrase.indexOf("magenta")); // 5 console.log(phrase.indexOf("blue")); // -1str.search(regexp)
正規表現regexpとマッチする文字列があるか検索し、最初にヒットしたインデックスを返す。
存在しない場合-1を返す。searchconst phrase = "cyan magenta yellow"; console.log(phrase.search(/m/)); // 5 console.log(phrase.search(/\*z/)); // -1str.includes(searchString[, position])
strにsearchStringが含まれているか検索し、含まれている場合はtrueを返す。
含まれていない場合はfalseを返す。includesconst phrase = "cyan magenta yellow"; console.log(phrase.includes("cyan")); // true console.log(phrase.includes("blue")); // falsestr.split([separator[, limit]])
strをseparatorで指定した区切り文字列で分割し、分割した結果を配列として返す。
separatorには文字列のほか正規表現も指定できる。splitconst phrase = "cyan magenta yellow"; console.log(phrase.split(" ")); // ["cyan", "magenta", "yellow"] console.log(phrase.split(" ", 1)); // ["cyan"]パディングには以下のメソッドを使う
str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])padStartはtargetLengthの文字列サイズになるまで左からpadStringを詰めていく。
padEndはtargetLengthの文字列サイズになるまで右からpadStringを詰めていく。
padStringを指定しなかった場合" "が詰められる。paddingconst str = "01234"; console.log(str.padStart(10,"*")); // *****01234 console.log(str.padEnd(10,"*")); // 01234*****
substring,slice,substrで文字の切り抜きができる
ただし、substrは非推奨。
str.substring(indexStart[, indexEnd])
indexStartがindexEndより大きい場合、二つの変数は交換されて扱われる
負の数やNaNの値を指定すると0として扱うstr.slice(beginIndex[, endIndex])
基本はsubstringと一緒
sliceの場合負の数を使用できるstr.substr(start[, length])
substrの場合、二つ目のオプションは文字数を表すことに注意(インデックスではない)substringconst phrase = "cyan magenta yellow"; console.log(phrase.substring(5,12)); // magenta console.log(phrase.slice(-14,12)); // magenta console.log(phrase.slice(5,7)); // magentaNumber
倍精度64ビット浮動小数点形式の数値データ型
+Infinity, -Infinity, NaNの値もあわせて持つ
+Infinity, -Infinityはtruthyな値、NaNはfalsyな値Numberconsole.log(Number.MAX_SAFE_INTEGER); // 2^53-1 console.log(Number.MIN_SAFE_INTEGER); // -2^53-1 //上記とは別に最大値としてはNumber.MAX_VALUE,Number.MIN_VALUEが存在するBigInt
任意の精度で整数を表現できる JavaScript の数値プリミティブ。
BigIntは、整数の末尾にnを追加するか、コンストラクターを呼び出すことで作成する。
BigIntはNumberと一緒に使用できない。Number> const x = 2n ** 53n; 9007199254740992n > const y = x + 1n; 9007199254740993nnull
typeof null はobjectが返る。
falsyな値。undefined
宣言のみが行われた変数の値。
undefinedは数値のコンテキストで使用されるとNaNとなる(nullは0として評価される)。
falsyな値。Symbol
Symbolは文字列に変換されない,Sym.toString()を使う
プリミティブな型ではないがそのほかのデータ型としてDateとJSONについて以下に記載する。
Date
協定世界時(UTC)の1970年1月1日からの経過ミリ秒数を持つオブジェクト。
初期化・宣言方法は以下の4種類
なお、生成したDateオブジェクトは実行した環境のタイムゾーンとして扱われる。
new Date()
現在の時刻のオブジェクトを返す。
※let d = Date()
だと現在の時刻を文字列形式で返す。new Date(value)
valueにミリ秒を渡すとその時刻のオブジェクトを返す。new Date(dateString)
dateStringにISO8601や拡張ISO8601で定められたフォーマットの時刻を表す文字列を渡すとその時刻のオブジェクトを返す。
ISO8601形式:2014-10-10T13:50:40+09:00
date.parseなど上記以外のフォーマットで渡すと環境依存となる。
実際iOS13と14では動作が変わるようである。new Date(year, monthIndex[, day[, hours[, minutes[, seconds[, milliseconds]]]]])
年、日付を指定することでその時刻のオブジェクトを返す。
monthindexについては1月は0なので注意Dateconst dt1 = new Date(); // 現在の時刻 ex) Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時) const dt1_string = Date(); // "Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時)" const value = dt1.getTime(); // 1609512388374 const dt2 = new Date(value); // Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時) const dateString = dt1.toISOString(); // "2021-01-01T14:46:28.374Z" これは拡張ISO8601フォーマット const dt3 = new Date(dateString); // Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時) const dt4 = new Date(2021, 0, 1, 23, 46, 28); // Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時)Dateオブジェクトのメソッドいろいろ
Date.now
現在の時刻のUNIX元期からの経過ミリ秒を返す。dateObj.getTime()
dateObjのUNIX元期からの経過ミリ秒を返す。dateObj.toString()
dateObjの日付・時刻を以下のようなフォーマットの文字列で返す。
"Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時)"dateObj.toDateString()
dateObjの日付を以下のようなフォーマットの文字列で返す。
"Fri Jan 01 2021"dateObj.toISOString()
dateObjの日付を拡張ISO8601フォーマットの文字列で返す。
"2021-01-01T14:46:28.374Z"dateObj.getFullYear()
dateObj.getDate()
dateObjの年を取得する場合はgetFullYearを使用する。
それ以外の月や日付を取得する場合はgetDateやgetMonthなどget*を使用する。dateObj.setFullYear()
dateObj.setDate()
dateObjの年を設定する場合はgetFullYearを使用する。
それ以外の月や日付を設定する場合はgetDateやgetMonthなどget*を使用する。日付の計算
getDateなどで日付を取得し、そこから数値演算したものをsetDateなどで設定する。
CalcDateconst dt = new Date(2021, 0, 1, 23, 46, 28); // Fri Jan 01 2021 23:46:28 GMT+0900 (日本標準時) dt.setDate(dt.getDate() + 3); // 3日後を計算 console.log(dt.toString()); // Mon Jan 04 2021 23:46:28 GMT+0900 (日本標準時) dt.setDate(dt.getDate() - 7); // 7日前を計算 console.log(dt.toString()); // Mon Dec 28 2020 23:46:28 GMT+0900 (日本標準時)JSON
JavaScript Object Notationの略
記載方法は省略して変換方法だけ以下に記載する。JSON.parse(text[, reviver])
textにJSONとして解析する文字列を指定してJSON型へ変換する。
reviverには関数を設定でき、変換したJSONのキーと値に対して何らかの処理を行って返すことが可能。
変換したいテキストの最後にカンマがあるとエラーが発生する。JSON.stringify(value[, replacer[, space]])
オブジェクトやJSON型のデータを文字列に変換できる。
replacerに関数を指定すると変換前のデータを処理できる。
spaceを指定すると文字列のインデント用のスペースなどを追加できる。
オブジェクトの関数は無視されて文字列化される。JSONconst JSONStr = '{"a":1,"b":"2"}'; console.log(JSON.parse(JSONStr)); // {a: 1, b: "2"} const obj = {a:1,b:"2",c: function() {console.log("c")}}; console.log(JSON,stringify(obj)); // {"a":1,"b":"2"}コレクション
Map
オブジェクトの一種。
オブジェクトとの違う点は以下(他にもある)
- キーの型はStringやSymbolに限らない
- キーの順序は挿入順で固定されている
- sizeプロパティで項目数を取得できる
- iterableなのでfor...ofループが使える
初期化
new Map()
で初期化する
初期化の際にはキーと値のペアを配列形式でコンストラクタの引数に追加できる。各種プロパティ・メソッド
myMap.size
マップの項目数を取得できる。メソッドではないことに注意。myMap.get(key)
keyに対応する値を返す。myMap.has(key)
キーに対する要素がマップに存在していればtrue、なければfalseを返す。myMap.set(key, value)
keyに対応するvalueをマップに追加するmyMap.delete(key)
keyに対応する要素があれば削除しtrueを返す。
なければfalseを返す。Maplet map = new Map([['London', 'UK']]); map.has('London'); // true map.get('London'); // 'UK' map.set('Paris', 'FR'); map.size; // 2 map.delete('Paris'); // true console.log(map); // Map(1) {"London" => "US"}Set
値の重複を許さないコレクションのこと。
初期化
new Set()
で初期化する
初期化の際には値を配列形式でコンストラクタの引数に追加できる。各種プロパティ・メソッド
mySet.size
セットの項目数を取得できる。メソッドではないことに注意。mySet.add(value)
値を追加するmySet.has(key)
要素が存在していればtrue、なければfalseを返す。mySet.delete(key)
keyに対応する要素があれば削除しtrueを返す。
なければfalseを返す。Setlet set = new Set([1,2,3]); set.add(4); // Set(4) {1, 2, 3, 4} set.add(4); // 重複した値を登録しようとしているので無視される set.has(1); // true set.size; // 4 set.delete(4); // trueデータ型の変換
数値と文字列を+演算子で結合すると数値を文字列に変換する。
それ以外の演算子では数値を文字列に変換せず、文字列を数値に変換する。変換1x = '答えは ' + 42 // "答えは 42" y = 42 + ' が答え' // "42 が答え" x = '37' - 7 // 30 y = '37' + 7 // 377文字列を数値に変換するにはparseInt,parseFloatを使用する
※parseIntは小数を切り捨てる
もしくは+演算子を使うparseIntlet num = '10'; parseInt(num,10); //数値型の10が返る +num; //数値型の10が返るリテラル
配列リテラル
配列リテラル中ではカンマを二つつなげることでundefinedの値で要素を埋めることができる
ただし、要素のリストの最後にカンマを付けた場合、そのカンマは無視されるarraylet fish = ['Lion', , 'Angel']; console.log(fish[1]); //undefined let myList = ['home', , 'school', , ]; console.log(myList); // ["home", empty, "school", empty]数値リテラル
Number および BigInt 型は、10進数、16進数、8進数、2進数で書くことができる。
- 10進数は先頭が0でない一連の数字
- 8進数は先頭に0もしくは0o
- 16進数は先頭に0x
- 2進数は先頭に0b
オブジェクトリテラル
オブジェクトのプロパティには空の文字列を含むあらゆる文字列が使える。
プロパティ名が JavaScript で有効な識別子か数値でなければ、引用符で囲む必要がある。Objectvar unusualPropertyNames = { '': '空文字列', '!': 'バン!' } console.log(unusualPropertyNames.''); // SyntaxError: Unexpected string が発生 console.log(unusualPropertyNames['']); // 空文字列 console.log(unusualPropertyNames.!); // SyntaxError: Unexpected token ! が発生 console.log(unusualPropertyNames['!']); // バン!正規表現リテラル
/で囲むと正規表現を表す
ループと反復処理
ラベル
ラベル:
の形で使用することでループ処理などにラベルを付けることができる。
そのラベルを使用してbreak labelName;
などでlabelNameのループを抜けることが可能。
また、contunue labelName;
で次のlabelNameの処理に移ることができるLabelmarkLoop: while (themark === true) { if (flag === true) { break markLoop; } }for...in ループ
キーが文字列であるオブジェクトの列挙可能プロパティに対して反復処理を行う(Symbolキーは無視する)。
任意の順序でオブジェクトのプロパティに対して反復する
継承したプロパティもfor...inでは列挙するのでhasOwnpropertyメソッドで自身のインスタンスにあるプロパティかチェックしたほうがよい。
Object.keys()などでキーを取得してループするか、forEachを使ったほうが良い
配列にも使用しないほうが良い
反復処理するpropertyはStringなので実際の値をとるにはそれをキーとして使うfor...inconst SYM =Symbol(); const o = {a: 1, b: 2, [SYM]: 3}; for (let property in o) { if (!o.hasOwnProperty(property)) continue; console.log(`${property}: ${o[property]}`); } //a: 1 //b: 2for...of ループ
反復可能オブジェクト(String,Array,Map,Setなど)に対して反復処理するループを作成する
Mapなどに使用した場合はキーと値の要素を持つ配列を返すfor...ofconst str = "abc"; const arr = [1,2,3]; const mp = new Map([["X","x"], ["Y","y"], ["Z","z"]]); for (const value of str) { console.log(value); // "a" "b" "c" } for (const value of arr) { console.log(value); // 1 2 3 } for (const value of mp) { console.log(value); // ["X","x"] ["Y","y"] ["Z","z"] }オブジェクト
オブジェクトのいろいろ
オブジェクトの初期化
オブジェクトはnew Object()
もしくはObject.create()
、リテラルを使用して初期化される。初期化const a = 'foo', b = 42, c = {}; const o = {a, b, c}; // a: 'foo', b:42, c: {}と同義 const obj1 = Object.create(o) // prototypeがoオブジェクトとなるオブジェクトを作成する console.log(obj1); // {} ->prototypeがoなだけなので何もプロパティは設定されていない状態getter, setter
オブジェクトのプロパティはgetter,setterを定義することもできるgetter,settervar o = { prop: 'value', get propValue() { return this.prop; }, set propValue(param) { this.prop = param; } } o.propValue = 'test'; console.log(o.propValue); // 'test'スプレッドプロパティ
渡されたオブジェクトから新しいオブジェクトに独自の列挙可能なプロパティをコピーする。スプレッド演算子const obj1 = { foo: 'bar', x: 42 }; const obj2 = { foo: 'baz', y: 13 }; var clonedObj1 = {obj1} // Object {obj1 : { foo: "bar", x: 42 }} obj1をプロパティに持つ var clonedObj2 = { ...obj1 }; // Object { foo: "bar", x: 42 } obj1のプロパティを持つ var mergedObj = { ...obj1, ...obj2 }; // Object { foo: "baz", x: 42, y: 13 }プロパティの削除
オブジェクトのプロパティを削除するメソッドはない、削除したい場合はdelete演算子を使うdeleteconst Employee = { firstname: 'John', lastname: 'Doe' }; delete Employee.firstname; console.log(Employee.firstname); // undefinedオブジェクトの代入
オブジェクトの代入は参照渡しであり、===で比較するとtrueになる。
オブジェクトの比較にはObject.is()を使う(のちほど)。
代入元のオブジェクトを変更すると代入先も変更される。代入const obj1 = {a:1,b:2}; const obj2 = obj1; console.log(obj1 === obj2) // true obj1.a = 3; console.log(obj2); // {a: 3, b: 2}静的メソッド
Object.assign(target, ...source)
コピー元オブジェクトから列挙可能かつ直接所有のプロパティだけをコピー先オブジェクトにコピー
String と Symbol の両方のプロパティがコピーされる
代入なのでgetter, setterが呼び出される
プロパティの値をコピーするためオブジェクトの参照値がコピーされる(DeepCloneは別の方法が必要)assignconst target = { a: 1, b: 2 }; const SYM =Symbol(); const source = { b: 4, c: 5, [SYM]: 6 }; const returnedTarget = Object.assign(target, source); console.log(returnedtarget); // {a: 1, b: 4, c: 5, Symbol(): 6} console.log(returnedtarget === target); // true const copy = Object.assign({},target); console.log(copy); // { a: 1, b: 2 }Object.defineProperty(obj, prop, descriptor)
obj: 対象のオブジェクト
prop: プロパティ名またはシンボル
descriptor: 定義または変更されるプロパティの記述子
writeable, enumerable, configurableといった属性値がある
あるオブジェクトにプロパティを直接定義したり、既存のプロパティを変更できる
デフォルトではこのメソッドで追加された値は不変となり列挙可能ではないdefinePropertyconst obj = {foo: 'bar'}; Object.defineProperty(obj, 'color', { get: () => this.color, set: value => this.color = value } ); // colorというプロパティにgetter,setterを追加 Object.defineProperty(obj, 'prop', { value: 42, writable: false, enumerable: true }); // 書き込み不可で列挙可能なpropプロパティを定義Object.getOwnPropertyDescriptor(obj, prop)
obj: 対象のオブジェクト
prop: 対象のプロパティ名
プロパティの構成を記述するオブジェクトを返すgetOwnPropertyDescriptorconst object1 = { property1: 42 }; console.log(Object.getOwnPropertyDescriptor(object1, 'property1')); // {value: 42, writable: true, enumerable: true, configurable: true}Object.entries(obj)
obj: 対象のオブジェクト
引数に与えられたオブジェクトの列挙可能な所有プロパティのキーと値を配列として返す。
返される順序は不定なので必要に応じてsortする必要がある
ObjectからMapへの変換が簡単にできる
配列が返ってくるのでforEachが使えるentriesconst obj = { foo: 'bar', baz: 42 }; console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ] for ( let [key, value] of Object.entries(obj)) { console.log(`${key} ${value}`); } const map = new Map(Object.entries(obj)); // Map { foo: "bar", baz: 42 } Object.entries(obj).forEach(([key, value]) => console.log(`${key}: ${value}`)); // "foo: bar", "baz: 42"Object.keys(obj)
obj: 対象のオブジェクト
引数のオブジェクトの列挙可能な自身のプロパティ名を配列で返す。keysconst obj = { foo: 'bar', baz: 42 }; console.log(Object.keys(obj)); // ['foo', 'baz']Object.values(obj)
obj: 対象のオブジェクト
引数のオブジェクトの列挙可能な自身のプロパティに対応する値を配列で返す。valuesconst obj = { foo: 'bar', baz: 42 }; console.log(Object.values(obj)); // ['bar', 42]Object.is(value1, value2)
二つの引数が同じ値であるかどうかを比較する。
===との違いはNanとNanを比較したときにObject.isはtrueになるが===はfalseとなることと、
+0と-0を比較したときにObject.isはfalseとなるが===はtrueになることisconst obj1 = { foo: 'bar', baz: 42 }; const obj2 = { foo: 'bar', baz: 42 }; const obj3 = obj1; console.log(Object.is(obj1, obj2)); // false console.log(Object.is(obj1, obj3)); // trueオブジェクトの編集を制限するメソッド
Object.freeze(obj)
obj: 凍結対象のオブジェクト
オブジェクトを凍結した状態に変更する。
配列も凍結できる
この状態ではプロパティ値の変更、setterの呼び出し、プロパティの追加、ディスクリプタの変更ができなくなるfreezeconst obj = { foo: 'bar', baz: 42 }; Object.freeze(obj); obj.foo = 'fizz'; // No Change or error in strict modeObject.seal(obj)
obj: 対象のオブジェクト
対象オブジェクトに対して新たなプロパティの追加をできなくさせるほか、
既存プロパティの記述子(writeableなど)の変更もできなくなる。
既存のプロパティの値に対する変更は可能sealconst obj = { foo: 'bar', baz: 42 }; Object.seal(obj); Object.defineProperty(obj, 'foo', { writable : false });// No Change or error in strict modeObject.preventExtensions(obj)
obj: 対象のオブジェクト
objに対して新たなプロパティの追加をできなくさせる。
既存のプロパティに対する削除・値の変更・記述子の変更は可能preventExtensionsconst obj = { foo: 'bar', baz: 42 }; Object.preventExtensions(obj); obj.fizz = 'fizz'; // No Change or error in strict modefreeze > seal > preventExtentionsの順にできることが増えていく
配列
配列へのアクセス
不正なインデックス番号を使った場合は undefinedを返す。
静的メソッド
Array.isArray(value)
value:判定対象
valueが配列かどうかチェックし配列であればtrueを返す。Array.of(element0[, element1[, ...[, elementN]]])
elemenN: 作成する配列の要素
可変長の引数からそれらを要素とする配列を生成する。
コンストラクタArray()との違いは単一の数字要素を与えたときにそれを要素として配列を作るか、要素数として配列を作るかの違いofconsole.log(Array.of(1,2,3,"4")); // [1,2,3,"4"] console.log(Array.of(7)); // [7] console.log(Array(7)); // [empty x 7] 要素数7で要素がすべてundefinedの配列Array.from(arrayLike[, mapFn[, thisArg]])
arrayLike: 配列に変換する配列のようなオブジェクトもしくは反復可能オブジェクト(map, setなど)
mapFN: 各要素に適用されるmap関数
thisArg: 任意で指定するmap関数内でthisとして使うオブジェクト
指定したオブジェクトを配列に変換するfromconsole.log(Array.from('foo')); // [ "f", "o", "o" ] let map = new Map([['London', 'UK'], ['Paris', 'FR']]); console.log(Array.from(map)); // [['London', 'UK'], ['Paris', 'FR']]配列の操作
arr.push([element1[, ...[, elementN]]])
elementN: 追加する要素
配列の最後に要素を追加し、追加後のarray.lengthを返す。
実行した配列そのものを変更する。pushlet arr = ['a','b','c']; console.log(arr.push('d')); //4 console.log(arr) // ['a','b','c','d']arr.pop()
配列の最後の要素を取り除き、取り除いた値を返す。
空の配列に対してpop()を実行した場合は、undefinedを返す。
実行した配列そのものを変更する。poplet arr = ['a','b','c']; console.log(arr.pop()); // c console.log(arr); // ['a','b']arr.shift()
配列から先頭の要素を削除し、その値を返す。
空の配列に対してshift()を実行した場合は、undefinedを返す。
実行した配列そのものを変更するshiftlet arr = ['a','b','c']; console.log(arr.shift()); // a console.log(arr); // ['b','c']arr.unshift(element1[, ...[, elementN]])
elementN: 追加する要素
配列の先頭に要素を追加し、追加後のarray.lengthを返す。
実行した配列そのものを変更する。unshiftlet arr = ['a','b','c']; console.log(arr.unshift('d')); // 4 console.log(arr); // ['d','a','b','c']const new_array = old_array.concat([value1[, value2[, ...[, valueN]]]])
valueN: 新しい配列に連結する配列や値
呼び出した配列に引数の要素を追加した配列を返す。
呼び出した配列自体は変更されない。
引数には要素のほか配列を渡すことができる、配列を渡した場合は要素に分解されて結合される。
ネストされた配列は配列として追加される。concatlet arr1 = ['a','b','c']; let arr2 = arr1.concat('d', ['e','f'], [['g']]); console.log(arr1); // ['a','b','c'] console.log(arr2); // ['a','b','c','d','e','f',["g"]]arr.slice([start[, end]])
start: 取り出し開始位置を示すインデックス
end: 取り出し終了位置を示すインデックス(取り出されるのは指定したインデックスの一つ前の要素まで)
呼び出し元の配列から特定の範囲内を切り取った部分配列を返す。
呼び出し元の配列は変更されない。
startを指定しない場合は0スタート、endを指定しない場合は最後の要素まで。
endに負の数を指定した場合は最後の要素から数えた値までを取り出す。slicelet arr = ['a','b','c']; console.log(arr.slice(1,2)); // ['b'] console.log(arr.slice(0,-1)); // ['a','b']let arrDeletedItems = array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
start: 配列を変更する開始位置を示すインデックス
deleteCount: 削除する要素の数
itemN: 配列に追加する要素
呼び出し元の配列から削除された要素からなる配列を返す。
呼び出し元の配列が変更される。
startが負の数の場合最後の要素から戻った場所を示す。
deleteCountが省略された場合start以降の要素を削除する。
deleteCountが0以下の場合削除はされない。splicelet arr = ['a','b','c']; console.log(arr.splice(1,1)); // ['b'] arr[1]から1個の要素が削除された console.log(arr); // ['a','c'] 削除された残りの配列 console.log(arr.splice(1,0,'b1','b2')); //[] 削除された要素はない console.log(arr); // ['a','b1','b2','c'] 二つ要素がarr[1]の位置に追加されたarr.copyWithin(target[, start[, end]])
target: 値のコピー先のインデックス
start: コピー元の開始インデックス
end: コピー元の終了インデックス
配列の一部をサイズ変更せずにコピーする。
変更されたオブジェクトそのものを返す、呼び出し元のオブジェクトを変更する
endが省略された場合最後の要素までをコピーするcopyWithinlet arr = ['a','b','c']; console.log(arr.copyWithin(1,2)); // ['a','c','c'] arr[1]の要素がarr[2]から最後までの値にコピーされた console.log(arr); // ['a','c','c'] console.log(arr.copyWithin(2,0,1)); //['a','c','a'] arr[2]の要素がarr[0]からarr[1]の手前までの値でコピーされたarr.fill(value[, start[, end]])
value: 配列に設定する値
start: 値の設定開始インデックス
end: 値の設定終了インデックス
配列内の特定の要素をvalueに変更する。
startやendが省略された場合すべての要素を変更する。
呼び出し元が変更され、変更された配列そのものを返す。filllet arr = ['a','b','c']; console.log(arr.fill('d')); // ["d", "d", "d"] console.log(arr); // ["d", "d", "d"]配列の並べ替え
arr.sort([compareFunction])
compareFunction: ソート順を定義する関数
配列の要素を並び替える。
関数を指定しなかった場合は要素はすべて文字列に変換されて昇順にソートされる。
関数を指定する場合、compareFunction(a, b)について負の数を返したときはaが先になるように並び替える
正の数を返したときはbが先になるように並び替える。
0が返るときは何もしない。
呼び出し元の配列を変更する。sortlet arr = [1, 3, 11]; arr.sort(); // 文字列として昇順ソート console.log(arr); // [1, 11, 3] 11が3よりも先に来る arr.sort((a,b) => a - b); //数値として昇順ソート console.log(arr); // [1, 3, 11]arr.reverse()
配列の要素を反転させる。
呼び出し元の配列を変更する。reverselet arr = [1, 3, 11]; arr.reverse(); console.log(arr); // [11, 3, 1]配列の検索
arr.indexOf(searchElement[, fromIndex])
配列内の要素からsearchElementを検索し、最初にヒットしたインデックス、なければ-1を返す。
fromIndexを指定すると検索するインデックスの範囲を指定できる。
比較自体は===で実行される。arr.lastIndexOf(searchElement[, fromIndex])
配列内の最後の要素からsearchElementを検索し、最初にヒットしたインデックス、なければ-1を返す。
fromIndexを指定すると検索するインデックスの範囲を指定できる。
比較自体は===で実行される。index/lastIndexOflet arr = [1, 3, 11, 3, 4]; console.log(arr.indexOf(3)); // 1 console.log(arr.lastIndexOf(3)); // 3 後ろから検索して最初にヒットしたインデックスarr.findIndex(callback( element[, index[, array]] )[, thisArg])
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
配列内の要素からコールバック関数で指定した条件にヒットする要素を検索し、最初にヒットしたインデックス、なければ-1を返す。
検索の際に使用する関数をコールバック関数に指定する。
コールバック関数ではtrueを返した際に検索にヒットしたと判定する。arr.find(callback(element[, index[, array]])[, thisArg])
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
配列内の要素からコールバック関数で指定した条件にヒットする要素を検索し、最初にヒットした要素、なければ-1を返す。
検索の際に使用する関数をコールバック関数に指定する。
コールバック関数ではtrueを返した際に検索にヒットしたと判定する。
検索した結果、要素がほしい場合にはfindを使用する。findIndex/findlet arr = [{id:1, value:"foo"}, {id:2, value:"foo"}]; console.log(arr.findIndex(element => element.id === 2)); // 1 console.log(arr.find(element => element.id === 2)); // {id:2, value:"bar"}arr.some(callback(element[, index[, array]])[, thisArg])
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
呼び出し元の配列内で一つでもコールバック関数で指定した結果に一致する要素があればtrue,なければfalseを返す。somelet arr = [{id:1, value:"foo"}, {id:2, value:"foo"}]; console.log(arr.some(element => element.id === 2)); // true console.log(arr.some(element => element.id === 3)); // falsearr.every(callback(element[, index[, array]])[, thisArg])
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
呼び出し元の配列の要素すべてがコールバック関数で指定した結果に一致する場合true,なければfalseを返す。everylet arr = [{id:1, value:"foo"}, {id:2, value:"foo"}]; console.log(arr.every(element => element.value === "foo")); // true console.log(arr.every(element => element.id === 1)); // false配列の要素の変換
let new_array = arr.map(callback(element[, index[, array]])[, thisArg])
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
配列の要素をひとつづつ処理して別の配列に変換して返す。
呼び出し元の配列は変更しない。Mapconst cart = [{name: "iPhone", price: 10000}, {name: "Android", price: 5000}]; console.log(cart.map(x => x.name)); // ["iPhone", "Android"] console.log(cart.map(x => x.price)); // [10000, 5000] console.log(cart.map(x => x.price * 0.8)); // [8000, 4000]let newArray = arr.filter(callback(element[, index, [array]])[, thisArg])
element: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
thisArg: 任意で指定するコールバック関数内でthisとして使うオブジェクト
配列の要素をひとつずつテストしてtrueを返した要素のみで構成される別の配列に変換して返す。
呼び出し元の配列は変更しない。filterconst cart = [{name: "iPhone", price: 10000}, {name: "Android", price: 5000}]; console.log(cart.filter(x => x.price > 5000)); // [{name: "iPhone", price: 10000}]arr.reduce(callback( accumulator, currentValue[, index[, array]])[, initialValue])
accumulator: 最終的に配列が変換される先
currentValue: 配列内で現在処理されている要素
index: 配列内で現在処理されている要素のインデックス
array: 呼び出し元の配列
initialValue: accumulatorの初期値
配列内の要素を一つずつ処理して変換し、accumulatorに結果を格納する
呼び出し元の配列は変更されない。reduceconst arr = [1,2,3,4,5]; console.log(arr.reduce((a,x) => a += x)); // 15 配列内の要素をすべて足し合わせた結果arr.join([separator])
配列内の要素をsepartatorで連結した文字列で返す。
separatorはデフォルトでは","であり、""を指定すると何も区切らずに連結する。
null, undefinedは空文字として連結されるjoinconst elements = ["hello", null, 'world']; console.log(elements.join()); // "hello,,world" console.log(elements.join("")); // helloworld console.log(elements.join("-")); // "hello--world"配列が元の配列を変更するか
変更する 変更しない push, pop concat shift, unshift slice splice map copyWithin filter fill reduce reverse join sort thisの扱いについて
this
1.すべての関数の外、最上位で呼ばれる関数内ではthisはグローバルオブジェクトを参照する。
2.関数がオブジェクトから呼び出される場合はthisはその呼び出し元のオブジェクトを参照する。
3.関数がコンストラクタとして使用される場合、thisは新しく作成されるオブジェクトを参照するthis//1のパターン function displayThis() { console.log(this); }; displayThis(); // windowオブジェクトが表示される //2のパターン let obj = { prop: "val", objFunc: displayThis}; obj.objFunc(); // {prop: "val", objFunc: ƒ} 呼び出し元のオブジェクトが表示される //3のパターン function Product(name) { this.name = name; console.log(this); } let p = new Product('prod'); // Product {name: "prod"} 作成されたインスタンスが表示される console.log(p.name); // prod関数の中の関数ではthisはグローバルオブジェクトかundefinedになってしまう。
下の例だとfunc2はXXX.func2()の形で呼ばれていないため、window.func2()として解釈されてしまう。this_in_functionconst o = {name:'TEST', func1: function () { function func2() { console.log('func2',this); } console.log('func1',this); func2(); } }; o.func1(); // func1 {name: "TEST", func1: ƒ} // func2 Window {0: global, window: Window, self: Window, document: document, name: "", location: Location, …}アロー関数を使うとthisを語彙的に束縛できるため上記事象を回避できる。
すなわち、アロー関数では通常の変数と同様に内側のスコープから順にthisを探索していくこととなる。
下記の例だとfunc2はXXX.func2()の形で呼ばれていないため次にスコープであるfunc1のthisを探す。
見つかったのでthisは{name: "TEST", func1: ƒ}
となるthis_in_allowfunctionconst o = {name:'TEST', func1: function () { let func2 = () => { console.log('func2',this); } console.log('func1',this); func2(); } }; o.func1(); // func1 {name: "TEST", func1: ƒ} // func2 {name: "TEST", func1: ƒ}thisを明示的に指定するメソッド
func.call([thisArg[, arg1, arg2, ...argN]])
thisArg: func関数で使用するthisの値
argN: func関数の引数
func関数を呼び出す際にcallをチェーンすることでその中で使用されるthisの値を指定できる。
argNはfunc関数の引数となる。calllet obj = {a:1,b:2}; function func1(value) { this.a = value; } func1.call(obj,3); console.log(obj); //{a: 3, b: 2}func.apply(thisArg, [ argsArray])
thisArg: func関数で使用するthisの値
arsArray: 配列風のオブジェクト
func関数を呼び出す際にapplyをチェーンすることでその中で使用されるthisの値を指定できる。
callとの違いは引数が配列風オブジェクトなこと、配列を使いたい場合はこっちが便利。applylet arr = [1,2,3,4,5]; console.log(Math.min.apply(null, arr)); // 1 //これはMath.min(arr[0],arr[1],...)と一緒 //すなわちMath.min(...arr) func1.call(obj,3); console.log(obj); //{a: 3, b: 2}let boundFunc = func.bind(thisArg[, arg1[, arg2[, ...argN]]])
thisArg: func関数で使用するthisの値
argN: func関数の引数
func関数を呼び出す際に使用するthisの値を明示的に固定し、指定したthisを使う関数を返す。
callと違って関数を返すためその場でfunc関数は実行されない。
一度bindされるとほかのcallなどでthisを指定しても反映されない。bindlet obj = {a:1,b:2}; function func1(value) { this.a = value; } let bindfunc = func1.bind(obj); console.log(obj); //{a: 1, b: 2} bindfunc(3); console.log(obj); //{a: 3, b: 2} let obj2 = {a:100,b:101}; bindfunc.call(obj2,200); //obj2をthisに指定したが無視されてobj1をthisとして実行される console.log(obj,obj2); // {a: 200, b: 2} {a: 100, b: 101}クラス
クラスの定義
以下のように記載することでClassを定義できる。
クラス定義の実態は関数となる。
クラスの定義は巻き上げされない。class//パターン1: prototypeの糖衣構文 class Car { constructor() { } } let c = new Car(); //パターン2 関数による定義 function Car() { } let c = new Car();静的メソッド
クラス定義中に静的メソッドを定義できる。
静的メソッドはインスタンスからは実行できない。staticclass Car { static func1() { console.log('static'); } constructor() { } } let c = new Car(); Car.func1(); // static c.func1(); // TypeError継承
extends構文を使うとオブジェクトを継承できる。
継承先のクラスのコンストラクタではsuper()を起動しないと失敗する。
継承元と継承先に同じメソッド名がある場合は継承先のメソッドが使用される。
継承元のメソッドもプロトタイプチェーンを利用して使用できる。extendsclass Viecle { static staticride() { console.log('static ride'); } ride() { console.log('viecle ride'); } ride2() { console.log('viecle ride2'); } constructor() { console.log('Viecle Constructor'); } } class Car extends Viecle { constructor() { super(); // 継承時は必須 console.log('Car Constructor'); } ride() { console.log('car ride'); } } let c = new Car(); // Viecle Constructor // Car Constructor c.ride(); // Car ride c.ride2(); // viecle ride2 Car.staticride(); // static rideモジュール
<script type="module" src="...">
の記載でHTMLファイルにjavascriptモジュールをインポートできる
module自体はstrictモードで実行されるエクスポートには名前付きエクスポートとデフォルトエクスポートの2種類がある
名前付きエクスポート
namedexport.jsexport let myVariable = Math.sqrt(2); export const CONST_VALUE = 'VALUE'; export function myFunction() { ... }; //もしくは・・・ let myVariable = Math.sqrt(2); const CONST_VALUE = 'VALUE'; function myFunction() { ... }; export {myVariable, CONST_VALUE, myFunction}名前付きエクスポートをインポートするときは同じ名前を{}で囲んでインポートする。
namedimport.jsimport {myVariable, CONST_VALUE, myFunction} from 'namedexport.js';デフォルトエクスポート
1ファイルにつき1つのみエクスポートできる。
defaultexport.jsexport default function () { ... } //もしくは・・・ function myFunction() { ... }; export { myFunction as default };デフォルトエクスポートをインポートするときは任意の名前を設定してインポートする。
その際は{}で記載しない。defaultimport.jsimport defaultFunction from 'defaultexport.js';いずれも
import {default as func1} や import { myFunction as func1}
の形で別名を付けることができる。
import * as myModule from 'sample.js'
でモジュールをインポートした場合、
sample.jsからエクスポートされたすべての変数や関数などをmyModule名前空間で参照できる。
myModule.func1();
みたいな形。非同期処理
よく使うメソッド
var timeoutID = scope.setTimeout(function[, delay])
function: 指定した時間経過した後に実行したい関数
delay: タイマーの秒数(ミリ秒単位)
タイマーを設定し、タイマーの時間が来た後にfunctionを実行する。
実行後はタイマーのIDを返し、これを使用してタイマーを中止できる(clearTimeout)。
setTimeoutにて指定された関数はコールスタックからWeb APIを経由して最終的にキューに追加される。
その後コールスタックの処理が空になったタイミングでキューからコールスタックに追加され実行される。
以下のサイトがわかりやすかった。JavaScript イベントループの仕組みをGIFアニメで分かりやすく解説
https://coliss.com/articles/build-websites/operation/javascript/javascript-visualized-event-loop.htmlscope.clearTimeout(timeoutID)
timeoutID: setTimeoutを実行して帰ってきたId
指定されたIdのタイマーの処理を中止させる。setTimeout//カウントダウン処理 for(let i = 5; i >= 0; i--) { setTimeout(() => { console.log(i); },(5-i)*1000); } let timerId = setTimeout(() => {console.log('TEST');},1*1000); clearTimeout(timerId); //console.log('TEST')は実行されないvar intervalID = scope.setInterval(func, delay[, param1, param2, ...]);
func: 指定した時間経過した後に実行したい関数
delay: タイマーの秒数(ミリ秒単位)
paramN: funcに追加で渡す引数
delayに指定した間隔で引数に渡した関数を実行する。
実行後はタイマーのIDを返し、これを使用してタイマーを中止できる(clearInterval)。
clearIntervalを使用して処理を中止させない限り処理し続ける。scope.clearInterval(timeoutID)
timeoutID: setTimeoutを実行して帰ってきたId
指定されたIdのタイマーの処理を中止させる。setInterval//1秒ごとに時刻を表示する処理を5回まで実施する。 let i = 0; const timerId = setInterval(() => { let now = new Date(); if (i < 5) { console.log(now.toString()); i++ } else { clearInterval(timerId); } }, 1*1000);Promise
非同期処理を表すオブジェクト、これを使うことでコールバック地獄から解消される。
promiseの処理結果に対してthenやcatchをチェインできる。
チェイン内でエラーが起きた場合はどこの処理であってもcatchが実行される。使い方
- 非同期処理を記述し、実行するための関数func1を用意する。
- func1内では return new Promiseを使用してPromiseを返すようにする(返したPromiseをthenハンドラなどで処理する)。
- 上記で返すPromiseの引数には実際に行いたい処理を記載する関数func2を設定する。
- func2の引数にはresolve,rejectの二つの引数を設定する。
- func2内の処理で処理に成功した場合はresolve(value)を実行するようにする。
- 処理が実際に成功すると、呼び出し元の処理でthenを使用している場合、そこへresolveへ渡した引数を渡すことができる。
- func2内の処理で処理に失敗した場合はreject(value)を実行するようにする。
- 処理が実際に成功すると、呼び出し元の処理でthenやcatchを使用している場合、そこへrejectへ渡した引数を渡すことができる。
- Promiseを受け取った側ではthenメソッドに処理が成功した場合に実行する関数、処理が失敗した場合に実行する関数を引数として設定して実行する。
コードにするとこんな感じ。
Promise_image//Promiseを返すfunc1を定義 function func1() { return new Promise((resolve,reject) => {//ここがfunc2の部分 //実際に実行したい非同期処理処理... if (処理に成功した) { resolve('成功'); // '成功'の文字列がthenメソッドへ渡される } else { reject('失敗'); } }); } func1() .then((msg) => { console.log(msg) }, (errmsg) => { console.log(errmsg) }); //成功時は'成功',失敗時は'失敗'の文字列が出力される }例
呼び出し元から文字列を受け取ってそれを基にオブジェクトを作成して返す処理を非同期で実装する。
その際に文字列が'ERR'だと作成に失敗するようにする。Promise_imagefunction createObject(str) { return new Promise((resolve, reject) => { if (str === 'ERR') { reject(new Error('引数が不正です。')); } else { resolve({param: str}); } }); } createObject('TEST') .then(obj => { console.log(obj); }, err => { console.error(err); }); // {param: "TEST"} createObject('ERR') .then(obj => { console.log(obj); }, err => { console.error(err); }); // Error: 引数が不正です。 //catchを使う場合 createObject('ERR') .then(obj => { console.log(obj); }) .catch(err => { console.error(err); });Promise.all(iterable)
iterable: 反復可能なオブジェクト
複数のPromiseを配列にして渡すことで渡したPromiseを並列に処理し、
すべてのPromiseがresolveになればthenを、一つでもrejectになればその時点でPromiseを返すような処理が実現できる。
処理結果は配列として返される。
配列の添え字と渡したPromiseの順番は一致する。Promise.alllet p1 = new Promise((resolve,reject) => {resolve('p1');}); let p2 = new Promise((resolve,reject) => {resolve('p2');}); let p3 = new Promise((resolve,reject) => {reject('p3');}); Promise.all([p1, p2]) .then(results => { console.log(results[0], results[1]); // p1 p2 }); Promise.all([p1, p2, p3]) .then(results => { console.log(results[0], results[1]); // p1 p2 }) .catch(results => { console.error(results); // p3 });Promise.allSettled(iterable);
iterable: 反復可能なオブジェクト
複数のPromiseを配列にして渡すことで渡したPromiseを並列に処理し、
すべてのPromiseがresolveもしくはrejectedの状態になった時点でPromiseを返すような処理を実現できる。
Promise.allとの違いはrejectの状態のPromiseが発生してもすぐにCatchが実行されないこと。
実行結果は配列で返されるが、配列の要素には関数から渡された値のほかにstatusを持つ。
Statusの値が"fulfilled"なら処理成功、"rejected"なら処理失敗を示す。Promise.alllet p1 = new Promise((resolve,reject) => {resolve('p1');}); let p2 = new Promise((resolve,reject) => {resolve('p2');}); let p3 = new Promise((resolve,reject) => {reject('p3');}); Promise.allSettled([p1, p2, p3]) .then(results => { results.forEach(x => console.log(x)); }); // {status: "fulfilled", value: "p1"} // {status: "fulfilled", value: "p2"} // {status: "rejected", reason: "p3"}Promise.race(iterable)
iterable: 反復可能なオブジェクト
複数のPromiseを配列にして渡すことで渡したPromiseを並列に処理し、
一つでもPromiseがresolveもしくはrejectになった時点でその結果をthenやcatchに渡す処理が実現できる。Promise.racelet p1 = new Promise((resolve,reject) => {resolve('p1');}); let p2 = new Promise((resolve,reject) => {resolve('p2');}); let p3 = new Promise((resolve,reject) => {reject('p3');}); Promise.race([p1, p2, p3]) .then(result => { console.log(result); // p1 or p2 }) .catch(result => { console.error(result); // p3 });async / await
async/awaitキーワードを使うことでPromiseを使った処理を同期処理のように記載することができる。
async function name() {}
awaitを使用する関数で使用する。[rv] = await expression
expression: 解決を待つPromise
Promise内でresolve/reject関数に渡された引数をそのまま使用できるようになる。
通常はthen/catchなどでコールバック関数を記載しないと引数を取り出せないため処理が簡略化できる。
await キーワードを使用したpromiseがrejectされた場合はtry-catchでエラーハンドリングを行う。async/awaitlet p1 = new Promise((resolve,reject) => {resolve('p1');}); let p3 = new Promise((resolve,reject) => {reject('p3');}); //Promiseを使う場合 p1 .then(result => console.log(result)) //p1 .catch(err => console.log(err)); p3 .then(result => console.log(result)) .catch(err => console.log(err)); // p3 //async/awaitを使う場合 try { console.log(await p1); // p1 } catch (e) { console.log(e); } try { console.log(await p3); } catch (e) { console.log(e); // p3 }Promiseを返すメソッド例
const fetchResponsePromise = fetch(resource [, init])
resource: 取得したいリソースのURL
init: リクエストに設定したい内容を含むオブジェクト
resouceで設定したURLに対してHTTPリクエストを実行する。
何もinitに指定がなければGETで実行する。
実行するとPromiseを返す。
※下記の例はchromeのブラウザから実行しているのでCORSのエラーが出るfetchfetch('https://www.google.co.jp') .then(res=> console.log(res)) .catch(err => console.log(err)); // TypeError: Failed to fetch //もしくは try { console.log(await fetch('https://www.google.co.jp')); } catch (e) { console.log(e); // TypeError: Failed to fetch }ブラウザとイベント
不安なところだけ抜粋
DOM
Windowオブジェクト
ユーザーの現在のウィンドウに関する情報を保持しているほか、ページ遷移などのメソッドも持つ。
window.location.href = "https://www.google.com"
で指定したURLへ遷移できる。window.open("https://www.google.com");
で指定したURLへ遷移できる。window.history.back/window.history.forward
でブラウザでいう前に戻る/進むボタンと同じ動作を行うdocumentオブジェクト
HTMLの操作に必要なメソッドなどを持つ。基本的な
querySelector()
のセレクタ指定方法。
これらを組み合わせれば大体のものは検索できる
querySelector("h2")
: h2タグの要素querySelector("#id")
: id="id"を持つ要素querySelector(".cls")
: class="cls"を持つ要素querySelector('[name="name1"]')
: [name="name1"]を持つ要素querySelector('h2[name="name1"]')
: [name="name1"]を持つh2要素querySelector('div.div1 h2[name="name1"]')
: class="div1"を持つdivタグの子供である[name="name1"]を持つh2要素HTMLElementオブジェクトのプロパティ
- innerHTML: 指定した要素の中身を示す。HTMLで記載するとそれが反映される
- innerTEXT: 指定した要素の中身を示す。HTMLタグは解析されずそのまま表示される
- dataset: data-接頭辞を付けた属性を取得するのに使う。
data-foo="bar"
と設定していた場合dataset.foo
で"bar"を取得可能- tagName: tagの名前を示す
javascriptでの要素の変更
var element = document.createElement(tagName[, options]);
tagNameに作成したいタグ名を文字列で指定するとそのタグの要素を作ることができる。let insertedNode = parentNode.insertBefore(newNode, referenceNode)
parentNode配下にある、referenceNodeの直前にnewNodeを追加する。
referenceNodeにnullを指定すると最後に追加される。insertBeforelet parent = document.querySelector('div'); let reference = document.getElementById('p2'); let newNode1 = document.createElement('input'); let newNode2 = document.createElement('button'); parent.insertBefore(newNode1, reference); parent.insertBefore(newNode2, null);<div> <p id="p1">p1</p> <p id="p2">p2</p> </div> <!-- 上記HTMLが以下のようになる --> <div> <p id="p1">p1</p> <input> <p id="p2">p2</p> <button></button> </div>
var aChild = element.appendChild(aChild)
elementの直下(一番後ろ)に要素を追加する。let div = document.querySelector('div'); div.appendChild(document.createElement('input'));<div> Content <div> <!-- 上記HTMLが以下のようになる --> <div> Content <input></input> <div>node.remove()
nodeを削除する。DOMイベント
eventオブジェクト
イベントの伝搬
カスタムイベントの作成
-->時間がなく、それなりに何とかなる自信があったので省略。データ保持
ブラウザ側からデータを保存する際には以下3つを使用できる。
cookie
- 容量は4KB
- document.cookieで取得できる
- HTTPリクエストを送ると一緒に送られる
- 有効期限を任意に設定できる
- クライアントだけでなくサーバ側でも設定可能
- key=valueの組み合わせとなり、複数のcookieをセットする場合はセミコロンで区切る
- cookieを削除するにはexpires=過去の日付もしくはmax-age=0を付与する
cookiedocument.cookie = "name=test"; console.log(document.cookie); // name=test; ... すべてのクッキーが表示される //特定のcookieを取得する document.cookie.split(";").find(row => row.startsWith("name")).split("=")[1]; // test document.cookie = "name=test; max-age=0"; console.log(document.cookie); // nullsessionStorage
- 容量は5MB
- window.sessionStorageで取得できる
- セッションが終了するとデータは破棄される
sessionStorage.setItem("key", "value");
で値を設定できるsessionStorage.getItem("key");
で値を取得できる- 削除するには
sessionStorage.removeItem("key");
を使用する。sessionStorageconsole.log(sessionStorage); // Storage {length: 0} sessionStorage.setItem("name", "test"); console.log(sessionStorage); // Storage {name: "test", length: 1} console.log(sessionStorage.getItem("name")); // test sessionStorage.removeItem("name"); console.log(sessionStorage); Storage {length: 0}localStorage
- 容量は10MB
- window.localStorageで取得できる
- セッションが終了してもデータは破棄されず、明示的に削除しない限り残り続ける
- 使い方はsessionStorageと一緒
- 投稿日:2021-01-14T09:50:06+09:00
VSCode拡張機能(自動ブラウザリロード)
HTML CSS JavaScript 学習効率化Live Serverの使い方
使用環境: macOS
エディタ: VSCodeLive Serverとは
VSCode上のファイルの変更を監視して、ブラウザを自動でリロードしてくれるVSCodeの拡張機能です。
通常VSCode使ってプログラミング学習をする場合、
ファイルを編集 -> ブラウザをリロード -> console.logを確認
といった形で学習を進められるかと思います。そこで、Live Serverを使えばファイルが更新されるたびに
自動でブラウザがリロードされるのでログの確認などがスムーズにおこなえます。導入は簡単で、VSCodeの拡張機能検索窓(画像の四角のマークをクリック)に
Live Server と入力して検索。
以下画像のLive Serverをインストールします。
あとはご自身で作成したindex.htmlファイルなどをVSCodeで開き、
VSCode下部の Go Live をクリックするとOSデフォルトのブラウザでローカルサーバが立ち上がります。
※Go Liveが表示されない場合はVSCodeを一度終了する。
※同様にVSCode下部のPort : 5500 をクリックするとサーバーを停止できます。
これでファイルが更新されるたびにブラウザがリロードされるようになります。初学者の方など、少しでも参考いただければ幸いです^^
また、LGTMやコメントいただけると励みになります!
- 投稿日:2021-01-14T02:43:49+09:00
Pure JavaScript 高速 SHA-256 ハッシュ実装
SHA-256 のダイジェスト・ハッシュ値を計算する高速なピュア JavaScript 実装のライブラリを npm で公開したので紹介します。
Uint8Array
・Int32Array
を使うことで、このほかのピュア JavaScript 実装のライブラリよりも高速にハッシュ値を計算します。SHA-2 (SHA-256) 版
SHA-256 は、256ビット(16進数64桁)のハッシュ値です。
例:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
→ https://www.npmjs.com/package/sha256-uint8array
const createHash = require("sha256-uint8array").createHash; const text = ""; const hex = createHash().update(text).digest("hex"); // => "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" const data = new Uint8Array(0); const hash = createHash().update(data).digest(); // => <Uint8Array e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55>インターフェースは、Node.js の ネイティブの crypto モジュールのサブセットです。
v0.9.0 時点のベンチマーク結果は以下の通り。(macOS 10.15.7 Intel Core i7 3.2GHz)
1KB 程度の JSON と日本語文字列を、各 10,000 回ずつハッシュ計算したときのミリ秒です。
module version node.js V14 Chrome 87 Safari 14 minified backend crypto - 103ms ? - - - OpenSSL sha256-uint8array 0.9.0 274ms 446ms ? 243ms ? 3KB ? Uint8Array crypto-js 4.0.0 805ms 910ms 918ms 108KB Uint8Array jssha 3.2.0 835ms 892ms 913ms 10KB Uint8Array hash.js 1.1.7 635ms 611ms 1,577ms 7KB Array sha.js 2.4.11 356ms 965ms 3,512ms 27KB Buffer create-hash 1.2.0 381ms 1,002ms 3,502ms 97KB Buffer jshashes 1.0.8 1,450ms 2,239ms 1,164ms 23KB Array 1回あたりのハッシュ計算にかかる時間は、マイクロ秒単位なので、いろんな用途で使えそうです。
そのほかの特徴:
- 入力は、文字列または
Uint8Array
- 出力は、16進数文字列または
Uint8Array
- 複数チャンクに分割された入力にも対応しています。
- 絵文字など
U+10000
以降のサロゲートペア、UTF-8 で4バイトの文字に対応しています。- IE11 でも動きます。(IE10 ですら動くようだ)
SHA-1 版
同じインターフェースで SHA-1 版もあります。
SHA-1 は、160ビット(16進数40桁)のハッシュ値です。
例:da39a3ee5e6b4b0d3255bfef95601890afd80709
→ https://www.npmjs.com/package/sha1-uint8array
const createHash = require("sha1-uint8array").createHash; const text = ""; const hex = createHash().update(text).digest("hex"); // => "da39a3ee5e6b4b0d3255bfef95601890afd80709" const data = new Uint8Array(0); const hash = createHash().update(data).digest(); // => <Uint8Array da 39 a3 ee 5e 6b 4b 0d 32 55 bf ef 95 60 18 90 af d8 07 09>v0.9.0 時点のベンチマーク結果は以下の通り。
module version node.js V14 Chrome 87 Safari 14 minified backend crypto - 70ms ? - - - OpenSSL sha1-uint8array 0.9.0 218ms 346ms ? 192ms ? 2KB ? Uint8Array hash.js 1.1.7 513ms 573ms 908ms 7KB Array jssha 3.2.0 690ms 782ms 770ms 9KB Uint8Array crypto-js 4.0.0 779ms 829ms 961ms 108KB Uint8Array jshashes 1.0.8 686ms 1,448ms 727ms 23KB Array tiny-sha1 0.2.1 209ms 775ms 3,573ms 2KB Uint8Array sha.js 2.4.11 360ms 930ms 3,534ms 26KB Buffer create-hash 1.2.0 387ms 976ms 3,591ms 97KB Buffer 古いライブラリだと ASCII 文字列のみ対応で、日本語すら使えないものもあったので、注意が必要。
上記リストの各ライブラリごとの minified したときの容量や、バックエンド(内部で使われる仕組み)は、ざっくり調査したものなので、厳密には違うかも。高速化実装のキモ
JavaScript の実行を高速化するには、できるだけオブジェクトを作らない・メモリを確保しない実装が大切です。
V8 なら、確保されたメモリ内で済む処理ならば、かなり高速に動作してくれます。本ライブラリでは、1回のハッシュ値計算ごとに、3つオブジェクトを作っています。
もし複数チャンク分割に対応せずに、1入力だけとすれば、再入を考慮せずに済むので、さらに減らせるのだが。
メモリは 8KB のプールを分割して利用することで、毎回はメモリを確保しないようにしています。また、文字列からの入力時は、JavaScript の内部の文字コードは UTF-16 なので、UTF-8 への変換が必要。
文字列全体をいちどに TypedArray に展開するのではなくて、64・80バイト毎のブロックの範囲ごとに変換しています。
どちらかというと、この処理を書いてみたくて、このライブラリを作ったようなものだ。ネイティブ実装との速度比較
上記のベンチマークの通り、ほかのピュア JavaScript 実装のライブラリよりは高速なものの、
Node.js だけで使うなら、ネイティブの crypto モジュールを使ったほうが3倍くらい速いです。
内部で使われる OpenSSL の実装がカリカリで速すぎるみたい。なお、ブラウザでは、TextEncoder と crypto.subtle.digest() の両方が使える環境であれば、その方が速いです。
iOS 10.3 以降あるいは Android 5 以降かつ、HTTPS サーバ配下なら使えるようです。IE や古い方の Edge では使えない。Node.js の
crypto
とブラウザのcrypto.subtle
はインターフェースが異なるので注意が必要。
ブラウザでは、即値ではなくPromise
を返したり、まだチャンク分割入力できないようだし、ArrayBuffer
専用のようだ。Browserify
crypto
モジュール全体を呼んだアプリを、ブラウザ向けに普通にbrowserify
すると、minify した後でも +300KB 超の容量増になります。
あるいは大きめのライブラリを使うと 100KB になるところ、本ライブラリなら 3KB 前後と、コンパクトに利用できます。
もし、crypto
モジュールをcrypto.createHash("sha256").update(data).digest("hex");
形式のハッシュ計算でしか使っていないアプリなら、下記のような browserify 設定にすることで、『Node.js ならネイティブ実装』・『ブラウザなら本ライブラリ』と切り替えて利用することができます。package.json{ "browser": { "crypto": "sha256-uint8array/dist/sha256-uint8array.min.js" }, "devDependencies": { "browserify": "^17.0.0", "sha256-uint8array": "^0.9.0", "terser": "^5.5.1" } }(備考)どのハッシュを使うか
ハッシュはいくつも種類があります。その昔は軽い MD5 を使っていました。
- MD5 は弱いので、現代のサービスでは使われないです。
- SHA-1 もすでに推奨されていないものの、用途によっては、まだまだみかけます。
- SHA-256 を使うと SHA-1 よりも2〜3割ほど遅くなるものの、大量に使う用途でもなければ、無視できるレベルの差かなと。
- SHA-368 や SHA-512 については、64ビット整数演算が必要なので、ピュア JavaScript で実用的な速度で実装するのは難しそう。
ネイティブ実装と、ピュア JavaScript 実装の両方を混在利用したい場合は、今なら SHA-256 を使うのが良さそう。
- 投稿日:2021-01-14T00:50:05+09:00
最短経路問題-ベルマンフォード法を学ぶ
最短経路問題
最短経路問題とは、ある2頂点が与えられたグラフの頂点を始点・終点とするとき、その頂点同士を結ぶ辺のコスト(距離)の和が最小とする問題です。
ベルマンフォード法
最短経路問題を解く手法の一つにベルマンフォード法というものがあります。
ベルマンフォード法では、始点から他のすべての頂点への最短経路を求めることができます。
アルゴリズム概要
始点 s から頂点 i への最短距離を d[i] , 頂点 i から頂点 j へのコストを Cij とすると以下の等式が成り立ちます。(ちなみにEは辺の集合です)
ちなみに、これは負の閉路が存在しないことをまずは前提(というのも負の閉路検出の方法もできるので)とし考えていきます。
なので、負の閉路が存在しなければ、最短経路は同じ頂点を通らないので、繰り返す回数の最大値は(頂点数−1)回になります。d[i] = min{ d[j] + Cij ∣ e = (j,i) ∈ E }式だとわからないと思うので、簡単にアルゴリズムの動きを言語化するならば下記のようになります。
1. 全ての辺に対し、その辺を通った頂点のコストが小さければ値を更新する。 2. 1のプロセスを(頂点の数-1)回行います。実装してみよう
sample.js// 初期化 const graph = [ [null, 1, 3, null, null, null], [null, null, 1, 4, null, null], [null, null, null, 1, null, null] ]; let dist = []; for(var i = 0; i < graph.length; i++){ dist[i] = Number.POSITIVE_INFINITY; } dist[0] = 0; const shortest_path = () => { const n = graph.length; for(var i = 1; i <= n; i++){ failOnUpdate = (i === n); let update = false; for(var u = 0; u < n; u++){ for(var ci = 0; ci < n; ci++){ if(!graph[u][ci]){ continue; } const newLen = dist[u] + graph[u][ci]; if(newLen < dist[ci]){ if(failOnUpdate){ throw new Error('負の閉路'); } dist[ci] = newLen; update = true; } } } if(!update){ break; } } }; // 結果 shortest_path() console.log("最短距離"); console.log(dist);参考文献
- 投稿日:2021-01-14T00:39:41+09:00
JavaScriotのfilterとsortについて
備忘録です。
const satoru = { age: 38, name: 'tanaka' }; const ken = { age: 34, name: 'yamada' }; const nozomi = { age: 30, name: 'koyama' }; const users = [ken, nozomi, satoru]; const ages = users.map(user => { const tall = user.age*5; // 年齢を5倍した数を、仮に身長の数値に見立てただけで、意味はありません。 return tall; }); const segatakaihito = ages.filter(tall => tall > 172) console.log(ages.sort().reverse()); //並び替え、背が低い順 console.log(segatakaihito);結果
(3) [190,170,150] (1) [190]