JavaScript基础语法笔记

概念

ECMAScript == JavaScript

有些时候你会看到<script>标签还设置了一个type属性, 这个属性可以去掉:

<script type="text/javascript">
  ...
</script>

要让浏览器运行JavaScript,必须先有一个HTML页面,在HTML页面中引入JavaScript,然后,让浏览器加载该HTML页面,就可以执行JavaScript代码。

由于浏览器的安全限制,以file://开头的地址无法执行如联网等JavaScript代码.

Number

JavaScript不区分整数和浮点数,统一用Number表示.

NaN; // NaN表示Not a Number,当无法计算结果时用NaN表示
Infinity; // Infinity表示无限大
2 / 0; // Infinity
0 / 0; // NaN

比较运算符

JavaScript在设计时,有两种比较运算符: 第一种是==比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果; 第二种是===比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。 由于JavaScript这个设计缺陷,不要使用==比较,始终坚持使用===比较

另一个例外是NaN这个特殊的Number与所有其他值都不相等,包括它自己:

NaN === NaN; // false

唯一能判断NaN的方法是通过isNaN()函数:

isNaN(NaN); // true

null和undefined

null表示一个“空”的值,它和0以及空字符串”不同,0是一个数值,”表示长度为0的字符串,而null表示“空”. JavaScript的设计者希望用null表示一个空的值,而undefined表示值未定义。事实证明,这并没有什么卵用,区分两者的意义不大。大多数情况下,我们都应该用null。undefined仅仅在判断函数参数是否传递的情况下有用。

变量名

变量名是大小写英文、数字、$_的组合(变量名也可以用中文),且不能用数字开头。变量名也不能是JavaScript的关键字,如if、while等。申明一个变量用var语句, 只能用var声明一次.

var a; // 申明了变量a,此时a的值为undefined
var $b = 1; // 申明了变量$b,同时给$b赋值,此时$b的值为1

这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言.

打印变量: 使用console.log()代替alert()的好处是可以避免弹出烦人的对话框。

strict模式

如果一个变量没有通过var申明就被使用,那么该变量就自动被申明为全局变量.

使用var申明的变量则不是全局变量,它的范围被限制在该变量被申明的函数体内.

在strict模式下运行的JavaScript代码,强制通过var申明变量,未使用var申明变量就使用的,将导致运行错误。

启用strict模式的方法是在JavaScript代码的第一行写上:

'use strict';

这是一个字符串,不支持strict模式的浏览器会把它当做一个字符串语句执行,支持strict模式的浏览器将开启strict模式运行JavaScript。

字符串

`这是一个
多行
字符串`;

// +号连接多个字符串
var name = '小明';
var age = 20;
var message = '你好, ' + name + ', 你今年' + age + '岁了!';

// 模版字符串用法
var name = '小明';
var age = 20;
var message = `你好, ${name}, 你今年${age}岁了!`;

var s = 'Hello, world!';
s.length; // 13

var s = 'Hello, world!';
s[0]; // 'H'
s[6]; // ' '
s[7]; // 'w'
s[12]; // '!'
s[13]; // undefined 超出范围的索引不会报错,但一律返回undefined

数组

var arr = [1, 2, 3.14, 'Hello', null, true];
arr.length; // 6

// 请注意,如果通过索引赋值时,索引超过了范围,同样会引起Array大小的变化:
var arr = [1, 2, 3];
arr[5] = 'x';
arr; // arr变为[1, 2, 3, undefined, undefined, 'x']

大多数其他编程语言不允许直接改变数组的大小,越界访问索引会报错。然而,JavaScript的Array却不会有任何错误。在编写代码时,不建议直接修改Array的大小,访问索引时要确保索引不会越界。

var arr = [10, 20, '30', 'xyz'];
arr.indexOf(10); // 元素10的索引为0
arr.indexOf(30); // 元素30没有找到,返回-1
arr.indexOf('30'); // 元素'30'的索引为2

var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
arr.slice(0, 3); // 从索引0开始,到索引3结束,但不包括索引3: ['A', 'B', 'C']
arr.slice(3); // 从索引3开始到结束: ['D', 'E', 'F', 'G']

// 复制数组
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
var aCopy = arr.slice();
aCopy; // ['A', 'B', 'C', 'D', 'E', 'F', 'G']

// push和pop
var arr = [1, 2];
arr.push('A', 'B'); // 返回Array新的长度: 4
arr; // [1, 2, 'A', 'B']
arr.pop(); // pop()返回'B'
arr; // [1, 2, 'A']

// unshift和shift
var arr = [1, 2];
arr.unshift('A', 'B'); // 返回Array新的长度: 4
arr; // ['A', 'B', 1, 2]
arr.shift(); // 'A'
arr; // ['B', 1, 2]

// sort
// sort()可以对当前Array进行排序,它会直接修改当前Array的元素位置,直接调用时,按照默认顺序排序:
var arr = ['B', 'C', 'A'];
arr.sort();
arr; // ['A', 'B', 'C']

// reverse
// reverse()把整个Array的元素给掉个个,也就是反转:
var arr = ['one', 'two', 'three'];
arr.reverse();
arr; // ['three', 'two', 'one']

// splice
// splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:
var 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
// concat()方法把当前的Array和另一个Array连接起来,并返回一个新的Array:
var arr = ['A', 'B', 'C'];
var added = arr.concat([1, 2, 3]);
added; // ['A', 'B', 'C', 1, 2, 3]
arr; // ['A', 'B', 'C']

// 请注意,concat()方法并没有修改当前Array,而是返回了一个新的Array。
// 实际上,concat()方法可以接收任意个元素和Array,并且自动把Array拆开,然后全部添加到新的Array里:
var arr = ['A', 'B', 'C'];
arr.concat(1, 2, [3, 4]); // ['A', 'B', 'C', 1, 2, 3, 4]


// join
// join()方法是一个非常实用的方法,它把当前Array的每个元素都用指定的字符串连接起来,然后返回连接后的字符串:
var arr = ['A', 'B', 'C', 1, 2, 3];
arr.join('-'); // 'A-B-C-1-2-3'
// 如果Array的元素不是字符串,将自动转换为字符串后再连接。

对象

var xiaoming = {
    name: '小明',
    birth: 1990,
    school: 'No.1 Middle School',
    // 如果属性名包含特殊字符,就必须用''括起来, 
    // 访问这个属性也无法使用.操作符,必须用['xxx']来访问
    'middle-school': 'No.1 Middle School',
    height: 1.70,
    weight: 65,
    score: null
};

JavaScript用一个{...}表示一个对象,键值对以xxx: xxx形式申明,用,隔开。注意,最后一个键值对不需要在末尾加,, 如果加了,有的浏览器(如低版本的IE)将报错。

JavaScript对象的所有属性都是字符串,不过属性对应的值可以是任意数据类型。

访问不存在的属性不报错,而是返回undefined.

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

// 不过要小心,如果in判断一个属性存在,这个属性不一定是xiaoming的,它可能是xiaoming继承得到的
'toString' in xiaoming; // true

// 因为toString定义在object对象中,而所有对象最终都会在原型链上指向object,所以xiaoming也拥有toString属性。
// 要判断一个属性是否是xiaoming自身拥有的,而不是继承得到的,可以用hasOwnProperty()方法:
xiaoming.hasOwnProperty('name'); // true
xiaoming.hasOwnProperty('toString'); // false

条件判断

var age = 20;
if (age >= 18) { // 如果age >= 18为true,则执行if语句块
    alert('adult');
} else if (age >= 6) {
    alert('teenager');
} else { // 否则执行else语句块
    alert('teenager');
}

JavaScript把null、undefined、0、NaN和空字符串”视为false,其他值一概视为true.

循环

for

var x = 0;
var i;
for (i=1; i<=10000; i++) {
    x = x + i;
}
x; // 50005000

// for循环最常用的地方是利用索引来遍历数组:
var arr = ['Apple', 'Google', 'Microsoft'];
var i, x;
for (i=0; i<arr.length; i++) {
    x = arr[i];
    console.log(x);
}

// 死循环
var x = 0;
for (;;) { // 将无限循环下去
    if (x > 100) {
        break; // 通过if判断来退出循环
    }
    x ++;
}

// for in 用法
var o = {
    name: 'Jack',
    age: 20,
    city: 'Beijing'
};
for (var key in o) {
    console.log(key); // 'name', 'age', 'city'
}
// 由于Array也是对象,而它的每个元素的索引被视为对象的属性,
// 因此,for ... in循环可以直接循环出Array的索引;
// 请注意,for ... in对Array的循环得到的是String而不是Number。
var a = ['A', 'B', 'C'];
for (var i in a) {
    console.log(i); // '0', '1', '2'
    console.log(a[i]); // 'A', 'B', 'C'
}

while

// while循环只有一个判断条件,条件满足,就不断循环,条件不满足时则退出循环;
// 比如我们要计算100以内所有奇数之和,可以用while循环实现:
var x = 0;
var n = 99;
while (n > 0) {
    x = x + n;
    n = n - 2;
}
x; // 2500

do while

// 用do { ... } while()循环要小心,循环体会至少执行1次,而for和while循环则可能一次都不执行
var n = 0;
do {
    n = n + 1;
} while (n < 100);
n; // 100

Map和Set

map

JavaScript的默认对象表示方式{}可以视为其他语言中的Map或Dictionary的数据结构,即一组键值对。 但是JavaScript的对象有个小问题,就是键必须是字符串, 为了解决这个问题,最新的ES6规范引入了新的数据类型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

// Set和Map类似,也是一组key的集合,但不存储value。
// 由于key不能重复,所以,在Set中,没有重复的key。
// 要创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set:
var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3

// 重复元素在Set中自动被过滤:
var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}

// 注意数字3和字符串'3'是不同的元素。
// 通过add(key)方法可以添加元素到Set中,可以重复添加,但不会有效果:
s.add(4);
s; // Set {1, 2, 3, 4}
s.add(4);
s; // 仍然是 Set {1, 2, 3, 4}

// 通过delete(key)方法可以删除元素:
var s = new Set([1, 2, 3]);
s; // Set {1, 2, 3}
s.delete(3);
s; // Set {1, 2}

iterable

遍历Array可以采用下标循环,遍历Map和Set就无法使用下标。 为了统一集合类型,ES6标准引入了新的iterable类型, Array、Map和Set都属于iterable类型。

for … of

具有iterable类型的集合可以通过新的for … of循环来遍历。 for … of循环是ES6引入的新的语法.

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
    console.log(x);
}
for (var x of s) { // 遍历Set
    console.log(x);
}
for (var x of m) { // 遍历Map
    console.log(x[0] + '=' + x[1]);
}

// for ... of 和 for ... in 的区别:
// for ... in循环将把name包括在内,但Array的length属性却不包括在内。
var a = ['A', 'B', 'C'];
a.name = 'Hello';
for (var x in a) {
    console.log(x); // '0', '1', '2', 'name'
}
// for ... of循环则完全修复了这些问题,它只循环集合本身的元素:
var a = ['A', 'B', 'C'];
a.name = 'Hello';
for (var x of a) {
    console.log(x); // 'A', 'B', 'C'
}

forEeach

更好的方式是直接使用iterable内置的forEach方法,它接收一个函数, 每次迭代就自动回调该函数.

// Set与Array类似,但Set没有索引,因此回调函数的前两个参数都是元素本身:
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) { // set参数是整个s
    console.log(element);
});

// Map的回调函数参数依次为value、key和map本身:
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
    console.log(value);
});

// 如果对某些参数不感兴趣,由于JavaScript的函数调用不要求参数必须一致,
// 因此可以忽略它们。例如,只需要获得Array的element:
var a = ['A', 'B', 'C'];
a.forEach(function (element) {
    console.log(element);
});

参考

https://www.liaoxuefeng.com/wiki/1022910821149312/1023020895584256