0
点赞
收藏
分享

微信扫一扫

JavaScript基本语法(一)

爱写作的小土豆 2021-09-26 阅读 172

什么是JavaScript

JavaScript是一种脚本,一种编程语言,通过它可以在网页上实现各种复杂的功能。因此网页不再是简单的静态信息,而是动态的、实时更新的页面。

历史回顾

期初的JS仅仅是作为一种客户端脚本语言,用来处理频繁来往于表单和服务器之间的验证数据。当时,大部分因特网用户还仅仅通过 28.8 kbit/s 的调制解调器连接到网络,随着网页已经不断地变得更大和更复杂,仅仅为了简单的表单有效性验证,就要与服务器进行多次地往返交互。用户填完一个表单,点击提交按钮,等待了 30 秒的处理后,看到的却是一条告诉你忘记填写一个必要的字段。Netscape(网景)是当时引领技术革新的公司,它将开发一个客户端脚本语言用来处理这种简单的数据验证提上了日程

当时工作于 Netscape 的 Brendan Eich,开始着手为即将在 1995 年发行的 Netscape Navigator 2.0 开发一个称之为 LiveScript 的脚本语言,当时的目的是在浏览器和服务器(本来要叫它 LiveWire)端使用它。因此 Netscape 与 Sun 两家公司结盟及时完成了LiveScript 的实现

就在 Netscape Navigator 2.0 即将正式发布前,Netscape 将其更名为 JavaScript,目的是为了利用 Java 这个因特网时髦词汇。Netscape 的赌注最终得到回报,JavaScript 从此变成了因特网的必备组件

因为 JavaScript 1.0 如此成功,Netscape 在 Netscape Navigator 3.0 中发布了 1.1 版。恰巧那个时候,微软决定进军浏览器,发布了 IE 3.0 并搭载了一个 JavaScript 的克隆版,叫做 JScript(这样命名是为了避免与 Netscape 潜在的许可纠纷)。微软步入 Web 浏览器领域的这重要一步虽然令其声名狼藉,但也成为 JavaScript 语言发展过程中的重要一步

在微软进入后,有 3 种不同的 JavaScript 版本同时存在:Netscape Navigator 3.0 中的 JavaScript、IE 中的 JScript 以及 CEnvi 中的 ScriptEase。与 C 和其他编程语言不同的是,JavaScript 并没有一个标准来统一其语法或特性,而这 3 种不同的版本恰恰突出了这个问题。随着业界担心的增加,这个语言的标准化显然已经势在必行

1997 年,JavaScript 1.1 作为一个草案提交给欧洲计算机制造商协会(ECMA)。第 39 技术委员会(TC39)被委派来“标准化一个通用、跨平台、中立于厂商的脚本语言的语法和语义”(http://www.ecma-international.org/memento/TC39.htm)。由来自 Netscape、Sun、微软、Borland 和其他一些对脚本编程感兴趣的公司的程序员组成的 TC39 锤炼出了 ECMA-262,该标准定义了名为 ECMAScript 的全新脚本语言

在接下来的几年里,国际标准化组织及国际电工委员会(ISO/IEC)也采纳 ECMAScript 作为标准(ISO/IEC-16262)。从此,Web 浏览器就开始努力(虽然有着不同的程度的成功和失败)将 ECMAScript 作为 JavaScript 实现的基础

JavaScript可以用来做什么?

客户端 JavaScript 语言的核心包含一些普遍的编程特性,以让你可以做到如下的事情:

  • 在变量中储存有用的值
  • 操作一段文本(在编程中称为“字符串”(string))
  • 运行代码以响应网页中发生的特定事件
  • 以及更多!
    JavaScript 语言核心之上所构建的功能更令人兴奋。应用程序接口(Application Programming Interfaces(API))将为你的代码提供额外的超能力
API

API 是已经建立好的一套代码组件,可以让开发者实现原本很难甚至无法实现的程序。就像现成的家具套件之于家居建设,用一些已经切好的木板组装一个书柜,显然比自己设计,寻找合适的木材,裁切至合适的尺寸和形状,找到正确尺寸的螺钉,再组装成书柜要简单得多

API 通常分为两类
浏览器API
浏览器 API 内建于 web 浏览器中,它们可以将数据从周边计算机环境中筛选出来,还可以做实用的复杂工作。例如:

  • 文档对象模型API(DOM API) 能通过创建、移除和修改 HTML,为页面动态应用新样式等手段来操作 HTML 和 CSS。比如当某个页面出现了一个弹窗,或者显示了一些新内容,这就是 DOM 在运行
  • 地理位置API 可以获取地理置。这就是为什么谷歌地图能够找到你的位置,并标示在地图上的原因
  • 画布(Canvas)和WebGL API可以创建生动的2D或3D图像
  • HTMLMediaElement 和 WebRTC 等 影音类 API 让你可以利用多媒体做一些非常有趣的事,比如在网页中直接播放音乐和影片,或用自己的网络摄像头获取录像,然后在其他人的电脑上展示

第三方API
第三方 API 并没有默认嵌入浏览器中,一般要从网上取得它们的代码和信息,比如:

  • Twitter API、新浪微博 API 可以在网站上展示最新推文之类。
  • 谷歌地图 API、高德地图 API](https://lbs.amap.com/ 可以在网站嵌入定制的地图等等
JS在页面上做了什么?

浏览器在读取一个网页时,代码(HTML, CSS 和 JavaScript)将在一个运行环境(浏览器标签页)中得到执行。就像一间工厂,将原材料(代码)加工为一件产品(网页)

在 HTML 和 CSS 集合组装成一个网页后,浏览器的 JavaScript 引擎将执行 JavaScript 代码。这保证了当 JavaScript 开始运行之前,网页的结构和样式已经就位

这样很好,因为JavaScript 最普遍的用处是通过 DOM API(见上文)动态修改 HTML 和 CSS 来更新用户界面 (user interface)。如果 JavaScript 在 HTML 和 CSS 就位之前加载运行,就会引发错误

JavaScript的实现

即便JavaScript和ECMAScript基本上是同义词,但它们之间仍然存在区别,且JavaScript并不仅仅局限于ECMA-262所定义的那样。完整的JavaScript应该包含以下几个部分:

  • 核心(ECMAScript)

  • DOM(文档对象模型)

  • BOM(浏览器对象模型)


EMSAScript

即ECMA-262所定义的语言,并不局限于web浏览器。实际上,这门语言并没有输入输出之类的方法。ECMA-262将这门语言作为一个基准来定义,以便于在此基础上构建更加稳健的脚本语言。web浏览器只是ECMAScript实现可能存在的一种宿主环境。宿主环境必须提供ECMAScript的基准实现和环境自身交互的必须扩展。其它宿主环境还有服务器端的JavaScript平台node.js和已经被淘汰的Adobe Flash

若不涉及浏览器的话,在基本层面,ECMA-262定义了如下几个部分:

  • 语法
  • 类型
  • 语句
  • 关键字
  • 保留字
  • 操作符
  • 全局对象

ECMAScript只是对实现了这个规范描述的所有方面的一门语言的称呼。因此JavaScript实现了ECMAScript,而Adobe ActionScript也实现了ECMAScript

DOM

文档对象模型(DOM)是一个应用编程接口(API),用于在html中使用扩展的XML。DOM将整个页面抽象为一组分层节点。html或XML页面的每个组成部分都是一种节点,包含不同的数据,比如:

<html>
    <head>
        <title>文档标题</title>
    </head>

    <body>
        <a href=“”>我的链接</a>
        <h1>我的标题</h1>
    </body>
</html>

这些代码可以通过DOM表示为一节点,如图:


DOM可以通过创建表示文档的树,让开发者可以随心所欲的控制网页的内容和结构。使用DOM API可以轻松的删除、添加、替换和修改节点

BOM

浏览器对象模型(BOM)API。用于支持访问和操作浏览器的窗口,使用BOM,开发者可以操控浏览器显示页面以外的部分

html中的JavaScript

JavaScript是通过<Script>元素插入到html页面中的。这个元素可以用于把JavaScript代码内嵌如html页面中,跟其他标签混合到一起;也可以用于引入保存在外部文件中的JavaScript

<script>元素

这个标签是由Netscape创造出来的,并且拥有以下属性:

  • async:可选,表示应该立即开始下载脚本,但不阻止其它页面的动作,比如下载资源或等待其它脚本加载。只对外部脚本文件有效
  • charset:可选,使用src属性指定的代码字符集。很少使用
  • crossorigin:可选,配置相关请求的CORS(跨资源共享)设置,默认不使用CORS
  • defer:可选,表示文档解析和显示完成后再执行脚本是没问题的。只对外部脚本文件有效
  • integrity:可选,此属性用于确保分发网络(CDN)不会提供恶意内容
  • src:可选,表示包含要执行的代码的外部文件
  • type:可选,表示代码块中脚本语言的内容类型

在 HTML 中,JavaScript 代码必须位于 <script> 与 </script> 标签之间,比如:

<script>
document.getElementById("demo").innerHTML = "我的第一段 JavaScript";
</script>

外部文件

脚本可放置于外部文件中
外部脚本很实用,相同的脚本被用于许多不同的网页
JavaScript 文件的文件扩展名是 .js
如需使用外部脚本,请在 <script> 标签的 src (source) 属性中设置脚本的名称:
外部文件:myScript.js

function myFunction() {
   document.getElementById("demo").innerHTML = "demo";
}

之后可以在 <head> 或 <body> 中放置外部脚本引用
该脚本的表现与它被置于 <script> 标签中是一样的,

<script src="myScript.js"></script>

需要注意的是:外部脚本不应该再包含<Script>标签

外部JavaScript的优势:

  • 分离了 HTML 和代码
  • 使 HTML 和 JavaScript 更易于阅读和维护
  • 已缓存的 JavaScript 文件可加速页面加载

行内代码

要嵌入行内JavaScript代码,直接将代码放入<JavaScript>标签内即可:

<script>
function myFunction() {
    document.getElementById("demo").innerHTML = "demo";
}
</script>

包含在<Script>标签内的代码会被从上到下进行解释。并且在<Script>标签中的代码被计算完成之前,页面其余内容不会被加载,也不会被显示
默认情况下:<Script>标签会阻断html页面的加载与渲染

标签位置

<head>中的JavaScript
JavaScript函数放置于html页面的<head>部分:

<!DOCTYPE html>
<html>
<head>
<script>
function myFunction() {
    document.getElementById("demo").innerHTML = "段落被更改。";
}
</script>
</head>

<body>

<h1>一张网页</h1>
<p id="demo">一个段落</p>
<button type="button" onclick="myFunction()">试一试</button>

</body>
</html>

把所有JavaScript文件都放到<head>里,意味着需要将所有的JavaScript代码都下载、解析并解释完成之后,才进行html的渲染。这对于有很多JavaScript的页面,会造成明显的页面渲染延迟,因为在此期间浏览器将会是一片空白,故现在浏览器大多数做法都是讲JavaScript内容放到<body>元素中的页面内容后面

<body> 中的 JavaScript
JavaScript 函数被放置于 HTML 页面的 <body> 部分:

<!DOCTYPE html>
<html>
<body> 

<h1>A Web Page</h1>
<p id="demo">一个段落</p>
<button type="button" onclick="myFunction()">试一试</button>

<script>
function myFunction() {
   document.getElementById("demo").innerHTML = "段落被更改。";
}
</script>

</body>
</html>

这样一来,浏览器就会在处理JavaScript之前完全将页面渲染,用户会感觉加载速度更快了

推迟执行脚本

通过设置<Script>标签的defer属性,脚本在执行的时候不会改变页面的结构,因此这个脚本完全可以在整个页面解析之后再运行

<html>
  <head>
  <title>demo</title>
  <script defer src="js/vendor/jquery.js"></script>
  <script defer src="js/script2.js"></script>
  <script defer src="js/script3.js"></script>
  </head>

  <body>demo</body>
</html>

虽然这个例子中对于<Script>元素的声明包含在<head>中,但是因为设置了defer属性,它们会在浏览器解析到结束的</html>标签后才会执行。并且添加 defer 属性的脚本将按照在页面中出现的顺序加载,因此示例中可确保 jquery.js 必定加载于 script2.js 和 script3.js 之前,同时 script2.js 必定加载于 script3.js 之前

需要注意的是:defer属性只对外部脚本文件有效

异步执行脚本

<Script>标签中的async属性与defer属性类似,它同样也只适用于外部脚本文件。只是标记为async的脚本并不能按照它们出现的次序运行:

<html>
  <head>
  <title>demo</title>
  <script async src="js/vendor/jquery.js"></script>
  <script async src="js/script2.js"></script>
  <script async src="js/script3.js"></script>
  </head>

  <body>demo</body>
</html>

三者的调用顺序是不确定的。jquery.js 可能在 script2 和 script3 之前或之后调用,如果这样,后两个脚本中依赖 jquery 的函数将产生错误,因为脚本运行时 jquery 尚未加载

脚本调用策略小结:

  • 如果脚本无需等待页面解析,且无依赖独立运行,那么应使用 async
  • 如果脚本需要等待页面解析,且依赖于其它脚本,调用这些脚本时应使用 defer,将关联的脚本按所需顺序置于 HTML 中

语言基础(一)

变量

ECMAScript变量是松散类型:即可以用来保存任何数据类型,变量只是用于保存数据并对其命名的占位符

var

声明:
var i = 'hi';
var变量不仅可以更改已声明值的数值,还可以更改值的类型,因此:

var i = 'hi';
i = 100;

这是完全合法的,但并不推荐使用

作用域:
由var定义的变量会成为当前函数的局部变量,在函数被调用之后即被销毁,如:

function test(){
    var i = 'hi';    //局部变量
}
test();
console.log(i);    //出错:i未定义

省略var操作符可以创建一个全局变量,因此下面代码不会报错:

function test(){
    i = 'hi';    //全局变量
}
test();
console.log(i);    // hi

定义多个变量的方式:

var a = 'hi',
      b = true,
      c = 300;

这是完全可行的

声明提升:
使用var关键词声明变量会自动提升到函数作用域顶部,因此下面代码不会报错:

function foo(){
    console.log(i);
    var i = 26;
}
foo();       // undefined

实际上等同于:

function foo(){
    var i;
    console.log(i);
    i = 26;     
}
foo();       // undefined

除此之外,反复对一个变量进行多次声明也没有关系(但并不推荐这么做!)

function foo(){
    var i = 100;
    var i = 200;
    var i = 300;
    console.log(i);
}
foo();
let

let与var的区别:前者声明范围是块作用域,后者声明范围是函数作用域
以下语句在var声明下不会报错:

if (true){
    var name = 'Matt';
    console.log()(name);
}
console.log(name);

如果换成let声明变量,则会报错:

if (true){
    let age = 11;
    console.log(age);
}
console.log(age);     // age没有定义

块作用域即函数的代码块,实际上是函数作用域的子集,因此对var作用域的限制同样适用于let

let无法做到像var那样对一个变量进行重复声明:

let name;
let name;     // 提示声明重复

可嵌套:
JS引擎会自动记录变量声明的标识符以及其作用范围,因此嵌套标识符进行同名变量定义不会报错,这是因为声明并不在同一个块中:

var name = 'kana'
console.log(name);    //  kana
if (true) {
  var name = 'miho';
  console.log(name);     // miho
}
let name = 'kana'
console.log(name);    // kana
if (true) {
  let name = 'miho';
  console.log(name);    // miho
}

上面两段代码都能正常运行

var与let冗余声明产生的报错:

var id;
let id;
let id;
var id;

上面两种情况都会报错:变量重复声明。这两个不同关键字声明的并不是不同类型的变量,它们只是指出变量的相关作用域应当如何存在

暂时性死区
即:相比于var,let声明的变量不会在其作用域中提升

console.log(age);
let age = 22;    // 报错,age没有声明

全局声明
与var不同,let在全局作用域当中声明的变量不会成为Windows的对象属性

let age = 22;
console.log(window.age);    // undefined

不过let声明依然是在全局作用域中发生的,相应的变量在页面的生命周期中存续

条件声明
使用var声明变量时,由于声明会被提升,JS引擎会自动将多余的声明在作用域顶部合并为一个声明。因为let的作用域是块,所以不可能检查在此之前是否已经使用let进行过同名变量的声明,也不可能在声明的情况下对它再次进行声明

const

const行为与let基本相同,主要区别在于两点:
1.用const操作符进行变量声明时一定要对其进行初始化变量
2.你无法对一个const变量的值进行修改
因此:

const age = 36;
age = 50;   // 报错:给常量赋值

const声明的限制只适用于它指向的变量的引用。即:如果const声明的是一个对象,那么修改这个对象的内部属性并不违反const的限制

除此之外,因为const变量无法修改值的特性,因此无法用于for语句中的迭代变量

for ( const i = 0 ; i <= 10 ; i++ ){}    //  错误:给常量赋值

声明风格与最佳实践:
1.不使用var
2.const优先,let其次

举报

相关推荐

0 条评论