En JS confiamos: la mejor manera de aprender es construyendo/codificando y enseñando. Creo desafíos para ayudar a mis amigos a aprender JavaScript y, a cambio, me ayuda a adoptar el lenguaje en un nivel mucho más profundo. Siéntete libre de clonar, bifurcar y tirar.
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
y 1, 2, 3
3, 3, 3
y 3, 4, 5
3, 3, 3
y 1, 2, 3
1, 2, 3
y 3, 3, 3
Esta pregunta vuelve a abordar el cierre, uno de los conceptos más confusos de JavaScript. El cierre nos permite crear una stateful function
y dicha función puede acceder a la variable fuera de su alcance. En pocas palabras, un cierre puede tener acceso a la variable global
(alcance), al alcance father function
y its
propio alcance.
Tenemos aquí la única respuesta correcta, 3, 3, 3 y 3, 4, 5 porque primero simplemente llamamos a la función a()
. Funciona como una función normal y todavía no hemos visto nada llamado stateful
. En el siguiente código, declaramos una variable x
y almacena el valor de la función a(1)
, es por eso que obtenemos 3. 4. 5 en lugar de 3, 3, 3.
Este tipo de problema me da la sensación de una variable static
en el 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
Nos volvemos verdaderos en la consola. La parte complicada es cuando creamos un objeto a partir de la función constructora Nombre pero NO UTILIZAMOS new
teclas. Eso hace que la variable a
global y obtenga el valor "Vuong". Recuerde que en realidad es una propiedad de la window
del objeto global (en el navegador) o global
en los nodejs.
Luego obtenemos a.length
~ 5 y window.a.length
~ 5 que devuelven 0. !0 devuelve verdadero.
Imagínese lo que sucedería cuando creemos la me
con la new
clave. ¡Esa es una pregunta interesante!
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
El operador de extensión ...x
podría ayudarnos a obtener el parámetro de la función en forma de matriz. Sin embargo, en Javascript el tipo de matriz devuelve "objeto" en lugar de "matriz". Es totalmente extraño si vienes de PHP.
Dicho esto, ahora tenemos la longitud del object
de cadena que devuelve 6. zy() simplemente devuelve la longitud de la cadena 'freetut' (7).
Tenga en cuenta que la función x() (en forma de function express
o anonymous function
(si viene de PHP) devuelve -1 cuando se llama y cuando se convierte a bool con Boolean(-1)
devuelve verdadero en lugar de falso. ese Boolean(0)
devuelve falso.
( function js ( x ) {
const y = ( j ) => j * x ;
console . log ( y ( s ( ) ) ) ;
function s ( ) {
return j ( ) ;
}
function j ( ) {
return x ** x ;
}
} ) ( 3 ) ;
undefined
La función js()
se puede ejecutar automáticamente sin llamarla y se conoce como IIFE (Expresión de función invocada inmediatamente). Observó que el parámetro x
de la función js
en realidad se pasa con el valor 3.
El valor de retorno de la función es y(s())), lo que significa llamar a otras tres funciones y()
, s()
y j()
porque la función s()
devuelve j()
.
j() devuelve 3^3 = 27 para que s() devuelva 27.
y(s()) significa y(27) que devuelve 27*3 = 81.
Tenga en cuenta que podemos llamar declare function
ANTES de que la función se declare realmente, pero no con expression function
.
var tip = 100 ;
( function ( ) {
console . log ( "I have $" + husband ( ) ) ;
function wife ( ) {
return tip * 2 ;
}
function husband ( ) {
return wife ( ) / 2 ;
}
var tip = 10 ;
} ) ( ) ;
Tenemos aquí una IIFE (Expresión de función invocada inmediatamente). Significa que no tenemos que llamarlo pero se ejecutará automáticamente cuando se declare. El flujo es el siguiente: marido() devuelve esposa()/2 y esposa() devuelve propina*2.
Podríamos pensar que tip = 100 porque es una variable global al declarar con la palabra clave var
. Sin embargo, en realidad no está undefined
porque también tenemos var tip = 10
DENTRO de la función. Como la variable tip
se eleva con el valor predeterminado undefined
, el resultado final sería D. Sabemos que undefined
devuelve NaN cuando intentamos dividir por 2 o multiplicar por 2.
Si no volvemos a declarar var tip = 10;
al final de la función, definitivamente obtendremos B.
JS es divertido, ¿verdad?
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 desafío revisa la característica de ES6 con respecto spread operator ...
El operador de extensión es bastante útil para recuperar parámetros en una función, para unite
o combine
objetos y matrices en JavaScript. PHP también tiene esta característica.
En la variable edu
, usamos ...js
(aquí operador de extensión) para combinar ambos objetos en uno. Funciona de la misma manera con array.
Luego declaramos otra variable llamada newbie
. Nota IMPORTANTE: Al declarar la variable así, ambas variables apuntan a la MISMA POSICIÓN en la memoria. Es posible que hayamos conocido algo como $a = &$b
en PHP, que permite que ambas variables funcionen de la misma manera. Es posible que hayamos sabido sobre pass by reference
en el caso.
Luego tenemos 2 ya que se elimina edu.language
. Ambos objetos ahora tienen sólo dos elementos.
Ahora es el momento de pensar en cómo hacer frente a un objeto en JS, ya sea superficial o 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 ( ) ) ;
El método incorporado Object.assign(candidate, job)
fusiona los dos objetos candidate
y job
en un solo objeto. Luego, el método Object.keys
cuenta el número de key
en el objeto.
Tenga en cuenta que dos métodos get()
y count()
se definen como static
, por lo que deben llamarse estáticamente utilizando la sintaxis Class.staticmethod()
. Entonces el objeto final obtiene 5 elementos.
var x = 1 ;
( ( ) => {
x += 1 ;
++ x ;
} ) ( ) ;
( ( y ) => {
x += y ;
x = x % y ;
} ) ( 2 ) ;
( ( ) => ( x += x ) ) ( ) ;
( ( ) => ( x *= x ) ) ( ) ;
console . log ( x ) ;
Inicialmente x
se declara con el valor 1. En la primera función IIFE, hay dos operaciones. Primero x
se convierte en 2 y luego en 3.
En la segunda función IIFE, x = x + y
entonces el valor actual es 5. En la segunda operación, devuelve solo 1 ya que pasa por 5%2
.
En la tercera y cuarta funciones IIFE, obtenemos 2 x = x + x
y luego 4 x = x * x
. Es más que sencillo.
$ 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 pregunta ilustra las diferencias entre PHP y JavaScript al manejar el cierre. En el primer fragmento, declaramos un cierre con la palabra clave use
. El cierre en PHP es simplemente una función anónima y los datos se pasan a la función usando la palabra clave use
. De lo contrario, se llama lambda
cuando no utilizamos la palabra clave use
. Puedes consultar el resultado del fragmento aquí https://3v4l.org/PSeMY. closure
de PHP solo acepta el valor de la variable ANTES de que se defina el cierre, sin importar dónde se llame. Como tal, $var
es 10 en lugar de 15.
Por el contrario, JavaScript trata la variable de forma un poco diferente cuando se pasa a una función anónima. No tenemos que usar la palabra clave use
aquí para pasar la variable al cierre. La variable x
en el segundo fragmento se actualiza antes de llamar al cierre, luego obtenemos 26.
Tenga en cuenta que en PHP 7.4, tenemos la función de flecha y luego no tenemos que usar la palabra clave use
para pasar la variable a la función. Otra forma de llamar a una ariable global
dentro de una función en PHP es usar la palabra clave global
o emplear la variable GLOBAL incorporada $GLOBALS.
let x = { } ;
let y = { } ;
let z = x ;
console . log ( x == y ) ;
console . log ( x === y ) ;
console . log ( x == z ) ;
console . log ( x === z ) ;
Técnicamente, x
y
el mismo valor. Ambos son objetos vacíos. Sin embargo, no utilizamos el valor para comparar objetos.
z
es x
son dos objetos que hacen referencia a la misma posición de memoria. En JavaScript, la matriz y el objeto se pasan por reference
. Por lo tanto, x
y z
devuelven verdadero cuando se comparan.
console . log ( "hello" ) ;
setTimeout ( ( ) => console . log ( "world" ) , 0 ) ;
console . log ( "hi" ) ;
Dado que la función setTimeout() se mantendrá en la task queue
antes de volver a stack,
primero se imprimirán "hola" y "hola", luego A es incorrecto. Ese es también el caso de las respuestas C y D.
No importa cuántos segundos establezca la función setTimeout()
, se ejecutará después del código sincrónico. Entonces, primero recibiremos "hola", ya que primero se coloca en la pila de llamadas. Aunque setTimeout()
luego se coloca en la pila de llamadas, posteriormente se descargará a la API web (o API de nodo) y luego se llamará cuando se borre otros códigos sincrónicos. Significa que luego obtenemos "hola" y finalmente "mundo".
Entonces B es la respuesta correcta.
Crédito: @kaitoubg (voz) por su sugerencia sobre el timeout throttled
por el cual he decidido modificar ligeramente la pregunta. Garantizará que los lectores no se confundan, ya que el código anterior podría generar resultados diferentes cuando se pruebe en otros navegadores o entornos. El punto principal de la pregunta es sobre la discrepancia entre el código sincrónico y el código asincrónico cuando se usa setTimeout.
.
String . prototype . lengthy = ( ) => {
console . log ( "hello" ) ;
} ;
let x = { name : "Vuong" } ;
delete x ;
x . name . lengthy ( ) ;
String.prototype.someThing = function () {}
es la forma común de definir un nuevo método integrado para String
. Podemos hacer lo mismo con Array
, Object
o FunctionName
donde FunctionName es la función diseñada por nosotros mismos.
No es difícil darse cuenta de que "string".lengthy()
siempre devuelve hello
. Sin embargo, la parte complicada radica en delete object
donde podríamos pensar que esta expresión eliminará por completo el objeto. Ese no es el caso ya que delete
se utiliza para eliminar únicamente la propiedad del objeto. No elimina el objeto. Entonces obtenemos hello
en lugar de ReferenceError
.
Tenga en cuenta que si declaramos un objeto sin let, const
o var
, entonces tenemos un objeto global. delete objectName
y luego devuelva true
. De lo contrario, siempre devuelve false
.
let x = { } ;
x . __proto__ . hi = 10 ;
Object . prototype . hi = ++ x . hi ;
console . log ( x . hi + Object . keys ( x ) . length ) ;
Primero tenemos un objeto vacío x
, luego agregamos otra propiedad hi
para x con x.__proto__.hi
. Tenga en cuenta que esto es equivalente a Object.prototype.hi = 10
y estamos agregando al objeto father
Object
la propiedad hi
. Significa que cada objeto heredará esta propiedad. La propiedad pasa a hi
compartida. Digamos que ahora declaramos un nuevo objeto como let y = {}
, y
ahora tiene una propiedad hi
heredada del Object
father
. En pocas palabras, x.__proto__ === Object.prototype
devuelve true
.
Luego sobrescribimos la propiedad hi
con un nuevo valor 11. Por último tenemos 11 + 1 = 12. x
tiene una propiedad y x.hi
devuelve 11.
Actualizado (27 de julio de 2021). Si escribe Object.prototype.hi = 11;
en lugar de Object.prototype.hi = ++x.hi;
como está escrito en el código anterior, entonces Object.keys(x)
devolverá una matriz vacía ya que Object.keys(object)
solo devuelve la propiedad del objeto en sí, no las heredadas. Significa que el resultado final será 11 en lugar de 12. Por alguna razón, el código ``Object.prototype.hi = ++x.hi; will create a property for the object
x` y luego `Object.keys(x)` nos da la matriz `["hi"]`.
Sin embargo, si ejecuta console.log(x.hasOwnProperty("hi"))
todavía devuelve false
. Por cierto, cuando agregas deliberadamente una propiedad para x como x.test = "testing"
, entonces console.log(x.hasOwnProperty("test"))
devuelve 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 pregunta examina cómo funciona el operador delete
en JavaScript. En resumen, no hace nada cuando escribimos delete someObject
o delete someArray
. No obstante, elimina y elimina por completo una propiedad de un objeto al escribir algo como delete someObject.someProperty
. En el caso de una matriz, cuando escribimos delete someArray[keyNumber]
, solo elimina el value
del index
, mantiene el index
intacto y el nuevo value
ahora se establece en undefined
. Por esa razón, en el primer fragmento de código, obtenemos (la longitud) 4 elementos como en la matriz original, pero solo quedan 3 propiedades en el objeto pasado cuando se llama a la función object(), como en el segundo fragmento.
El tercer fragmento nos da 4, ya que declarar la propiedad de un objeto como null
o undefined
no elimina por completo la propiedad. La llave está intacta. Entonces la longitud del objeto es inmutable.
Para aquellos que están familiarizados con PHP, tenemos unset($someArray[index])
que elimina el elemento de la matriz, tanto la clave como el valor. Cuando print_r
la matriz, es posible que no veamos la clave y el valor que no se han configurado. Sin embargo, cuando insertamos (usando array_push($someArray, $someValue)
) un nuevo elemento en esa matriz, es posible que veamos que la clave anterior aún se conserva, pero sin valor y no se muestra. Eso es algo que debes tener en cuenta. Eche un vistazo a 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
y b
devuelven falso porque apuntan a una ubicación de memoria diferente aunque los valores sean los mismos. Si viene del mundo PHP, obviamente devolverá verdadero cuando comparemos valor o valor + tipo. Échale un vistazo: https://3v4l.org/IjaOs.
En JavaScript, el valor se pasa por referencia en el caso de array
y object
. Por tanto, en el segundo caso, d
es la copia de c
pero ambos apuntan a la misma posición de memoria. Todo lo que cambia en c
resultará en el cambio en d
. En PHP, podríamos tener $a = &$b;
, trabajando de manera similar.
El tercero nos da una pista para copiar una matriz en JavaScript usando el método slice()
. Ahora tenemos f
, que es la copia de e
pero apuntan a diferentes ubicaciones de memoria, por lo que tienen una "vida" diferente. En consecuencia, obtenemos false
cuando se comparan.
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
) ;
}
} ) ( ) ;
El fragmento de código es bastante complicado ya que tiene un par de métodos integrados diferentes que manejan objetos en JavaScript
. Por ejemplo, tanto Object.keys
como Object.getOwnPropertyNames
se utilizan aunque son bastante similares, excepto que este último puede devolver propiedades no enumerables. Es posible que desee echar un vistazo a esta referencia detalladamente escrita https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
Object.values
y Object.keys
devuelven el valor de propiedad y el nombre de propiedad del objeto, respectivamente. Eso no es nada nuevo. object.hasOwnProperty('propertyName')
devuelve un boolean
que confirma si una propiedad existe o no.
Tenemos flag
verdadero porque Object.values(languages)[0][4].name
devuelve feature
, que también es el nombre de la propiedad.
Luego tenemos 4 << 4 en el flujo if-else
que devuelve el 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 .