Linguagens e Ambientes de Programação (2017/2018)



Teórica 19 (15/mai/2018)

Linguagens de scripting. A linguagem Bash.

Elementos da linguagem JavaScript.



Linguagens de scripting

As linguagens de programação clássicas são concebidas para criar estruturas de dados e algoritmos a partir do zero, usando os elementos primitivos da linguagem. Exemplos: OCaml, C, C++, Java.

Em constaste, nas linguagens de scripting assume-se que já existe uma coleção, pronta a ser usada, de componentes escritas noutras linguagens. As linguagens de scripting são concebidas para permitir ligar e organizar componentes existentes de forma simples, expedita e flexível. Exemplos: JavaScript, Bash, Tcl, Perl, PHP, Ruby, Python.

Por exemplo, quando se usa JavaScript para adicionar interatividade a uma página Web, as componentes em causa, são os objetos DOM que representam essa página Web.

Chama-se script a um programa escrito numa linguagem de scripting.

Linguagens clássicas versus linguagens de scripting

As linguagens clássicas são geralmente estaticamente tipificadas para permitir detetar cedo os erros que podem ocorrer quando se utilizam elementos primitivos na construção de estruturas de dados complexas. Em constaste, as linguagens de scripting são sempre tipificadas dinamicamente pois um sistema de tipos estático seria uma complicação burocrática que só serviria para atrasar a escrita de scripts.

Outra diferença é o facto das linguagens de scripting serem geralmente interpretadas e não compiladas. Um dos objetivos disso é acelerar o desenvolvimento do código. Outro objetivo é facilitar a geração dinâmica de scripts que possam ser executados imediatamente pelo interpretador.

Nas linguagens de scripting dá-se pouca importância à questão eficiência. Em contrapartida dá-se a máxima importância à simplicidade, flexibilidade de utilização e a um grande poder expressivo que permita escrever scripts compactos. A questão da eficiência é pouco importante porque os scripts tendem a ser pequenos e porque a eficiência da linguagem é dominada pela eficiência das componentes, as quais são normalmente implementadas numa linguagens de programação clássica.

Algumas linguagem de scripting são também concebidas para serem usadas no interior duma aplicação de software, com o objetivo de fornecer ao utilizador um elevado grau de controlo do comportamento da aplicação, incluindo a adição de novas funcionalidades. Se é verdade que o utilizador não pode alterar o código de base da aplicação, ele pode escrever scripts para adaptar a aplicação às suas necessidades. Exemplos: Emacs Lisp é a linguagem de scripting do editor de texto emacs; JavaScript é linguagem de scripting mais usada nos browsers da WEB.

Como escolher entre linguagem clássicas e de scripting

Quando estão em causa aplicações que envolvem acima de tudo a coordenação de componentes já implementadas, podemos escolher programar essa aplicação numa linguagem clássica, e.g. Java, ou numa linguagem de scripting, e.g. JavaScript. Mas estudos mostram que se escolhermos uma linguagem de scripting, tanto o tempo de desenvolvimento como o tamanho da aplicação se reduzem num fator de 5 a 10, em média!

Quando estão em causa aplicações com algoritmos e estruturas de dados complexas, o melhor é usar uma linguagens de programação clássica. Usando uma linguagem de scripting, o script não ficaria mais pequeno, não haveria o benefício da tipificação estática para ajudar a apanhar antecipadamente erros subtis na utilização das estruturas de dados e, no final, o programa correria 10 vezes mais devagar.

Complementaridade dos dois tipos de linguagens

Se puderem ser usadas em conjunto, os dois tipos de linguagens permitem a criação de ambientes de desenvolvimento e execução de programas particularmente poderosos e flexíveis.

As linguagens de scripting sempre tiveram alguma popularidade, mas ultimamente a sua importância tem aumentado. A principal razão é a tendência atual para escrever aplicações baseadas em componentes já disponíveis. É o que se passa, por exemplo, quando está em causa o desenvolvimento de interfaces gráficas e de aplicações que correm sobre a WEB.

Exemplo: linguagem Bash

Bash (bourne-again shell) é uma linguagem de scripting muito usada no Linux na qual as "componentes" são as aplicações disponíveis e o sistema de ficheiros. Em Bash, um script implementa uma nova funcionalidade usando as aplicações do sistema. Uma das construções mais importantes do Bash é o pipe, que permite ligar o output duma aplicação ao input de outra aplicação. Em Bash também é possível testar o código-resultado duma aplicação e tomar decisões em conformidade (zero significa que a aplicação terminou sem erro; um valor diferente de zero representa um código de erro particular). Também é possível colocar o output duma aplicação numa variável e processar a seguir o conteúdo dessa variável.

Eis um exemplo de script em bash, retirado daqui. Este script lista na consola o nome de todos os ficheiros HTML que se encontram na diretoria corrente e, além disso, escreve a primeira linha de cada um desses ficheiros num ficheiro chamado FILE_HEADS.

No Windows, a linguagem de scripting chama-se PowerShell e foi introduzida em 2006 na versão Windows XP SP2. Antes do PowerShell usava-se a linguagem de scripting implementada pelo programa COMMAND.COM.

Discussão

As linguagens de scripting são geralmente concebidas para a execução de tarefas simples. Contudo, por vezes acabam por ser usadas em projetos grandes e de formas que ultrapassam as intenções originais. As características da linguagem que a tornam fácil de usar acabam por se revelar problemáticas nessas situações.

Quando comunidade de utilizadores é grande, então justifica-se fazer o seguinte:



Elementos da linguagem JavaScript

Regressemos ao estudo da linguagem JavaScrip, desta vez para apresentar os elementos de base mais importantes da linguagem.


Comparação do JavaScript com o Java

A primeira é uma linguagens de scripting. A segunda é uma linguagem clássica.

Palavras reservadas do JavaScript

Esta é a lista das principais palavras reservadas usadas em JavaScript: Note que muitas das palavras anteriores são também palavras reservadas em Java.

As restantes palavras reservadas do Java, que não aparecem na lista anterior, também estão reservadas em JavaScript, apesar de não serem usadas de momento. Todos os interpretadores de JavaScript deveriam proibir a utilização destas palavras. Contudo alguns não o fazem.


Insersão automática dos pontos e vírgula (não usar)

Em JavaScript, o ponto e vírgula é usado como terminador da maioria das instruções e declarações.

Mas se tivermos uma sucessão de instruções em linha diferentes, podemos omitir o ponto e vírgula porque o JavaScript insere implicitamente o terminador nesses casos. Exemplo:

A insersão automática dos pontos e vírgula foi feita para ajudar o programador, mas infelizmente as regras são um pouco complicadas. O melhor será explicitar sempre os pontos e vírgula, como estamos habituados em Java e C.

Um exemplo confuso tirado da expecificação do ECMAScript® 2017. No seguinte caso

não é inserido ponto e vírgula, resultando o equivalente a

Variáveis

O JavaScript é uma linguagem dinamicamente tipificada. Uma consequência disso é o facto das variáveis não terem tipos associados. Os tipos ficam associados aos valores e não às variáveis. Exemplo:

Definição de variáveis

A palavra var permite definir variáveis. Factos diversos:

Exemplo com variáveis globais e variáveis locais: Exemplo com aninhamento de funções: Exemplo relativo ao facto de não existir âmbito de bloco em JavaScript:

Variáveis indefinidas

Variáveis definidas num determinado âmbito, ficam com o valor undefined enquanto não forem inicializadas.

Exemplos sobre variáveis indefinidas:

Atribuição a variáveis

A atribuição efetua-se usando o operador =. É possível efetuar uma atribuição a um nome ainda não definido. Nesse caso é automaticamente criada uma variável global inicializada. Para garantir que a variável fica local, é preciso declará-la usando var.

Constantes

A palavra const permite definir constantes. Mas, cuidado, que isto não faz parte do JavaScript padrão. Contudo está disponível no Rhino e Node.js, por exemplo.


Tipos primitivos

Os tipos primitivos são os seguintes: Note que em JavaScript, uma string não é um objeto. No entanto também há objetos de tipo String que simplesmente encapsulam valores primitivos de tipo string. Em Javascript as strings são imutáveis, tal como em Java.

Um literal de tipo string é uma porção de texto rodeada, ou por aspas ou plicas. Em código JavaScript, é comum usar plicas entre aspas e aspas entre plicas, como nestes exemplos: "He is called 'Neko-chan'"; 'He is called "Neko-chan"'.

O operador typeof pode ser usado para saber o tipo de qualquer valor.

São efetuadas conversões automáticas de tipo, entre os tipos primitivos. Exemplos:


Operadores

Eis a tabela de operadores do JavaScript ordenada por prioridade decrescente:

Operadores de igualdade


Expressões regulares

O JavaScript suporta expressões regulares semelhantes às da linguagem Perl.

A seguinte expressão regular representa dois "a"s seguidos de zero ou mais dígitos:

A próxima expressão regular, mais abaixo na caixa, representa um "d" seguido de um ou mais "b"s seguido dum "d". As flags "i" e "g" indicam que o emparelhamento deve ignorar a caixa das letras e que deve ser global.

O método test determina se uma string emparelha com a expressão regular.

O método exec produz um array com o resultado do emparelhamento na posição 0 do array, mais os resultados dos emparelhamentos das sub-expressões entre parêntesis. Se a expressão regular tiver a flag "g" ligada, então sucessivas chamadas de exec produzem sucessivos resultados de emparelhamentos até ser retornado null; sem a flag "g" apenas o resultado do primeiro emparelhamento é retornado.

As expressões regulares suportam ainda os métodos match, search, replace, split.

Arrays

Em JavaScript os arrays podem ser inicializados, pelo menos de duas maneiras diferentes. Exemplo: Tal como em Java os índices começam em zero e existe uma propriedade length. Eis um exemplo dum array de comprimento 6 com apenas 4 elementos. Dois elementos estão indefinidos. Ao contrário do Java, os arrays crescem automaticamente. Basta atribuir a uma posição inexistente para o array crescer. Para fazer crescer um array na primeira posição livre, fazer assim: Para aceder e remover o último elemento dum array fazer: É possível escrever diretamente na propriedade length dum array para fazer um array crescer, ou para truncar o array: Para percorrer os elementos dum array pode usar-se um for clássico, mas também se pode fazer assim: ou assim: Eis um array a duas dimensões, 2x3: Outros métodos disponíveis para arrays: join, reverse, shift, slice, splice, sort, e muitos outros.

Funções

O JavaScript suporta o paradigma de programação funcional pois inclui funções anónimas, funções de ordem superior e funções que retornam outras funções. Eis um exemplo duma função de ordem superior, que depois é chamada usando uma função anónima como argumento: Nas chamadas das funções o número de argumentos não é validado: argumentos a mais na chamada são ignorados; argumentos a menos na chamada ficam indefinidos.

Dentro da cada função há um array predefinido chamado arguments que representa a sequência de argumentos realmente usados na chamada. Assim é fácil implementar funções com um número variável de argumentos, como no seguinte exemplo:

A passagem de argumentos de tipos primitivos é feita por valor. Os objetos são passados por referência.

Eis algumas funções predefinidas em JavaScript:

O argumento this

Todas as funções possuem automaticamente um primeiro argumento que não se declara e se chama this.

No caso de funções que não fazem parte de objetos, quando elas são chamadas, o argumento this fica associado a um objeto que depende da implementação. Por exemplo, no caso do Rhino e do Node.js, this fica associado a um objeto especial que guarda as ligações de todos os nomes globais. No seguinte exemplo, se a função f abaixo for chamada assim - f() - ela imprime-se a si própria de duas maneiras diferentes:

No caso duma função f ser um método dum objeto obj e for chamada assim obj.f(), o argumento this fica associado ao próprio objeto (aliás, como também acontece em Java).

A operação primitiva call

Ao nível mais básico, em JavaScript todas as chamadas de função são feitas através da operação primitiva call que está disponível em todas as funções. Numa chamada de call, tem de se explicitar o primeiro argumento, this.

Todas as chamadas normais de funções são automaticamente traduzidas para invocações de call. Concretamente, a chamada duma função independente é assim traduzida, em Rhino:

A chamada do método dum objeto é assim traduzida:

A operação primitiva apply

Existe ainda outra operação primitiva chamada apply, semelhante a call, com a única diferença que se passa para ela um array com todos os argumentos que aparecem a seguinte a this. Se estiver em causa um método, as seguintes três chamadas são equivalentes:

Tratamento de exceções

Semelhante ao Java. Exemplo:

Objetos

Em JavaScript para além dos tipos primitivos, temos os tipos objeto. Os arrays também são considerados objetos.

Como habitualmente, um objeto é um elemento de dados que possui identidade e que interage com outros objetos através da troca de mensagens.

Em JavaScript existem os seguintes objetos predefinidos, que oferecem funcionalidades úteis: Date, Array, Boolean, Function, Math, Number, RegExp e String .

Mas no ambiente de execução envolvente, estão geralmente disponíveis mais objetos predefinidos. Por exemplo, no ambiente dum browser, todos os tipos de objetos previstos no DOM estão disponíveis: Document, Window, Form, Link, etc.



#100