Descripción del problema
Aparece alguna cadena vacía "" al dividir una cadena utilizando el método de división de JavaScript, especialmente cuando se usa expresiones regulares como delimitadores.
Preguntas relacionadas
¿Las expresiones regulares de JavaScript producen un grupo de cadenas vacías al agrupar cadenas?
En la pregunta anterior, el interrogador usó la expresión regular para dividir la cadena y generó múltiples cadenas vacías "" y el código es el siguiente:
La copia del código es la siguiente:
'Zhang SDF cuatro métodos asdf wengf aa33net s'.split (/([/u4e00-/u9fa5] {1})/gi);
// salida ["", "zhang", "sdf", "cuatro", "up", "", "ley", "asdf", "weng", "", "", "fen", "aa33", "net", "s"]
Entonces, ¿cuál es la razón de estas cuerdas vacías?
Análisis de problemas
Después de buscar en Google, descubrí que no había muchos resultados relacionados. Incluso si hubiera, no había muchas explicaciones detalladas. Lo dije aproximadamente y luego di un enlace a la especificación de ECMAScript. Parece que si quieres saber la verdadera razón, solo puedes morder la bala y mirar las normas.
Normas relacionadas
Luego, según la práctica internacional, primero vaya al edificio estándar de la ciudad de ECMAScript.
La copia del código es la siguiente:
String.prototype.split (separador, límite)
Este capítulo presenta los pasos de ejecución del método dividido en detalle. Si está interesado, puede leerlo cuidadosamente paso a paso. Solo explicaré los pasos relacionados con la generación de cuerdas vacías aquí. Si hay puntos inapropiados, todos son bienvenidos para mencionarlos.
Pasos relacionados
Pasos parciales para extraer:
El paso más importante en todo el proceso es el 13º ciclo, y las principales cosas que hace este ciclo son las siguientes:
• Defina los valores de P y Q. Los valores de P y Q son los mismos al comienzo de cada bucle (este paso está fuera del bucle);
• Llame al método SplitMatch (S, Q, R) para dividir la cadena;
• Ejecutar diferentes ramas de acuerdo con los resultados devueltos, y las ramas principales son ramas;
• La rama se divide en 8 pequeños pasos para llenar el resultado devuelto en la matriz predefinida a
• En estos 8 pequeños pasos, el propósito del paso 1 es devolver una subcadena de la cadena original, la posición de inicio es P (incluida) y la posición final es Q (incluida). Nota: En este paso, se generará una cadena vacía, y la marcé como interceptando la cadena para la comodidad de citar a continuación.
• Agregue la subcadena del paso anterior para matar
• Los siguientes pasos son actualizar las variables relevantes y continuar el siguiente bucle. (El propósito del paso 7 es guardar la agrupación de captura en la expresión regular en la matriz A, que no tiene nada que ver con la generación de una cadena vacía)
SplitMatch (S, Q, R)
A continuación, necesitamos entender qué hace el método SplitMatch (S, Q, R). Este método se menciona a continuación en la especificación dividida. Lo que hace principalmente es realizar las operaciones correspondientes de acuerdo con el tipo de separador:
• Si el delimitador es de tipo REGEXP, llame al método interno de Regexp [[coincidir]] para que coincida con la cadena. Si la coincidencia falla, devuelva la falla. De lo contrario, devuelva un resultado MatchResult.
• Si el delimitador es una cadena, se realiza el juicio de coincidencia, se devuelve el fracaso y el resultado del tipo MatchResult se devuelve correctamente.
MatchResult
En los pasos anteriores, se introduce una variable de tipo MatchResult. Al buscar el documento, se descubrió que las variables de este tipo tienen dos atributos endindex y capturas. El valor de EndIndex es la posición que coincide con la cadena más 1. Las capturas se pueden entender como una matriz. Cuando el delimitador es una expresión regular, los elementos dentro son los valores capturados por el grupo; Cuando el delimitador es una cadena, es una matriz vacía.
Próximo
Podemos ver en los pasos anteriores que la cadena dividida se genera en el paso de interceptar la cadena (excepto para la captura del grupo de expresiones regulares). Su función es interceptar la cadena entre el inicio especificado (incluido) y la posición final (incluida), entonces, ¿cuándo volverá ""? Hay un caso especial en el que los valores de la posición de inicio y la posición final son iguales, lo cual es solo una suposición, porque la especificación no da los pasos de especificación para interceptar la cadena.
Todos hemos venido aquí, ¿por qué no dar un paso adelante?
Entonces, intenté buscar algún código fuente V8 para ver si podía encontrar un método de implementación específico. Encontré el código relevante, enlace del código fuente
Estos son algunos de ellos:
La copia del código es la siguiente:
function stringsplitjs (separador, límite) {
...
...
// El delimitador es una cadena
if (! is_regexp (separador)) {
var separator_string = to_string_inline (separador);
if (límite === 0) return [];
// ECMA-262 dice que si el separador está indefinido, el resultado debe
// ser una matriz de tamaño 1 que contiene toda la cadena.
if (is_undefined (separator)) return [sujeto];
var separator_length = separator_string.length;
// El separador es una cadena vacía, que devuelve directamente la matriz de caracteres
if (separator_length === 0) return %stringToArray (sujeto, límite);
VAR result = %StringsPlit (Sujeto, Separator_String, Limit);
resultado de retorno;
}
if (límite === 0) return [];
// Cuando el delimitador es una expresión regular, llame a StringsPlitonRegexp
return stringsplitonregExp (sujeto, separador, límite, longitud);
}
// varios códigos se omiten aquí
Encontré en el código que al llenar la matriz, se llamará al método %_sUspstring para interceptar la cadena. Desafortunadamente, no encontré su definición relevante. Si hay algún estudiante que lo haya encontrado, hágamelo saber. Sin embargo, descubrí que el método Stringsubstring correspondiente al método de subcadena en JavaScript llamará al método %_substring y devolverá el resultado. Entonces, si 'ABC'.Substring (1,1) devuelve "", significa que el método %_substring devolverá "" cuando la posición de inicio y la posición final son las mismas. Puede decir el resultado probándolo.
Entonces, ¿cuándo ocurrirá la posición de inicio igual a la posición final (es decir, Q === P)? Seguí los pasos anteriores paso a paso y finalmente encontré:
• Cuando la cadena S original coincide con el delimitador una vez, inmediatamente después, la siguiente posición de la cadena S también coincide con el delimitador. Por ejemplo: 'Abbbc'.split (' B '),' Abbbc'.split (/(b) {1}/)
• Otro caso es que uno o varios caracteres al comienzo de una cadena coinciden con el separador. Por ejemplo: 'abc'.split (' a '),' abc'.split (/ab/)
• Hay otro caso en el que una o varias cadenas al final de la cadena coinciden con el delimitador, y el paso relevante es el paso 14.
Por ejemplo: 'abc'.split (' c '),' abc'.split (/bc/)
Además, cuando se usa expresiones regulares como delimitadores, puede aparecer indefinido en el resultado devuelto.
Por ejemplo: 'abc'.split (/(d)*/)
Veamos el ejemplo al principio. ¿Satisface las situaciones anteriores?
Fuera de tema
Esta es la primera vez que leo las especificaciones estándar de ECMAScript con tanto cuidado. El proceso de lectura es realmente muy doloroso, pero después de entenderlo, me siento muy feliz. Gracias por esta pregunta y la pregunta de seguimiento.
Por cierto, cuando se usa una expresión regular como separador, se ignorará el Modificador G Global G, que también es una ganancia adicional.