blog

JS学習シリーズ3 - 配列、算術演算子、比較演算子

はじめに\nデータ型 III - 配列\n配列の概要\n配列は、順番に並べられた値の集合です。各値の位置には番号が振られ、配列全体は角括弧で囲まれます。\nvar c = [\n 'a',\n 'b'...

Jun 1, 2020 · 11 min. read
シェア

前書き

データ型 III-

配列の概要

配列は、順番に並べられた値の集合です。各値の位置には番号が付けられ、配列全体は角括弧で囲まれます。

var c = [
 'a',
 'b',
 'c'
];
var c = [
 { 'a': 0x1 },
 [
 0x1,
 0x2,
 0x3
 ],
 function () {
 return !false;
 }
];

配列の性質

typeof演算子は、配列の型をオブジェクトとして返します。配列は、整数の連続した集合をキーとするという点で特殊です。

var b = [
 'a',
 'b',
 'c'
];
Object.keys(b);

JavaScript言語では、オブジェクトのキーは常に文字列であると規定されているため、配列のキーは実際には文字列です。配列のキーを値として読めるのは、文字列でないキー名が文字列に変換されるからです。

これは代入にも当てはまります。値は常に最初に文字列に変換され、それからキー名として代入されます。

数値キー名の場合、ドット構造は使えません。

length

配列の length 属性は、配列のメンバの数を返します。

['a', 'b', 'c'].length // 3

この属性は、キー名の最大の整数に 1 を加えた動的な値です。

配列を空にする効率的な方法は、length 属性を 0 に設定することです。

var bar = [
 'a',
 'b',
 'c'
];
bar.length = 0x0;
bar;

length 属性が配列の数より大きな値に設定されている場合、追加された位置の読み取りは未定義を返します。

var b = [];
b.c = 'abc';
b.length;
b[2.1] = 'abc';
b.length;

length 属性は最大の数値キーに 1 を足した値に等しく、配列には整数キーがないため、length 属性は 0 に設定されます。

配列のキー名が範囲外に追加された場合、キー名は自動的に文字列に変換されます。

加算演算子

キー名が存在するかどうかを調べる in 演算子は、配列だけでなくオブジェクトにも適用されます。

var a = [
 'a',
 'b',
 'c'
];
0x2 in a;
'2' in a;
0x4 in a;

for...ループと配列の走査

for....ループはオブジェクトだけでなく配列もトラバースすることができます。

var obj = [
 0x1,
 0x2,
 0x3
];
obj.b = !false;
for (var foo in obj) {
 console.log(foo);
}

上記のコードでは、配列をトラバースする際に非整数のキーfooもトラバースしてしまうので、for.....を使用することはお勧めしません。forループやwhileループを使って配列を走査することを検討してください。**

var a = [1, 2, 3];
// for 
for(var i = 0; i < a.length; i++) {
 console.log(a[i]);
}
// while 
var i = 0;
while (i < a.length) {
 console.log(a[i]);
 i++;
}
var l = a.length;
while (l--) {
 console.log(a[l]);
}

配列のforEachメソッドも配列の走査に使用できます。詳細は標準ライブラリの「配列オブジェクト」の章を参照してください。

var c = [
 'red',
 'green',
 'blue'
];
c.forEach(function (a) {
 console.log(a);
});

配列内の空白

配列のある位置が空要素である場合、つまり2つのカンマの間に値がない場合、その配列はヌル位置であると言われます。

var c = [
 0x1,
 ,
 0x1
];
c.length;
var c = [
 0x1,
 0x2,
 0x3
];
c.length;
c;

deleteコマンドで配列メンバを削除すると、ヌル位置が作成され、length属性には影響しません。

var foo = [
 0x1,
 0x2,
 0x3
];
delete foo[0x1];
foo[0x1];
foo.length;

つまり、length属性は空白をフィルタリングしません。

ヌル位置の配列は、未定義の位置の配列とは異なります。ヌル位置の場合は、配列の forEach メソッド、for.....in構造体、およびObject.keysメソッドを使用してトラバーサルすると、ヌル位置はスキップされます。位置が未定義の場合は、トラバース時にスキップされません

配列のようなオブジェクト

オブジェクトのキーがすべて正の整数または0であり、length属性を持つ場合、そのオブジェクトは配列に似ており、構文上 "配列類似オブジェクト "と呼ばれます。

var b = {
 0x0: 'a',
 0x1: 'b',
 0x2: 'c',
 'length': 0x3
};
b[0x0];
b[0x1];
b.length;
b.push('d');

配列に似たオブジェクト」の基本的な特徴は、length属性を持つことです。length属性がある限り、そのオブジェクトは配列に似ていると考えることができます。しかし、length属性は動的な値ではなく、メンバが変わっても変化しないという問題があります。

配列のslice メソッドを使用すると、"配列のようなオブジェクト" を本当の配列にすることができます。

var arr = Array.prototype.slice.call(arrayLike);

配列のようなオブジェクト "が配列のメソッドを使用するには、実際の配列に変換する以外に、call()を使用してオブジェクトの上に配列のメソッドを置く方法があります。

function obj(b, foo) {
 console.log(foo + '\x20:\x20' + b);
}
Array.prototype.forEach.call(arrayLike, obj);

以下はその例です:

// forEach  
function logArgs() {
 Array.prototype.forEach.call(arguments, function (elem, i) {
 console.log(i + '. ' + elem);
 });
}
// forループに相当する
function logArgs() {
 for (var i = 0; i < arguments.length; i++) {
 console.log(i + '. ' + arguments[i]);
 }
}
Array.prototype.forEach.call('abc', function (chr) {
 console.log(chr);
});
// a
// b
// c

この方法は、配列のネイティブメソッドである forEach を直接使うよりも遅いので、「配列ライクなオブジェクト」を本当の配列にしてから、配列の forEach メソッドを直接呼び出すほうがよいでしょう

var foo = Array.prototype.slice.call('abc');
foo.forEach(function (c) {
 console.log(c);
});

算術演算子

加算演算子: x+ y
減算演算子: x- y
乗算演算子: x* y
除算演算子: x/ y
指数演算子: x** y
剰余演算子: x% y
自己増加演算子++x または x++
自己減少演算子--x または x--
数値演算子+x
負の値演算子-x

加算演算子

JavaScript では、数値以外の値の加算が可能です。

!false + !false;
0x1 + !false;
'a' + 'bc';
0x1 + 'a';
false + 'a';
'3' + 0x4 + 0x5;

演算子によって構文の挙動が異なるのは、「オーバーロード」として知られる現象です。加算演算子以外の算術演算子はオーバーロードされません。すべての演算子は数値に変換され、対応する数学演算が実行されるというルールです。

オブジェクトの追加

演算子がオブジェクトの場合は、加算する前に元の型の値に変換する必要があります。

var obj = { p: 1 };
obj + 2 // "[object Object]2"

オブジェクトは、以下のルールで元の型の値に変換されます:

  1. まず、自動的にオブジェクトのvalueOfメソッドを呼び出します。
  2. 一般に、オブジェクトの valueOf メソッドは常にオブジェクト自身を返し、次にオブジェクトのtoStringメソッドを自動的に呼び出して文字列に変換します。
  3. オブジェクトの toString メソッドは、デフォルトで [object Object] を返します。

![var obj = { p: 1 };
obj.valueOf()]().toString() // "[object Object]"

このルールを知った上で、独自の valueOf メソッドや toString メソッドを定義すれば、望みの結果を得ることができます。

var b = {
 'valueOf': function () {
 return 0x1;
 }
};
b + 0x2;
var b = {
 'toString': function () {
 return 'hello';
 }
};
b + 0x2;

一般的には、valueOfメソッドはオーバーラップの後に優先されます。

しかし、特殊なケースとして、演算子がDate オブジェクトのインスタンスの場合、toString メソッドが先に実行されます。
var a = new Date();
a.valueOf = function () {
 return 0x1;
};
a.toString = function () {
 return 'hello';
};
a + 0x2;

剰余演算子

剰余演算子は、前の演算子を次の演算子で割ったときの負の数の余りを返します。負の数の正しい余り値を得るには、まず絶対値関数を使用します。

function b(a) {
 return a % 0x2 === 0x1;
}
b(-0x5);
b(-0x4);
function b(bar) {
 return Math.abs(bar % 0x2) === 0x1;
}
b(-0x5);
b(-0x4);

浮動小数点数は正確な値ではないので、完全に正確な結果を得ることはできません。

自己増加演算子と自己減少演算子

単項演算子である自己加算演算子と自己減算演算子は、演算子を1つだけ必要とします。その機能は、まず演算子を数値に変換し、次に 1 を加算または 1 を減算することで、元の変数を変更します。

インクリメント演算子とサブトラクト演算子について注意すべき点は、変数の後に置かれた場合は、その変数が操作される前の値を返し、その後インクリメント/サブトラクトを行います。

var x = 1;
var y = 1;
x++ // 1
++y // 2

数値演算子、負の数値演算子

数値演算子もプラス記号を使いますが、単項演算子です。数値演算子は、どんな値でも数値に変換できるので便利です。

負の数値演算子も、値を数値に変換する機能を持ちますが、結果の値は逆になります。負の数値演算子を 2 つ並べて使うと、数値演算子と同じになります。

+!false + [] + {};
var a = 0x1;
-a - -a;

数値記号も負の数値演算子も、元の変数の値を変えずに新しい値を返します。

指数演算子

前者が底、後者が指数です。

2 ** 4 // 16

指数演算子は左抱合ではなく右抱合であることに注意してください。つまり、複数の指数演算子を併用する場合、右端の計算が最初に実行されます。

// 2 ** 2 ** 3 ** 2 // 215 に相当します。

代入演算子

変数に値を代入するために使用します。

複合代入演算子は、すべて指定された演算を最初に行い、その結果の値を左側の変数に返します。

比較演算子

比較演算子は 2 つの値を比較し、指定された条件を満たすかどうかを示すブール値を返します。

2 ** 4 // 16

8 つの比較演算子は、等しい比較と等しくない比較の 2 つのカテゴリに分かれています。

  • 等しくない比較の場合、アルゴリズムはまず両方の演算子が文字列かどうかを確認し、文字列の場合は辞書順に比較し、そうでない場合は両方の演算子を数値に変換してから値の大小を比較します。

非等価演算子:文字列の比較

文字列は辞書順に比較されます。内部的には、JavaScript エンジンはまず最初の文字の Unicode コードポイントを比較します。等しい場合、2 番目の文字の Unicode コードポイントが比較され、以下同様です。

// 2と等価である。** (3 ** 2)
2 ** 3 ** 2
// 512

非等価演算子: 文字列以外の比較

2つの演算子の少なくとも一方が文字列でない場合、次の2つのケースに分ける必要があります:

生の型の値

両方の演算子がプリミティブ値である場合、数値に変換してから比較します。

> 大なり演算子
< 小なり演算子
<= 小なりまたは等しい演算子
>= 大なり小なり演算子
== 等式演算子
=== 厳密等号演算子
!= 不等号演算子
!== 厳密な不等号演算子

NaNとの比較に注意してください。NaNと比較された値はfalseを返します。詳細はJS学習シリーズ1を参照してください。

オブジェクト

演算子がオブジェクトの場合、元の型の値に変換され、再度比較されます。

オブジェクトを元の型に変換するアルゴリズムは、まず valueOf メソッドを呼び出します

'cat' & gt;
'dog';
'cat' & gt;
'catalog';
'cat' & gt;
'Cat';

厳密な等号演算子

JavaScript には、2 種類の等号演算子 == と === があります。

等号演算子は2つの値が等しいかどうかを比較し、厳密等号演算子は2つの値が「同じ値」であるかどうかを比較します。2つの値が同じ型でない場合、厳密等号演算子は偽を返し、等号演算子は同じ型に変換して厳密等号演算子で比較します。

  • 2つの値が異なる型の場合は、そのまま偽を返します。
  • 同じ型のプリミティブ型の値を比較する場合、値が同じなら真を、異なるなら偽を返します。
    • NaNはどの値とも等しくないことに注意。また、正の0は負の0に等しい。
  • 2つの複合型のデータを比較する場合は、値が等しいかどうかを比較するのではなく、同じアドレスを指しているかどうかを比較します。
    • 2つの空のオブジェクト、2つの空の配列、2つの空の関数を比較すると、不等式になります。その理由は、複合値の場合、厳密な等号演算はそれらが同じメモリ・アドレスを指すかどうかを比較しますが、演算子の両側にある空のオブジェクト、空の配列、空の関数の値は異なるメモリ・アドレスに格納されるため、結果は確実に偽になるからです。
    • 2つのオブジェクトを比較する場合、厳密等号演算子はアドレスを比較し、大なり小なり演算子は値を比較することに注意してください。
  • undefinedとnullは厳密に等しくなります。
    • したがって、宣言されただけで値が割り当てられていない2つの変数は等しくなります。

厳密には等しくない演算子

この演算子のアルゴリズムは、まず厳密な等号演算子の結果を求め、その反対の値を返すというものです。

加算演算子

等号演算子は、厳密等号演算子と全く同じで、同じ型のデータを比較するために使用されます。

異なる型のデータを比較する場合、等号演算子は厳密等号演算子と比較する前にデータを変換します。

プリミティブ型

プリミティブ型の値は、比較のために数値に変換されます。

0x5 & gt;
'4';
!false & gt;
false;
0x2 & gt;
!false;

オブジェクト

オブジェクトがプリミティブ型の値と比較される場合、オブジェクトはプリミティブ型の値に変換され、比較されます。

var x = [2];
x > '11' // true
// と等価である。[2].valueOf().toString() > '11'
//  '2' > '11'
x.valueOf = function () { return '1' };
x > '11' // false
// と等価である。[2].valueOf() > '11'
//  '1' > '11'
[2] > [1] // true
// と等価である。[2].valueOf().toString() > [1].valueOf().toString()
//  '2' > '1'
[2] > [11] // true
// と等価である。[2].valueOf().toString() > [11].valueOf().toString()
//  '2' > '11'
{ x: 2 } >= { x: 1 } // true
// と等価である。{ x: 2 }.valueOf().toString() >= { x: 1 }.valueOf().toString()
//  '[object Object]' >= '[object Object]'

配列[1]を数値と比較する場合は、数値に変換してから比較します。文字列と比較する場合は、文字列に変換してから比較します。ブーリアン値と比較する場合は、オブジェクトとブーリアンの両方を数値に変換してから比較します。

プリミティブ型

undefinedとnullは、どちらも他の型の値と比較するとfalseになり、互いに比較するとtrueになります。

オブジェクト

等号演算子によって隠された型変換は、いくつかの直感に反する結果につながる可能性があります。

0x1 == !false;
0x0 == false;
0x2 == !false;
0x2 == false;
'true' == !false;
'' == 0x0;
'' == false;
'1' == !false;
'\x0a\x20123\x20\x09' == 0x7b;

したがって、等号演算子は使わないことを推奨します。

不等号演算子

この演算子のアルゴリズムは、まず等号演算子の結果を求め、その反対の値を返すというものです。

Read next

Springbootプロジェクトのトランザクション設定 @グローバルトランザクション管理を実現するためのトランザクションアノテーションとAOP

その後、いくつかの学生は、あなたがその上のメソッドを持つクラスで構成する場合は、粒度が細かいので、それは推奨されるメソッドのオーバーライドクラスの上のメソッドでなければなりません、質問する必要があります。

Jun 1, 2020 · 4 min read