Javascript之旅(一)
一、基础知识
- 基本语法
- 变量
- 数据类型
- 字符串
- 数组
- 对象
- 条件判断
- 循环
- Map和Set
- iterable
为什么要学习JavaScript
JavaScript 是web 开发人员必须学习的 3 门语言中的一门:
- HTML 定义了网页的内容(人体)
- CSS 描述了网页的布局(衣服)
- JavaScript 网页的行为(动作)
一个合格的开发人员应该精通JavaScript,不要被Web时代所淘汰。
什么是JavaScript?
JavaScript是世界上最流行的脚本语言。
一种运行在浏览器中的解释型的动态的编程语言,不会被预编译。
在Web世界里,只有JavaScript能跨平台、跨浏览器驱动网页,与用户交互。
JavaScript的历史
网景公司让编程大牛Brendan Eich在2周内设计的,它实际只花了10天。
和JAVA语言没任何关系,但是初期想蹭点光。
ECMA组织制定了ECMAScript标准(简称ES)。
JavaScript是网景公司对ECMAScript标准的一种实现。
ES不断发展,JavaScript随之不断提升版本。
JavaScript的组成
完整的JavaScript实际上由下面三部分组成。
- ECMAScript,描述了该语言的语法和基本对象。
- DOM,文档对象模型,描述处理网页内容的方法和接口。
- BOM,浏览器对象模型,描述与浏览器进行交互的方法和接口。
JavaScript的使用位置
- 放在
<script></script>
标签内 - 放在.js文件内,然后通过
<script src="..."></script>
引用
具体到html文件中,又可分为:
- 放在head标签内
- 放在body标签的最底部
推荐使用方法:
- 以.js文件的方式保存代码
- 在body标签的最底部引用代码
解释:把JavaScript代码放入一个单独的.js文件中更利于维护和重用代码。可以在同一个页面中引入多个.js文件,可以在页面中多次编写,浏览器按照顺序依次执行。将JavaScript代码引用放在body标签的最底部可以防止js代码执行有错或耗时过长的时候html主体页面能够正常显示。
新手一定要注意:.js文件中不需要<script></script>
标签。
运行和调试
请使用前端利器Google Chrome浏览器。
JavaScript 没有任何打印或者输出的函数。
JavaScript 可以通过不同的方式来输出数据:
- 使用 window.alert() 弹出警告框。
- 使用 document.write() 方法将内容写到 HTML 文档中。
- 使用 innerHTML 写入到 HTML 元素。
- 使用 console.log() 写入到浏览器的控制台。
进入Chrome浏览器控制台(Console),直接输入JavaScript代码,按回车后执行。要查看一个变量的内容输入console.log(a);,回车后显示。
请使用 document.write()仅仅向文档输出写内容。如果在文档已完成加载后再执行 document.write,则整个 HTML 页面将被覆盖。
一、基础知识
1. 基本语法
- 严格区分大小写
- 每个语句以分号结束,语句块用{...}。
- 分号和缩进不是强制的,但是请把它当做强制的。
- 代码块可以嵌套,但是请控制层级数。
- 单行注释用双斜杠//
- 多行注释用/* .... */
- 使用 Unicode 字符集。
- 常见的是驼峰命名规则
JavaScript 关键字
关键字 | 关键字 | 关键字 | 关键字 |
---|---|---|---|
abstract | else | instanceof | super |
boolean | enum | int | switch |
break | export | interface | synchronized |
byte | extends | let | this |
case | false | long | throw |
catch | final | native | throws |
char | finally | new | transient |
class | float | null | true |
const | for | package | try |
continue | function | private | typeof |
debugger | goto | protected | var |
default | if | public | void |
delete | implements | return | volatile |
do | import | short | while |
double | in | static | with |
2. 变量
- 变量名是大小写英文、数字、$和_的组合,且不能用数字开头。
- 申明一个变量用var关键字,该变量默认为局部变量。
- 如果第一次申明变量是未添加var关键字,则为全局变量。
- strict模式:强制使用var申明变量,未使用将导致运行错误。使用方法:js代码的第一行添加
'use strict';
constructor 属性
constructor 属性返回所有 JavaScript 变量的构造函数。
"John".constructor // 返回函数 String() { [native code] }
(3.14).constructor // 返回函数 Number() { [native code] }
false.constructor // 返回函数 Boolean() { [native code] }
[1,2,3,4].constructor // 返回函数 Array() { [native code] }
{name:'John', age:34}.constructor // 返回函数 Object() { [native code] }
new Date().constructor // 返回函数 Date() { [native code] }
function () {}.constructor // 返回函数 Function(){ [native code] }
可以使用 constructor 属性来查看是对象是否为数组 (包含字符串 "Array"):
function isArray(myArray) {
return myArray.constructor.toString().indexOf("Array") > -1;}
同样,可以使用 constructor 属性来查看是对象是否为日期 (包含字符串 "Date"):
function isDate(myDate) {
return myDate.constructor.toString().indexOf("Date") > -1;}
3. 数据类型
在 JavaScript 中有 5 种不同的数据类型:
- string
- number
- boolean
- object
- function
3 种对象类型:
- Object
- Date
- Array
2 个不包含任何值的数据类型:
- null
- undefined
查看数据类型使用typeof,请一定注意其中的几个特殊情况!
typeof "John" // 返回 string
typeof 3.14 // 返回 number
typeof false // 返回 boolean
typeof [1,2,3,4] // 返回 object
typeof {name:'John', age:34} // 返回 object
typeof new Date() // 返回 object
typeof function () {} // 返回 function
typeof myCar // 返回 undefined (如果 myCar 没有声明)
typeof null // 返回 object
typeof undefined // undefined
typeof null // object
null === undefined // false
null == undefined // true
3.1 数字number
- JavaScript不区分整数和浮点数,统一用Number表示。
- NaN表示Not a Number,当无法计算结果时用NaN表示。
- Infinity表示无限大。
- Number可以直接做四则运算,规则和数学一致。
3.2 布尔值
- 只有true、false两种值
- &&与
- ||或
- !非
关于比较运算
- 第一种是双等号==比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;
- 第二种是三等号===比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。
- 不要使用,坚持使用=
- 同理,不要使用!=,坚持使用!==
- NaN这个特殊的Number与所有其他值都不相等,包括它自己。
NaN === NaN; // false
- 唯一能判断NaN的方法是通过isNaN()函数。
isNaN(NaN); // true
- Infinity可使用 isFinite(num) 来判断
- 浮点数在运算过程中会产生误差
1 / 3 === (1 - 2 / 3); // false
,要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值。Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true
三目运算
result = (condition)?value1:value2
3.3 null和undefined
- null表示一个“空”的值,它和0以及空字符串''不同,0是一个数值,''表示长度为0的字符串,而null表示“空”。Python用None表示。
- undefined表示未定义,仅仅在判断函数参数是否传递的情况下有用。
3.4 字符串
字符串是以单引号'或双引号"括起来的任意文本,比如'abc',"xyz"等等。
3.5 数组
数组是一组按顺序排列的集合,集合的每个值称为元素。数组可以包括任意数据类型。python中称为列表。
- 数组用[]表示,元素之间用,分隔。
- 可以通过new Array()创建数组,但建议直接使用[]。
- 通过索引访问元素,起始值为0,用法alist[1]。
3.6 对象
JavaScript的对象是一组由键-值组成的无序集合,类似python中的字典。
要获取一个对象的属性,使用对象变量.属性名或对象变量['属性名']的方式。
3.7 数据类型的转换
注意大小写,这个真的很烦人。
- String() 转换为字符串
- Number() 将字符串转换为数字
- number.tostring() 例如:(123).toString()
- toExponential() 把对象的值转换为指数计数法。
- toFixed() 把数字转换为字符串,结果的小数点后有指定位数的数字。
- toPrecision() 把数字格式化为指定的长度。
- parseInt(..) 将某值转换成数字,不成功则NaN
- parseFloat(..) 将某值转换成浮点数,不成功则NaN
4. 字符串
- 字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,但也没有任何效果:
- 使用单引号包围双引号或反之,或用转义符\。
- \n表示换行,\t表示制表符,
\\
表示\,\x##形式为十六进制,\u##表示一个Unicode字符。 - 最新的ES6标准新增了多行字符串表示方法,用两个反引号将多行引起来。
- 拼接字符串使用+号
- 格式化字符串的方式:注意要用反引号,不能是单引号或双引号
var name = '小明';
var age = 20;
alert(`你好, ${name}, 你今年${age}岁了!`);
JavaScript为字符串提供了一些常用方法,调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串:
obj.length 长度
obj.trim() 移除空白
obj.trimLeft()
obj.trimRight()
obj.charAt(n) 返回字符串中的第n个字符
obj.concat(value, ...) 拼接
obj.indexOf(substring,start) 子序列位置
obj.lastIndexOf(substring,start) 子序列位置
obj.substring(from, to) 根据索引获取子序列
obj.slice(start, end) 切片
obj.toLowerCase() 大写
obj.toUpperCase() 小写
obj.split(delimiter, limit) 分割
obj.search(regexp) 从头开始匹配,返回匹配成功的第一个位置(g无效)
obj.match(regexp) 全局搜索,如果正则中有g表示找到全部,否则只找到第一个。
obj.replace(regexp, replacement) 替换,正则中有g则替换所有,否则只替换第一个匹配项,
$数字:匹配的第n个组内容;
$&:当前匹配的内容;
$`:位于匹配子串左侧的文本;
$':位于匹配子串右侧的文本
$$:直接量$符号
5. 数组
- 定义数组元素,最后不能添加逗号
- 直接给Array的length赋一个新的值会导致Array大小的变化。
- 通过索引赋值时,如果索引超过了范围,同样会引起Array大小的变化,这点和python不一样,python会弹出异常。
- js对数组的索引没有较强的保护,容易出现问题,请一定注意。
下面是数组的常用方法:
obj.length 数组的大小
obj.push(ele) 尾部追加元素
obj.pop() 尾部获取一个元素
obj.unshift(ele) 头部插入元素
obj.shift() 头部移除元素
obj.splice(start, deleteCount, value, ...) 插入、删除或替换数组的元素
obj.splice(n,0,val) 指定位置插入元素
obj.splice(n,1,val) 指定位置替换元素
obj.splice(n,1) 指定位置删除元素
obj.slice( ) 切片
obj.reverse( ) 反转
obj.join(sep) 将数组元素连接起来以构建一个字符串
obj.concat(val,..) 连接数组
obj.sort( ) 对数组元素进行排序
下面是一些例子,请注意其中的注释:
var arr = [10, 20, '30', 'xyz'];
arr.indexOf(30); // 元素30没有找到,返回-1
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
arr.slice(0, 3); // 从索引0开始,到索引3结束,但不包括索引
arr.slice(3); // 从索引3开始到结束
var aCopy = arr.slice();
aCopy; // ['A', 'B', 'C', 'D', 'E', 'F', 'G']
aCopy === arr; // false
var arr = [1, 2];
arr.push('A', 'B'); // 返回Array新的长度: 4
arr; // [1, 2, 'A', 'B']
arr.pop(); // pop()返回'B'
arr; // [1, 2, 'A']
arr.pop(); arr.pop(); arr.pop(); // 连续pop 3次
arr; // []
arr.pop(); // 空数组继续pop不会报错,而是返回undefined
arr; // []
var arr = [1, 2];
arr.unshift('A', 'B'); // 返回Array新的长度: 4
arr; // ['A', 'B', 1, 2]
arr.shift(); // 'A'
arr; // ['B', 1, 2]
arr.shift(); arr.shift(); arr.shift(); // 连续shift 3次
arr; // []
arr.shift(); // 空数组继续shift不会报错,而是返回undefined
arr; // []
//splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素.
ar arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
// 从索引2开始删除3个元素,然后再添加两个元素:
arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
// 只删除,不添加:
arr.splice(2, 2); // ['Google', 'Facebook']
arr; // ['Microsoft', 'Apple', 'Oracle']
// 只添加,不删除:
arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
//concat()方法并没有修改当前Array,而是返回了一个新的Array。接收任意个元素和Array,并且自动把Array拆开,然后全部添加到新的Array里.
var arr = ['A', 'B', 'C'];
arr.concat(1, 2, [3, 4]); // ['A', 'B', 'C', 1, 2, 3, 4]
6. 对象
- 定义对象,最后不能添加逗号
- 对象是一种无序的集合数据类型,它由若干键值对组成。
- 访问属性是通过.操作符完成的,但这要求属性名必须是一个有效的变量名。如果属性名包含特殊字符,就必须用''括起来,并以方括号访问。
- 访问不存在的属性不报错,而是返回undefined
- 可以*地随时给一个对象添加或删除属性,就像python一样
- 要检测对象是否拥有某一属性,可以用in操作符。但这个属性有可能是对象继承得来的。例如”tostring“就是所有对象的属性,因为toString定义在object对象中,而所有对象最终都会在原型链上指向object。
- 要判断一个属性是否是xiaoming自身拥有的,而不是继承得到的,可以用hasOwnProperty()方法。
下面是一些例子:
var xiaoming = {
name: '小明'
};
xiaoming.age; // undefined
xiaoming.age = 18; // 新增一个age属性
xiaoming.age; // 18
delete xiaoming.age; // 删除age属性
xiaoming.age; // undefined
delete xiaoming['name']; // 删除name属性
xiaoming.name; // undefined
delete xiaoming.school; // 删除一个不存在的school属性也不会报错
'name' in xiaoming; // true
'grade' in xiaoming; // false
'toString' in xiaoming; // true
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('toString'); // false
7. 条件判断
- if语句:if () { ... } else { ... }
- 多行if语句:if () { ... }else if(){ ... } else { ... }
- JavaScript把null、undefined、0、NaN和空字符串''视为false,其他值一概视为true。
请永远不要省略大括号!得不偿失!
switch语句:
默认是采用三等号的模式进行判断。===
switch(name){
case '1':
age = 123;
break;
case '2':
age = 456;
break;
default :
age = 777;
}
8. 循环
JavaScript中有四种循环语句,分别是:
- for(i;条件;i++){...}
var names = ["alex", "tony", "rain"];
for(var i=0;i<names.length;i++){
console.log(i);
console.log(names[i]);
}
- for(item in 迭代器){ ... }
注意在数组中循环的是下标字符串而不是元素,这一点和python不一样。
在对象中循环的是属性。
var names = ["alex", "tony", "rain"];
for(var index in names){
console.log(index);
console.log(names[index]);
}
- while(条件){ ... }
while(条件){
// break;
// continue;
}
- do{...}while(条件)
它和while循环的唯一区别在于,不是在每次循环开始的时候判断条件,而是在每次循环完成的时候判断条件,因此,该循环体会至少执行1次。
var n = 0;
do {
n = n + 1;
} while (n < 100);
n; // 100
任意跳转:label
对于循环的控制,通常有break和continue,这为大家所熟知。但是与python不同的是js中还有label标签,可以设置跳转的点。
用法: label_name:
具体看例子:
未添加label时:
var num = 0;
for (var i = 0 ; i < 10 ; i++){
for (var j = 0 ; j < 10 ; j++){
if( i == 5 && j == 5 ){
break;
}
num++;
}
}
alert(num); // 循环在 i 为5,j 为5的时候跳出 j循环,但会继续执行 i 循环,输出 95
添加 Label 后:
var num = 0;
outPoint:
for (var i = 0 ; i < 10 ; i++){
for (var j = 0 ; j < 10 ; j++){
if( i == 5 && j == 5 ){
break outPoint;
}
num++;
}
}
alert(num); // 循环在 i 为5,j 为5的时候跳出双循环,返回到outPoint层继续执行,输出 55
9. Map和Set
JavaScript的默认对象表示方式{}可以视为其他语言中的Map或Dictionary的数据结构,即一组键值对。
但是JavaScript的对象有个小问题,就是键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。
为了解决这个问题,最新的ES6规范引入了新的数据类型Map。
Map是一组键值对的结构,具有极快的查找速度。
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
初始化Map需要一个二维数组,或者直接初始化一个空Map。Map具有以下方法:
var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:
var m = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88
Set和Map类似,也是一组key的集合,但不存储value。同样的,在Set中不能重复key。
要创建一个Set,需要一个Array作为输入,或者直接创建一个空Set:
var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3
重复元素在Set中自动被过滤;
通过add(key)方法可以添加元素到Set中,可以重复添加,但无效。
通过delete(key)方法可以删除元素:
10. iterable
ES6标准引入了新的iterable类型,Array、Map和Set都属于iterable类型。具有iterable类型的集合可以通过新的for ... of循环来遍历。
var a = ['A', 'B', 'C'];
var s = new Set(['A', 'B', 'C']);
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
for (var x of a) { // 遍历Array
alert(x);
}
for (var x of s) { // 遍历Set
alert(x);
}
for (var x of m) { // 遍历Map
alert(x[0] + '=' + x[1]);
}
为什么要有for...of
for ... in循环由于历史遗留问题,它遍历的实际上是对象的属性名称。for ... of循环则完全修复了这些问题,它只循环集合本身的元素。
然而,更好的方式是直接使用iterable内置的forEach方法,它接收一个函数,每次迭代就自动回调该函数。注意,forEach方法是ES5.1标准引入的,需要测试你的浏览器是否支持。
对于数组:
var a = ['A', 'B', 'C'];
a.forEach(function (element, index, array) {
// element: 指向当前元素的值
// index: 指向当前索引
// array: 指向Array对象本身
alert(element);
});
对于set:
与Array类似,但Set没有索引,因此回调函数的前两个参数都是元素本身:
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {
alert(element);
});
对于map:
Map的回调函数参数依次为value、key和map本身:
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
alert(value);
});