JavaScript 笔记
Brief Syntax Introduction
- JS 是解释型语言。
解释型语言 与 编译型语言:
解释型:一行一行看,容易出错但方便,可以及时方便地找到出错位置以及出错原因,容易跨平台(可以嵌入到其他软件)。
编译型:把高级语言预先翻译成机器语言,并把结果保存下来,速度快(编译器优化),不易出错,但不能跨平台(e.g. .exe can't be run in MacOS)。
-
行末分号非必需: JS 中的每一行都可以选择使用分号终止,即编译器会自动补全没有用分号的地方。
-
JS 中的变量是弱类型的,即变量的类型只有在被赋值的时候才得到确认。
数据类型与变量
-
const
用来申明常量(一旦声明后续值不可更改),let
用来申明变量(一个变量只能申明一次,值以及值的类型可以被多次更改)。 -
JS 中允许一个变量未被提前申明就使用。此时该变量会被自动被申明为全局变量。若在程序的最开始加上
use strict
语句,则可以避免这支双刃剑带来的弊端。(在strict
模式下,变量必须要先经过申明,才能被使用,否则会报错) -
JS 中的基本类型有数字、字符串、布尔值、符号、
null
和undefined
。数字中不区分整数和浮点数。 -
JS 中遇到计算除法时除数为
0
的情况时,不会报错,会根据被除数的值来返回对应的结果。若被除数是正数,则返回Infinity
;若被除数是负数,则返回-Infinity
;若被除数是0
,则返回NaN
(Not a Number)。
除以0
的余数(1 % 0
)为NaN
。 -
与
python
类似, JS 中一个*
代表相乘,**
则代表幂运算。 -
JS 中
===
表示检查是否相等,!==
表示检查是否不等。
字符串
-
JS 中的字符串用
''
或""
括起来表示。若想表达的字符串中本身含有'
或"
,则需要用到转义字符。
e.g.I'm "OK"!
的表示是:'I\'m \"OK\"!';
-
在 JS 中输出多行字符串的方式是用反引号。
e.g.`No \n anymore `;
-
与 python 类似,要把多个字符串连接起来,可以用
+
号连接。也可以通过模版字符串的方式来方便输出过程。(反引号与${}
的结合)let name = '小明'; let age = 20; let message1 = '你好, ' + name + ', 你今年' + age + '岁了!'; let message2 = `你好, ${name}, 你今年${age}岁了!` //message1 = message2
-
JS 中的字符串是不可变的,即不能通过
s[id] = 'A'
的方式来将s
中id
位置的字母改成A
。 -
JS 中有很多类似 python 和 C++ STL 中的字符串函数,如
toUpperCase
,indexOf
(搜索元素在数据结构中出现的位置,未找到则返回-1
),substring
等。
数组
-
JS 中的数组可以包含任意数据类型,并通过索引来访问/修改每个元素。
a = [1, 'hello, world', ['a', b, 100]]; a[0] = 'qaq'; a; // a = ['qaq', 'hello, world', ['a', b, 100]]
-
在 JS 中允许给数组的
length
赋一个新值。除此之外,假如进行了数组的越界访问,也不会报错。var arr = [1, 2, 3]; arr.length = 6; arr; // arr 变为 [1, 2, 3, undefined, undefined, undefined] arr.length = 2; arr; // arr变为[1, 2] arr[4] = 'qaq'; arr; // arr 变为 [1, 2, undefined, undefined, 'qaq']
-
同 python 的切片类似,
slice()
可以将一个数组的局部“拿出来”,变成一个新的数组。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 aCopy = arr.slice(); aCopy; // ['A', 'B', 'C', 'D', 'E', 'F', 'G']
-
如果要在数组的头部添加若干元素,可以使用
unshift()
;shift()
则可以用来把数组的第一个元素删除。
splice()
则可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素。var arr = [1, 2, 3, 4]; arr.unshift('A', 'B'); // 返回Array新的长度: 6 arr; // ['A', 'B', 1, 2, 3, 4] arr.shift(); // 'A' arr; // ['B', 1, 2, 3, 4] // 从索引 1 开始删除 3 个元素,然后再添加 2 个元素: arr.splice(1, 3, 'qaq', 222); // 返回删除的元素 [1, 2, 3] arr; // ['B', 4, 'qaq', 222]
-
如果要在数组的尾部添加/删除若干元素,可以使用
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'] arr.pop(); arr.pop(); arr.pop(); // 连续pop 3次 arr; // [] arr.pop(); // 空数组继续pop不会报错,而是返回undefined arr; // []
对象
在 JS 中,对象是一种无序的集合数据类型,由若干键值对组成。用法与 python 中的 dictionary
很像。但 JS 对象的键必须是字符串,值可以是任意数据类型。这一点和 dictionary
对键和值类型的无限制不同( JS 中 Map
的引入解决了这个问题)。
var xiaoming = {
name: 'Yiling Zhang',
birth: 2003,
school: 'Xidian University',
height: 1.77,
weight: 65,
score: 100
};
函数
函数的定义与调用
function get_pow(x, y) {
var res = 1, i;
for (i = 1; i <= y; i++) res = res * x;
return res;
}
var get_pow = function (x, y) {
var res = 1, i;
for (i = 1; i <= y; i++) res = res * x;
return res;
};
//匿名函数,在 JS 中函数也可以作为变量,通过这个变量就可以调用该函数。
传入函数的多个变量以逗号分隔开。如果传入某个函数的变量的个数,比该函数预先设定的要多,程序仍然会返回正确的结果,且不会报错。如果要少,则不会报错,但是结果可能不正确。
在函数的内部,系统提供了关键字 arguments
。该关键字只在函数内部起作用,通过该参数可以访问到传入该函数的所有参数。可以通过访问 arguments.length()
来获取到传入某个函数的参数的个数。
function abs() {
if (arguments.length() == 0) return 0;
else {
var x = arguments[0];
return x >= 0 ? x : -x;
}
}
abs(); // 0
abs(-2, 1); // 2
变量作用域与解构赋值
局部作用域、块级作用域
若在函数内部用 var
申明变量,则该变量的作用域是该函数内部,即这是一个局部变量。 在 JS 中允许函数的嵌套,即某个函数内可以定义另一个函数。同时允许内函数和外函数中定义重名的变量。 函数在查找变量时从自身函数定义开始,从内向外查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。
use strict;
function f() {
var x = 1;
function g() {
var x = 2;
x; // x = 2
}
x; // x = 1
}
在函数内部中用 var
申明一个变量,该变量会在整个函数中都起作用,不具有块级作用域。用 let
来申明则不会有这种问题,新申明的变量会具有块级作用域。
function f() {
var sum = 0;
for (var i = 0; i < 100; i++) sum += i;
i++; // 仍然可以使用在上一个循环内定义的i
}
function f() {
var sum = 0;
for (let i = 0; i < 100; i++) sum += i;
i++; // SyntaxError (与在c++中的逻辑一样)
}
请严格遵守在函数内部首先申明所有变量这一规则!否则会出一些奇奇怪怪的错误。
全局作用域
不在任何函数内定义的变量就具有全局作用域。 JS 默认有一个全局对象window
,全局作用域的变量(变量、函数等)实际上被绑定到 window
的一个属性。
var x = 1;
alert(x); // 1
alert(windows.x); // 1
解构赋值
在 JS 中可以使用解构赋值,直接对多个变量同时赋值:
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
x; // 'hello'
y; // 'JavaScript'
z; // 'ES6'
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school'
};
let {name, passport : id} = person; // 把passport属性赋值给变量id:
name; // '小明'
id; // 'G-12345678'
// 注意: passport不是变量,而是为了让变量id获得passport属性:
passport; // Uncaught ReferenceError: passport is not defined
解构赋值在很多时候可以大大简化代码。例如,交换两个变量 \(x\) 和 \(y\) 的值,可以这么写,不再需要临时变量:
var x = 1, y = 2;
[x, y] = [y, x]
快速获取当前页面的域名和路径:
var {hostname : domain, pathname : path} = location;
如果一个函数接收一个对象作为参数,那么,可以使用解构直接把对象的属性绑定到变量中。例如,下面的函数可以快速创建一个 Date
对象:
function buildDate({year, month, day, hour = 0, minute = 0, second = 0}) {
return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);
}
buildDate({ year: 2017, month: 1, day: 1 }); // Sun Jan 01 2017 00:00:00 GMT+0800 (CST)
// 传入的对象只需要year、month和day这三个属性
方法
可以给对象绑定函数。绑定到对象上的函数叫 方法 ,其在内部使用了 this
关键字。 在方法内部, this
始终指向当前对象。
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
xiaoming.age; // function xiaoming.age()
xiaoming.age(); // 今年调用是25,明年调用就变成26了
若不通过某个对象直接在函数里调用 this
,this
默认指向 windows
。在 strict
模式下,函数的 this
指向的是 undefined
,从而系统会报错提醒你这里的不规范写法。故要保证 this
指向正确,必须用 obj.xxx()
的形式调用,或者可以用一个变量首先捕获 this
。
'use strict';
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var y = new Date().getFullYear();
return y - this.birth;
}
};
var fn = xiaoming.age;
fn(); // Uncaught TypeError: Cannot read property 'birth' of undefined
'use strict';
var xiaoming = {
name: '小明',
birth: 1990,
age: function () {
var that = this; // 在方法内部一开始就捕获this
function getAgeFromBirth() {
var y = new Date().getFullYear();
return y - that.birth; // 用that而不是this
}
return getAgeFromBirth();
}
};
xiaoming.age(); // 25