廖雪峰js学习笔记:基础

快速入门

代码书写

HTML+CSS+JavaScript三件套

在编辑器VS Code中直接键入doc可以快速创建html结构

在html中嵌入CSS样式:

<head>

1
<link href="./chatroom.css" rel="stylesheet" type="text/css" />

在html中嵌入js脚本:

一般在<body>末尾

1
<script src="test1.js"></script>

直接嵌入

将JavaScript代码放在<head><script>中,将直接被浏览器执行

1
2
3
4
5
6
7
8
<html>
<head>
<script>
alert('Hello, world');
</script>
</head>
<body></body>
</html>

引用外部文件

把JavaScript代码放到一个单独的.js文件,然后在HTML中通过<script src="..."></script>引入这个文件,这样,/static/js/abc.js就会被浏览器执行

  • 如果是DOM无关的,可以放在head

  • 如果要操作DOM,放在body的末尾

  • jQuery没有直接操作DOM,放哪都行

1
2
3
4
5
6
7
8
<html>
<head>
<script src="/static/js/abc.js"></script>
</head>
<body>
...
</body>
</html>

把JavaScript代码放入一个单独的.js文件中更利于维护代码,并且多个页面可以各自引用同一份.js文件。
可以在同一个页面中引入多个.js文件,还可以在页面中多次编写,浏览器按照顺序依次执行。

代码调试

使用chrome浏览器,点击菜单“查看(View)”-“开发者(Developer)”-“开发者工具(Developer Tools)”,快捷键F12
Elements可以查看页面源代码,在Console可以输入js指令。

基本语法

代码 功能
; 结束语句
{…} 语句块
// 注释单行
/*...*/ 注释多行语句

JavaScript是大小写敏感的

数据类型

Number

JavaScript不区分整数和浮点数,统一用Number表示,以下都是合法的Number类型:

Number 类型
123; 整数123
0.456; 浮点数0.456
1.2345e3; 科学计数法表示1.2345x1000,等同于1234.5
-99; 负数
NaN; NaN表示Not a Number,当无法计算结果时用NaN表示
Infinity; Infinity表示无限大,当数值超过Number所能表示的最大值时,就表示为Infinity

Number可以直接做四则运算+,-,*,/,%(求余)

字符串

字符串是以单引号'或双引号"括起来的任意文本
转义字符\\n换行,\t制表符,\\表示\,\x##可以表示ASCII码
多行字符串
字符串连接+
模板字符串…${varname}…用变量替换${}的内容

1
2
var name = '小明'; var age = 20;
console.log(`你好, ${name}, 你今年${age}岁了!`);

常用属性/函数

属性/函数 解释 用例
length 字符串长度属性 var s = 'Hello, world!' s.length; // 13
[] 字符串索引 s[12]; // '!' s[13]; // undefined
toUpperCase() 全部变为大写 s.toUpperCase() // "HELLO, WORLD!"
toLowerCase() 全部变为小写 s.toLowerCase() // "hello, world!"
indexOf() 搜索指定字符串出现的位置 s.indexOf(“world”) //7
substring() 返回指定索引区间的子串 s.substring(0, 5); // 从0到5(不包括5),返回'hello'

布尔值

truefalse两种值

布尔运算:&&与运算,||或运算,!非运算

比较运算符

当我们对Number做比较时,可以通过比较运算符得到一个布尔值,<,>,<=,>=,==,===,!==
JavaScript允许对任意数据类型做比较,注意:

  1. ==和===

    1
    2
    false == 0; // true
    false === 0; // false
    • ==,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;
    • ===,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。
    • 由于JavaScript这个设计缺陷,不要使用==比较,始终坚持使用===比较
  2. NaN与所有其他值都不相等,包括它自己:
    唯一能判断NaN的方法是通过isNaN()函数:

    1
    isNaN(NaN); // true
  3. 浮点数的相等比较
    要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值Math.abs<阈值

    1
    Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true

null和undefined

  • null表示一个“空”的值,辨析:
    • 0是一个数值,
    • ''表示长度为0的字符串
  • undefined表示值未定义。

区分两者的意义不大。大多数情况下,我们都应该用nullundefined仅仅在判断函数参数是否传递的情况下有用。

另,在变量前加!!判断可以将nullundefined都变成布尔值false

数组

https://www.runoob.com/jsref/jsref-obj-array.html
数组是一组按顺序排列的集合,集合的每个值称为元素。JavaScript的数组可以包括任意数据类型。

  1. 数组的创建
    法一:数组用[]表示,元素之间用,分隔。

    1
    [1, 2, 3.14, 'Hello', null, true];

    法二:通过Array()函数实现

    1
    new Array(1, 2, 3); // 创建了数组[1, 2, 3]

    然而,出于代码的可读性考虑,强烈建议直接使用[]。

  2. 数组元素的访问
    数组的元素可以通过索引来访问。索引的起始值为0:

    1
    2
    3
    4
    var arr = [1, 2, 3.14, 'Hello', null, true];
    arr[0]; // 返回索引为0的元素,即1
    arr[5]; // 返回索引为5的元素,即true
    arr[6]; // 索引超出了范围,返回undefined
  3. 常用属性/方法

    length 数组长度 arr.length
    [] 数组索引 arr[0]
    indexOf() 搜索指定元素位置 arr.indexOf(10)
    slice() 截取数组的部分元素 arr.slice(0,3) //从索引0开始到3,不包括3
    arr.slice() //复制arr
    push() 向数组末尾添加若干元素 返回数组长度 arr.push(1,2)
    pop() 删除数组的最后一个元素 返回删除的元素 arr.pop()
    unshift() 向数组头部添加若干元素 返回数组长度 arr.unshift(‘a’,’b’)
    shift() 删除数组的第一个元素 返回删除的元素 arr.shift()
    sort() 对数组排序,默认顺序 arr.sort()
    reverse() 反转数组 arr.reverse()
    splice() 从指定索引开始删除,添加元素 返回删除的元素 var arr=[1,2,3,4,5]; arr.splice(2, 3, 'a', 'b'); // 返回[3,4,5]
    arr; // [1,2,’a’,’b’]
    concat() 连接两个数组,返回新的数组 arr.concat(1.2.[3,4]); //在arr后面连接1,2,3,4
    join() 把数组元素用指定字符连起来 返回字符串 arr.join(‘-‘)
  4. 多维数组
    如果数组的某个元素又是一个Array,则可以形成多维数组
    练习:1)如何通过索引取到500这个值 2)打印数组元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //1
    'use strict';
    var arr = [[1, 2, 3], [400, 500, 600], '-'];
    //2
    'use strict';
    var arr = ['小明', '小红', '大军', '阿黄'];
    arr.sort();
    console.log(`欢迎${arr[0]},${arr[1]},${arr[2]}${arr[3]}同学`);
    //console.log(`欢迎${arr.sort().slice(0,-1)}和${arr.sort().slice(-1)}同学!`);

对象

JavaScript的对象是一组由键-值组成的无序集合,例如:

1
2
3
4
5
var person = {
name: 'Bob',
age: 20,
tags: ['js', 'web', 'mobile'],
};

对象的键都是字符串类型,值可以是任意数据类型。
其中每个键又称为对象的属性,要获取一个对象的属性,用对象变量.属性名

1
person.name; // 'Bob'

判断一个对象是否包含某个属性

方法 解释 用例
in 返回true/false,对于变量继承的属性也会判为真 ‘name’ in person; // true
‘toString’ in person; // true
hasOwnProperty() 判断属性是否是变量自身拥有的而不是继承的 person.hasOwnProperty(‘name’); //true
person. hasOwnProperty(‘toString’); //false

变量

变量名规范:大小写英文、数字、$和_的组合,且不能用数字开头,不能是JavaScript的关键字,一般是驼峰式命名

  • 申明变量用var语句,只能申明一次
    • letconst都是块级声明,不同点在于const声明后值无法修改
    • 详见var, let, const异同
  • 变量赋值用=,JavaScript是动态语言,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,可以是不同类型的变量
1
2
3
4
5
var a; // 申明了变量a,此时a的值为undefined
var $b = 1; // 申明了变量$b,同时给$b赋值,此时$b的值为1
var s_007 = '007'; // s_007是一个字符串
var Answer = true; // Answer是一个布尔值true
var t = null; // t的值是null

要显示变量的内容,可以用console.log(x)

strict模式

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

strict模式下,强制通过var申明变量,未使用var申明变量就使用的,将导致运行错误。
启用strict模式的方法是在JavaScript代码的第一行写上:

1
'use strict';

这是一个字符串,不支持strict模式的浏览器将报错

条件和循环

条件判断

1
2
3
4
5
6
7
if () {

} elseif () {

} else {

}

For

1
2
3
for (i=1; i<=100; i++) {

}

For in

遍历对象、数组、字符串key

遍历对象,可以用hasOwnProperty()过滤继承的属性

1
2
3
4
5
6
7
8
9
10
var o = {
name: 'Jack',
age: 20,
city: 'Beijing'
};
for (var key in o) {
if (o.hasOwnProperty(key)) {
console.log(key); // 'name', 'age', 'city'
}
}

遍历数组,索引就是key,可以通过索引循环访问

1
2
3
4
5
var a = ['A', 'B', 'C'];
for (var i in a) {
console.log(i); // '0', '1', '2'
console.log(a[i]); // 'A', 'B', 'C'
}

for of

遍历对象、数组、字符串value,比较快捷

1
2
3
4
5
6
var myArray = [1, 2, 4, 5, 6, 7];
myArray.name = "数组";
myArray.getName = function() { return this.name; }
for (var value of myArray) {
console.log(value);
}

While

1
2
while () {…}
do {…} while ()

Iterable:Map & Set

JavaScript的对象有个小问题,就是键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。

Map

Map是一组key-value键值对结构,具有极快的查找速度。对一个key多次赋不同的value会覆盖之前的值

方法 解释 用例
new Map() 创建Map var m = new Map(); // 空Map
set(key,value) 添加键值对 m.set('Adam', 67); // 添加新的key-value
has(key) 检验key是否存在 m.has('Adam'); // 是否存在key 'Adam': true
get(key) 访问key对应的值 m.get('Adam'); // 67
delete(key) 删除元素 m.delete('Adam'); // 删除key 'Adam'

Set

Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key,重复的元素会自动被过滤。

方法 解释 用例
new Set() 创建Set var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3
add(key) 添加元素,可以重复添加,但不会有效果 s.add(4);
delete(key) 删除元素 s.delete(3);

Iterable

ES6标准引入了新的iterable类型,Array、Map和Set都属于iterable类型。

for … in循环中,它遍历的是对象的属性,所以如果给Array对象手动增加属性,会导致属性也被遍历。

通过新的for ... of循环来遍历,可以解决这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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]);
}

此外,iterable还内置了forEach()方法

Array

1
2
3
4
5
6
7
var a = ['A', 'B', 'C'];
a.forEach(function (element, index, array) {
// element: 指向当前元素的值
// index: 指向当前索引
// array: 指向Array对象本身
console.log(element + ', index = ' + index);
});

Set

1
2
3
4
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {
console.log(element);
});

Map

1
2
3
4
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
console.log(value);
});

函数

函数基础

函数定义

function 函数名(参数) {函数体}

return语句返回结果,没有return返回undefined

var 函数名=function(参数) {函数体};

注意第二种定义最后要加分号;

函数调用

函数名(输入参数),按顺序输入参数,参数比定义的多或少都不会报错,但会因为参数为undefined不能完成功能,可以在函数中进行参数检查

1
2
3
4
function abs(x) {
if (typeof x !== 'number') {throw 'Not a number';}

}

arguments

只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数,即使函数没有定义形参也能获取实参,常用于判断传入参数

1
2
3
4
5
6
7
function foo(x) {
console.log('x = ' + arguments[0]); // 10
for (var i=0; i<arguments.length; i++) {
console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
}
}
foo(10, 20, 30);

rest参数

指向除了函数内已定义参数的其他输入参数

1
2
3
4
5
6
7
8
9
function sum(...rest) {
s=0;
for (i=0;i<rest.length;i++){
if (typeof rest[i]==='number'){
​ s+=rest[i];
​ }
}
return s;
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var i, args = [];
for (i=1; i<=100; i++) {
args.push(i);
}

if (sum() !== 0) {
console.log('测试失败: sum() = ' + sum());
} else if (sum(1) !== 1) {
console.log('测试失败: sum(1) = ' + sum(1));
} else if (sum(2, 3) !== 5) {
console.log('测试失败: sum(2, 3) = ' + sum(2, 3));
} else if (sum.**apply**(null, args) !== 5050) {
console.log('测试失败: sum(1, 2, 3, ..., 100) = ' + sum.apply(null, args));
} else {
console.log('测试通过!');
}

return

由于JavaScript引擎会自动在句末添加分号,可能会导致语句无法执行,正确的写法是把return {写在一行

1
2
3
4
5
function foo() {
return { // 这里不会自动加分号,因为{表示语句尚未结束
​ name: 'foo'
};
}

变量作用域与结构赋值

作用域

函数内部声明的变量作用域为整个函数

嵌套的函数内部可以访问外部函数的变量

JavaScript编译时会先扫描函数体把所有声明的变量提升到函数顶部,所以通常首先声明所有变量

1
2
3
4
5
6
7
8
function foo() {
**var**
​ x = 1, // x初始化为1
​ y = x + 1, // y初始化为2
​ z, i; // z和i为undefined
// 其他语句:
...
}

不在函数内部声明的变量就是全局变量,默认绑定到全局对象window

名字空间

全局变量会绑定到window上,不同的js文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突

减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。例如:

1
2
3
4
5
6
7
8
9
10
11
// 唯一的全局变量MYAPP:
var MYAPP = {};

// 其他变量:
MYAPP.name = 'myapp';
MYAPP.version = 1.0;

// 其他函数:
MYAPP.foo = function () {
return 'foo';
};

局部作用域

用关键字let替代var声明变量,可以使for循环等语句中定义的变量具有块级(函数)作用域

1
for (**let** i=0; i<100; i++) {...}

常量

const定义常量,具有块级作用域

解构赋值

直接对多个变量同时赋值

1
2
3
var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
let [, , z] = ['hello', 'JavaScript', 'ES6'];

从一个对象中取出若干属性,对象中嵌套的属性也可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var person = {
name: '小明',
age: 20,
gender: 'male',
passport: 'G-12345678',
school: 'No.4 middle school'
address: {
​ city: 'Beijing',
​ street: 'No.1 Road',
​ zipcode: '100001'
}
};

var {name, age, passport} = person;
var {gender, address: {city, zip}} = person;
```

如果要使用的变量名和属性名不一致,可以用下面的语法获取

```js
let {name, passport:id} = person;
name; // '小明'
id; // 'G-12345678',passport不是变量

解构赋值还可以使用默认值

1
var {name, single=true} = person;

如果变量已经被声明了,使用{}批量赋值会报错,可以用小括号括起来

1
({x, y} = { name: '小明', x: 100, y: 200});

实例

交换两个变量的值:[x,y]=[y,x]

快速获取当前页面的域名和路径:var {hostname:domain, pathname:path} = location;

如果函数把对象作为参数,可以直接把对象属性绑定到变量上,如输入Date对象

function buildDate({year, month, day, hour=0, minute=0, second=0}){...}

方法

绑定到对象上的函数称为方法,在一个方法内部,this是一个特殊变量,它始终指向当前对象

var that = this;,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

apply

要指定函数的this指向哪个对象,可以用函数本身的apply方法,它接收两个参数,第一个参数就是需要绑定的this变量,第二个参数是Array,表示函数本身的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
function getAge() {
var y = new Date().getFullYear();
return y - this.birth;
}

var xiaoming = {
name: '小明',
birth: 1990,
age: getAge
};

xiaoming.age(); // 25
getAge.apply(xiaoming, []); // 25, this指向xiaoming, 参数为空

call

apply()类似,区别是:

apply()把参数打包成Array再传入;

call()把参数按顺序传入。

比如调用Math.max(3, 5, 4),分别用apply()call()实现如下:

1
2
Math.max.apply(null, [3, 5, 4]); // 5
Math.max.call(null, 3, 5, 4); // 5

对普通函数调用,我们通常把this绑定为null

修饰器

利用apply(),我们还可以动态改变函数的行为。JavaScript的所有对象都是动态的,即使内置的函数,我们也可以重新指向新的函数。

1
2
3
4
5
6
7
var count = 0;
var oldParseInt = parseInt; // 保存原函数

window.parseInt = function () {
count += 1;
return oldParseInt.apply(null, arguments); // 调用原函数
};
------ 本文结束 ❤ 感谢你的阅读 ------
------ 版权信息 ------

本文标题:廖雪峰js学习笔记:基础

文章作者:Lury

发布时间:2021年07月14日 - 11:24

最后更新:2022年04月14日 - 23:27

原始链接:https://luryzhu.github.io/2021/07/14/JavaScript/liao1/

许可协议:署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。