私たちは JS を信頼しています - 学習する最良の方法は、構築/コーディングして教えることです。私は友達が JavaScript を学ぶのを助けるために課題を作成し、その見返りとして私がこの言語をより深いレベルで受け入れるのに役立ちます。自由にクローン、フォーク、プルしてください。
function a ( x ) {
x ++ ;
return function ( ) {
console . log ( ++ x ) ;
} ;
}
a ( 1 ) ( ) ;
a ( 1 ) ( ) ;
a ( 1 ) ( ) ;
let x = a ( 1 ) ;
x ( ) ;
x ( ) ;
x ( ) ;
1, 2, 3
と1, 2, 3
3, 3, 3
と3, 4, 5
3, 3, 3
と1, 2, 3
1, 2, 3
および3, 3, 3
この質問では、JavaScript で最も混乱を招く概念の 1 つであるクロージャについて再検討します。クロージャを使用すると、 stateful function
を作成でき、そのような関数はスコープ外の変数にアクセスできます。一言で言えば、クロージャはglobal
変数 (スコープ)、 father function
スコープ、およびits
のスコープにアクセスできます。
ここでは、最初に関数a()
を呼び出すだけなので、唯一の正解は 3, 3, 3 と 3, 4, 5 です。これは通常の関数のように動作し、いわゆるstateful
ものはまだ見たことがありません。次のコードでは、変数x
を宣言し、関数a(1)
の値を格納します。そのため、3、3、3 ではなく 3. 4. 5 が得られます。
この種の落とし穴は、PHP の世界におけるstatic
変数の感覚を私に与えます。
function Name ( a , b ) {
this . a = a ;
this . b = b ;
}
const me = Name ( "Vuong" , "Nguyen" ) ;
console . log ( ! ( a . length - window . a . length ) ) ;
undefined
NaN
true
false
コンソールで true が得られます。難しいのは、コンストラクター関数 Name からオブジェクトを作成するときに、 new
キーワークを使用しないことです。これにより、変数がグローバルa
になり、値「Vuong」が取得されます。これは実際には、(ブラウザー内の) グローバル オブジェクトwindow
のプロパティ、または、nodejs 内のglobal
あることに注意してください。
次に、 a.length
~ 5 とwindow.a.length
~ 5 を取得します。これらは 0 を返します。!0 は true を返します。
new
キーワークでインスタンスme
作成すると何が起こるかを想像してみてください。それは興味深い調査ですね!
const x = function ( ... x ) {
let k = ( typeof x ) . length ;
let y = ( ) => "freetut" . length ;
let z = { y : y } ;
return k - z . y ( ) ;
} ;
console . log ( Boolean ( x ( ) ) ) ;
true
false
スプレッド演算子...x
、関数内のパラメータを配列形式で取得するのに役立つ場合があります。ただし、JavaScript では配列の type は「配列」ではなく「オブジェクト」を返します。 PHP から来ている場合はまったく奇妙です。
つまり、6 を返す文字列object
の長さが得られました。zy() は単に文字列 'freetut' (7) の長さを返します。
関数 x() ( function express
またはanonymous function
(PHP からの場合) の形式) は、呼び出されると -1 を返し、 Boolean(-1)
で bool に変換されると false ではなく true を返すことに注意してください。 Boolean(0)
が false を返すこと。
( function js ( x ) {
const y = ( j ) => j * x ;
console . log ( y ( s ( ) ) ) ;
function s ( ) {
return j ( ) ;
}
function j ( ) {
return x ** x ;
}
} ) ( 3 ) ;
undefined
関数js()
呼び出さなくても自動的に実行でき、IIFE (Immediately Invoked Function Expression) として知られています。関数js
のパラメータx
、実際には値 3 で渡されることに注意してください。
関数の戻り値は y(s())) です。これは、関数s()
が j() を返すため、他の 3 つの関数y()
、 s()
およびj()
を呼び出すことをj()
。
j() は 3^3 = 27 を返すため、s() は 27 を返します。
y(s()) は 27*3 = 81 を返す y(27) を意味します。
関数が実際に宣言される前に、 declare function
を呼び出すことはできますが、 expression function
使用することはできないことに注意してください。
var tip = 100 ;
( function ( ) {
console . log ( "I have $" + husband ( ) ) ;
function wife ( ) {
return tip * 2 ;
}
function husband ( ) {
return wife ( ) / 2 ;
}
var tip = 10 ;
} ) ( ) ;
ここには IIFE (Immediately Invoked Function Expression) があります。つまり、呼び出す必要はありませんが、宣言されると自動的に実行されます。フローは次のようになります。夫() は妻()/2 を返し、妻()はチップ*2 を返します。
var
キーワードで宣言する場合はグローバル変数なので、tip = 100 と考えるかもしれません。ただし、関数内にvar tip = 10
があるため、実際にはundefined
です。変数のtip
デフォルト値undefined
でホイストされるため、最終結果は D になります。2 に除算するか、2 の倍数にしようとすると、 undefined
NaN を返すことがわかります。
var tip = 10;
再宣言しないと、関数の最後には、間違いなく B が得られます。
JSって楽しいですよね?
const js = { language : "loosely type" , label : "difficult" } ;
const edu = { ... js , level : "PhD" } ;
const newbie = edu ;
delete edu . language ;
console . log ( Object . keys ( newbie ) . length ) ;
このチャレンジは、 spread operator ...
スプレッド演算子は、関数内のパラメータを取得したり、JavaScript でオブジェクトと配列をunite
またはcombine
のに非常に便利です。 PHPにもこの機能があります。
変数edu
では、 ...js
(ここではスプレッド演算子) を使用して、両方のオブジェクトを 1 つに結合します。配列でも同様に機能します。
次に、 newbie
という名前の別の変数を宣言します。重要な注意: このように変数を宣言すると、両方の変数がメモリ内の同じ位置を指します。 PHP では、両方の変数を同じように機能させる$a = &$b
のようなことを知っているかもしれません。この場合、 pass by reference
については知っていたかもしれません。
次に、 edu.language
が削除されるので 2 になります。どちらのオブジェクトにも要素が 2 つだけあります。
ここで、JS でオブジェクトを浅いものにするか深いものにするかについて考えてみましょう。
var candidate = {
name : "Vuong" ,
age : 30 ,
} ;
var job = {
frontend : "Vuejs or Reactjs" ,
backend : "PHP and Laravel" ,
city : "Auckland" ,
} ;
class Combine {
static get ( ) {
return Object . assign ( candidate , job ) ;
}
static count ( ) {
return Object . keys ( this . get ( ) ) . length ;
}
}
console . log ( Combine . count ( ) ) ;
組み込みメソッドObject.assign(candidate, job)
は、 candidate
とjob
2 つのオブジェクトを 1 つのオブジェクトにマージします。次に、メソッドObject.keys
オブジェクト内のkey
の数をカウントします。
get()
とcount()
2 つのメソッドはstatic
として定義されているため、 Class.staticmethod()
構文を使用して静的に呼び出す必要があることに注意してください。次に、最終的なオブジェクトは 5 つの要素を取得します。
var x = 1 ;
( ( ) => {
x += 1 ;
++ x ;
} ) ( ) ;
( ( y ) => {
x += y ;
x = x % y ;
} ) ( 2 ) ;
( ( ) => ( x += x ) ) ( ) ;
( ( ) => ( x *= x ) ) ( ) ;
console . log ( x ) ;
最初にx
値 1 で宣言されます。最初の IIFE 関数には 2 つの演算があります。最初のx
2 になり、次に 3 になります。
2 番目の IIFE 関数では、 x = x + y
の場合、現在の値は 5 になります。 2 番目の演算では、 5%2
が実行されるため、1 のみが返されます。
3 番目と 4 番目の IIFE 関数では、 2 x = x + x
が得られ、次に 4 x = x * x
得られます。それは単純ではありません。
$ var = 10 ;
$ f = function ( $ let ) use ( $ var ) {
return ++ $ let + $ var ;
};
$ var = 15 ;
echo $ f ( 10 );
var x = 10 ;
const f = ( l ) => ++ l + x ;
x = 15 ;
console . log ( f ( 10 ) ) ;
この質問は、クロージャを処理する際の PHP と JavaScript の違いを示しています。最初のスニペットでは、キーワードuse
使用してクロージャを宣言します。 PHP のクロージャは単なる匿名関数であり、データはキーワードuse
を使用して関数に渡されます。それ以外の場合、キーワードuse
使用しない場合はlambda
として呼び出されます。スニペットの結果は https://3v4l.org/PSeMY で確認できます。 PHP closure
呼び出される場所に関係なく、クロージャが定義される前の変数の値のみを受け入れます。そのため、 $var
15 ではなく 10 になります。
逆に、JavaScript では、変数が匿名関数に渡される場合、変数の扱いが少し異なります。ここで変数をクロージャに渡すためにキーワードuse
を使用する必要はありません。 2 番目のスニペットの変数x
、クロージャが呼び出される前に更新され、26 を取得します。
PHP 7.4 にはアロー関数があるため、変数を関数に渡すためにキーワードuse
を使用する必要がないことに注意してください。 PHP の関数内でglobal
変数を呼び出すもう 1 つの方法は、キーワードglobal
を使用するか、組み込みの GLOBAL 変数 $GLOBALS を使用することです。
let x = { } ;
let y = { } ;
let z = x ;
console . log ( x == y ) ;
console . log ( x === y ) ;
console . log ( x == z ) ;
console . log ( x === z ) ;
技術的には、 x
とy
同じ値です。どちらも空のオブジェクトです。ただし、オブジェクトの比較には値を使用しません。
z
x
であり、同じメモリ位置を参照する 2 つのオブジェクトです。 JavaScript では、配列とオブジェクトはreference
によって渡されます。したがって、 x
とz
比較されると true を返します。
console . log ( "hello" ) ;
setTimeout ( ( ) => console . log ( "world" ) , 0 ) ;
console . log ( "hi" ) ;
setTimeout() 関数がスタックに戻る前にtask queue
に保持されるとするとstack,
最初に「hello」と「hi」が出力され、次に A は誤りです。回答CとDも同様です。
setTimeout()
関数に何秒を設定しても、この関数は同期コードの後に実行されます。したがって、最初に「hello」がコールスタックに入れられるため、最初に取得します。 setTimeout()
はコール スタックに入れられますが、その後 Web API (または Node API) にオフロードされ、他の同期コードがクリアされたときに呼び出されます。つまり、次に「hi」、最後に「world」になります。
したがって、B が正解です。
クレジット: @kaitoubg (voz) のtimeout throttled
に関するご提案に対して、質問を少し変更することにしました。これにより、前のコードが他のブラウザや環境でテストされた場合に異なる結果が得られる可能性があるため、読者が混乱することがなくなります。質問の主な点は、 setTimeout.
。
String . prototype . lengthy = ( ) => {
console . log ( "hello" ) ;
} ;
let x = { name : "Vuong" } ;
delete x ;
x . name . lengthy ( ) ;
String.prototype.someThing = function () {}
String
の新しい組み込みメソッドを定義する一般的な方法です。同じことをArray
、 Object
、またはFunctionName
で行うことができます。ここで FunctionName は自分で設計した関数です。
"string".lengthy()
常にhello
返すことを理解するのは難しくありません。ただし、注意が必要な部分はdelete object
にあり、この式でオブジェクトが完全に削除されると考えられる場合があります。 delete
はオブジェクトのプロパティのみを削除するために使用されるため、これは当てはまりません。オブジェクトは削除されません。次に、 ReferenceError
ではなくhello
が返されます。
let, const
、またはvar
使用せずに object を宣言すると、グローバル オブジェクトが作成されることに注意してください。 delete objectName
てからtrue
を返します。それ以外の場合は、常にfalse
を返します。
let x = { } ;
x . __proto__ . hi = 10 ;
Object . prototype . hi = ++ x . hi ;
console . log ( x . hi + Object . keys ( x ) . length ) ;
まず空のオブジェクトx
があり、次にx.__proto__.hi
を使用して x に別のプロパティhi
を追加します。これはObject.prototype.hi = 10
と同等であり、 father
オブジェクトObject
にプロパティhi
追加していることに注意してください。これは、すべてのオブジェクトがこのプロパティを継承することを意味します。プロパティhi
共有プロパティになります。ここで、 let y = {}
のような新しいオブジェクトを宣言するとします。 y
には、 father
Object
から継承されたプロパティhi
があります。簡単に言えば、 x.__proto__ === Object.prototype
true
を返します。
次に、プロパティhi
新しい値 11 で上書きします。最後に、11 + 1 = 12 が得られますx
は 1 つのプロパティがあり、 x.hi
11 を返します。
更新しました(2021年7月27日)。 Object.prototype.hi = 11;
と書くと、代わりにObject.prototype.hi = ++x.hi;
上のコードに書かれているように、 Object.keys(object)
継承されたプロパティではなくオブジェクト自体のプロパティのみを返すため、 Object.keys(x)
空の配列を返します。これは、最終結果が 12 ではなく 11 になることを意味します。何らかの理由で、コード「Object.prototype.hi = ++x.hi;」は、 will create a property for the object
、次に Object.keys(x) から配列 `["hi"]` を取得します。
ただし、 console.log(x.hasOwnProperty("hi"))
を実行すると、依然としてfalse
が返されます。ちなみに、 x.test = "testing"
のように x のプロパティを意図的に追加すると、 console.log(x.hasOwnProperty("test"))
true
を返します。
const array = ( a ) => {
let length = a . length ;
delete a [ length - 1 ] ;
return a . length ;
} ;
console . log ( array ( [ 1 , 2 , 3 , 4 ] ) ) ;
const object = ( obj ) => {
let key = Object . keys ( obj ) ;
let length = key . length ;
delete obj [ key [ length - 1 ] ] ;
return Object . keys ( obj ) . length ;
} ;
console . log ( object ( { 1 : 2 , 2 : 3 , 3 : 4 , 4 : 5 } ) ) ;
const setPropNull = ( obj ) => {
let key = Object . keys ( obj ) ;
let length = key . length ;
obj [ key [ length - 1 ] ] = null ;
return Object . keys ( obj ) . length ;
} ;
console . log ( setPropNull ( { 1 : 2 , 2 : 3 , 3 : 4 , 4 : 5 } ) ) ;
この質問では、JavaScript でdelete
演算子がどのように機能するかを調べます。つまり、 delete someObject
またはdelete someArray
と書いても、何も行われません。それでも、 delete someObject.someProperty
ような記述を行うと、オブジェクトのプロパティが完全に削除されます。配列の場合、 delete someArray[keyNumber]
と書くと、 index
のvalue
のみが削除され、 index
そのまま維持され、新しいvalue
はundefined
に設定されます。そのため、コードの最初のスニペットでは、元の配列と同様に (長さ) 4 つの要素を取得しますが、2 番目のスニペットのように、関数 object() が呼び出されたときに渡されるオブジェクトには 3 つのプロパティのみが残ります。
オブジェクトのプロパティをnull
またはundefined
に宣言してもプロパティが完全に削除されないため、3 番目のスニペットでは 4 が得られます。鍵はそのままです。したがって、オブジェクトの長さは不変です。
PHP に精通している人のために、配列要素 (キーと値の両方) を削除するunset($someArray[index])
があります。配列をprint_r
場合、設定が解除されたキーと値が表示されない場合があります。ただし、その配列内の新しい要素を ( array_push($someArray, $someValue)
を使用して) プッシュすると、前のキーはまだ保持されていますが、値がなく、表示されていないことがわかります。それは知っておくべきことです。 https://3v4l.org/7C3Nf をご覧ください。
var a = [ 1 , 2 , 3 ] ;
var b = [ 1 , 2 , 3 ] ;
var c = [ 1 , 2 , 3 ] ;
var d = c ;
var e = [ 1 , 2 , 3 ] ;
var f = e . slice ( ) ;
console . log ( a === b ) ;
console . log ( c === d ) ;
console . log ( e === f ) ;
a
とb
値が同じであっても異なるメモリ位置を指しているため、false を返します。 PHP の世界から来ている場合は、値または値 + 型を比較すると、明らかに true が返されます。チェックしてください: https://3v4l.org/IjaOs
JavaScript では、 array
とobject
の場合、値は参照によって渡されます。したがって、2 番目のケースでは、 d
c
のコピーですが、両方とも同じメモリ位置を指します。 c
のすべての変化はd
の変化をもたらします。 PHP では、 $a = &$b;
、同様の方法で動作します。
3 つ目は、 slice()
メソッドを使用して JavaScript で配列をコピーするためのヒントを提供します。これでe
のコピーであるf
ができましたが、これらは異なるメモリ位置を指しているため、異なる「寿命」を持ちます。比較すると、それに応じてfalse
返されます。
var languages = {
name : [ "elixir" , "golang" , "js" , "php" , { name : "feature" } ] ,
feature : "awesome" ,
} ;
let flag = languages . hasOwnProperty ( Object . values ( languages ) [ 0 ] [ 4 ] . name ) ;
( ( ) => {
if ( flag !== false ) {
console . log (
Object . getOwnPropertyNames ( languages ) [ 0 ] . length <<
Object . keys ( languages ) [ 0 ] . length
) ;
} else {
console . log (
Object . getOwnPropertyNames ( languages ) [ 1 ] . length <<
Object . keys ( languages ) [ 1 ] . length
) ;
}
} ) ( ) ;
このコード スニペットには、 JavaScript
でオブジェクトを処理するさまざまな組み込みメソッドがいくつかあるため、非常に複雑です。たとえば、 Object.keys
とObject.getOwnPropertyNames
は両方とも、後者が列挙不可能なプロパティを返すことができるという点を除けば非常に似ていますが、使用されます。この詳細に書かれたリファレンス https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames を参照するとよいでしょう。
Object.values
とObject.keys
、それぞれオブジェクトのプロパティ値とプロパティ名を返します。それは何も新しいことではありません。 object.hasOwnProperty('propertyName')
プロパティが存在するかどうかを確認するboolean
を返します。
Object.values(languages)[0][4].name
プロパティの名前でもあるfeature
を返すので、 flag
true です。
次に、ビット単位の値を返すif-else
フローには 4 << 4 があり、これは4*2^4
~ 4*16
~ 64 に相当します。
var player = {
name : "Ronaldo" ,
age : 34 ,
getAge : function ( ) {
return ++ this . age - this . name . length ;
} ,
} ;
function score ( greeting , year ) {
console . log (
greeting + " " + this . name + `! You were born in ${ year - this . getAge ( ) } `
) ;
}
window . window . window . score . call ( window .