JavaScript 详解系列,主要是对自学JavaScript的一个总结和分享.以语言对比的形式更快速的入门JavaScript.有Java或C++基础,更加深入的了解JavaScript.(快速掌握一门语言)

JavaScript是什么(Hello JavaScript)

JavaScript 是一种脚本,一门编程语言.它可以实时的更新静态网页、交互、动画、视频等.JavaScript是Web技术最重要的核心技术.

Web技术的蛋糕有三层

  • HTML 是一种标记语言,用来结构化网页布局内容并赋予内容含义.定义段落、标题、数据表等.相当于Android中的layout.xml文件来定义布局内容
  • CSS 一种样式规则语言,主要用于HTML内容样式,这个Android中Style.xml 一样的形式
  • JavaScript 是一种脚本语言,用来动态更新内容,动态设置数据等

引自MDN

JavaScript的组成:

  • ECMAScript 提供语言的核心功能(变量、操作符、对象、函数等)
  • DOM(文档对象模型) 定义了JavaScript 操作HTML文档的接口(提供了访问HTML文档如body、form、div等接口和操作方法) 实现了对DOM的动态操作
  • BOM(浏览器对象模型) 定义了操作浏览器的接口(提供了某些访问功能如浏览器窗口大小、版本信息、浏览器历史等)

参考《JavaScript高级程序设计3》、MDN

怎样向页面添加JavaScript

script 标签的使用

  • script 写在head标签内部

type 默认值为text/javascript,可以不用进行设置; 包含在元素内部的JavaScript代码被从上至下依次解释.在解释器将 标签之间再包含额外的JavaScript代码,只会下载和执行外部脚本文件,而嵌入的代码会被忽略 ,例子如下:

<script src="example-1.js"></script>

src 也可以使用外部域的URL

<script src="https://www.somewhere.com/afile.js"></script>

script 的属性:async defer 延迟脚本

如果在文档的<head>元素中包含所有JavaScript元素,就意味着必须等到全部JavaScript代码都被下载、解析和执行完成以后,才能呈现页面内容. 为了避免这个问题,现代Web应用一般都全部JavaScript引用放在<body>元素中页面内容的后面

<body>
        <!-- 这里放内容 -->

        <script src="example-1.js"></script>
    </body>

如上代码,这样在解析JavaScript代码之前,页面的内容将完全呈现在浏览器中.

script 标签也提供了延迟脚本的属性,分别是:async 和 defer

script 元素的defer和async属性,浏览器就会按照script元素在页面出现的先后顺序对它们依次进行解析.就是在第一个script元素包含对代码解析完成以后,第二个script包含对代码才会被解析,……

  • defer 延迟脚本

这个属性的用途表明脚本在执行时不会影响页面的构造.脚本会被延迟到整个页面都解析完毕后再运行

 <script defer="defer" src="example-1.js"></script>

上述代码,脚本将延迟到浏览器遇到</html>标签后再执行,脚本按照它们出现的先后顺序执行,第一个脚本会先于第二个脚本,脚本会先于DOMContentLoaded事件,最好只包含一个延迟脚本.(最好将延迟脚本放在页面底部最佳的选择)

  • async 异步脚本

async 只适用于外部脚本,并告诉浏览器立即下载文件.async标记的脚本并不保证按照它们的先后顺序执行

<!-- 异步脚本 async ,同样只适用于外部文件 不一定按照顺序执行-->
<script async src="example-1.js"></script>

还需要注意,在html文档的最上方加入如下代码:

<!DOCTYPE html> <!-- HTML 5 定义文档模式为标准模式,如果不定义在混杂模式下会影响css的呈现和JavaScript的解释执行 -->


 <!-- noscript ,浏览器不支持脚本或者浏览器支持脚本,但是脚本被禁用的情况下才会显示noscript的内容其他情况下不显示 -->
        <noscript>
            <p>本页面需要浏览器支持(启用) JavaScript脚本</p>
        </noscript>

JavaScript 基本概念

变量

ECMAScript 变量是松散类型的,可以用来保存任何类型的数据.也就是说每个变量仅仅是一个用于保存值的占位符而已.

变量定义如下:

var message;//未经过初始化的变量,会保存一个特殊的值undefined

unction doSoming(){
    "use strict"//启用严格模式 对于某些不安全的操作会抛出错误
var message = 'hi';

var message = true;
message = 10;//有效 但不推荐这要使用 这样相当于定义了一个全局变量
}

var操作符定义的变量是该变量作用域中的局部变量,这个变量在函数退出后就会被销毁.省去var操作符会变成全局变量,外部函数的任何地方都可以访问,需要注意的是在局部作用域中定义全局变量是很难维护的.给未经声明的变量赋值在严格模式下会导致ReferenceError错误

在严格模式下,不能定义名为eval或arguments的变量,否则会导致语法错误.

数据类型

ECMAScript中有6中简单的数据类型:Undfined、Null、Boolean、Number、String、Object(Object本质上是由一组无序的名值对组成的).ECMAScript 不支持创建自定义类型(Java和C++是支持创建自定义类型的).由于ECMAScript数据类型具有动态性,就没有在定义其他类型的必要了

  • boolean 类型
 //boolean 类型
    var message = "mes";
    var messageBoolean = Boolean(message);//字符串不为空 返回true; 非0的数字值返回true;object 不为空 返回true
    alert(messageBoolean);

    //注意不要使用下面的方式
    //字符串message会被自动转换成了对应的Boolean值,由于存在这种自动执行的Boolean转换,要确切的知道什么是变量
    if(message){
        alert("Value is true");
    }
  • Number
 //永远不要测试某个特定的浮点数值
    console.log(0.1+0.2);//0.30000000000000004
    if(0.1+0.2 == 0.3){
        alert("You Got 0.3");
    }

    //数值范围
    var result = Number.MAX_VALUE + Number.MAX_VALUE;
    alert(result);//false 超出数值范围

    //NaN 
    console.log("NaN == NaN:"+(NaN == NaN));//NaN 和任何数值都不相等 包括NaN本身

    //isNaN 会帮我们确定这个参数是否“不是数值”;如果返回true 表示不能转换为数值
    console.log(isNaN(NaN));//true 
    console.log(isNaN(10));//false
    console.log(isNaN('10'));//false
    console.log(isNaN("blue"));//true 
    console.log(isNaN(true));// 1 false

    //数值转换 将非数值转换成数值
    //Number()
    var num1 = Number("hello");//NaN
    var num2 = Number("");//0
    var num3 = Number(000011);//9
    var num4 = Number(true);//1
    var num5 = Number("00011");//11

    console.log("num1",num1,"num2",num2,"num3",num3,"num4",num4,"num5",num5);

    //parseInt 转换规则
    //parseInt  处理整数是更常用的函数 会看其是否符合数值模式
    var p1 = parseInt("1234blue");//1234
    var p2 = parseInt("");//NaN
    var p3 = parseInt("0xA");//十六进制 10
    var p4 = parseInt(22.5);//22 
    var p5 = parseInt("070",8);//ECMAScript 3 认为是八进制 56;ECMAScript 5认为是十进制  70;为此我们可以指定基数来保证正确的结果
    var p6 = parseInt("70");//70

    var p7 = parseInt("0xf");//15
    var p8 = parseInt("f",16);//15 p7 和 p8 可以看成是相等的

    console.log("p1",p1,"p2",p2,"p3",p3,"p4",p4,"p5",p5,"p6",p6,"p7",p8);

    //parseFloat 和 parseInt类似;需要注意的parseFloat只解析 十进制,十六进制字符串会被转换成0,解析字符串中的第一个小数点是有效的
    //第二个小数点是无效的
    var pf1 = parseFloat("1234blue");//1234
    var pf2 = parseFloat("0xA");//0
    var pf3 = parseFloat("22.5");//22.5
    var pf4 = parseFloat("22.34.5");//22.34
    var pf5 = parseFloat("0908.5");//908.5

    console.log(pf1,pf2,pf3,pf4,pf5);
  • String/Object
function doString(){
     //js 中字符串可以使用“或者单引号‘表示;而Java中的字符串只能以双引号表示
     //ECMAScript 中这两种形式没有区别,表示的字符串完全相同;注意以双引号开头必须以双引号结尾,以单引号开头必须以单引号结尾
     var frist = "frist";
     var last = 'last';
     var text = 'This is the letter sigma \n This is the letter prim';
     console.log(text);
     //ECMAScript 中的字符串是不可变的,也就是说字符串一旦创建 它们的值就不能改变
     //要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个新值的字符串填充该变量
     var lang = "java";
     lang = lang + "Script";
     //首先创建一个容纳10个字符的新字符串,然后在这个字符串中填充“java" 和 “Script” 最后一步销毁原来的字符串
     //“Java”和字符串“Script” 这也是在旧版浏览器拼接字符串很慢的原因 现在的浏览器已经解决了这个低效率的问题(如何解决??)

     //转换字符串 toString 可以输出以二进制 八进制 十六进制的字符串值
     var nume = "10";
     console.log(nume.toString());
     console.log(nume.toString(2));
     console.log(nume.toString(8));
     console.log(nume.toString(10));
     console.log(nume.toString(16));

     //object 类型 创建对象和Java中的类似 都是通过new 关键字创建的
     var o = new Object();//可以省去原括号 但是不推荐
     //关于 object 的属性和构造函数 会在后面中详细的讲解
}

关于递增和递减操作符和其他基本的操作符和Java/C++中的逻辑一样的 这里就不进行讲解了 非常基础的东西,主要讲解与Java和C++中不一样的东西

理解ECMAScript的基本语句

基本的if、for、while、do-while、in、switch这些基本的语句几乎在所有的语言中都是通用的,如果你有其他语言基础了解一下即可

function doStatement(){

    //if 语句,javascript的if语句和Java中的if语句一样 if语句后可以跟一行代码或代码块不过业界推崇的是始终使用代码块
    //注意Javascript 的if语句中的条件可以是任意表达式,而且表达式的求值不一定是布尔值,ECMAScript会自动调用Boolean()转换函数
    //将这个表达式的结果转换为一个布尔值,这一点跟Java的有所不同,Java的if语句条件必须是布尔值.
    var i = 10;
    if(i > 25)
        alert('Greater than 25.');//单行语句
    else{
        alert('Less than or equal to 25.'); //代码块中的语句
    }

    //do-while while for 语句和Java的语句并没有区别,举个例子
    //do-while语句
    var li = 0;
    do{
        i+=2;
    }while(i<10);
    //for语句
    for (let i = 0; i < array.length; i++) {
        const element = array[i];
    }
    //包括for无限循环也和Java的一模一样
    for(;;){
        //TODO 
    }
    //只给出控制表达式实际上就是把for循环转换成了while循环
    for(;li<20;){
       li++;
    }

    //while 语句
    while (li<20) {
        li+=2;
    }

    alert(i);

    //for-in 语句,而在Java中没有for-in语句但是在kotlin中有for-in语句,kotlin中的一些语法也借鉴了ECMAScript中的语法
    //语法的简洁确实在编程中提高了效率,这也是我为什么喜欢上了JavaScript和Kotlin这两种语言
    //下面我们看这个例子
    for (const propName in window) {
       document.write(propName);
    }
    //需要注意的是ECMAScript对象的属性没有顺序,所以遍历返回的先后次序可能会因浏览器而异
    //还需要注意的是如果表达式的对象的变量值为null或undefined,for-in语句会抛出错误.
    //ECMAScript5 更正了这一行为,不再抛出错误,而是不执行循环体,建议在使用for-in循环之前,先检测该对象的值不是null或undefined
    for (const key in object) {
        if (object.hasOwnProperty(key)) {
            const element = object[key];
        }
    }
}

label语句这个语句貌似只有JavaScript和Kotlin中见到过,主要个循环语句配和使用

// doLabel();

function doLabel(){
 //label 语句,在Java中同样没有这样的语句但是在kotlin 中有这样的语句,kotlin大量借鉴了JavaScript语法
    //label :statement label语句可以在代码中添加标签,以便将来使用,看下面的一个例子:
    //label 语句一般都要与for语句等循环语句配合使用

    //break 和continue语句都可以与label语句联合使用,从而返回代码中特定的位置
    //这种联合使用的情况多发生在循环嵌套的情况中
    var num = 0;
    start:for(var i=0;i<11;i++){
       for(var j = 0;j<10;j++){
           if(i==5 && j==5){
               break start;//强制退出内部和外部循环
           }
           num++
       }
    }
    alert(num);//55
}

JavaScript的函数

ECMAScript 中函数是通过function关键字声明的,对函数的命名没有什么要求,但是在严格模式下不能把函数/参数命名为eval或argumentsECMAScript中的函数在定义时不必指定是否返回值,任何函数在任何时候都可以通过return语句后跟要返回的值,在Java和kotlin、C++等语言中必须要指定返回值的类型的

function sayHi(name,message){
    alert(name,message);
     eval(name);
    return 100+name;
}
//报错误:Uncaught SyntaxError: Unexpected eval or arguments in strict mode 
function eval(arguments){
     "use strict"//启用严格模式 对于某些不安全的操作会抛出错误,严格模式下JavaScript的执行结果会有很大的不同
    alert(arguments);
}

函数中的参数:

ECMAScript 函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型.

也就是说,即便定义的函数只接收两个参数,在调用这个函数时也未必一定要传递两个参数,可以传递一个、三个甚至不传递参数

(PS:感觉ECMAScript的这种弱定义不是很理解,这种方法容易出现错误,不像Java中的强制定义一样)

ECMAScript 为什么会这么做呢?

ECMAScript中的参数在内部是用一个数组来表示的.函数接收到的始终都是这个数组,而不关心数组中包含哪些参数.

实际上函数体内可以通过arguments对象(这也是为什么在严格模式下arguments不能用作参数和函数名的原因)来访问这个参数数组,从而获取传递给函数的每一个参数.arguments对象与数组类似. 使用arguments获取参数

//看下面的一个例子

function sayHi(){
    alert("1: "+arguments[0]+" 2:"+arguments[1]);
}
sayHi("JakePrim","HelloWorld");

ECMAScript 函数的一个重要特点:命名的参数只提供便利,但不是必需的.ECMAScript 函数不存在函数重载这一重要特性(因为传递的参数没有个数和类型限制)另一个与参数相关的重要方面,arguments对象可以与命名参数一起使用arguments的值永远与对应命名参数的值保持同步,没有传递值的命名参数将自动被赋予undefined 值

function doAdd(num1,num2){
    arguments[1] = 10;//相当于重写了第二个参数,如果只传递了一个参数那么num2 还是undefined,如果传递了两个参数 num
    if(arguments.length == 1){
        alert(num1+10);
    }else if(arguments.length == 2){
        alert(arguments[0]+num2);
    }
}
doAdd();

ECMAScript 函数没有签名,因为其参数是有包含零或多个值的数组来表示的. 而没有函数签名 真正的重载是不可能做到的

function addSomeNumber(params) {
    return params + 100;
}

function addSomeNumber(params) {
    return params + 200;
}
// 函数addSomeNumber 被定义了两次,后定义的函数覆盖了先定义的函数,因此这个函数的返回结果就是300
var result = addSomeNumber(100);//300

console.log(result);

总结ECMAScript的基本要素:

ECMAScript 中的基本数据类型包括Undefined、Null、Boolean、Number、String

与其他语言不同,ECMAScript没有为整数和浮点值分别定义不同的数据类型,Number类型可用于表示所有数值

严格模式为这门语言容易出错的地方施加了限制

ECMAScript提供了很多与C及其他类C语言中相同的基本操作符,包括算术操作符、布尔操作符、关系操作符、相等操作符及赋值操作符

无须指定函数的返回值,因为任何ECMAScript函数都可以在任何时候返回任何值

实际上,未指定返回值的函数返回的是一个特殊的undefined值

ECMAScript中也没有函数签名的概念,因为其函数参数是以一个包含零或多个值的数组的形式传递的

可以向ECMAScript函数传递任意数量的参数,并且通过arguments对象来访问这些参数

由于不存在函数签名的特性,ECMAScript函数不能重载