すべてのプログラマーが知っておくべき JavaScript の「隠れた機能」は何だと思いますか?
以下の質問に対する質の高い回答を見た後、JavaScript について質問する時が来たと思いました。
JavaScript は間違いなく現在最も重要なクライアント側言語ですが (Google に聞いてみてください)、ほとんどの Web 開発者が JavaScript が実際にどれほど強力であるかを認識していないのは驚くべきことです。
すべてのプログラマーが知っておくべき JavaScript の「隠れた機能」は何だと思いますか?
以下の質問に対する質の高い回答を見た後、JavaScript について質問する時が来たと思いました。
JavaScript は間違いなく現在最も重要なクライアント側言語ですが (Google に聞いてみてください)、ほとんどの Web 開発者が JavaScript が実際にどれほど強力であるかを認識していないのは驚くべきことです。
関数のパラメーターを定義する必要はありません。関数のarguments
配列のようなオブジェクトを使用するだけです。
function sum() {
var retval = 0;
for (var i = 0, len = arguments.length; i < len; ++i) {
retval += arguments[i];
}
return retval;
}
sum(1, 2, 3) // returns 6
Douglas Crockford の優れた本 JavaScript: The Good Partsのほとんどを引用できます。
ただし、1 つだけ取り上げます。andの代わりに常に===
andを使用します。!==
==
!=
alert('' == '0'); //false
alert(0 == ''); // true
alert(0 =='0'); // true
==
は推移的ではありません。これを使用===
すると、予想どおり、これらのすべてのステートメントに対して false が返されます。
関数は JavaScript の第一級市民です。
var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };
var sum = function(x,y,z) {
return x+y+z;
};
alert( passFunAndApply(sum,3,4,5) ); // 12
関数型プログラミング手法を使用して、洗練された JavaScript を作成できます。
特に、関数はパラメーターとして渡すことができます。たとえば、Array.filter()はコールバックを受け入れます。
[1, 2, -1].filter(function(element, index, array) { return element > 0 });
// -> [1,2]
特定の関数のスコープ内にのみ存在する「プライベート」関数を宣言することもできます。
function PrintName() {
var privateFunction = function() { return "Steve"; };
return privateFunction();
}
in演算子を使用して、オブジェクトにキーが存在するかどうかを確認できます。
var x = 1;
var y = 3;
var list = {0:0, 1:0, 2:0};
x in list; //true
y in list; //false
1 in list; //true
y in {3:0, 4:0, 5:0}; //true
オブジェクト リテラルがあまりにも醜い場合は、パラメーターなしの関数のヒントと組み合わせることができます。
function list()
{ var x = {};
for(var i=0; i < arguments.length; ++i) x[arguments[i]] = 0;
return x
}
5 in list(1,2,3,4,5) //true
変数へのデフォルト値の割り当て
代入式で論理 or 演算子||
を使用して、デフォルト値を指定できます。
var a = b || c;
a
変数は、が偽の場合 ( 、、、、またはの場合)c
のみの値を取得し、そうでない場合は の値を取得します。b
null
false
undefined
0
empty string
NaN
a
b
これは、引数が指定されていない場合に備えて引数にデフォルト値を与えたい場合に、関数でよく役立ちます。
function example(arg1) {
arg1 || (arg1 = 'default value');
}
イベント ハンドラでの IE フォールバックの例:
function onClick(e) {
e || (e = window.event);
}
次の言語機能は長い間使用されており、すべての JavaScript 実装でサポートされていますが、ECMAScript 5th Editionまで仕様の一部ではありませんでした:
debugger
ステートメント_
説明: § 12.15 デバッガー ステートメント
このステートメントを使用すると、次のようにしてコードにプログラムでブレークポイントを配置できます。
// ...
debugger;
// ...
デバッガーが存在するかアクティブな場合、その行ですぐに中断します。
それ以外の場合、デバッガーが存在しないかアクティブでない場合、このステートメントは目に見える効果がありません。
複数行の文字列リテラル
説明: § 7.8.4 文字列リテラル
var str = "This is a \
really, really \
long line!";
の隣の文字は行末記号で\
なければならない\
ため、注意が必要です。たとえば、の後にスペースがある場合、コードはまったく同じように見えますSyntaxError
が、 .
JavaScript にはブロック スコープがありません(ただし、クロージャがあるので、それを呼びましょうか?)。
var x = 1;
{
var x = 2;
}
alert(x); // outputs 2
[]
の代わりにオブジェクトのプロパティにアクセスできます.
これにより、変数に一致するプロパティを検索できます。
obj = {a:"test"};
var propname = "a";
var b = obj[propname]; // "test"
これを使用して、名前が正当な識別子ではないオブジェクト プロパティを取得/設定することもできます。
obj["class"] = "test"; // class is a reserved word; obj.class would be illegal.
obj["two words"] = "test2"; // using dot operator not possible with the space.
一部の人々はこれを知らず、このようにeval()を使用することになりますが、これは本当に悪い考えです:
var propname = "a";
var a = eval("obj." + propname);
これは読みにくく、エラーを見つけるのが難しく (jslint を使用できません)、実行が遅く、XSS エクスプロイトにつながる可能性があります。
特定のトピックに関する適切な JavaScript リファレンスをグーグルで検索している場合は、クエリに「mdc」キーワードを含めてください。最初の結果は Mozilla Developer Center から取得されます。オフラインの参考資料や本は持ち歩いていません。私は常に「mdc」キーワード トリックを使用して、探しているものに直接アクセスします。例えば:
Google: javascript array sort mdc
(ほとんどの場合、「javascript」は省略できます)
更新: Mozilla Developer Centerは Mozilla Developer Networkに名前が変更されました。「mdc」キーワード トリックは引き続き機能しますが、すぐに代わりに「mdn」を使用する必要があるかもしれません。
多分少し明白な人もいます...
Firebugをインストールし、console.log( "hello")を使用します。ランダムなalert();を使用するよりもはるかに優れています。これは、数年前によく行ったことを覚えています。
プライベート メソッド
オブジェクトはプライベート メソッドを持つことができます。
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
// A private method only visible from within this constructor
function calcFullName() {
return firstName + " " + lastName;
}
// A public method available to everyone
this.sayHello = function () {
alert(calcFullName());
}
}
//Usage:
var person1 = new Person("Bob", "Loblaw");
person1.sayHello();
// This fails since the method is not visible from this scope
alert(person1.calcFullName());
Crockford の「Javascript: The Good Parts」でも言及されています。
parseInt()
危険です。適切な基数を通知せずに文字列を渡すと、予期しない数値が返される場合があります。たとえばparseInt('010')
、10 ではなく 8 を返します。base を parseInt に渡すと、正しく機能します。
parseInt('010') // returns 8! (in FF3)
parseInt('010', 10); // returns 10 because we've informed it which base to work with.
関数はオブジェクトであるため、プロパティを持つことができます。
fn = 関数(x) { // ... } fn.foo = 1; fn.next = 関数(y) { /// }
私は自己実行関数を言わなければならないでしょう。
(function() { alert("hi there");})();
Javascriptにはブロックスコープがないため、ローカル変数を定義する場合は、自己実行関数を使用できます。
(function() {
var myvar = 2;
alert(myvar);
})();
ここで、myvar
isはグローバルスコープに干渉したり汚染したりすることはなく、関数が終了すると消えます。
関数によって期待されるパラメーターの数を知る
function add_nums(num1, num2, num3 ){
return num1 + num2 + num3;
}
add_nums.length // 3 is the number of parameters expected.
関数が受け取るパラメーターの数を知る
function add_many_nums(){
return arguments.length;
}
add_many_nums(2,1,122,12,21,89); //returns 6
ここにいくつかの興味深いことがあります:
NaN
含む何か(でも)との比較NaN
は常に偽です。==
<
>
NaN
Not a Numberの略ですが、タイプを要求すると、実際には数値が返されます。Array.sort
コンパレータ関数を使用でき、クイックソートのようなドライバによって呼び出されます(実装によって異なります)。$0
ます。$1
$2
null
他のものとは異なります。オブジェクトでも、ブール値でも、数値でも、文字列でも、 。でもありませんundefined
。これは「代替」に少し似ていundefined
ます。(注typeof null == "object"
:)this
は、他の方法では名前を付けられない[Global]オブジェクトを生成します。var
変数の自動宣言だけに頼るのではなく、で変数を宣言すると、ランタイムはその変数へのアクセスを最適化する本当のチャンスを得ることができます。with
コンストラクトはそのような最適化を破壊しますbreak
。ループにラベルを付けて、のターゲットとして使用できますcontinue
。undefined
ます。(実装によって異なります)if (new Boolean(false)) {...}
{...}
ブロック を実行します[良いコメントに応えて少し更新しました。コメントをご覧ください]
私はパーティーに遅れていることを知っていますが、+
演算子の有用性が「何かを数値に変換する」以外に言及されていないとは信じられません. 多分それはそれがどれほどうまく隠されている機能ですか?
// Quick hex to dec conversion:
+"0xFF"; // -> 255
// Get a timestamp for now, the equivalent of `new Date().getTime()`:
+new Date();
// Safer parsing than parseFloat()/parseInt()
parseInt("1,000"); // -> 1, not 1000
+"1,000"; // -> NaN, much better for testing user input
parseInt("010"); // -> 8, because of the octal literal prefix
+"010"; // -> 10, `Number()` doesn't parse octal literals
// A use case for this would be rare, but still useful in cases
// for shortening something like if (someVar === null) someVar = 0;
+null; // -> 0;
// Boolean to integer
+true; // -> 1;
+false; // -> 0;
// Other useful tidbits:
+"1e10"; // -> 10000000000
+"1e-4"; // -> 0.0001
+"-12"; // -> -12
もちろん、Number()
代わりに を使用してこれらすべてを行うこともできますが、+
オペレーターの方がはるかにきれいです!
valueOf()
プロトタイプのメソッドをオーバーライドして、オブジェクトの数値の戻り値を定義することもできます。そのオブジェクトに対して実行された数値変換はではなく、メソッドNaN
の戻り値になります。valueOf()
var rnd = {
"valueOf": function () { return Math.floor(Math.random()*1000); }
};
+rnd; // -> 442;
+rnd; // -> 727;
+rnd; // -> 718;
" JavaScript の拡張メソッド" は、prototype プロパティを介して。
Array.prototype.contains = function(value) {
for (var i = 0; i < this.length; i++) {
if (this[i] == value) return true;
}
return false;
}
contains
これにより、すべてのArray
オブジェクトにメソッドが追加されます。この構文を使用してこのメソッドを呼び出すことができます
var stringArray = ["foo", "bar", "foobar"];
stringArray.contains("foobar");
オブジェクトからプロパティを適切に削除するには、単にundefinedに設定するのではなく、プロパティを削除する必要があります:
var obj = { prop1: 42, prop2: 43 };
obj.prop2 = undefined;
for (var key in obj) {
...
プロパティprop2は引き続き反復の一部になります。prop2を完全に取り除きたい場合は、代わりに次のようにする必要があります。
delete obj.prop2;
プロパティを繰り返し処理しているときに、プロパティprop2が表示されなくなります。
with
.
めったに使用されず、率直に言って、めったに役に立ちません... しかし、限られた状況では、その用途があります。
たとえば、オブジェクト リテラルは、新しいオブジェクトのプロパティをすばやく設定するのに非常に便利です。しかし、既存のオブジェクトのプロパティの半分を変更する必要がある場合はどうすればよいでしょうか?
var user =
{
fname: 'Rocket',
mname: 'Aloysus',
lname: 'Squirrel',
city: 'Fresno',
state: 'California'
};
// ...
with (user)
{
mname = 'J';
city = 'Frostbite Falls';
state = 'Minnesota';
}
Alan Storm は、これはやや危険である可能性があると指摘しています。コンテキストとして使用されるオブジェクトに、割り当てられているプロパティが 1 つも含まれていない場合、外側のスコープで解決され、グローバル変数が作成または上書きされる可能性があります。これは、デフォルト値または空の値を持つプロパティが未定義のままになっているオブジェクトを操作するコードを書くことに慣れている場合は特に危険です。
var user =
{
fname: "John",
// mname definition skipped - no middle name
lname: "Doe"
};
with (user)
{
mname = "Q"; // creates / modifies global variable "mname"
}
with
したがって、このような代入にはステートメントを使用しないことをお勧めします。
メソッド (または関数) は、動作するように設計された型ではないオブジェクトで呼び出すことができます。これは、カスタム オブジェクトでネイティブ (高速) メソッドを呼び出すのに最適です。
var listNodes = document.getElementsByTagName('a');
listNodes.sort(function(a, b){ ... });
listNodes
ではないため、このコードはクラッシュします。Array
Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);
このコードが機能するlistNodes
のは、 によって使用される配列のようなプロパティ (長さ、[] 演算子) が十分に定義されているためsort()
です。
プロトタイプの継承(Douglas Crockford によって普及) は、Javascript の多くのことについての考え方に完全に革命をもたらします。
Object.beget = (function(Function){
return function(Object){
Function.prototype = Object;
return new Function;
}
})(function(){});
それはキラーです!ほとんど誰も使っていないのが残念です。
これにより、任意のオブジェクトの新しいインスタンスを「生成」し、それらを拡張しながら、他のプロパティへの (ライブ) プロトタイプ継承リンクを維持できます。例:
var A = {
foo : 'greetings'
};
var B = Object.beget(A);
alert(B.foo); // 'greetings'
// changes and additionns to A are reflected in B
A.foo = 'hello';
alert(B.foo); // 'hello'
A.bar = 'world';
alert(B.bar); // 'world'
// ...but not the other way around
B.foo = 'wazzap';
alert(A.foo); // 'hello'
B.bar = 'universe';
alert(A.bar); // 'world'
これを好みの問題と呼ぶ人もいますが、
aWizz = wizz || "default";
// same as: if (wizz) { aWizz = wizz; } else { aWizz = "default"; }
三項演算子は、Scheme の (cond ...) のように動作するように連鎖できます。
(cond (predicate (action ...))
(predicate2 (action2 ...))
(#t default ))
...のように書くことができます
predicate ? action( ... ) :
predicate2 ? action2( ... ) :
default;
これは、副作用なしでコードを分岐するため、非常に「機能的」です。したがって、代わりに:
if (predicate) {
foo = "one";
} else if (predicate2) {
foo = "two";
} else {
foo = "default";
}
あなたは書ける:
foo = predicate ? "one" :
predicate2 ? "two" :
"default";
再帰でもうまく動作します:)
数字もオブジェクトです。だからあなたは次のようなクールなことをすることができます:
// convert to base 2
(5).toString(2) // returns "101"
// provide built in iteration
Number.prototype.times = function(funct){
if(typeof funct === 'function') {
for(var i = 0;i < Math.floor(this);i++) {
funct(i);
}
}
return this;
}
(5).times(function(i){
string += i+" ";
});
// string now equals "0 1 2 3 4 "
var x = 1000;
x.times(function(i){
document.body.innerHTML += '<p>paragraph #'+i+'</p>';
});
// adds 1000 parapraphs to the document
JavaScript のクロージャーはどうですか(C# v2.0+ の匿名メソッドに似ています)。関数または「式」を作成する関数を作成できます。
閉鎖の例:
//Takes a function that filters numbers and calls the function on
//it to build up a list of numbers that satisfy the function.
function filter(filterFunction, numbers)
{
var filteredNumbers = [];
for (var index = 0; index < numbers.length; index++)
{
if (filterFunction(numbers[index]) == true)
{
filteredNumbers.push(numbers[index]);
}
}
return filteredNumbers;
}
//Creates a function (closure) that will remember the value "lowerBound"
//that gets passed in and keep a copy of it.
function buildGreaterThanFunction(lowerBound)
{
return function (numberToCheck) {
return (numberToCheck > lowerBound) ? true : false;
};
}
var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];
var greaterThan7 = buildGreaterThanFunction(7);
var greaterThan15 = buildGreaterThanFunction(15);
numbers = filter(greaterThan7, numbers);
alert('Greater Than 7: ' + numbers);
numbers = filter(greaterThan15, numbers);
alert('Greater Than 15: ' + numbers);
クラスを拡張 (継承)し、 spoon16 で言及したプロトタイプ チェーンを使用してプロパティ/メソッドをオーバーライドすることもできます。
次の例では、クラス Pet を作成し、いくつかのプロパティを定義します。Object から継承された .toString() メソッドもオーバーライドします。
この後、 Pet を拡張し、 .toString() メソッドをオーバーライドして動作を変更する Dog クラスを作成します(ポリモーフィズム)。さらに、子クラスにいくつかのプロパティを追加します。
この後、継承チェーンをチェックして、Dog がまだ Dog 型、Pet 型、Object 型であることを示します。
// Defines a Pet class constructor
function Pet(name)
{
this.getName = function() { return name; };
this.setName = function(newName) { name = newName; };
}
// Adds the Pet.toString() function for all Pet objects
Pet.prototype.toString = function()
{
return 'This pets name is: ' + this.getName();
};
// end of class Pet
// Define Dog class constructor (Dog : Pet)
function Dog(name, breed)
{
// think Dog : base(name)
Pet.call(this, name);
this.getBreed = function() { return breed; };
}
// this makes Dog.prototype inherit from Pet.prototype
Dog.prototype = new Pet();
// Currently Pet.prototype.constructor
// points to Pet. We want our Dog instances'
// constructor to point to Dog.
Dog.prototype.constructor = Dog;
// Now we override Pet.prototype.toString
Dog.prototype.toString = function()
{
return 'This dogs name is: ' + this.getName() +
', and its breed is: ' + this.getBreed();
};
// end of class Dog
var parrotty = new Pet('Parrotty the Parrot');
var dog = new Dog('Buddy', 'Great Dane');
// test the new toString()
alert(parrotty);
alert(dog);
// Testing instanceof (similar to the `is` operator)
alert('Is dog instance of Dog? ' + (dog instanceof Dog)); //true
alert('Is dog instance of Pet? ' + (dog instanceof Pet)); //true
alert('Is dog instance of Object? ' + (dog instanceof Object)); //true
この質問に対する両方の回答は、Ray Djajadinata による優れた MSDN 記事から変更されたコードです。
頭のてっぺんから...
関数
arguments.calleeは、「arguments」変数をホストする関数を参照するため、無名関数を再帰的に使用できます。
var recurse = function() {
if (condition) arguments.callee(); //calls recurse() again
}
これは、次のようなことをしたい場合に便利です。
//do something to all array items within an array recursively
myArray.forEach(function(item) {
if (item instanceof Array) item.forEach(arguments.callee)
else {/*...*/}
})
オブジェクト
オブジェクトメンバーの興味深い点:名前として任意の文字列を使用できます。
//these are normal object members
var obj = {
a : function() {},
b : function() {}
}
//but we can do this too
var rules = {
".layout .widget" : function(element) {},
"a[href]" : function(element) {}
}
/*
this snippet searches the page for elements that
match the CSS selectors and applies the respective function to them:
*/
for (var item in rules) {
var elements = document.querySelectorAll(rules[item]);
for (var e, i = 0; e = elements[i++];) rules[item](e);
}
文字列
String.splitは、正規表現をパラメーターとして受け取ることができます。
"hello world with spaces".split(/\s+/g);
//returns an array: ["hello", "world", "with", "spaces"]
String.replaceは、検索パラメーターとして正規表現を取り、置換パラメーターとして関数を受け取ることができます。
var i = 1;
"foo bar baz ".replace(/\s+/g, function() {return i++});
//returns "foo1bar2baz3"
ほとんどの場合、スイッチの代わりにオブジェクトを使用できます。
function getInnerText(o){
return o === null? null : {
string: o,
array: o.map(getInnerText).join(""),
object:getInnerText(o["childNodes"])
}[typeis(o)];
}
更新:事前に評価されたケースが非効率的であることが心配な場合(プログラムの設計の早い段階で効率が心配なのはなぜですか??)、次のようなことができます。
function getInnerText(o){
return o === null? null : {
string: function() { return o;},
array: function() { return o.map(getInnerText).join(""); },
object: function () { return getInnerText(o["childNodes"]; ) }
}[typeis(o)]();
}
これは、スイッチやオブジェクトよりも入力(または読み取り)するのが面倒ですが、以下のコメントセクションで詳しく説明するように、スイッチの代わりにオブジェクトを使用する利点は維持されます。このスタイルはまた、十分に成長したら、これを適切な「クラス」にスピンアウトすることをより簡単にします。
update2:ES.nextの構文拡張が提案されているため、これは次のようになります。
let getInnerText = o -> ({
string: o -> o,
array: o -> o.map(getInnerText).join(""),
object: o -> getInnerText(o["childNodes"])
}[ typeis o ] || (->null) )(o);
オブジェクトのプロパティを反復処理するときは、必ずhasOwnPropertyメソッドを使用してください。
for (p in anObject) {
if (anObject.hasOwnProperty(p)) {
//Do stuff with p here
}
}
これは、 anObjectの直接のプロパティにのみアクセスし、プロトタイプ チェーンの下にあるプロパティを使用しないようにするために行われます。
パブリック インターフェイスを持つプライベート変数
これは、自己呼び出し関数の定義を使用した巧妙な小さなトリックを使用します。返されたオブジェクト内のすべてが公開インターフェイスで利用可能ですが、それ以外はすべて非公開です。
var test = function () {
//private members
var x = 1;
var y = function () {
return x * 2;
};
//public interface
return {
setx : function (newx) {
x = newx;
},
gety : function () {
return y();
}
}
}();
assert(undefined == test.x);
assert(undefined == test.y);
assert(2 == test.gety());
test.setx(5);
assert(10 == test.gety());
JavaScript のタイムスタンプ:
// Usual Way
var d = new Date();
timestamp = d.getTime();
// Shorter Way
timestamp = (new Date()).getTime();
// Shortest Way
timestamp = +new Date();
左側の [] を使用してローカル変数を割り当てることができます。不必要な配列を作成せずに関数から複数の値を返したい場合に便利です。
function fn(){
var cat = "meow";
var dog = "woof";
return [cat,dog];
};
var [cat,dog] = fn(); // Handy!
alert(cat);
alert(dog);
それはコア JS の一部ですが、どういうわけか私は今年まで気づきませんでした。
Javascript のすべてのオブジェクトはハッシュテーブルとして実装されるため、それらのプロパティにはインデクサーを介してアクセスでき、その逆も同様です。また、 for/in演算子を使用してすべてのプロパティを列挙できます。
var x = {a: 0};
x["a"]; //returns 0
x["b"] = 1;
x.b; //returns 1
for (p in x) document.write(p+";"); //writes "a;b;"
このスレッドには、プロトタイプを介して Array オブジェクトを拡張する方法を示すいくつかの回答があります。for (i in a)
これはステートメントを壊すため、悪い考えです。
for (i in a)
では、コードのどこにも使用していなくても大丈夫ですか? 実行しているコードが独自のコードだけである場合に限りますが、これはブラウザー内ではあまりありません。人々がこのように Array オブジェクトを拡張し始めると、スタック オーバーフローが一連の不可解な JavaScript バグであふれ始めるのではないかと心配しています。
役立つ詳細については、こちらをご覧ください。
配列から要素を削除したい場合は、次のようにdelete演算子を使用できます。
var numbers = [1,2,3,4,5];
delete numbers[3];
//numbers is now [1,2,3,undefined,5]
ご覧のとおり、要素は削除されましたが、要素が未定義の値に置き換えられたため、配列に穴が残っています。
したがって、この問題を回避するには、 deleteを使用する代わりに、splice配列メソッドを使用します...そのように:
var numbers = [1,2,3,4,5];
numbers.splice(3,1);
//numbers is now [1,2,3,5]
spliceの最初の引数は配列 [index] の序数で、2 番目は削除する要素の数です。
関数では、関数自体を返すことができます。
function showSomething(a){
alert(a);
return arguments.callee;
}
// Alerts: 'a', 'b', 'c'
showSomething('a')('b')('c');
// Or what about this:
(function (a){
alert(a);
return arguments.callee;
})('a')('b')('c');
いつ役立つかはわかりませんが、かなり奇妙で楽しいものです。
var count = function(counter){
alert(counter);
if(counter < 10){
return arguments.callee(counter+1);
}
return arguments.callee;
};
count(5)(9); // Will alert 5, 6, 7, 8, 9, 10 and 9, 10
実際、Node.js のFAB フレームワークはこの機能を実装しているようです。たとえば、このトピックを参照してください。
JavaScript が Date() で動作する方法は、私を興奮させます!
function isLeapYear(year) {
return (new Date(year, 1, 29, 0, 0).getMonth() != 2);
}
これはまさに「隠れた機能」です。
編集:「?」を削除しました 丁寧さのコメントで提案されている状態。以前: ... new Date(year, 1, 29, 0, 0).getMonth() != 2 ? true : false ... 詳細はコメントをご覧ください。
ここにいくつかのショートカットがあります:
var a = []; // equivalent to new Array()
var o = {}; // equivalent to new Object()
私のお気に入りのトリックはapply
、オブジェクトのメソッドへのコールバックを実行し、正しい "this" 変数を維持するために を使用することです。
function MakeCallback(obj, method) {
return function() {
method.apply(obj, arguments);
};
}
var SomeClass = function() {
this.a = 1;
};
SomeClass.prototype.addXToA = function(x) {
this.a = this.a + x;
};
var myObj = new SomeClass();
brokenCallback = myObj.addXToA;
brokenCallback(1); // Won't work, wrong "this" variable
alert(myObj.a); // 1
var myCallback = MakeCallback(myObj, myObj.addXToA);
myCallback(1); // Works as expected because of apply
alert(myObj.a); // 2
閉鎖の禅
他の人は閉鎖について言及しています。しかし、クロージャーについて知っていて、クロージャーを使用してコードを書いているにもかかわらず、クロージャーが実際に何であるかについてまだ間違った認識を持っている人がどれだけ多いかは驚くべきことです。一部の人々は、ファーストクラスの関数をクロージャと混同しています。しかし、それを一種の静的変数と見なす人もいます。
私にとって、クロージャーは一種の「プライベート」グローバル変数です。つまり、一部の関数はグローバルとして認識しますが、他の関数は認識できない一種の変数です。今、私はこれが基本的なメカニズムの説明で速くて緩いことを知っていますが、それがどのように感じられ、どのように動作するかです. 説明する:
// Say you want three functions to share a single variable:
// Use a self-calling function to create scope:
(function(){
var counter = 0; // this is the variable we want to share;
// Declare global functions using function expressions:
increment = function(){
return ++counter;
}
decrement = function(){
return --counter;
}
value = function(){
return counter;
}
})()
現在、3 つの関数increment
があり、実際のグローバル変数ではなく変数decrement
をvalue
共有しています。これがクロージャーの本質です。counter
counter
increment();
increment();
decrement();
alert(value()); // will output 1
上記は、クロージャーの実際の有用な使用方法ではありません。実際、このように使用することはアンチパターンだと思います。しかし、クロージャーの性質を理解するのに役立ちます。たとえば、次のようなことをしようとすると、ほとんどの人が捕まってしまいます。
for (var i=1;i<=10;i++) {
document.getElementById('span'+i).onclick = function () {
alert('this is span number '+i);
}
}
// ALL spans will generate alert: this span is span number 10
それは、彼らが閉鎖の性質を理解していないからです。彼らはi
、実際には関数が単一の変数を共有しているのに、関数に値を渡していると考えていますi
。前に言ったように、特別な種類のグローバル変数です。
これを回避するには、 * クロージャをデタッチする必要があります:
function makeClickHandler (j) {
return function () {alert('this is span number '+j)};
}
for (var i=1;i<=10;i++) {
document.getElementById('span'+i).onclick = makeClickHandler(i);
}
// this works because i is passed by reference
// (or value in this case, since it is a number)
// instead of being captured by a closure
*注: ここでの正しい用語はわかりません。
eval()
グローバル変数名をアセンブルするために使用する必要はありません。
つまり、(何らかの理由で) という名前のグローバルが複数ある場合、 でspec_grapes, spec_apples
それらにアクセスする必要はありませんeval("spec_" + var)
。
すべてのグローバルは のメンバーなwindow[]
ので、実行できますwindow["spec_" + var]
。
Firebug に console.log() を使用する際に、Internet Explorer でのテスト中に迷惑なエラーが発生しないようにします。
function log(message) {
(console || { log: function(s) { alert(s); }).log(message);
}
JavaScript は単純なオブジェクト リテラルを使用します。
var x = { intValue: 5, strValue: "foo" };
これにより、本格的なオブジェクトが構築されます。
JavaScript はプロトタイプベースのオブジェクト指向を使用し、実行時に型を拡張する機能を提供します。
String.prototype.doubleLength = function() {
return this.length * 2;
}
alert("foo".doubleLength());
オブジェクトは、自身に含まれていない属性へのすべてのアクセスを、その「プロトタイプ」である別のオブジェクトに委譲します。これは継承を実装するために使用できますが、実際にはより強力です (より面倒ではありますが):
/* "Constructor" */
function foo() {
this.intValue = 5;
}
/* Create the prototype that includes everything
* common to all objects created be the foo function.
*/
foo.prototype = {
method: function() {
alert(this.intValue);
}
}
var f = new foo();
f.method();
私のお気に入りの1つは、コンストラクター型のチェックです。
function getObjectType( obj ) {
return obj.constructor.name;
}
window.onload = function() {
alert( getObjectType( "Hello World!" ) );
function Cat() {
// some code here...
}
alert( getObjectType( new Cat() ) );
}
したがって、typeofキーワードでよく使用される、疲れた古い[Object object]の代わりに、コンストラクターに基づいて実際のオブジェクトタイプを実際に取得できます。
もう1つは、関数を「オーバーロード」する方法として変数引数を使用することです。式を使用して引数の数を検出し、オーバーロードされた出力を返すだけです。
function myFunction( message, iteration ) {
if ( arguments.length == 2 ) {
for ( i = 0; i < iteration; i++ ) {
alert( message );
}
} else {
alert( message );
}
}
window.onload = function() {
myFunction( "Hello World!", 3 );
}
最後に、代入演算子の省略形を言います。私はこれをjQueryフレームワークのソースから学びました...古い方法:
var a, b, c, d;
b = a;
c = b;
d = c;
新しい(省略形)方法:
var a, b, c, d;
d = c = b = a;
楽しい :)
JavaScriptで最速のループはwhile(i--)ループです。すべてのブラウザで。したがって、ループの要素が処理される順序がそれほど重要でない場合は、while(i--)形式を使用する必要があります。
var names = new Array(1024), i = names.length;
while(i--)
names[i] = "John" + i;
また、今後for()ループを使用する必要がある場合は、常に.lengthプロパティをキャッシュすることを忘れないでください。
var birds = new Array(1024);
for(var i = 0, j = birds.length; i < j; i++)
birds[i].fly();
大きな文字列を結合するには、配列を使用します(より高速です)。
var largeString = new Array(1024), i = largeString.length;
while(i--) {
// It's faster than for() loop with largeString.push(), obviously :)
largeString[i] = i.toString(16);
}
largeString = largeString.join("");
largeString += "something"
ループ内よりもはるかに高速です。
ステートメントをコンマで区切ると、括弧の間でほとんど何でもできます。
var z = ( x = "can you do crazy things with parenthesis", ( y = x.split(" "), [ y[1], y[0] ].concat( y.slice(2) ) ).join(" ") )
alert(x + "\n" + y + "\n" + z)
出力:
can you do crazy things with parenthesis
can,you,do,crazy,things,with,parenthesis
you can do crazy things with parenthesis
真実と虚偽の値の概念。次のようなことをする必要はありません
if(someVar === 未定義 || someVar === null) ...
単に行う:
if(!someVar)。
すべての値には、対応するブール表現があります。
window.name
の値は、ページが変更されても保持され、同じドメイン内にある場合は親ウィンドウで読み取ることができ (iframe 内にある場合はdocument.getElementById("your frame's ID").contentWindow.name
、それにアクセスするために使用します)、使用可能なメモリによってのみ制限されます。
新しい「オブジェクト」を作成する場合、括弧はオプションです。
function Animal () {
}
var animal = new Animal();
var animal = new Animal;
同じこと。
関数文と関数式の扱いは異なります。
function blarg(a) {return a;} // statement
bleep = function(b) {return b;} //expression
すべての関数ステートメントは、コードが実行される前に解析されます。JavaScript ファイルの下部にある関数は、最初のステートメントで使用できます。一方、周囲with
のステートメントなど、特定の動的コンテキストを利用することはできませwith
ん。関数が解析されたときに実行されていません。
関数式は、見つかった場所でインラインで実行されます。それまでは利用できませんが、動的なコンテキストを利用できます。
整数プロパティと長さプロパティを持つ「any* オブジェクトを適切な配列に変換して、push、pop、splice、map、filter、reduce などのすべての配列メソッドを与えることができます。
Array.prototype.slice.call({"0":"foo", "1":"bar", 2:"baz", "length":3 })
// ["foo", "bar", "baz"] を返す
これは、jQuery オブジェクト、html コレクション、および他のフレームの Array オブジェクトで機能します (配列型全体に対する 1 つの可能な解決策として)。長さのプロパティがあれば、それを配列に変えることができ、それは問題ではありません。引数オブジェクトを超えて、長さプロパティを持つ非配列オブジェクトがたくさんあります。
JSON 文字列をやみくもeval()
に逆シリアル化すると、問題が発生する可能性があります。
JSON 文字列を括弧で囲まないと、プロパティ名がラベルと間違えられ、予期しない動作や構文エラーが発生する可能性があります。
eval("{ \"foo\": 42 }"); // syntax error: invalid label
eval("({ \"foo\": 42 })"); // OK
Javascript には、関数内に静的変数があります。
function someFunction(){
var Static = arguments.callee;
Static.someStaticVariable = (Static.someStaticVariable || 0) + 1;
alert(Static.someStaticVariable);
}
someFunction() //Alerts 1
someFunction() //Alerts 2
someFunction() //Alerts 3
また、オブジェクト内に静的変数があります。
function Obj(){
this.Static = arguments.callee;
}
a = new Obj();
a.Static.name = "a";
b = new Obj();
alert(b.Static.name); //Alerts b
== 演算子には、非常に特殊なプロパティがあり、この不穏な等式が作成されます (はい、Perl などの他の動的言語ではこの動作が予想されることはわかっていますが、JavaScript は通常、比較を賢くしようとはしません)。
>>> 1 == true
true
>>> 0 == false
true
>>> 2 == true
false
すべての関数は、実際には組み込みのFunction型のインスタンスであり、関数定義を含む文字列を受け取るコンストラクターを持っているため、たとえば文字列を連結することにより、実行時に関数を実際に定義できます。
//e.g., createAddFunction("a","b") returns function(a,b) { return a+b; }
function createAddFunction(paramName1, paramName2)
{ return new Function( paramName1, paramName2
,"return "+ paramName1 +" + "+ paramName2 +";");
}
また、ユーザー定義関数の場合、Function.toString()は関数定義をリテラル文字列として返します。
let
.
varのブロック スコープの欠如に対応するのはlet
、JavaScript 1.7 で導入された です。
- let ステートメントは、ブロック外の同名の変数の値に影響を与えることなく、ブロックのスコープ内の変数に値を関連付ける方法を提供します。
- let 式を使用すると、単一の式のみにスコープが設定された変数を確立できます。
- let 定義は、スコープが定義されているブロックに制限されている変数を定義します。この構文は、var で使用される構文とよく似ています。
- let を使用して、for ループのコンテキスト内にのみ存在する変数を設定することもできます。
function varTest() {
var x = 31;
if (true) {
var x = 71; // same variable!
alert(x); // 71
}
alert(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // different variable
alert(x); // 71
}
alert(x); // 31
}
2008 年現在、JavaScript 1.7 は FireFox 2.0+ および Safari 3.x でサポートされています。
メソッドがあるかどうかに関係なく、オブジェクトのメソッドを任意のオブジェクトで実行できます。もちろん、常に機能するとは限りません (メソッドがオブジェクトに存在しないものがあると想定している場合) が、非常に便利です。例えば:
function(){
arguments.push('foo') // This errors, arguments is not a proper array and has no push method
Array.prototype.push.apply(arguments, ['foo']) // Works!
}
JavaScript コードをサンドボックス化し、文字列を評価して JavaScript コードにするあらゆる方法を無効にしようとしている場合は、すべての明白な eval/document.write/new Function/setTimeout/setInterval/innerHTML およびその他の DOM 操作をブロックしないことに注意してください。足りる。
オブジェクト o を指定o.constructor.constructor("alert('hi')")()
すると、"hi" という単語を含む警告ダイアログが表示されます。
次のように書き換えることができます
var Z="constructor";
Z[Z][Z]("alert('hi')")();
楽しいもの。
Microsoftから JavaScript への贈り物: AJAX
AJAXCall('http://www.abcd.com/')
function AJAXCall(url) {
var client = new XMLHttpRequest();
client.onreadystatechange = handlerFunc;
client.open("GET", url);
client.send();
}
function handlerFunc() {
if(this.readyState == 4 && this.status == 200) {
if(this.responseXML != null)
document.write(this.responseXML)
}
}
Function.toString() (暗黙的):
function x() {
alert("Hello World");
}
eval ("x = " + (x + "").replace(
'Hello World',
'STACK OVERFLOW BWAHAHA"); x("'));
x();
これは Javascript ではなく jQuery の隠れた機能ですが、「jQuery の隠れた機能」という質問は決してないので...
:something
jQuery で独自のセレクターを定義できます。
$.extend($.expr[':'], {
foo: function(node, index, args, stack) {
// decide if selectors matches node, return true or false
}
});
:foo
などの を使用した選択の場合、セレクターの処理済み部分に一致するすべてのノードに対して$('div.block:foo("bar,baz") span')
関数が呼び出されます。foo
引数の意味:
node
現在のノードを保持しますindex
ノード セット内のノードのインデックスです。args
セレクターに引数または複数の名前がある場合に役立つ配列です。
args[0]
セレクターテキスト全体です (例: :foo("bar, baz")
)args[1]
セレクター名 (例: foo
)args[2]
引数をラップするために使用される引用文字 ( "
for:foo("bar, baz")
など)、または引用符がない場合は空の文字列 ( :foo(bar, baz)
)、引数がない場合は未定義args[3]
引用符を含む引数 (例: "bar, baz"
)、または引数がない場合は undefined です。stack
ノードセットです(その時点で一致するすべてのノードを保持する配列)true
セレクターが一致する場合は関数が返され、false
そうでない場合は返されます。
たとえば、次のコードは、全文正規表現検索に基づいてノードを選択できるようにします。
$.extend($.expr[':'], {
matches: function(node, index, args, stack) {
if (!args.re) { // args is a good place for caching
var re = args[3];
if (args[2]) { // get rid of quotes
re = re.slice(1,-1);
}
var separator = re[0];
var pos = re.lastIndexOf(separator);
var modifiers = re.substr(pos+1);
var code = re.substr(1, pos-1);
args.re = new RegExp(code, modifiers);
}
return $(node).text().match(args.re);
}
});
// find the answers on this page which contain /**/-style comments
$('.answer .post-text code:matches(!/\\*[\\s\\S]*\\*/!)');
.filter()のコールバック バージョンでも同様の効果が得られますが、カスタム セレクターははるかに柔軟で、通常は読みやすくなっています。
ジェネレーターとイテレーター(Firefox 2+とSafariでのみ機能します)。
function fib() {
var i = 0, j = 1;
while (true) {
yield i;
var t = i;
i = j;
j += t;
}
}
var g = fib();
for (var i = 0; i < 10; i++) {
document.write(g.next() + "<br>\n");
}
yield
キーワードを含む関数はジェネレーターです。これを呼び出すと、その正式なパラメーターは実際の引数にバインドされますが、その本体は実際には評価されません。代わりに、generator-iteratorが返されます。ジェネレーター-イテレーターのnext()
メソッドを呼び出すたびに、反復アルゴリズムを介して別のパスが実行されます。各ステップの値は、yield
キーワードで指定された値です。yield
アルゴリズムの各反復間の境界を示す、returnのジェネレーター-イテレーターバージョンと考えてください。を呼び出すたびnext()
に、ジェネレータコードは。に続くステートメントから再開しますyield
。通常の使用法では、イテレータオブジェクトは「非表示」です。それらを明示的に操作する必要はありませんが、代わりにJavaScript
for...in
とfor each...in
ステートメントを使用して、オブジェクトのキーや値を自然にループします。
var objectWithIterator = getObjectSomehow();
for (var i in objectWithIterator)
{
document.write(objectWithIterator[i] + "<br>\n");
}
undefined
未定義です。だからあなたはこれを行うことができます:
if (obj.field === undefined) /* ... */
訪問:
この JavaScript コードを Web ブラウザのアドレス バーに貼り付けます。
JavaScriptディスコショーをお楽しみください:-p
「隠れた」機能はすべて Mozilla wiki にあります: http://developer.mozilla.org/en/JavaScript。
コア JavaScript 1.5 リファレンス、JavaScript 1.6の新機能、JavaScript 1.7の新機能、JavaScript 1.8の新機能があります。実際に機能し、間違っていない例については、これらすべてに目を通してください。
これは非常に隠されており、たまにしか役に立たない ;-)
プロトタイプ チェーンを使用して、元のオブジェクトを変更せずに、別のオブジェクトに委任するオブジェクトを作成できます。
var o1 = { foo: 1, bar: 'abc' };
function f() {}
f.prototype = o1;
o2 = new f();
assert( o2.foo === 1 );
assert( o2.bar === 'abc' );
o2.foo = 2;
o2.baz = true;
assert( o2.foo === 2 );
// o1 is unchanged by assignment to o2
assert( o1.foo === 1 );
assert( o2.baz );
これは、o1 の「単純な」値のみを対象としています。配列または別のオブジェクトを変更すると、プロトタイプは元のオブジェクトを「保護」しなくなります。クラス定義/プロトタイプに {} または [] がある場合は常に注意してください。
jQuery と JavaScript:
変数名には、奇数の文字を含めることができます。$ 文字を使用して、jQuery オブジェクトを含む変数を識別します。
var $links = $("a");
$links.hide();
オブジェクトをチェーンする jQuery のパターンは非常に優れていますが、このパターンを適用すると少し混乱する可能性があります。幸いなことに、JavaScript では次のように改行できます。
$("a")
.hide()
.fadeIn()
.fadeOut()
.hide();
一般的な JavaScript:
自己実行関数を使用してスコープをエミュレートすると便利です。
function test()
{
// scope of test()
(function()
{
// scope inside the scope of test()
}());
// scope of test()
}
それがオブジェクト指向でもあることに気付いていない人がいかに多いかは驚くべきことです。
大規模なループは、while
-condition および逆方向 (つまり、ループの順序が問題にならない場合) の方が高速です。私のコードの約 50% では、通常そうではありません。
すなわち
var i, len = 100000;
for (var i = 0; i < len; i++) {
// do stuff
}
より遅い:
i = len;
while (i--) {
// do stuff
}
存在チェック。こんなものをよく見かけます
var a = [0, 1, 2];
// code that might clear the array.
if (a.length > 0) {
// do something
}
代わりに、たとえば次のようにします。
var a = [0, 1, 2];
// code that might clear the array.
if (a.length) { // if length is not equal to 0, this will be true
// do something
}
実行できる存在チェックにはさまざまな種類がありますが、これは要点を説明するための単純な例にすぎません。
デフォルト値の使用方法の例を次に示します。
function (someArgument) {
someArgument || (someArgument = "This is the deault value");
}
それは私の2セントです。他にもナゲットがありますが、今のところはそれだけです。
シンタックス シュガー: インライン for ループ クロージャ
var i;
for (i = 0; i < 10; i++) (function ()
{
// do something with i
}());
Douglas Crockford のコード規則のほとんどすべてを破っていますが、私はそれを見てとてもいいと思います。
別:
var i;
for (i = 0; i < 10; i++) (function (j)
{
// do something with j
}(i));
配列または null で使用されるJavaScripttypeof
演算子は常にobject
値を返しますが、これは場合によってはプログラマーが期待するものではない可能性があります。
これらのアイテムに対しても適切な値を返す関数を次に示します。配列認識は、Douglas Crockford の著書「JavaScript: The Good Parts」からコピーされました。
function typeOf (value) {
var type = typeof value;
if (type === 'object') {
if (value === null) {
type = 'null';
} else if (typeof value.length === 'number' &&
typeof value.splice === 'function' &&
!value.propertyIsEnumerable('length')) {
type = 'array';
}
}
return type;
}
Jooseは、CLOS のような感じのクラスベースの OO が必要な場合に最適なオブジェクト システムです。
// Create a class called Point
Class("Point", {
has: {
x: {
is: "rw",
init: 0
},
y: {
is: "rw",
init: 0
}
},
methods: {
clear: function () {
this.setX(0);
this.setY(0);
}
}
})
// Use the class
var point = new Point();
point.setX(10)
point.setY(20);
point.clear();
浮動小数点数を整数に変換するには、次の暗号化されたハックのいずれかを使用できます (使用しないでください)。
3.14 >> 0
(経由2.9999999999999999 >> .5? )3.14 | 0
( JavaScriptで浮動小数点を整数に変換する最良の方法は何ですか?経由)3.14 & -1
3.14 ^ 0
~~3.14
基本的に、最終値を変更しない二項演算 (恒等関数) を float に適用すると、float が整数に変換されます。
Marius がすでに指摘したように、関数内に public static 変数を含めることができます。
通常、これらを使用して、一度だけ実行される関数を作成したり、複雑な計算結果をキャッシュしたりします。
私の古い「シングルトン」アプローチの例を次に示します。
var singleton = function(){
if (typeof arguments.callee.__instance__ == 'undefined') {
arguments.callee.__instance__ = new function(){
//this creates a random private variable.
//this could be a complicated calculation or DOM traversing that takes long
//or anything that needs to be "cached"
var rnd = Math.random();
//just a "public" function showing the private variable value
this.smth = function(){ alert('it is an object with a rand num=' + rnd); };
};
}
return arguments.callee.__instance__;
};
var a = new singleton;
var b = new singleton;
a.smth();
b.smth();
ご覧のとおり、どちらの場合も、コンストラクターは 1 回だけ実行されます。
たとえば、2004 年に、ページ全体を覆う灰色の背景を持つモーダル ダイアログ ボックス ( Lightboxのようなもの) を作成する必要があったときに、このアプローチを使用しました。Internet Explorer 5.5 および 6 は、「ウィンドウ」の性質により、<select> または <iframe> 要素のスタック コンテキストが最も高くなります。そのため、ページに select 要素が含まれている場合、それらをカバーする唯一の方法は、iframe を作成してページの「上」に配置することでした。そのため、スクリプト全体は非常に複雑で、少し時間がかかりました (filter: 式を使用して、カバーする iframe の不透明度を設定しました)。「shim」スクリプトには「.show()」メソッドが 1 つしかなく、これにより shim が 1 回だけ作成され、静的変数にキャッシュされました :)
これは Firefox (SpiderMonkey) でのみ動作するようです。関数内:
arguments[-2]
引数の数を与える ( と同じarguments.length
)arguments[-3]
呼び出された関数を与える ( と同じarguments.callee
)うーん、トピック全体を読んだわけではありませんが、私にとっては非常に興味深いものですが、少し寄付させてください。
// forget the debug alerts
var alertToFirebugConsole = function() {
if ( window.console && window.console.log ) {
window.alert = console.log;
}
}
JavaScript は、そのすべてのオブジェクトを公開するのに非常に優れていると考えられているため、そのウィンドウ オブジェクト自体に関係なく.
したがって、パラメーターとして文字列を受け入れる JQuery/YUI div ポップアップでブラウザー アラートをオーバーライドしたい場合は、次のスニペットを使用するだけで実行できます。
function divPopup(str)
{
//code to show the divPopup
}
window.alert = divPopup;
この変更により、alert() へのすべての呼び出しは、ブラウザー固有のアラートではなく、適切な新しい div ベースのポップアップを表示します。
たぶんあまり知られていないものの1つ:
arguments.callee.caller + Function#toString()
function called(){
alert("Go called by:\n"+arguments.callee.caller.toString());
}
function iDoTheCall(){
called();
}
iDoTheCall();
-- Deprecatedのソース コードを出力しますiDoTheCall
が、警告が唯一のオプションである場合に役立つことがあります....
ほとんど知られていない JavaScript 構文もあります。
var a;
a=alert(5),7;
alert(a); // alerts undefined
a=7,alert(5);
alert(a); // alerts 7
a=(3,6);
alert(a); // alerts 6
詳細については、こちらをご覧ください。
window.alert
jQuery UI のダイアログ ウィジェットで関数
をオーバーライドするコードを次に示します。これを jQuery プラグインとして実行しました。それについては私のブログで読むことができます。パーソナライズされたアラート メッセージ用の jQuery プラグインである altAlert。
jQuery.altAlert = function (options)
{
var defaults = {
title: "Alert",
buttons: {
"Ok": function()
{
jQuery(this).dialog("close");
}
}
};
jQuery.extend(defaults, options);
delete defaults.autoOpen;
window.alert = function ()
{
jQuery("<div />", {
html: arguments[0].replace(/\n/, "<br />")
}).dialog(defaults);
};
};
JavaScriptオブジェクトをHTML要素属性としてバインドできます。
<div id="jsTest">Klick Me</div>
<script type="text/javascript">
var someVariable = 'I was klicked';
var divElement = document.getElementById('jsTest');
// binding function/object or anything as attribute
divElement.controller = function() { someVariable += '*'; alert('You can change instance data:\n' + someVariable ); };
var onclickFunct = new Function( 'this.controller();' ); // Works in Firefox and Internet Explorer.
divElement.onclick = onclickFunct;
</script>
単純な自己完結型関数の戻り値のキャッシュ:
function isRunningLocally(){
var runningLocally = ....; // Might be an expensive check, check whatever needs to be checked.
return (isRunningLocally = function(){
return runningLocally;
})();
},
高価な部分は最初の呼び出しでのみ実行され、その後、関数はこの値を返すだけです。もちろん、これは常に同じものを返す関数にのみ役立ちます。
私の最初の提出は、プロパティ再定義機能のめったに使用されないアプリケーションほど隠された機能ではありません。オブジェクトのメソッドを再定義できるため、メソッド呼び出しの結果をキャッシュできます。これは、計算にコストがかかり、遅延評価が必要な場合に役立ちます。これにより、最も単純な形式のメモ化が可能になります。
function Circle(r) {
this.setR(r);
}
Circle.prototype = {
recalcArea: function() {
this.area=function() {
area = this.r * this.r * Math.PI;
this.area = function() {return area;}
return area;
}
},
setR: function (r) {
this.r = r;
this.invalidateR();
},
invalidateR: function() {
this.recalcArea();
}
}
結果をメソッドにキャッシュするコードをリファクタリングすると、次のようになります。
Object.prototype.cacheResult = function(name, _get) {
this[name] = function() {
var result = _get.apply(this, arguments);
this[name] = function() {
return result;
}
return result;
};
};
function Circle(r) {
this.setR(r);
}
Circle.prototype = {
recalcArea: function() {
this.cacheResult('area', function() { return this.r * this.r * Math.PI; });
},
setR: function (r) {
this.r = r;
this.invalidateR();
},
invalidateR: function() {
this.recalcArea();
}
}
メモ化された関数が必要な場合は、代わりにそれを使用できます。プロパティの再定義は含まれていません。
Object.prototype.memoize = function(name, implementation) {
this[name] = function() {
var argStr = Array.toString.call(arguments);
if (typeof(this[name].memo[argStr]) == 'undefined') {
this[name].memo[argStr] = implementation.apply(this, arguments);
}
return this[name].memo[argStr];
}
};
これは標準の配列からStringへの変換に依存しており、多くの場合正しく機能しないことに注意してください。それを修正することは、読者の練習問題として残されています。
私の2番目の提出物はゲッターとセッターです。彼らがまだ言及されていないことに私は驚いています。公式標準は事実上の標準(definePropertyとdefine [GS] etter)とは異なり、Internet Explorerは公式標準をほとんどサポートしていないため、一般的には役に立ちません。たぶんそれが彼らが言及されなかった理由です。ゲッターと結果キャッシュをかなりうまく組み合わせることができることに注意してください。
Object.prototype.defineCacher = function(name, _get) {
this.__defineGetter__(name, function() {
var result = _get.call(this);
this.__defineGetter__(name, function() { return result; });
return result;
})
};
function Circle(r) {
this.r = r;
}
Circle.prototype = {
invalidateR: function() {
this.recalcArea();
},
recalcArea: function() {
this.defineCacher('area', function() {return this.r * this.r * Math.PI; });
},
get r() { return this._r; }
set r(r) { this._r = r; this.invalidateR(); }
}
var unit = new Circle(1);
unit.area;
ゲッター、セッター、および結果のキャッシュを効率的に組み合わせるのは、無効化を防ぐか、セットでの自動無効化なしで実行する必要があるため、少し面倒です。これは、次の例のようになります。1つのプロパティを変更すると、他の複数のプロパティが無効になる場合は、ほとんどの場合問題になります(これらの例に「diameter」プロパティがあると想像してください)。
Object.prototype.defineRecalcer = function(name, _get) {
var recalcFunc;
this[recalcFunc='recalc'+name.toCapitalized()] = function() {
this.defineCacher(name, _get);
};
this[recalcFunc]();
this.__defineSetter__(name, function(value) {
_set.call(this, value);
this.__defineGetter__(name, function() {return value; });
});
};
function Circle(r) {
this.defineRecalcer('area',
function() {return this.r * this.r * Math.PI;},
function(area) {this._r = Math.sqrt(area / Math.PI);},
);
this.r = r;
}
Circle.prototype = {
invalidateR: function() {
this.recalcArea();
},
get r() { return this._r; }
set r(r) { this._r = r; this.invalidateR(); }
}
閉鎖:
function f() {
var a;
function closureGet(){ return a; }
function closureSet(val){ a=val;}
return [closureGet,closureSet];
}
[closureGet,closureSet]=f();
closureSet(5);
alert(closureGet()); // gives 5
closureSet(15);
alert(closureGet()); // gives 15
ここでのクロージャのことは、いわゆるデストラクチャリング代入([c,d] = [1,3]
と同等c=1; d=3;
)ではなく、a
closureGetとclosureSetのオカレンスが同じ変数を参照しているという事実です。closureSetがa
新しい値を割り当てた後でも!
合体演算子は非常にクールで、特にそれを連鎖させると、クリーンで簡潔なコードになりますa || b || c || "default";
。落とし穴は、nullではなくboolで評価することで機能するため、falseと評価される値が有効である場合、多くの場合、時代は見過ごされます。心配しないでください。これらの場合は、古き良き三項演算子に戻ってください。
静的変数の代わりにグローバル変数をあきらめて使用しているコードをよく目にするので、次のようにします(例では、一般的なシングルトンファクトリと呼ぶことができます)。
var getInstance = function(objectName) {
if ( !getInstance.instances ) {
getInstance.instances = {};
}
if ( !getInstance.instances[objectName] ) {
getInstance.instances[objectName] = new window[objectName];
}
return getInstance.instances[objectName];
};
また、new window[objectName];
オブジェクトを名前で一般的にインスタンス化するための鍵であることに注意してください。私はちょうど2ヶ月前にそれを理解しました。
同じ精神で、DOMを操作するとき、追加する機能を最初に初期化するときに、機能するパラメーターやフラグをDOMノードに埋め込むことがよくあります。誰かがしゃがんだ場合の例を追加します。
驚いたことに、最初のページの誰も言及していませんhasOwnProperty
。これは残念です。in
反復に使用する場合は、反復されるコンテナーでメソッドを使用して、使用hasOwnProperty
されているメンバー名が期待どおりであることを確認することをお勧めします。
var x = [1,2,3];
for ( i in x ) {
if ( !x.hasOwnProperty(i) ) { continue; }
console.log(i, x[i]);
}
詳細については、こちらをお読みください。
最後に、with
ほとんどの場合、これは悪い考えです。
関数はメソッドを持つことができます。
私はこのパターンのAJAXフォーム送信を使用します。
var fn = (function() {
var ready = true;
function fnX() {
ready = false;
// AJAX return function
function Success() {
ready = true;
}
Success();
return "this is a test";
}
fnX.IsReady = function() {
return ready;
}
return fnX;
})();
if (fn.IsReady()) {
fn();
}
これが「これ」についての簡単な考え方です。関数内の「これ」は、通常、演算子newで作成された関数の将来のオブジェクトインスタンスを参照します。したがって、明らかに、内部関数の「これ」は、外部関数のインスタンスを参照することはありません。
上記は問題を回避する必要があります。しかし、「これ」でできることはもっと複雑です。
例1:
function DriveIn()
{
this.car = 'Honda';
alert(this.food); //'food' is the attribute of a future object
//and DriveIn does not define it.
}
var A = {food:'chili', q:DriveIn}; //create object A whose q attribute
//is the function DriveIn;
alert(A.car); //displays 'undefined'
A.q(); //displays 'chili' but also defines this.car.
alert(A.car); //displays 'Honda'
このルール:
関数がオブジェクトの属性として呼び出されるときはいつでも、関数の内部(ただし内部関数の外部)での「this」の出現はオブジェクトを参照します。
演算子newを使用する場合でも、「このルール」が適用されることを明確にする必要があります。舞台裏では、newは、オブジェクトのコンストラクター属性を介して「this」をオブジェクトにアタッチします。
例2:
function Insect ()
{
this.bug = "bee";
this.bugFood = function()
{
alert("nectar");
}
}
var B = new Insect();
alert(B.constructor); //displays "Insect"; By "The Rule of This" any
//ocurrence of 'this' inside Insect now refers
//to B.
これをさらに明確にするために、演算子newを使用せずにInsectインスタンスを作成できます。
例3:
var C = {constructor:Insect}; //Assign the constructor attribute of C,
//the value Insect.
C.constructor(); //Call Insect through the attribute.
//C is now an Insect instance as though it
//were created with operator new. [*]
alert(C.bug); //Displays "bee."
C.bugFood(); //Displays "nectar."
[*]私が識別できる唯一の実際の違いは、例3では、「コンストラクター」が列挙可能な属性であるということです。演算子newを使用すると、「コンストラクター」が属性になりますが、列挙できません。for-in操作"for(var name in object)"が属性の名前を返す場合、属性は列挙可能です。
コールバックを書くとき、次のようなコードがたくさんあります:
callback: function(){
stuff(arg1,arg2);
}
以下の関数を使用して、多少きれいにすることができます。
callback: _(stuff, arg1, arg2)
これは、javascript の Function オブジェクトのあまり知られていない関数 apply を使用します。
また、関数名として使用できる別の文字 _ も示しています。
function _(){
var func;
var args = new Array();
for(var i = 0; i < arguments.length; i++){
if( i == 0){
func = arguments[i];
} else {
args.push(arguments[i]);
}
}
return function(){
return func.apply(func, args);
}
}
Function.applyを使用して、関数が動作するオブジェクトを指定します。
クラスがあるとします
function myClass(){
this.fun = function(){
do something;
};
}
後で行う場合:
var a = new myClass();
var b = new myClass();
myClass.fun.apply(b); //this will be like b.fun();
呼び出しパラメーターの配列を 2 番目の引数として指定することもできます
これを見てください: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply
コンストラクターの変更Array
や の定義など、ランタイム環境の大部分をオンザフライで再定義できますundefined
。すべきことではありませんが、強力な機能になる可能性があります。
これのやや危険性の低い形式は、既存のオブジェクトへのヘルパー メソッドの追加です。たとえば、IE6 で arrays の indexOf を「ネイティブに」サポートするようにすることができます。
関数 l(f,n){n&&l(f,n-1,f(n));}
l( 関数 ( ループ ){ アラート ( ループ ); }, 5 );
アラート 5、4、3、2、1
まあ、それは大した機能ではありませんが、非常に便利です:
選択可能な書式設定されたアラートを表示します。
alert(prompt('',something.innerHTML ));