写在前面:计划先跟着廖雪峰的JavaScript教程学习,对Js有个大致的了解后再看看JavaScript的官方教程。
一、快速入门
1.“由于浏览器的安全限制,以file://
开头的地址无法执行如联网等JavaScript代码。。。”,最终还是要搭建Web服务器(如Tomcat等)来正常执行JavaScript代码。
2.在Firefox浏览器上尝试执行js代码,可以在页面上右键选择查看元素然后选择控制台,或者浏览器右上角选择Web开发者——>切换工具箱——>选择控制台,在该面板直接输入js代码并运行如下:
高级调试技巧参考
3.练习:打开新浪首页,并且打开Web控制台,输入console.log('Hello');
回车执行该代码得到的结果是Hello。
4.“JavaScript引擎自动加分号在某些情况下会改变程序的语义”,最好养成在每个语句最后加上符号;的习惯;并且缩进一般也是4个空格;“过多的嵌套会增加代码的复杂程度,需要将部分代码抽出来作为函数调用”。
5.字符串是以单引号’或双引号"括起来的任意文本,这和python的规定一致。
6.(1)注意:“JavaScript允许对任意数据类型做比较”,有两种比较符(设计缺陷),和=,前者会自动进行数据类型转换(false0;//成立),后者会先判断数据类型是否一致,然后比较具体的值(false=0; // 不成立),所以“不要使用==
比较,始终坚持使用===
比较”;
(2)除此之外,不能用NaN===NaN;判断一个数是否是NaN,只能通过函数isNaN()判断是否是NaN。
(3)“要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值”:
Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true
1 / 3 === (1 - 2 / 3); // false
7.undefined表示值未定义,用于判断函数参数是否传递,用索引访问数组越界时返回undefined。
8.js的数组可以包含任意数据类型,有两种创建数组的方式,“考虑代码可读性,最好用第一种”:
(1)直接用[]包含,例:[1, 5.7, true, null]
(2)通过Array数组实现,例:new Array(1, 3, 5);
9.“JavaScript的对象是一组由键-值组成的无序集合”,在表达形式上类似于Python的字典类型(但其实读到后面具体介绍对象的部分,js的对象更适合与Python的对象类比,以免陷入混乱),但js的键(属性)默认都是字符串类型,而且用对象变量.属性名来获取一个对象的属性(类似于Java中获取静态变量)。
10.js中“变量名是大小写英文、数字、$和_的组合,且不能用数字开头,变量名也不能是JavaScript的关键字,如if、while等,变量名最好不用中文”,用var语句声明变量,例:var $a = 5;注意:“同一个变量可以反复赋值,而且可以是不同类型的变量,但是要注意只能用var声明一次”。js有三种声明变量的方式,参考学习博客。
11.js和Python一样都是动态语言——“变量本身类型不固定”,java是静态语言——“在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错”。
12.未使用var声明的变量是全局变量,“在strict模式下运行的JavaScript代码,强制通过var声明变量,未使用var声明变量就使用的,将导致运行错误”,在js代码的第一行写:‘use strict’;启动strict模式。
13.看到strict模式时尝试运行在strict模式下不能定义全局变量的示例代码,结果没有报错,切换到控制台也不报错,初步判断可能是Firfox浏览器版本问题,但检查后我的浏览器是最新版的,换成Google浏览器(最新版)试了一下也没有报错,黑人问号???后来看了一下课程下面的评论,有人说是“在控制台上运行的每一个语句都是一个block,而在一个单独的block中,‘use strict’才有效”,感谢老兄,解决了,之前我都是逐个语句输入到控制台运行的(菜鸡一只),把示例代码一起输入控制台成功报错——引用错误ReferenceError(在作用域中找不到):
14.字符串部分注意转义字符的用法(和java格式字符串等类似),”如字符串中同时有符号’和符号"时,可用转义字符处理,如:‘I’ m “OK”!’’;“,用’\x##‘的形式表示ASCII字符,如’\x41’等同于’A’;用’\u####‘的形式表示Unicode字符,如’\u4e2d\u6587’等于’中文’。
15.多行字符串有两种表达方式,用\n或反引号的形式,示例如下:
'这是一个\n多行\n字符串';
`这是一个
多行
字符串`;
16.有两种方法连接多个字符串,用符号+或ES6模板字符串(反引号和${变量}组合),示例如下:
var name = '小明';
var age = 20;
var message = '你好, ' + name + ', 你今年' + age + '岁了!';
alert(message);
var name = '小明';
var age = 20;
var message = `你好, ${name}, 你今年${age}岁了!`;
alert(message);
17.注意:“字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,但也没有任何效果”,示例如下:
var s = 'Test';
s[0] = 'X';
alert(s); // s仍然为'Test'
18.js中字符串的一些常用方法和java中的一样,调用教程列举的方法不会改变字符串本身,而是返回新的字符串。获取字符串的长度,如:
var a = "Hello";
a.length; // 5
方法substring的两个参数可以类比左闭右开区间。
19.获取数组的长度可以访问属性length,“直接给数组的length属性赋一个新的值会导致数组大小的变化:”
var arr = [1, 2, 3];
arr.length; // 3
arr.length = 6;
arr; // arr变为[1, 2, 3, undefined, undefined, undefined]
arr.length = 2;
arr; // arr变为[1, 2]
20,“对数组的索引进行赋值会直接修改这个数组,但如果索引超过了范围,同样会引起数组大小的变化:”
var arr = [1, 2, 3];
arr[5] = 'x';
arr; // arr变为[1, 2, 3, undefined, undefined, 'x']
注意:“不建议直接修改数组的大小,访问索引时要确保索引不会越界”。
21.类似于字符串,数组可以通过调用方法indexOf来搜索一个指定的元素的位置。
22.“数组的方法slice对应字符串的方法substring,它截取数组的部分元素,然后返回一个新的数组,如果方法slice没有参数,就是复制该数组,但复制的数组和被复制的数组不相等(=),因为不是同一个对象”,数组之间不能用符号=判断是否相等(待学习)。
23.方法push或unshift向数组的末尾或头部添加若干元素,同时返回数组的长度,方法pop或shift则把数组的最后或头部一个元素删除掉,空数组调用pop或shift方法不会报错,而是返回undefined。
24.方法splice可以从数组指定的索引开始删除若干元素(可选项),然后再从该位置添加若干元素(可选项),同时返回删除的元素(数组形式),示例如下:
var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
// 从索引2开始删除3个元素,然后再添加两个元素:
arr.splice(2, 3, 'Google', 'Facebook'); // 返回Array(3)['Yahoo', 'AOL', 'Excite']
arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
25.“方法concat将当前数组和另一个数组连接起来,并返回一个新的数组,不会修改当前数组,该方法可以接收任意个元素和数组,并且自动把数组拆开,然后全部添加到新的数组里”,示例代码:
var arr = ['A', 'B', 'C'];
arr.concat(1, 2, [3, 4]); // ['A', 'B', 'C', 1, 2, 3, 4]
26.“join方法把当前数组的每个元素都用指定的字符串连接起来,然后返回连接后的字符串,如果数组的元素不是字符串,将自动转换为字符串后再连接”,示例代码:
var arr = ['A', 'B', 'C', 1, 2, 3];
arr.join('-'); // 'A-B-C-1-2-3'
27.练习1,如何通过索引取到500这个值:
'use strict';
var arr = [[1, 2, 3], [400, 500, 600], '-'];
var x = arr[1][1];
console.log(x); // x为500
28.练习2,在新生欢迎会上,你已经拿到了新同学的名单,请排序后显示:欢迎XXX,XXX,XXX和XXX同学!:
'use strict';
var arr = ['小明', '小红', '大军', '阿黄'];
arr.sort();
console.log(`欢迎${arr[0]},${arr[1]},${arr[2]}和${arr[3]}同学!`);
console.log(`欢迎${arr.slice(0,3).join(',')}和${arr[3]}同学!`);
var last = arr.pop();
console.log(`欢迎${arr.join(',')}和${last}同学!`);
29.js的对象“最后一个键值对不需要在末尾加,
,如果加了,有的浏览器(如低版本的IE)将报错”。
30.“如果对象的属性名包含特殊字符(不是有效的属性名,包含除了大小写字母、数字、符号$
或-
的其他字符?(待解决)),就必须用''
括起来,访问这个属性也无法使用.
操作符,必须用['xxx']
来访问;此外可以用.
操作符或['×××']
来访问有效属性名”,示例代码:
var xiaohong = {
name: '小红',
'middle-school': 'No.1 Middle School'
};
xiaohong['middle-school']; // 'No.1 Middle School'
xiaohong['name']; // '小红'
xiaohong.name; // '小红'
31.js的对象访问不存在的属性不报错,而是返回undefined;因为js的对象是动态的,所以可以随意给一个对象添加或删除属性(执行delete语句,delete 对象名.属性名),删除不存在的属性也不会报错(返回的都是true)。
32.用in
判断属性是否在对象中,用方法hasOwnProperty判断属性是否是对象自己的而不是继承得到的,示例代码:
var xiaoming = {
name: '小明',
birth: 1990,
school: 'No.1 Middle School',
height: 1.70,
weight: 65,
score: null
};
'name' in xiaoming; // true
'grade' in xiaoming; // false
var xiaoming = {
name: '小明'
};
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('toString'); // false
33.因为对js的对象讲解部分存在一点疑惑,翻评论的时候看到通过for循环正确访问对象的所有属性的方法,示例代码如下,for循环取出的prop都是字符串类型的,所以只能用对象名[prop]的方式访问属性值,而不是对象名.prop的形式(这样得到的结果是undefined):
var xiaoming = {
name: '小明',
birth: 1990,
school: 'No.1 Middle School',
height: 1.70,
weight: 65,
score: null
};
for(var prop in xiaoming){
console.log(`${prop}:${xiaoming[prop]}`);
}
//结果:
name:小明
birth:1990
school:No.1 Middle School
height:1.7
weight:65
score:null
34.对象部分小结:js中对象的所有属性都是字符串,只是创建对象时没有加引号,所以访问属性值时存在对象名[‘属性名’]的表达形式,以及判断属性是否存在于对象中时也是以'属性名' in 对象名
的形式进行判断,hasOwnProperty方法的参数也应该是'属性名'
的形式。
35.注意:“在多个if...else...
语句中,如果某个条件成立,则后续就不再继续判断了,此外,js把if条件判断语句的结果是null
、undefined
、0
、NaN
和空字符串''
的视为false
,其他值一概视为true
”。
36.条件判断部分练习代码:
'use strict';
var height = parseFloat(prompt('请输入身高(m):'));
var weight = parseFloat(prompt('请输入体重(kg):'));
var bmi = weight / (height * height);
if (bmi <= 18.5){
alert('过轻');
}else if (bmi < 25.0){
alert('正常');
}else if (bmi < 28.0){
alert('过重');
}else if (bmi < 32.0){
alert('肥胖');
}else{
alert('过于肥胖');
}
37.循环部分练习,利用for
循环计算1 * 2 * 3 * ... * 10
的结果:
'use strict';
var x = 1;
var i;
for (i = 2; i <= 10; i++) {
x = x * i;
}
if (x === 3628800) {
console.log('1 x 2 x 3 x ... x 10 = ' + x);
}
else {
console.log('计算错误');
}
38.“由于数组也是对象,而它的每个元素的索引(0,1,2)被视为对象的属性(’0‘,’1‘,’2‘),因此,for ... in
循环可以直接循环出数组的索引,示例代码:”
var a = ['A', 'B', 'C'];
for (var i in a) {
console.log(i); // '0', '1', '2'
console.log(a[i]); // 'A', 'B', 'C'
}
注意:“for ... in
对数组的循环得到的是字符串而不是数字”。
39.练习利用循环遍历数组中的每个名字,并显示Hello, xxx!
:
'use strict';
var arr = ['Bart', 'Lisa', 'Adam'];
console.log('正序遍历:');
for (var i in arr) {
console.log(`Hello,${arr[i]}!`);
}
console.log('\n');
for (var i = 0; i < arr.length; ++i) {
console.log(`Hello,${arr[i]}!`);
}
console.log('\n');
var w = 0;
while(w < arr.length) {
console.log(`Hello,${arr[w]}!`);
++w;
}
console.log('\n反序遍历:');
for (var i = arr.length - 1; i >= 0; --i) {
console.log(`Hello,${arr[i]}!`);
}
console.log('\n');
var w = arr.length - 1;
while(w >= 0) {
console.log(`Hello,${arr[w]}!`);
--w;
}
40.初始化Map有两种方法:用一个二维数组初始化;或者直接初始化一个空Map,然后调用set方法添加键值对,get方法得到对应键的值,has方法判断对应键是否存在,delete方法删除对应键值对,示例代码如下:
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
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
41.Set只存储键,并且没有重复的键,重复元素在自动被过滤,方法add将元素添加到Set中,方法delete将元素从Set中删除。注意:“Map和Set是ES6标准新增的数据类型,根据浏览器的支持情况决定是否要使用”。
42.“Array
、Map
和Set
都属于iterable
类型,具有iterable
类型的集合可以通过for ... of
循环来遍历”,对数组(也是对象)a而言,for (var x in a)
循环得到的是数组的索引(’0‘,’1‘…),而for (var x of a)
循环得到的是数组的元素值,如果意外给数组a增加了属性,如a.name = 'erin';
,前者在循环时会得到’name’,后者则仍然得到的是数组本身的元素,没有’erin’;对Map类型的m而言for (var x of m)
循环得到的x是一个由键值对组成的数组。
43.对于iterable
类型内置的forEach函数,它接受一个回调函数,如果不需要使用回调函数中的某些参数则可以省略,相关代码如下,但经过试验,回调函数的参数对应的内容顺序是不变的,如将Map的value和key参数交换顺序,交换后的key对应的是键值对中的值而不是键,value对应的是键。
// 数组调用forEach方法
var a = ['A', 'B', 'C'];
// element是当前元素的值,index是当前索引,array是数组对象本身
a.forEach(function(element, index, array){
console.log(element + ' index = ' + index);
});
// 集合Set调用forEach方法
var s = new Set(['A', 'B', 'C']);
// element和sameElement都是元素本身,set是集合对象本身
s.forEach(function (element, sameElement, set) {
console.log(element);
});
// 映射Map调用forEach方法
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
// value是键值对中的值,key是键值对中的键,map是映射对象本身
m.forEach(function (value, key, map) {
console.log(value);
});