Di JS kami percaya - Cara terbaik untuk belajar adalah dengan membangun/mengkode dan mengajar. Saya menciptakan tantangan untuk membantu teman-teman saya mempelajari JavaScript dan sebagai imbalannya, hal ini membantu saya memahami bahasa tersebut lebih dalam. Jangan ragu untuk mengkloning, memotong, dan menarik.
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
dan 1, 2, 3
3, 3, 3
dan 3, 4, 5
3, 3, 3
dan 1, 2, 3
1, 2, 3
dan 3, 3, 3
Pertanyaan ini membahas kembali penutupan - salah satu konsep paling membingungkan dalam JavaScript. Penutupan memungkinkan kita membuat stateful function
dan fungsi tersebut dapat mengakses variabel di luar cakupannya. Singkatnya, penutupan dapat memiliki akses ke variabel global
(ruang lingkup), ruang lingkup father function
, dan ruang lingkupnya its
.
Di sini kita punya, satu-satunya jawaban yang benar, 3, 3, 3 dan 3, 4, 5 karena pertama-tama kita cukup memanggil fungsinya a()
. Ini berfungsi seperti fungsi normal dan kami belum melihat apa pun yang disebut stateful
. Dalam kode berikut, kita mendeklarasikan variabel x
dan menyimpan nilai fungsi a(1)
, itulah sebabnya kita mendapatkan 3.4.5 daripada 3, 3, 3.
Gotcha semacam ini memberi saya perasaan variabel static
di dunia PHP.
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
Kami menjadi kenyataan di konsol. Bagian yang sulit adalah ketika kita membuat objek dari nama fungsi konstruktor tetapi kita TIDAK MENGGUNAKAN keywork new
. Itu menjadikan variabel tersebut a
variabel global dan mendapatkan nilai "Vuong". Ingatlah bahwa ini sebenarnya adalah properti dari window
objek global (di browser) atau global
di nodejs.
Kami kemudian mendapatkan a.length
~ 5 dan window.a.length
~ 5 yang mengembalikan 0. !0 mengembalikan nilai true.
Bayangkan apa yang akan terjadi ketika kita membuat instance me
dengan keywork new
. Itu adalah pertanyaan yang menarik!
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
Operator spread ...x
dapat membantu kita mendapatkan parameter fungsi dalam bentuk array. Namun, dalam Javascript, typeof array mengembalikan "objek" daripada "array". Sangat aneh jika Anda berasal dari PHP.
Artinya, kita sekarang memiliki panjang object
string yang mengembalikan 6. zy() hanya mengembalikan panjang string 'freetut' (7).
Sadarilah bahwa fungsi x() (dalam bentuk function express
atau anonymous function
(jika Anda berasal dari PHP) mengembalikan -1 ketika dipanggil dan ketika dikonversi ke bool dengan Boolean(-1)
mengembalikan nilai true, bukan false. Tercatat Boolean(0)
itu menghasilkan nilai salah.
( function js ( x ) {
const y = ( j ) => j * x ;
console . log ( y ( s ( ) ) ) ;
function s ( ) {
return j ( ) ;
}
function j ( ) {
return x ** x ;
}
} ) ( 3 ) ;
undefined
Fungsi js()
dapat dijalankan secara otomatis tanpa memanggilnya dan dikenal sebagai IIFE (Immediately Invoked Function Expression). Tercatat parameter x
dari fungsi js
sebenarnya diteruskan dengan nilai 3.
Nilai yang dikembalikan dari fungsi tersebut adalah y(s())), artinya memanggil tiga fungsi lainnya y()
, s()
dan j()
karena fungsi s()
mengembalikan j()
.
j() mengembalikan 3^3 = 27 sehingga s() mengembalikan 27.
y(s()) berarti y(27) yang menghasilkan 27*3 = 81.
Perhatikan bahwa kita dapat memanggil declare function
SEBELUM fungsi tersebut benar-benar dideklarasikan tetapi tidak dengan expression function
.
var tip = 100 ;
( function ( ) {
console . log ( "I have $" + husband ( ) ) ;
function wife ( ) {
return tip * 2 ;
}
function husband ( ) {
return wife ( ) / 2 ;
}
var tip = 10 ;
} ) ( ) ;
Di sini kami memiliki IIFE (Ekspresi Fungsi Segera Dipanggil). Artinya kita tidak perlu memanggilnya tetapi akan dieksekusi secara otomatis saat dideklarasikan. Alurnya adalah sebagai: suami() mengembalikan istri()/2 dan istri() mengembalikan tip*2.
Kita mungkin mengira tip = 100 karena ini adalah variabel global ketika dideklarasikan dengan kata kunci var
. Namun, sebenarnya ini undefined
karena kita juga memiliki var tip = 10
DI DALAM fungsinya. Saat tip
variabel diangkat dengan nilai default undefined
, hasil akhirnya adalah D. Kita tahu bahwa undefined
mengembalikan NaN ketika kita mencoba membagi menjadi 2 atau mengalikannya dengan 2.
Jika kita tidak mendeklarasikan ulang var tip = 10;
di akhir fungsinya, kita pasti akan mendapatkan B.
JS itu menyenangkan, bukan?
const js = { language : "loosely type" , label : "difficult" } ;
const edu = { ... js , level : "PhD" } ;
const newbie = edu ;
delete edu . language ;
console . log ( Object . keys ( newbie ) . length ) ;
Tantangan ini merevisi fitur ES6 mengenai spread operator ...
Operator spread cukup berguna untuk mengambil parameter dalam fungsi, untuk unite
atau combine
objek dan array dalam JavaScript. PHP juga memiliki fitur ini.
Dalam variabel edu
, kita menggunakan ...js
(operator spread di sini) untuk menggabungkan kedua objek menjadi satu. Ia bekerja dengan cara yang sama dengan array.
Kemudian kita mendeklarasikan variabel lain bernama newbie
. Catatan PENTING: Dengan mendeklarasikan variabel seperti itu, kedua variabel menunjuk ke POSISI YANG SAMA di memori. Kita mungkin sudah mengetahui sesuatu seperti $a = &$b
di PHP, yang memungkinkan kedua variabel bekerja dengan cara yang sama. Kita mungkin sudah mengetahui tentang pass by reference
dalam kasus ini.
Kemudian kita memiliki 2 karena edu.language
dihapus. Kedua objek sekarang hanya memiliki dua elemen.
Sekarang saatnya memikirkan cara mengatasi objek di JS baik yang dangkal maupun yang dalam.
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 ( ) ) ;
Metode bawaan Object.assign(candidate, job)
menggabungkan dua objek candidate
dan job
menjadi satu objek. Kemudian metode Object.keys
menghitung jumlah key
dalam objek.
Perhatikan bahwa dua metode get()
dan count()
didefinisikan sebagai static
, sehingga keduanya perlu dipanggil secara statis menggunakan sintaksis Class.staticmethod()
. Kemudian objek terakhir mendapatkan 5 elemen.
var x = 1 ;
( ( ) => {
x += 1 ;
++ x ;
} ) ( ) ;
( ( y ) => {
x += y ;
x = x % y ;
} ) ( 2 ) ;
( ( ) => ( x += x ) ) ( ) ;
( ( ) => ( x *= x ) ) ( ) ;
console . log ( x ) ;
Awalnya x
dideklarasikan dengan nilai 1. Pada fungsi IIFE pertama, terdapat dua operasi. Pertama x
menjadi 2 dan kemudian 3.
Pada fungsi IIFE kedua, x = x + y
maka nilai saat ini adalah 5. Pada operasi kedua, ia hanya mengembalikan 1 saat mengalami 5%2
.
Pada fungsi IIFE ketiga dan keempat, kita mendapatkan 2 x = x + x
dan kemudian 4 x = x * x
. Ini lebih dari sederhana.
$ 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 ) ) ;
Pertanyaan ini menggambarkan perbedaan antara PHP dan JavaScript saat menangani penutupan. Pada cuplikan pertama, kami mendeklarasikan penutupan dengan kata kunci use
. Penutupan di PHP hanyalah sebuah fungsi anonim dan data diteruskan ke fungsi menggunakan kata kunci use
. Jika tidak, ini disebut lambda
jika kita tidak menggunakan kata kunci use
. Anda dapat memeriksa hasil cuplikannya di sini https://3v4l.org/PSeMY. closure
PHP hanya menerima nilai variabel SEBELUM penutupan ditentukan, di mana pun ia dipanggil. Dengan demikian, $var
adalah 10, bukan 15.
Sebaliknya, JavaScript memperlakukan variabel sedikit berbeda ketika diteruskan ke fungsi anonim. Kita tidak harus menggunakan kata kunci use
here untuk meneruskan variabel ke penutupan. Variabel x
pada cuplikan kedua diperbarui sebelum penutupan dipanggil, maka kita mendapatkan 26.
Perhatikan bahwa di PHP 7.4, kita memiliki fungsi panah dan kita tidak perlu menggunakan kata kunci use
untuk meneruskan variabel ke fungsi. Cara lain untuk memanggil ariable global
di dalam suatu fungsi di PHP adalah dengan menggunakan kata kunci global
atau menggunakan variabel GLOBAL bawaan $GLOBALS.
let x = { } ;
let y = { } ;
let z = x ;
console . log ( x == y ) ;
console . log ( x === y ) ;
console . log ( x == z ) ;
console . log ( x === z ) ;
Secara teknis, x
dan y
memiliki nilai yang sama. Keduanya adalah benda kosong. Namun, kami tidak menggunakan nilai tersebut untuk membandingkan objek.
z
adalah x
adalah dua objek yang mengacu pada posisi memori yang sama. Dalam JavaScript, array dan objek diteruskan dengan reference
. x
dan z
oleh karena itu mengembalikan nilai true ketika dibandingkan.
console . log ( "hello" ) ;
setTimeout ( ( ) => console . log ( "world" ) , 0 ) ;
console . log ( "hi" ) ;
Mengingat fungsi setTimeout() akan disimpan dalam task queue
sebelum melompat kembali ke stack,
"halo" dan "hai" akan dicetak terlebih dahulu, maka A salah. Begitu pula dengan jawaban C dan D.
Tidak peduli berapa detik Anda menyetel fungsi setTimeout()
, fungsi tersebut akan berjalan setelah kode sinkron. Jadi kita akan mendapatkan "halo" terlebih dahulu saat dimasukkan ke dalam tumpukan panggilan terlebih dahulu. Meskipun setTimeout()
kemudian dimasukkan ke dalam tumpukan panggilan, setTimeout() selanjutnya akan dipindahkan ke API web (atau Node API) dan kemudian dipanggil ketika kode sinkron lainnya dihapus. Artinya kita kemudian mendapatkan "hai" dan akhirnya "dunia".
Jadi B adalah jawaban yang benar.
Kredit: @kaitoubg (voz) atas saran Anda mengenai timeout throttled
sehingga saya memutuskan untuk sedikit mengubah pertanyaan. Ini akan memastikan bahwa pembaca tidak akan bingung karena kode sebelumnya mungkin memberikan hasil yang berbeda ketika diuji pada browser atau lingkungan lain. Inti pertanyaannya adalah tentang perbedaan antara kode sinkron dan kode asinkron saat menggunakan setTimeout.
.
String . prototype . lengthy = ( ) => {
console . log ( "hello" ) ;
} ;
let x = { name : "Vuong" } ;
delete x ;
x . name . lengthy ( ) ;
String.prototype.someThing = function () {}
adalah cara umum untuk mendefinisikan metode bawaan baru untuk String
. Kita bisa melakukan hal yang sama dengan Array
, Object
atau FunctionName
dimana FunctionName adalah fungsi yang dirancang sendiri.
Tidaklah sulit untuk menyadari bahwa "string".lengthy()
selalu mengembalikan hello
. Namun, bagian rumitnya terletak pada delete object
di mana kita mungkin berpikir bahwa ekspresi ini akan menghapus objek sepenuhnya. Hal ini tidak terjadi karena delete
digunakan untuk menghapus properti objek saja. Itu tidak menghapus objek. Lalu kita mendapatkan hello
daripada ReferenceError
.
Perhatikan bahwa jika kita mendeklarasikan objek tanpa let, const
atau var
, maka kita memiliki objek global. delete objectName
lalu kembalikan true
. Jika tidak, ia selalu mengembalikan false
.
let x = { } ;
x . __proto__ . hi = 10 ;
Object . prototype . hi = ++ x . hi ;
console . log ( x . hi + Object . keys ( x ) . length ) ;
Pertama kita punya objek kosong x
, lalu kita tambahkan properti lain hi
untuk x dengan x.__proto__.hi
. Perhatikan ini setara dengan Object.prototype.hi = 10
dan kami menambahkan properti hi
ke objek father
Object
. Artinya setiap objek akan mewarisi properti ini. Properti hi
menjadi milik bersama. Katakanlah sekarang kita mendeklarasikan objek baru seperti misalkan let y = {}
, y
sekarang memiliki properti hi
yang diwarisi dari Object
father
. Sederhananya x.__proto__ === Object.prototype
mengembalikan true
.
Lalu kita timpa properti hi
dengan nilai baru 11. Terakhir kita punya 11 + 1 = 12. x
punya satu properti dan x.hi
mengembalikan 11.
Diperbarui (27 Juli 2021). Jika Anda menulis Object.prototype.hi = 11;
alih-alih Object.prototype.hi = ++x.hi;
seperti yang tertulis pada kode di atas, maka Object.keys(x)
akan mengembalikan array kosong karena Object.keys(object)
hanya mengembalikan properti dari objek itu sendiri, bukan properti yang diwarisi. Artinya hasil akhirnya adalah 11, bukan 12. Untuk beberapa alasan, kode ``Object.prototype.hi = ++x.hi; will create a property for the object
x` itu sendiri dan kemudian `Object.keys(x)` memberi kita array `["hi"]`.
Namun, jika Anda menjalankan console.log(x.hasOwnProperty("hi"))
ia masih mengembalikan false
. Omong-omong, saat Anda dengan sengaja menambahkan properti untuk x seperti x.test = "testing"
, maka console.log(x.hasOwnProperty("test"))
mengembalikan 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 } ) ) ;
Pertanyaan ini membahas cara kerja operator delete
di JavaScript. Singkatnya, ia tidak melakukan apa pun saat kita menulis delete someObject
atau delete someArray
. Meskipun demikian, ia sepenuhnya menghapus dan menghapus properti suatu objek saat menulis sesuatu seperti delete someObject.someProperty
. Dalam kasus array, ketika kita menulis delete someArray[keyNumber]
, itu hanya menghilangkan value
index
, menjaga index
tetap utuh dan value
baru sekarang disetel ke undefined
. Oleh karena itu, pada cuplikan kode pertama, kita mendapatkan (panjang) 4 elemen seperti pada array asli tetapi hanya 3 properti yang tersisa pada objek yang diteruskan ketika fungsi objek() dipanggil, seperti pada cuplikan kedua.
Cuplikan ketiga memberi kita angka 4 karena mendeklarasikan properti suatu objek menjadi null
atau undefined
tidak sepenuhnya menghapus properti tersebut. Kuncinya masih utuh. Jadi panjang benda tidak dapat diubah.
Bagi yang familiar dengan PHP, kami memiliki unset($someArray[index])
yang menghapus elemen array, baik kunci maupun nilai. Saat print_r
array, kita mungkin tidak melihat kunci dan nilai yang belum disetel. Namun, ketika kita mendorong (menggunakan array_push($someArray, $someValue)
) elemen baru dalam array itu, kita mungkin melihat bahwa kunci sebelumnya masih disimpan, tetapi tidak ada nilai dan tidak ditampilkan. Itu adalah sesuatu yang harus Anda waspadai. Lihat 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
dan b
mengembalikan false karena menunjuk ke lokasi memori yang berbeda meskipun nilainya sama. Jika Anda berasal dari dunia PHP, maka itu akan kembali benar ketika kita membandingkan nilai atau nilai + tipe. Lihat: https://3v4l.org/IjaOs.
Dalam JavaScript, nilai diteruskan dengan referensi dalam bentuk array
dan object
. Oleh karena itu dalam kasus kedua, d
adalah salinan dari c
tetapi keduanya menunjuk ke posisi memori yang sama. Segala perubahan pada c
akan mengakibatkan perubahan pada d
. Di PHP, kita mungkin memiliki $a = &$b;
, bekerja dengan cara yang sama.
Yang ketiga memberi kita petunjuk untuk menyalin array dalam JavaScript menggunakan metode slice()
. Sekarang kita punya f
, yang merupakan salinan dari e
tetapi menunjuk ke lokasi memori yang berbeda, sehingga memiliki "kehidupan" yang berbeda. Kami mendapatkan false
ketika mereka dibandingkan.
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
) ;
}
} ) ( ) ;
Cuplikan kodenya cukup rumit karena memiliki beberapa metode bawaan berbeda yang menangani objek di JavaScript
. Misalnya, Object.keys
dan Object.getOwnPropertyNames
digunakan meskipun keduanya sangat mirip kecuali Object.getOwnPropertyNames dapat mengembalikan properti yang tidak dapat dihitung. Anda mungkin ingin melihat referensi yang ditulis secara menyeluruh ini https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
Object.values
dan Object.keys
masing-masing mengembalikan nilai properti dan nama properti objek. Itu bukanlah hal baru. object.hasOwnProperty('propertyName')
mengembalikan boolean
yang mengonfirmasi apakah suatu properti ada atau tidak.
Kami memiliki flag
benar karena Object.values(languages)[0][4].name
mengembalikan feature
, yang juga merupakan nama properti.
Kemudian kita memiliki 4 << 4 dalam aliran if-else
yang mengembalikan nilai bitwise, setara dengan 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 .