Em JS confiamos - A melhor maneira de aprender é construindo/codificando e ensinando. Eu crio desafios para ajudar meus amigos a aprender JavaScript e em troca isso me ajuda a abraçar a linguagem em um nível muito mais profundo. Sinta-se à vontade para clonar, bifurcar e puxar.
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
e 1, 2, 3
3, 3, 3
e 3, 4, 5
3, 3, 3
e 1, 2, 3
1, 2, 3
e 3, 3, 3
Esta questão revisita o encerramento – um dos conceitos mais confusos em JavaScript. O fechamento nos permite criar uma stateful function
e tal função pode acessar a variável fora de seu escopo. Resumindo, um encerramento pode ter acesso à variável global
(escopo), ao escopo father function
e its
próprio escopo.
Temos aqui a única resposta correta, 3, 3, 3 e 3, 4, 5 porque primeiro simplesmente chamamos a função a()
. Funciona como uma função normal e ainda não vimos nada chamado stateful
. No código a seguir, declaramos uma variável x
e ela armazena o valor da função a(1)
, é por isso que obtemos 3. 4. 5 em vez de 3, 3, 3.
Esse tipo de pegadinha me dá a sensação de variável static
no mundo 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
Tornamo-nos verdadeiros no console. A parte complicada é quando criamos um objeto a partir da função construtora Name, mas NÃO USAMOS new
teclas. Isso torna a variável a
e obtém o valor "Vuong". Lembre-se que na verdade é uma propriedade do objeto global window
(no navegador) ou global
no nodejs.
Obtemos então a.length
~ 5 e window.a.length
~ 5 que retornam 0. !0 retorna verdadeiro.
Imagine o que aconteceria quando criássemos a instância me
com a new
chave. Essa é uma pergunta interessante!
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
O operador spread ...x
pode nos ajudar a obter o parâmetro na função na forma de array. No entanto, em Javascript, o array typeof retorna "objeto" em vez de "matriz". É totalmente estranho se você vem do PHP.
Dito isto, agora temos o comprimento do object
string que retorna 6. zy() simplesmente retorna o comprimento da string 'freetut' (7).
Esteja ciente de que a função x() (na forma de function express
ou anonymous function
(se você vier do PHP) retorna -1 ao ser chamada e quando convertida para bool com Boolean(-1)
retorna verdadeiro em vez de falso. Observado que Boolean(0)
retorne falso.
( function js ( x ) {
const y = ( j ) => j * x ;
console . log ( y ( s ( ) ) ) ;
function s ( ) {
return j ( ) ;
}
function j ( ) {
return x ** x ;
}
} ) ( 3 ) ;
undefined
A função js()
pode ser executada automaticamente sem chamá-la e é conhecida como IIFE (Immediately Invoked Function Expression). Observe que o parâmetro x
da função js
é passado com o valor 3.
O valor retornado da função é y(s())), o que significa chamar três outras funções y()
, s()
e j()
porque a função s()
retorna j()
.
j() retorna 3^3 = 27 então s() retorna 27.
y(s()) significa y(27) que retorna 27*3 = 81.
Observe que podemos chamar declare function
ANTES de a função ser realmente declarada, mas não com expression function
.
var tip = 100 ;
( function ( ) {
console . log ( "I have $" + husband ( ) ) ;
function wife ( ) {
return tip * 2 ;
}
function husband ( ) {
return wife ( ) / 2 ;
}
var tip = 10 ;
} ) ( ) ;
Temos aqui um IIFE (Expressão de Função Imediatamente Invocada). Isso significa que não precisamos chamá-lo, mas ele será executado automaticamente quando declarado. O fluxo é como: marido() retorna esposa()/2 e esposa() retorna dica*2.
Poderíamos pensar que tip = 100 porque é uma variável global ao declarar com a palavra-chave var
. No entanto, na verdade é undefined
porque também temos var tip = 10
DENTRO da função. Como a variável tip
é içada com valor padrão undefined
, o resultado final seria D. Sabemos que undefined
retorna NaN quando tentamos dividir por 2 ou múltiplo por 2.
Se não declararmos novamente var tip = 10;
no final da função, definitivamente obteremos B.
JS é divertido, certo?
const js = { language : "loosely type" , label : "difficult" } ;
const edu = { ... js , level : "PhD" } ;
const newbie = edu ;
delete edu . language ;
console . log ( Object . keys ( newbie ) . length ) ;
Este desafio revisa a funcionalidade do ES6 em relação spread operator ...
O operador spread é bastante útil para recuperar parâmetros em função, para unite
ou combine
objeto e array em JavaScript. PHP também possui esse recurso.
Na variável edu
, usamos ...js
(operador spread aqui) para combinar os dois objetos em um. Funciona da mesma maneira com array.
Em seguida, declaramos outra variável chamada newbie
. Nota IMPORTANTE: Ao declarar a variável dessa forma, ambas as variáveis apontam para a MESMA POSIÇÃO na memória. Podemos ter conhecido algo como $a = &$b
em PHP, que permite que ambas as variáveis funcionem da mesma maneira. Poderíamos saber sobre pass by reference
no caso.
Então temos 2, pois edu.language
foi excluído. Ambos os objetos agora possuem apenas dois elementos.
Agora é hora de pensar em lidar com um objeto em JS, seja superficial ou profundo.
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 ( ) ) ;
O método integrado Object.assign(candidate, job)
mescla os dois objetos candidate
e job
em um objeto. Então o método Object.keys
conta o número de key
no objeto.
Observe que dois métodos get()
e count()
são definidos como static
, portanto, eles precisam ser chamados estaticamente usando a sintaxe Class.staticmethod()
. Então o objeto final obtém 5 elementos.
var x = 1 ;
( ( ) => {
x += 1 ;
++ x ;
} ) ( ) ;
( ( y ) => {
x += y ;
x = x % y ;
} ) ( 2 ) ;
( ( ) => ( x += x ) ) ( ) ;
( ( ) => ( x *= x ) ) ( ) ;
console . log ( x ) ;
Inicialmente x
é declarado com o valor 1. Na primeira função IIFE, existem duas operações. Primeiro x
se torna 2 e depois 3.
Na segunda função IIFE, x = x + y
então o valor atual é 5. Na segunda operação, ela retorna apenas 1 à medida que passa por 5%2
.
Na terceira e quarta funções IIFE, obtemos 2 x = x + x
e então 4 x = x * x
. É mais do que simples.
$ 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 ) ) ;
Esta questão ilustra as diferenças entre PHP e JavaScript ao lidar com encerramentos. No primeiro trecho, declaramos um encerramento com a palavra-chave use
. O fechamento em PHP é simplesmente uma função anônima e os dados são passados para a função usando a palavra-chave use
. Caso contrário, é chamado lambda
quando não usamos a palavra-chave use
. Você pode conferir o resultado do snippet aqui https://3v4l.org/PSeMY. closure
do PHP aceita apenas o valor da variável ANTES do fechamento ser definido, não importa onde seja chamado. Como tal, $var
é 10 em vez de 15.
Pelo contrário, o JavaScript trata a variável de maneira um pouco diferente quando ela é passada para uma função anônima. Não precisamos usar a palavra-chave use
aqui para passar a variável para o encerramento. A variável x
no segundo trecho é atualizada antes do encerramento ser chamado, então obtemos 26.
Observe que no PHP 7.4, temos a função arrow e então não precisamos usar a palavra-chave use
para passar a variável para a função. Outra maneira de chamar uma ariável global
dentro de uma função em PHP é usar a palavra-chave global
ou empregar a variável GLOBAL integrada $GLOBALS.
let x = { } ;
let y = { } ;
let z = x ;
console . log ( x == y ) ;
console . log ( x === y ) ;
console . log ( x == z ) ;
console . log ( x === z ) ;
Tecnicamente, x
e y
têm o mesmo valor. Ambos são objetos vazios. No entanto, não usamos o valor para comparar objetos.
z
is x
são dois objetos referentes à mesma posição de memória. Em JavaScript, array e objeto são passados por reference
. x
e z
, portanto, retornam verdadeiros quando comparados.
console . log ( "hello" ) ;
setTimeout ( ( ) => console . log ( "world" ) , 0 ) ;
console . log ( "hi" ) ;
Dado que a função setTimeout() será mantida na task queue
antes de voltar para stack,
"hello" e "hi" serão impressos primeiro, então A está incorreto. Esse também é o caso das respostas C e D.
Não importa quantos segundos você defina para a função setTimeout()
, ela será executada após o código síncrono. Portanto, receberemos "hello" primeiro, pois ele será colocado primeiro na pilha de chamadas. Embora setTimeout()
seja colocado na pilha de chamadas, ele será posteriormente descarregado para a API da web (ou API do nó) e será chamado quando outros códigos síncronos forem limpos. Significa que então recebemos “oi” e finalmente “mundo”.
Então B é a resposta correta.
Crédito: @kaitoubg (voz) pela sua sugestão sobre o timeout throttled
pelo qual decidi alterar um pouco a questão. Isso garantirá que os leitores não fiquem confusos, pois o código anterior pode trazer resultados diferentes quando testado em outros navegadores ou ambientes. O ponto principal da questão é sobre a discrepância entre o código síncrono e o código assíncrono ao usar setTimeout.
.
String . prototype . lengthy = ( ) => {
console . log ( "hello" ) ;
} ;
let x = { name : "Vuong" } ;
delete x ;
x . name . lengthy ( ) ;
String.prototype.someThing = function () {}
é a maneira comum de definir um novo método integrado para String
. Podemos fazer a mesma coisa com Array
, Object
ou FunctionName
onde FunctionName é a função projetada por nós mesmos.
Não é difícil perceber que "string".lengthy()
sempre retorna hello
. No entanto, a parte complicada está no delete object
, onde podemos pensar que esta expressão excluirá totalmente o objeto. Esse não é o caso, pois delete
é usado para excluir apenas a propriedade do objeto. Não exclui o objeto. Então recebemos hello
em vez de ReferenceError
.
Observe que se declararmos object sem let, const
ou var
, teremos então um objeto global. delete objectName
e retorne true
. Caso contrário, sempre retorna false
.
let x = { } ;
x . __proto__ . hi = 10 ;
Object . prototype . hi = ++ x . hi ;
console . log ( x . hi + Object . keys ( x ) . length ) ;
Primeiro temos um objeto vazio x
, depois adicionamos outra propriedade hi
para x com x.__proto__.hi
. Observe que isso é equivalente a Object.prototype.hi = 10
e estamos adicionando ao objeto father
Object
a propriedade hi
. Isso significa que cada objeto herdará esta propriedade. A propriedade hi
passa a ser compartilhada. Digamos que agora declaramos um novo objeto como let y = {}
, y
agora tem uma propriedade hi
herdada do father
Object
. Simplificando, x.__proto__ === Object.prototype
retorna true
.
Em seguida, sobrescrevemos a propriedade hi
por um novo valor 11. Por último, temos 11 + 1 = 12. x
tem uma propriedade e x.hi
retorna 11.
Atualizado (27 de julho de 2021). Se você escrever Object.prototype.hi = 11;
em vez de Object.prototype.hi = ++x.hi;
conforme escrito no código acima, então Object.keys(x)
retornará um array vazio, pois Object.keys(object)
retorna apenas a propriedade do próprio objeto, não as herdadas. Isso significa que o resultado final será 11 em vez de 12. Por alguma razão, o código ``Object.prototype.hi = ++x.hi; will create a property for the object
x` e então `Object.keys(x)` nos dará o array `["hi"]`.
No entanto, se você executar console.log(x.hasOwnProperty("hi"))
ele ainda retornará false
. A propósito, quando você adiciona deliberadamente uma propriedade para x como x.test = "testing"
, então console.log(x.hasOwnProperty("test"))
retorna 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 } ) ) ;
Esta questão examina como o operador delete
funciona em JavaScript. Resumindo, não faz nada quando escrevemos delete someObject
ou delete someArray
. No entanto, ele exclui e remove completamente uma propriedade de um objeto ao escrever algo como delete someObject.someProperty
. No caso do array, quando escrevemos delete someArray[keyNumber]
, ele apenas remove o value
do index
, mantém o index
intacto e o novo value
agora é definido como undefined
. Por esse motivo, no primeiro trecho de código, obtemos (o comprimento) 4 elementos como no array original, mas apenas 3 propriedades restantes no objeto passado quando a função object() é chamada, como no segundo trecho.
O terceiro trecho nos dá 4, pois declarar a propriedade de um objeto como null
ou undefined
não remove completamente a propriedade. A chave está intacta. Portanto, o comprimento do objeto é imutável.
Para quem está familiarizado com PHP, temos unset($someArray[index])
que remove o elemento do array, tanto chave quanto valor. Ao print_r
o array, podemos não ver a chave e o valor que não foram definidos. No entanto, quando enviamos (usando array_push($someArray, $someValue)
) um novo elemento nesse array, podemos ver que a chave anterior ainda é mantida, mas nenhum valor e não sendo exibida. Isso é algo que você deve estar ciente. Dê uma olhada em 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
retorna falso porque apontam para locais de memória diferentes b
embora os valores sejam iguais. Se você vem do mundo PHP, ele retornará true obviamente quando compararmos valor ou valor + tipo. Confira: https://3v4l.org/IjaOs.
Em JavaScript, o valor é passado por referência no caso de array
e object
. Portanto, no segundo caso, d
é a cópia de c
mas ambos apontam para a mesma posição de memória. Todas as mudanças em c
resultarão na mudança em d
. Em PHP, podemos ter $a = &$b;
, trabalhando de maneira semelhante.
O terceiro nos dá uma dica para copiar um array em JavaScript usando o método slice()
. Agora temos f
, que é a cópia de e
mas eles apontam para locais de memória diferentes, portanto têm "vidas" diferentes. Obtemos false
de acordo quando eles estão sendo comparados.
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
) ;
}
} ) ( ) ;
O trecho de código é bastante complicado, pois possui alguns métodos integrados diferentes para lidar com objetos em JavaScript
. Por exemplo, Object.keys
e Object.getOwnPropertyNames
são usados mesmo sendo bastante semelhantes, exceto que o último pode retornar propriedades não enumeráveis. Você pode querer dar uma olhada nesta referência completamente escrita https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
Object.values
e Object.keys
retornam o valor da propriedade e o nome da propriedade do objeto, respectivamente. Isso não é novidade. object.hasOwnProperty('propertyName')
retorna um boolean
confirmando se uma propriedade existe ou não.
Temos flag
true porque Object.values(languages)[0][4].name
retorna feature
, que também é o nome da propriedade.
Então temos 4 << 4 no fluxo if-else
que retorna o valor bit a bit, equivalente a 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 .