Noções básicas sobre a especificação ECMAScript, parte 1



Bom dia amigos

Neste artigo, pegamos uma função da especificação e analisamos sua explicação. Vai.

Prefácio


Mesmo se você conhece bem o JavaScript, a leitura da especificação pode ser difícil. O código a seguir demonstra o uso de Object.prototype.hasOwnProperty:

const o = {
    foo: 1
}
o.hasOwnProperty('foo') // true
o.hasOwnProperty('bar') // false

No exemplo, o objeto “o” não possui o método “hasOwnProperty”, portanto, nos referimos ao seu protótipo - “Object.prototype” (cadeia de protótipos).

Para descrever como Object.hasOwnProperty funciona, a especificação usa o seguinte pseudocódigo:

Object.prototype.hasOwnProperty (V)

Quando hasOwnProperty é chamado com o argumento V, são executadas as seguintes etapas: ... e HasOwnProperty (O, P) A operação abstrata HasOwnProperty é usada para determinar se o objeto tem sua própria propriedade com uma chave específica. O valor booleano é retornado. A operação é chamada com os argumentos O e P. Esta operação consiste nas seguintes etapas: O que é uma "operação abstrata"? O que [[ ]]? Por que uma função tem um ponto de interrogação? O que significa "afirmação"? Vamos descobrir.

  1. P ? ToPropertyKey(V)
  2. O ? ToObject( this)
  3. ? HasOwnProperty(O, P).







  1. (assert): Type(O) Object.
  2. : IsPropertyKey(P) true.
  3. desc ? O.[[GetOwnProperty]](P).
  4. desc undefined, false.
  5. true.





Tipos no idioma e tipos na especificação


Vamos começar com algo familiar. A especificação contém valores como indefinido, verdadeiro e falso, que são conhecidos por JS. Todos eles são "valores de idioma" , "valores de tipos de idioma" , que também são definidos pela especificação.

A especificação usa valores de idioma internos, por exemplo, um tipo de dados interno pode conter um campo com um valor verdadeiro ou falso. Os mecanismos JS geralmente não usam significados linguísticos em seus mecanismos internos. Por exemplo, se o mecanismo JS for escrito em C ++, provavelmente usará true e false no C ++, em vez da representação interna dos valores booleanos do JS.

Além dos tipos de idioma, a especificação usa tipos especiais (tipos de especificação) - tipos que são usados ​​apenas na especificação. Os mecanismos JS não são necessários para executá-los (mas podem). Neste artigo, vamos nos familiarizar com o tipo especial "Registro" (registro) e seu subtipo "Registro de conclusão" (registro de conclusão).

Operações abstratas


Operações abstratas são funções definidas na especificação; eles são definidos para reduzir a especificação. Os mecanismos JS não são necessários para executá-los como funções independentes. Em JS, eles não podem ser chamados diretamente.

Slots internos e métodos internos


Slots internos e métodos internos são indicados por nomes entre [[]].

Slots internos são elementos de dados (conjuntos) de um objeto JS ou tipo especial. Eles são usados ​​para armazenar informações sobre o estado do objeto. Métodos internos são funções de membro de um objeto JS.

Por exemplo, cada objeto JS possui um slot [[Prototype]] interno e um método [[GetOwnProperty]] interno.

Slots e métodos internos não estão disponíveis em JS. Por exemplo, não podemos acessar o. [[Prototype]] ou chamar o. [[GetOwnProperty]] (). O mecanismo JS pode executá-los para suas próprias necessidades (internas), mas não é necessário para fazer isso.

Às vezes, os métodos internos se tornam operações abstratas com o mesmo nome, como é o caso de [[GetOwnProperty]]:

[[GetOwnProperty]] (P)

Quando o método interno [[GetOwnProperty]] do objeto “O” é chamado com a chave “P”, são executadas as seguintes ações: OrdinaryGetOwnProperty não é um método interno, pois não está associado a nenhum objeto; o objeto com o qual trabalha é passado como parâmetro. OrdinaryGetOwnProperty é chamado de "comum" porque opera em objetos comuns. Os objetos no ECMAScript são comuns e incomuns (exóticos). Um objeto é comum se comportar de maneira previsível em resposta a um conjunto de métodos chamados métodos internos essenciais. Caso contrário (quando o objeto se comporta imprevisivelmente; não como o esperado; quando o comportamento do objeto se desvia do normal, é desviado), é considerado incomum.

  1. ! OrdinaryGetOwnProperty(O, P)





O objeto incomum mais famoso é Array, porque sua propriedade "length" se comporta de maneira não padrão: a configuração dessa propriedade pode remover elementos da matriz.

Uma lista de métodos internos básicos pode ser encontrada aqui .

Registro de conclusão


E quanto a perguntas e pontos de exclamação? Para entender isso, você precisa entender o que é um registro de conclusão .

Um registro de conclusão é um tipo especial (definido apenas para fins de especificação). O mecanismo JS não precisa ter o mesmo tipo de dados interno.

Um registro de conclusão é um tipo de dados que possui um conjunto fixo de campos nomeados. O registro de conclusão possui três campos:
[[Tipo]]normal, break, continue, return throw. , normal, « () » (abrupt comlpetions)
[[Value]], , , , ,
[[Target]]( )

Cada operação abstrata retorna implicitamente um registro de conclusão. Mesmo que o resultado da operação abstrata seja um valor lógico simples, ele é agrupado em um registro de conclusão do tipo normal (consulte Valores implícitos de conclusão ).

Nota 1: a especificação não é muito consistente nesta parte; Existem várias funções auxiliares que retornam valores simples usados ​​como estão, sem serem recuperados do registro de conclusão.

Nota 2: Os autores das especificações procuram tornar o processamento de registros de conclusão mais explícito.

Se o algoritmo lançar uma exceção, significa que um registro de conclusão será recebido com o tipo ([[Type]]) throw e o valor ([[Value]]) como um objeto de exceção. Por enquanto, não consideraremos outros tipos (interromper, continuar e retornar).

ReturnIfAbrupt (argumento) significa executar as seguintes operações: É isso que é o registro de conclusão; se terminar abruptamente, retorne imediatamente. Caso contrário, extraímos o valor do registro de conclusão. ReturnIfAbrupt parece uma chamada de função, mas não é. Chamamos uma função que retorna ReturnIfAbrupt (), e não ReturnIfAbrupt. Seu comportamento é mais parecido com uma macro em linguagens de programação do tipo C. ReturnIfAbrupt pode ser usado da seguinte maneira: Aqui entra em jogo

  1. argument - (abrupt), argument.
  2. argument argument.[[Value]].







  1. obj Foo() (obj - ).
  2. ReturnIfAbrupt(obj).
  3. Bar(obj) ( , , obj - , ).

Ponto de interrogação : registro  ? Foo () é equivalente a ReturnIfAbrupt (Foo ()). O uso dessa abreviação tem um valor prático: não precisamos escrever o código do manipulador de erros todas as vezes.

Por analogia, seja val a entrada ! Foo () é equivalente ao seguinte: Usando esse conhecimento, podemos reescrever Object.prototype.hasOwnProperty da seguinte maneira: Object.prototype.hasOwnProperty (P) ... HasOwnProperty pode ser reescrito assim: HasOwnProperty (O, P) Também podemos reescrever o método interno [ [GetOwnProperty]] sem um ponto de exclamação: O. [[GetOWnProperty]] Assumimos que temp é uma nova variável temporária que não interage com nada.

  1. val Foo().
  2. : val .
  3. val val.[[Value]].





  1. P ToProperty(V).
  2. P , P.
  3. P P.[[Value]].
  4. O ToObject( this).
  5. O , O.
  6. O O.[[Value]].
  7. temp HasOwnProperty(O, P).
  8. temp , temp.
  9. temp temp.[[Value]].
  10. NormalCompletion(temp).





  1. : Type(O) Object.
  2. : IsPropertyKey(P) true.
  3. desc O.[[GetOWnProperty]](P).
  4. desc , desc.
  5. desc desc.[[Value]].
  6. desc undefined, NormalCompletion(false).
  7. NormalCompletion(true).





  1. temp OrdinaryGetOwnProperty(O, P).
  2. : temp .
  3. temp temp.[[Value]].
  4. NormalCompletion(temp).



Também sabemos que, no caso em que a instrução de retorno retorna algo diferente de um registro de conclusão, isso envolve implicitamente em NormalCompletion.

Fallback: Retorno? Foo ()


A especificação usa Return? Foo () - por que existe um ponto de interrogação aqui?

Gravar retorno? Foo () pode ser expandido da seguinte forma: Comportamento de retorno? Foo () é o mesmo para terminação normal e repentina. Gravar retorno? Foo () permite indicar mais claramente que Foo retorna um registro de conclusão.

  1. temp Foo().
  2. temp , temp.
  3. temp temp.[[Value]].
  4. NormalCompletion.





Afirmações


As declarações na especificação "afirmam" as condições invariantes dos algoritmos. Eles são adicionados à especificação para maior clareza, mas não contêm requisitos de implementação, portanto, não precisam ser verificados por uma implementação específica.

Qual é o próximo?


Aprendemos a ler a especificação para métodos simples, como Object.prototype.hasOwnProperty e operações abstratas como HasOwnProperty. Tendo esse conhecimento, podemos entender o que outras operações abstratas fazem, o que será discutido na próxima parte. Também no próximo artigo, consideraremos os Descritores de propriedades, que são outro tipo especial.


Obrigado pela atenção. Feliz codificação!

All Articles