MDN Mozilla Developer Network
是一个非常重要的Web技术文档平台.由Mozilla维护,包括HTML,CSS,JavaScript等各种web技术的完整文档.许多开发者都视其为Web开发的官方参考手册.
MDN web doc
简介 JavaScript和Java除了语法上有点像以外毫无关系.但是java非常火,网景公司希望蹭热度推广JS,就改了这么个名字. ECMAScript: 网景开发了JS,一年后微软又模仿开发了JScript.为了JS成为全球标准,几个公司联合ECMA组织定制了JS语言的标准,称为ECMAScript.所以这个标准就是为了JS而生.也称ES
标准.其实ES6
是一个重大更新. JS设计到诞生只用了10天时间,所以无法避免,很多设计缺陷. 集成与运行 JS代码可以集成到网页的任何地方,不过一般放在<head>
中,用<script></script>
包起来,或者使用<script src="..."></script>
引入JS文件.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <html > <head > <script > alert ('Hello, world' ); </script > <script src ="/static/js/abc.js" > </script > <script src ="/static/js/def.js" > </script > <script type ="text/javascript" , src ="/static/js/ghi.js" > </script > </head > <body > ...</body > </html >
用chrome
随便打开一个网页,按F12
–> 点解console
tab,即刻在那里执行你的js代码.
基本语法 每个语句以;
结尾,虽然这不是强制的,因为浏览器中负责执行js代码的引擎会自动补上,但是建议不要忽略
,因为有些情况下引擎会理解错误加错分号导致语义改变. 语句块用{...}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var x = 1 ;'hello world' ;var x = 1 ; var y = 2 ;if (2 > 1 ) { x = 1 ; y = 2 ; z = 3 ; if (x < y) { z = 4 ; } if (x > y) { z = 5 ; } }
注意: JS严格区分大小写
数据类型和变量 Number JS不缺分整数和浮点数,统一用Number表示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 123 ; 0.456 ; 1.2345e3 ; -99 ; NaN ; Infinity ; 1 + 2 ; (1 + 2 ) * 5 / 2 ; 2 / 0 ; 0 / 0 ; 10 % 3 ; 10.5 % 3 ; 12.00 == 12 var r = 123.456 ;var s = 3.14 * r * r;console .log (s);console .log (Number .MAX_SAFF_INTEGER );
字符串 使用''
或""
括起来的任意文本
布尔值 true
/false
, 支持布尔运算与
/或
/非
: &&
/||
/!
1 2 3 4 true && true ; true || false ; ! false ; ! (2 > 5 );
比较运算符 js允许任意数据类型
做对比,注意相等运算符
1 2 false == 0 ; false === 0 ;
js有两种相等运算符:
==
: 它会自动转换数据类型,很多时候会得到奇怪的结果
===
: 表示不自动转换类型,如果类型不一致,返回false
,如果一致再比较
这是js的一个设计缺陷,所以要注意 : 始终坚持使用===
来比较
1 2 3 4 NaN === NaN ; isNaN (NaN );
注意浮点型数的比较
这就不是设计缺陷了,而是因为计算机无法精确表示无限循环小数.要比较两个浮点数是否相等,只能计算他们的差的绝对值,看看是否少于某个阈值
1 Math .abs (1 / 3 - (1 - 2 / 3 )) < 0.0000001 ;
BigInt js内置的BigInt
类型可以用来表示比2^53还要大的数,表示方法是整数后加一个n
,或者使用BigInt()
把字符串和Number转换成BigInt
1 2 3 4 5 6 7 var bi1 = 9223372036854775807n ;var bi2 = BigInt (12345 );var bi3 = BigInt ("0x7fffffffffffffff" );console .log (bi1 === bi2); console .log (bi1 === bi3); console .log (bi1 + bi2);
BigInt也可以正常加减乘除,结果依然是一个BigInt,但BigInt和Number不能放一齐运算
1 2 3 4 console .log (1234567n + 3456789n ); console .log (1234567n / 789n ); console .log (1234567n % 789n ); console .log (1234567n + 3456789 );
null和undefined null
表示一个空值,不同于0
和空字符串''
,它就是表示空
,类似python的None
,java的null
和swift的nil
js的设计者希望用null
表示空值,undefined
表示值未定义.但事实上这并没有什么卵用. 大多数情况下,我们都用null
,仅仅在判断函数参数是否传递的情况下使用undefined
. 数组 一组按顺序排列的集合,集合的每个值称为元素.元素可以是任意数据类型.
1 2 3 4 5 6 7 [1 , 2 , 3.14 , 'Hello' , null , true ]; new Array (1 , 2 , 3 ); var arr = [1 , 2 , 3.14 , 'Hello' , null , true ]; arr[0 ]; arr[6 ];
类比python的list
对象 键-值对的无序集合,键
都是字符串类型
,值
可以是任意类型
.
键
又称为对象的属性
.
1 2 3 4 5 6 7 8 9 10 11 12 var person = { name : 'Bob' , age : 20 , tags : ['js' , 'web' , 'mobile' ], city : 'Beijing' , hasCar : 'true' , zipcode : null } person.name ; person.zipcode ;
类比python的字典或groovy的map
变量 使用var
关键字来声明 变量名可以是:大小写英文 数字 $
和_
的组合,不能用数字开头不能是JS的关键字 也可以用中文,但是,请不要自找麻烦 用=
赋值 可以反复赋值,但只能用var
声明一次 1 2 3 4 5 6 7 var a; var $b = 1 ; var s_007 = '007' ; var Answer = true ; var t = null ; var a = 123 ; a = 'ABC'
这种变量赋值不固定的语言就是动态语言
,比如python也是.java则是静态语言.
strict模式 JS设计之初并没有强制要求使用var
申明变量,这导致了一个严重的后果: 如果一个变量没有通过var
申明就被使用,该变量就自动被申明为全局变量
:
也就是同一页面的的不同js文件中公用的一个变量,假如不同js文件都使用了同样一个变量名的变量,就会相互影响.
使用var
声明的变量,它的范围会限制在该变量被申明的函数体内,同名变量就不会冲突.
为了弥补这个严重的设计缺陷,ECMA后续退出了strict模式
,开启该模式后,如果未使用var
申明变量就使用,就会导致运行错误.
建议所有js都开启strict
模式.
还有一种申明变量的方式是let
,这也是现代js推荐的一种方式
1 2 3 let s = 'hello' console .log (s)
现代JS编程中基本不使用var
而是使用const
+let
的组合来定义变量
字符串 转义字符 除了常见的转义功能
1 2 3 4 '\x41' ; '\u4e2d\u6587' ;
多行字符串 有点特别,ES6后,js使用反引号表示:
模板字符串 与python和groovy类似,使用+
号连接
1 2 3 4 let name = '小明' ;let age = '20' ;let message = '你好,' + name + ',你今年' + age + '岁了!' ;alert (message)
如果有很多变量,使用+
就有点麻烦,ES6后提供一种新的模板字符串:
1 let message = `你好, ${name} , 你今年${age} 岁了!` ;
字符串操作 1 2 3 4 5 6 let s = 'Hello, world!' ; s.length ; s[0 ]; s[13 ];
字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,也不会有任何改变
1 2 3 4 5 6 7 8 9 10 s.toUpperCase (); s.toLowerCase ();let s = 'hello world' ; s.indexOf ('world' ); s.substring (0 , 5 ); s.substring (7 );
数组 大小 1 2 3 4 5 6 7 8 9 10 11 12 13 14 let arr = [1 , 2 , 3.14 , 'Hello' , null , true ];console .log (arr.length ); arr.length = 7 ;console .log (arr); let arr = ['A' , 'B' , 'C' ]; arr[1 ] = 99 ;console .log (arr); let arr = ['A' , 'B' , 'C' ]; arr[5 ] = 'x' ;console .log (arr);
大多数语言都不允许直接改变数组的大小.但JS对此没有任何报错,所以在使用JS array时要注意不要越界
切片/复制 1 2 3 4 5 6 7 8 let arr = [10 , 20 , '30' , 'xyz' ]; arr.indexOf (10 ); arr.slice (0 , 3 ); arr.slice (3 ); let aCopy = arr.slice (); aCopy === arr;
添加/删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let arr = [1 , 2 ] arr.push ('A' , 'B' ) arr; arr.pop (); arr; let arr = []; arr.pop (); arr; let arr = [1 , 2 ]; arr.unshift ('A' , 'B' ); arr; arr.shift (); arr; let arr = []; arr.shift ()
排序/反转 1 2 3 4 5 6 let arr = ['B' , 'C' , 'A' ]; arr.sort (); arr; let arr = ['one' , 'two' , 'three' ]; arr.reverse (); arr;
万能方法 splice()
是修改array的万能方法.
第一个参数是array的开始索引
,第二个参数是范围
.
1 2 3 4 5 6 7 8 9 10 let arr = ['Microsoft' , 'Apple' , 'Yahoo' , 'AOL' , 'Excite' , 'Oracle' ]; arr.splice (2 , 3 , 'Google' , 'Facebook' ); arr; arr.splice (2 , 2 ); arr; arr.splice (2 , 0 , 'Google' , 'Facebook' ); arr;
数组连接 1 2 3 4 let arr = ['A' , 'B' , 'C' ];let added = arr.concat ([1 , 2 , 3 ]); added; arr;
数组转字符串拼接 类似python的join()
.
如果array的元素不是字符串,会自动转成字符串然后再拼接
1 2 let arr = ['A' , 'B' , 'C' , 1 , 2 , 3 ]; arr.join ('-' );
多维数组 1 2 3 let arr = [[1 , 2 , 3 ], [400 , 500 , 600 ], '-' ];let x = arr[1 ][1 ];console .log (x);
对象 可以类比python的dict,groovy的map,但是它实际上也是一个对象.因为它可以继承.
JS的对象用来描述现实世界中的某个对象.
1 2 3 4 5 6 7 8 9 let xiaoming = { name : '小明' , birth : 1990 , school : 'No.1 Middle School' , height : 1.70 , weight : 65 , score : null };
注意: 最后一个键值对末尾不需要加,
,如果加了,某些旧版本浏览器会报错.
键值对的每个键(key)
,称为对象的属性. 属性的访问通过.
操作符. 如果属性名包含特殊字符,要通过''
括起来. 如果访问一个不存在的属性,返回undefined
1 2 3 4 let xiaohong = { name : '小红' , 'middle-school' : 'No.1 Middle School' };
js的对象是动态类型,可以自由地给一个对象添加或删除属性.
1 2 3 4 5 6 7 8 9 10 11 let xiaoming = { name : '小明' }; xiaoming.age ; xiaoming.age = 18 ; xiaoming.age ; delete xiaoming.age ; xiaoming.age ; delete xiaoming['name' ]; xiaoming.name ; delete xiaoming.school ;
in
操作符来判断对象是否拥有某一属性
1 2 'name' in xiaoming; 'grade' in xiaoming;
但有时候这个属性不一定是xiaoming
的,也有可能是xiaoming
继承得到的
这是有因为toString
定义在object
对象中,而所有对象最终都会在原型链上指向object
,所以xiaoming
也会有这个属性
要判断一个属性是否自身拥有,而不是继承所得,使用hasOwnProperty()
1 2 xiaoming.hasOwnProperty ('name' ); xiaoming.hasOwnProperty ('toString' );
条件判断 1 2 3 4 5 6 7 8 9 10 'use strict' ;let age = 20 ;if (age >= 6 ) { console .log ('teenager' ); } else if (age >= 18 ) { console .log ('adult' ); } else { console .log ('kid' ); }
if {} else if {} else {}
注意{}
不要省略 注意判断的顺序: js中,如果某个条件成立,后续就不会再判断,上面的例子中age = 20
,它满足了第一个条件age >= 6
,就不会再往下判断,所以结果就是teenager
. null
,undefined
,0
,NaN
和空字符串''
,都是false
;其他都是true
循环 for 1 2 3 4 5 6 for x = 0 ;let i;for (i=1 ; i<10000 ; i++) { x = x + i; } x;
i=1;
是初始条件,记得先声明i<10000;
这是判断条件,满足时就继续循环,不满足就结束i++
是递增条件1 2 3 4 5 6 7 let arr = ['Apple' , 'Google' , 'Microsoft' ]let i, x;for (i=0 ; i<arr.length ; i++){ x = arr[i]; console .log (x); }
for
的3个循环条件都是可以省略的,如果没有结束条件,就要手动加break
来跳出循环,否则就是一个死循环
1 2 3 4 5 6 7 let x = 0 ;for (;;) { if (x > 100 ) { break ; } x ++; }
for…in 一般用来把一个对象的所有属性一次循环出来
1 2 3 4 5 6 7 8 let o = { name : 'Jack' ; age : '20' , city : 'Beijing' };for (let key in o) { console .log (key); }
Array
也是对象,它每个元素的索引将被视为对象的属性
1 2 3 4 5 let a = ['A' , 'B' , 'C' ]for (let i in a) { console .log (i); console .log (a[i]); }
while 1 2 3 4 5 6 7 let x = 0 ;let n = 99 ;while (n > 0 ) { x = x + n; n = n - 2 ; } x;
do…while 与while
的区别是,不是在每次循环前判断条件,而是在每次循环后再判断
1 2 3 4 5 let n = 0 ;do { n = n + 1 ; } while (n < 100 ); n;
需要注意的是使用do..while
的话,循环体至少会执行一次
Map和Set JS的对象表现形式很类似其他语言的Map
或Dict
,但是JS的对象的键必须是字符串.然而实际上键是其他数据类型,比如Number,也是很合理的.所以ES6后引入了Map
1 2 3 let m = new Map ();let s = new Set ();
Map 是一个键值对结果,具有极快的查找速度,不论这个表多大,查找速度都不会变慢.
1 2 3 4 5 6 7 8 9 let m1 = new Map ([['Michael' , 95 ], ['Bob' , 75 ], ['Tracy' , 85 ]]) let m2 = new Map () m2.set ('Adam' , 67 ); m2.set ('Bob' , 59 ); m2.has ('Adam' ); m2.get ('Adam' ); m2.delete ('Adam' ); m2.get ('Adam' );
一个key只能对应一个value,多次对同一个key放入不同value,后面的value会把前面的value冲掉
Set 是一组key的集合
,且不能重复.
1 2 3 4 5 6 7 8 9 let s = new Set ([1 , 2 , 3 ]); let s = new Set (); let s = new Set ([1 , 2 , 3 , 3 , '3' ]); s; s.add (4 ); s; s.delete (3 ); s;
iterable 遍历Array
可以使用下标循环,但是遍历Map
和Set
就没有下标.为了统一集合类型,ES6引入新的iterable
类型,Array
,Map
和Set
都属于这个类型.
iterable
类型的集合可以通过新的for...of
循环来遍历.(也是ES6引入的语法)
1 2 3 4 5 6 let a = ['A' , 'B' , 'C' ];let s = new Set (['A' , 'B' , 'C' ]);let m = new Map ([[1 , 'x' ], [2 , 'y' ], [3 , 'z' ]]);for (let x of a) { console .log (x); }
for...in
有一个历史问题,它遍历的实际上是对象的属性名称.一个Array
也是一个对象,它每个元素的索引都被视为一个属性.当我们手动给Array
对象添加额外的属性后,for...in
会带来意想不到的效果
1 2 3 4 5 let a = ['A' , 'B' , 'C' ]; a.name = 'Hello' ;for (let x in a) { console .log (x); }
for...of
则修复了这个问题,它只循环集合元素本身
.
1 2 3 4 5 let a = ['A' , 'B' , 'C' ]; a.name = 'Hello' ;for (let x of a) { console .log (x); }
然而,更好的方式是使用ES5.1引入的iterable
内置的forEach
方法,它接收一个函数,每次迭代就自动回调该函数.
1 2 3 4 5 6 7 let a = ['A' , 'B' , 'C' ]; a.forEach (function (element, index, array ) { console .log (`${element} , index = ${index} ` ); });
Set
没有索引,所以回调函数的前两个参数都是元素本身
1 2 3 4 let s = new Set (['A' , 'B' , 'C' ]); s.forEach (function (element, sameElement, set ){ console .log (element); });
Map
回调函数参数依次为: value
, key
和 map
1 2 3 4 let m = new Map ([[1 , 'x' ], [2 , 'y' ], [3 , 'z' ]]); m.forEach (function (value, key, map ){ console .log (value) })
JS并不要求回调函数的参数完全一致,因此可以省略不感兴趣的参数.
1 2 3 4 let a = ['A' , 'B' , 'C' ]; a.forEach (function (element ){ console .log (element); })