文章内容为本人在最初学习 JS 时整理的笔记
由于技术有限,知识点较为基础,也稍微有些杂乱
如有错误,欢迎指出

输入输出

1
2
3
prompt(); //弹出输入框
alert(); //弹出提示框
console.log(); //浏览器控制台打印

变量

声明、赋值和变量的初始化

1
2
3
4
5
6
7
var a; //声明
a = 5; //赋值
var b = 10; //初始化
b = 20; //重新赋值
var x = 1,
y = 2,
z = 3; //同时声明多个变量,可以只写一个var,中间用‘,’隔开

特殊情况

  1. 只声明 不赋值 -> undefined
  2. 不声明 不赋值 -> 使用时会报错
  3. 不声明直接赋值 -> 可以使用

变量命名规范

  • 由字母、数字、下划线、美元符号$组成
  • 严格区分大小写
  • 不能以数字开头
  • 不能是关键字、保留字
  • 变量名必须有意义
  • 遵守驼峰命名法

数据类型

typeof -> 返回数据类型

  • 基本数据类型

    Number 数字

    Boolean 布尔值

    String 字符串

    undefined 未定义

    null 空值

    (ES6 新增)

    Symbol 唯一标识符

  • 引用数据类型

    Object 对象

基本和引用类型的区别

  • JS 中的变量都是保存到栈内存中的

    基本数据类型的值直接在栈内存中存储

    值与值之间是独立存在的,修改一个变量不会影响其他变量

  • 对象是保存到堆内存中的,每创建一个新的对象,就会在堆内存中开辟出一个新的空间

    而变量保存的是对象的内存地址(对象的引用),如果两个变量保存的是同一个对象引用,当通过一个变量修改属性时,另一个也会受到影响

当比较两个基本数据类型时,就是比较它们的值

当比较两个引用数据类型时,比较的是对象的内存地址

两个对象即使一模一样,如果地址不同,也会返回 false

数据类型转换

  • 转换为字符串

    变量.toString()

    String(变量) 强制转换

    ‘+’拼接字符串(隐式转换,推荐)

  • 转换为数字型

    parseInt(string) 函数 将 string 类型转成整型

    parseFloat(string) 函数 将 string 类型转成浮点型

    Number() 强制转换函数 将 string 类型转成数字型

    隐式转换 利用算术运算符隐式转换

  • 转换为布尔型

    Boolean() 函数:代表空、否定的值都会被转换为 false。

对象和函数

对象的分类

  1. 内建对象

    由 ES 标准中定义的对象,在任何 ES 的实现中都可以使用
    比如: Math String Number Boolean Function Object

  2. 宿主对象

    由 JS 的运行环境提供的对象,目前来讲主要指由浏览器提供的对象
    比如 BOM DOM

  3. 自定义对象

    由开发人员自己创建的对象

作用域

  • 作用域指一个变量的作用范围

  • 在 JS 中一共有两种作用域:

    全局作用域

    • 直接编写在 script 标签中的 JS 代码,都在全局作用域
    • 全局作用域在页面打开时创建,在页面关闭时销毁
    • 在全局作用域中有一个全局对象 window
      它代表的是一个浏览器的窗口,它由浏览器创建我们可以直接使用
    • 在全局作用域中:
      创建的变量都会作为 window 对象的属性保存
      创建的函数都会作为 window 对象的方法保存
    • 全局作用域中的变量都是全局变量,在页面的任意部分都可以访问到

    函数作用域

    • 调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
    • 没调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的
    • 在函数作用域中可以访问到全局作用域的变量,在全局作用域中无法访问到函数作用域的变量
    • 当在函数作用域中操作变量时,现在自身作用域中寻找,如果有就直接使用,没有则去上一级作用域中寻找

this

解析器在调用函数时会向函数内部传递一个隐含的参数,这个隐含的参数就是 this,

this 指向的是一个对象,这个对象我们称为为函数执行的上下文对象,

根据函数的调用方式的不同,this 会指向不同的对象,

主要有以下几个场景:

  1. 以函数的形式调用时,this 永远都是 window
  2. 以方法的形式调用时,this 就是调用方法的那个对象
  3. 以构造函数的形式调用时,this 就是新创建的那个对象
  4. 使用 call 和 apply 调用时,this 是传入的那个对象

构造函数

构造函数本质上就是一个普通的函数,创建方式和普通函数没有区别

构造函数命名上习惯首字母大写

构造函数需要使用 new 关键字来调用

构造函数的执行流程:

  1. 立刻创建一个新的对象
  2. 将新建的对象设置为函数中的 this,在构造函数中可以使用 this 来引用新建的对象
  3. 逐行执行函数中的代码
  4. 将新建的对象作为返回值返回

使用同一个构造函数创建的对象,我们称为一类对象,也将一个构造函数称为一个类

我们将通过一个函数创建的对象,称为是该类的实例

this 的情况:

当以函数的形式调用时,this就是window

当以方法的形式调用时,谁调用方法谁就是this

当以构造函数的形式调用时,this就是新创建的那个对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/* 创建一个Person构造函数
* - 在Person构造函数中,为每一个对象都添加了一个sayName方法
* 目前我们是在构造函数内部创建方法,这就导致了构造函数每执行一次就会创建一个方法
* 而且所有实例的sayName方法都是一模一样的但却是唯一的
* - 我们完全可以使所有对象共享一个方法
*/
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
/*this.sayName = function(){
alert("大家好,我是"+this.name);
};*/
//将sayName方法在全局作用域中定义,而不是在内部创建
this.sayName = fun;
}

//将sayName方法在全局作用域中定义
/*
* 将函数定义在全局作用域,污染了全局作用域的命名空间
* 而且定义在全局作用域中也很不安全
* 解决:原型
*/
function fun() {
alert('大家好,我是' + this.name);
}

//创建Person类的实例
var person1 = new Person('李四', 18, '男');
var person2 = new Person('王五', 16, '女');

person1.sayName();
person2.sayName();

//使用instanceof可以检查一个对象是否是一个类的实例
//console.log(person1 instanceof Person); //true

/*
* 所有对象都是Object的后代
* 所以任何对象和Object作instanceof检查时都会返回true
*/
//console.log(person1 instanceof Object); //true

原型

  • 函数 prototype 属性 ==》 原型对象 (显式原型)

    ​ 在定义函数时自动添加的,默认值是一个空的 Object 实例对象(Object 例外)

  • 实例 __proto__ 属性 ==》 原型对象 (隐式原型)

    ​ 在创建对象时自动添加的,默认值为构造函数的 prototype 属性值

    ​ 即实例对象的隐式原型的值为其对应的构造函数显式原型的值

​ 我们能直接操作显式原型,但不能操作隐式原型

  • 原型对象 constructor 属性 ==》 函数对象

所有函数的 __proto__ 都是一样的(都是由构造函数 Function 创建的实例,包括 Function 本身)

Object 的原型对象是原型链的尽头(Object.prototype.__proto__ == null)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
* 原型prototype
* 我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype
* 这个属性对应着一个对象,这个对象就是我们所谓的原型对象
* 如果函数作为普通函数调用prototype没有任何作用
* 当函数以创造函数的形式调用时,它所创建的对象中都会有一个隐含的属性
* 指向该构造函数的原型对象,我们可以通过__proto__来访问
*
* 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到
* 我们可以将对象中共有的内容,统一设置到原型对象中
*
* 当我们访问对象的属性或方法时,会现在对象自身中寻找,如果有则直接使用,
* 没有则去原型中寻找... 直到找到Object的原型
* Object的原型没有原型(null),如果在Object的原型中依然没有找到,则返回undefined
*
* 以后我们创建构造函数时,可以将这些对象共有的属性和方法,添加到构造函数的原型对象中
*/
function MyClass() {}

//向MyClass的原型中添加属性a
MyClass.prototype.a = 123;

//向MyClass的原型中添加方法
MyClass.prototype.sayHello = function () {
alert('Hello');
};

var mc = new MyClass();
var mc2 = new MyClass();

mc2.a = '我是mc2中的a';

//console.log(MyClass.prototype);
//console.log(mc.__proto__ == MyClass.prototype); //true

console.log(mc.a); //123
console.log(mc2.a); //我是mc2中的a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function MyClass() {}

//向MyClass的原型中添加属性name
MyClass.prototype.name = '我是原型中的名字';

var mc = new MyClass();
mc.age = 18;

//使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
//console.log("name" in mc); //true

//可以使用hasOwnProperty()来检查对象自身中是否有该属性
//使用该方法只有当对象自身含有该属性时,才会返回true
console.log(mc.hasOwnProperty('name')); //false
console.log(mc.hasOwnProperty('age')); //true

IIFE

Immediately-Invoked Function Expression 立即调用函数表达式、

1
2
3
4
5
6
7
8
9
//匿名函数自调用
(function () {
console.log('IIFE');
})();

/* 作用:
* 隐藏实现
* 不会污染外部命名空间
*/

instanceof

  • 表达式 A instanceof B
  • 如果 B 函数的显式原型对象在 A 对象的原型链上,则返回 true

函数的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function fun() {
alert(this);
}

/*
* call()和apply()
* - 这两个方法都是函数对象的方法,需要通过函数对象来调用
* - 当对函数调用call()和apply()都会调用函数执行
* - 在调用call()和apply()时可以将一个对象指定为第一个参数
* 这个对象将会成为函数执行时的this
*/

fun; //[object window]
fun.call(); //[object Object]
fun.apply(); //[object Object]

/*==============================================
- call()方法可以将实参在对象之后依次传递
- apply()方法需要将实参封装到一个数组中统一传递
==============================================*/

function fun(a, b) {
console.log('a = ' + a);
console.log('b = ' + b);
}

var obj = {
name: 'obj',
sayName: function () {
alert(this.name);
},
};

fun.call(obj); //a = undefined b = undefined
fun.call(obj, 2, 3); //a = 2 b = 3
fun.apply(obj, [2, 3]);
  • this 的情况
    1. 以函数的形式调用时,this 永远都是 window
    2. 以方法的形式调用时,this 就是调用方法的那个对象
    3. 以构造函数的形式调用时,this 就是新创建的那个对象
    4. 使用 call 和 apply 调用时,this 是指的那个对象

arguments

1
2
3
4
5
6
7
8
9
10
11
/*
* 在调用函数时,浏览器每次都会传递进两个隐含的参数:
* 1.函数的上下文对线this
* 2.封装实参的对象arguments
* - arguments是一个类数组对象,它也可以通过索引来操作,也可以获取长度
* - 在调用函数时,我们所传递的参数都会在arguments中保存
* - arguments.length可以用来获取实参的数量
* - 即使不定义形参也可以通过arguments来使用实参
* - arguments里面有一个属性叫做callee
* 这个属性对应的就是当前正在执行的函数对象
*/

Date 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//创建一个Date对象
//如果直接使用构造函数创建Date对象,则会封装为当前代码执行的时间
var d = new Date();

//创建一个指定的Date对象
//需要在构造函数中传递一个表示时间的字符串作为参数
//日期格式 月/日/年 时:分:秒

var myDate = new Date('12/25/2019 12:25:00');

console.log(myDate); //Wed Dec 25 2019 12:25:00 GMT+0800 (中国标准时间)

/*=========================================================
方法:
getDate() 获取一个月中的某一天(今天是几号? 1-31)
getDay() 获取一周中的某一天(今天是周几? 0-6,0表示周日)
getMonth() 获取一年中的某月(0-11)
...

getTime() 获取当前日期对象的时间戳
- 时间戳:从格林威治时间的1970年1月1日 0分0时0秒
到当前日期的毫秒数
- 计算机底层保存时间使用的是时间戳
=========================================================*/

Math 对象

Math 和其他对象不同,它不是一个构造函数,它属于一个工具类,不用创建对象,它里面封装了数学运算相关的属性和方法

  • 属性 E,LN2,LN10,PI …

  • 方法 abs(),sqrt(),max(),min() …

    ceil() 向上取整

    floor() 向下取整

    round() 四舍五入

    random() 生成一个 0-1 之间的随机数

    1
    2
    //生成一个x-y之间的随机数
    Math.round(Math.random() * (y - x) + x);

数组

数组的四个常用方法

push()

  • 该方法可以向数组的末尾添加一个或多个元素,并返回数组的新长度
  • 可以将要添加的元素作为方法的参数传递,这些元素将会自动添加到数组的末尾

pop()

  • 该方法可以删除数组的最后一个元素,并将被删除的元素返回

unshift()

  • 向数组开头添加一个或多个元素,并返回新的数组长度
  • 添加元素后,其他元素的索引会相应调整

shift()

  • 可以删除数组的第一个元素,并将其作为返回值返回

数组的遍历

  • for 循环
1
2
3
4
5
var arr = ['张三', '李四', '王五', '马六'];

for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
  • forEach *
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//forEach()  不支持IE9以下浏览器
var arr = ['张三', '李四', '王五', '马六'];

/*
* forEach()方法需要一个函数作为参数
* - 像这种函数,由我们创建但不是我们调用的,我们称之为回调函数
* - 数组中有几个元素就会执行几次,每次执行时,浏览器会将遍历到的元素以实参的形式
* 传递进来,我们可以定义形参,来读取这些内容
* - 浏览器会在回调函数中传递三个参数:
* 第一个参数:当前正在遍历的元素
* 第二个参数:当前正在遍历的元素的索引
* 第三个参数:当前正在遍历的数组
*/

arr.forEach(function (value, index, obj) {
console.log(value);
});

slice()和 splice()

slice()

  • 可以用来从数组中提取指定元素
  • 参数:截取开始的位置索引,截取结束的位置索引 (前闭后开,第二个参数可以不写,默认取到最后一个元素)
  • 索引可以是负值 -1 为最后一个元素

splice()

  • 删除元素并向数组添加新元素
  • 将指定元素从原数组中删除并返回
  • 参数:
    第一个:开始位置索引
    第二个:删除的数量
    第三个及以后:
    可以传递一些新的元素,这些元素会自动插入到开始索引位置之前

数组去重

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//创建一个数组
var arr = [1, 2, 3, 2, 1, 3, 4, 2, 5];

//数组去重1
//获取数组中的每一个元素(遍历)
for (var i = 0; i < arr.length; i++) {
//遍历当前元素后的所有元素
for (var j = i + 1; j < arr.length; j++) {
//判断两个元素值是否相等
if (arr[i] == arr[j]) {
//相等则证明出现重复,删除j对应元素
arr.splice(j, 1);
//当删除了当前j所对应元素后,后面的元素会自动补位
//导致j新对应的元素无法进行比较,如果该元素依旧重复则会遗漏
//故使j自减
j--;
}
}
}

console.log(arr);

数组其他方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*	1
* concat()可以连接两个或多个数组,并将新的数组返回
* - 该方法不会对原数组产生影响
* - 参数也可以为元素
*/

/* 2
* join()方法可以将数组转换为一个字符串
* - 该方法不会对原数组产生影响,而是将转换后的结果返回
* - 可以指定一个字符串作为参数,它将会作为数组元素的连接符,不填则默认为","
*/

/* 3
* reverse()方法可以用来反转数组
* - 会直接操作原数组
*/

/* 4
* sort()用来对数组元素进行排序
* - 改变原数组
* - 默认按照Unicode编码进行排序,即使对纯数字数组排序一会按照Unicode编码排序
* - 我们可以自己指定排序规则,在sort()添加一个回调函数
* 回调函数中需要定义两个形参
* 浏览器将会分别使用数组中的元素作为实参去调用回调函数
* - 浏览器会根据回调函数的返回值来决定元素的顺序
* 如果返回一个大于0的值,则元素交换位置
* 如果返回一个小于0的值,则元素位置不变
* 如果返回一个0,则认为两个元素相等,不交换位置
*/

var arr = [5, 4];

arr.sort(function (a, b) {
return a - b; //升序
//return b - a; //降序
});

包装类

在 JS 中为我们提供了三个包装类,通过包装类可以将基本数据类型转换为对象

  • String()

  • Number()

  • Boolean()

    但是我们在实际应用中不会使用基本数据类型的对象。

方法和属性只能添加给对象,不能添加给基本数据类型

当我们对一些基本数据类型去调用属性和方法时

浏览器会临时使用包装类将其转换为对象,然后再调用属性和方法

1
2
3
4
5
6
7
8
var s = 123;

s = s.toString(); //转换1

s.hello = '你好'; //转换2

//console.log(typeof s); //string
console.log(s.hello); //undefined //转换3 此时对象与转换2时不是同一个

字符串相关操作

在底层字符串是以字符数组的形式保存的

length 属性:获取字符串长度

方法

  • charAt() 可以返回字符串指定位置的字符

  • charCodeAt() 返回指定位置字符的 Unicode 编码

  • String.fromCharCode() 可以根据字符编码获取字符

  • concat() 连接字符串

  • indexOf()

    ​ 检查一个字符串中是否含有指定内容,如果有则返回第一次出现的索引,没有则返回-1

    ​ 可以指定第二个参数,表示开始查找的位置

  • lastIndexOf() 与从后往前找

  • slice() 从字符串中截取指定内容,同数组 slice()方法

  • substring()

    ​ 和 slice()类似,不同的是此方法参数不接受负值,传递负值则默认使用 0

    ​ 还会自动调整参数位置,如果前大于后,则交换位置

  • split()

    ​ 可以将一个字符串拆分为一个数组

    ​ 需要一个字符串作为参数,根据该字符串拆分数组

  • toUpperCase(), toLowerCase() 转大小写

正则表达式

  • 创建正则表达式对象

    语法:var 变量名 = new RegExp(“正则表达式”[,”匹配模式”]);

  • 使用字面量来创建正则表达式

    语法:var 变量名 = /正则表达式/匹配模式;

匹配模式: i 忽略大小写 g 全局匹配

正则表达式的方法:

​ test() 使用这个方法可以传入一个字符串来检查其是否符合正则表达式,返回布尔值

注意:使用构造函数创建正则表达式时,由于它的参数是一个字符串,而 \ 是字符串中的转义字符,如果要使用 \ 则需要使用 \\ 来代替

语法

方括号

用于查找某个范围内的字符

表达式 描述
[abc] 查找方括号之间的任何字符。
[^abc] 查找任何不在方括号之间的字符。
[0-9] 查找任何从 0 至 9 的数字。
[a-z] 查找任何从小写 a 到小写 z 的字符。
[A-Z] 查找任何从大写 A 到大写 Z 的字符。
[A-z] 查找任何从大写 A 到小写 z 的字符。
[adgk] 查找给定集合内的任何字符。
[^adgk] 查找给定集合外的任何字符。
(red|blue|green) 查找任何指定的选项。

元字符

元字符(Metacharacter)是拥有特殊含义的字符:

元字符 描述
. 查找单个字符,除了换行和行结束符。
\w 查找单词字符。
\W 查找非单词字符。
\d 查找数字。
\D 查找非数字字符。
\s 查找空白字符。
\S 查找非空白字符。
\b 匹配单词边界。
\B 匹配非单词边界。
\0 查找 NUL 字符。
\n 查找换行符。
\f 查找换页符。
\r 查找回车符。
\t 查找制表符。
\v 查找垂直制表符。
\xxx 查找以八进制数 xxx 规定的字符。
\xdd 查找以十六进制数 dd 规定的字符。
\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。

量词

量词 描述
n+ 匹配任何包含至少一个 n 的字符串。
n* 匹配任何包含零个或多个 n 的字符串。
n? 匹配任何包含零个或一个 n 的字符串。
n{X} 匹配包含 X 个 n 的序列的字符串。
n{X,Y} 匹配包含 X 至 Y 个 n 的序列的字符串。
n{X,} 匹配包含至少 X 个 n 的序列的字符串。
n$ 匹配任何结尾为 n 的字符串。
^n 匹配任何开头为 n 的字符串。
?=n 匹配任何其后紧接指定字符串 n 的字符串。
?!n 匹配任何其后没有紧接指定字符串 n 的字符串。

字符串和正则表达式相关的方法

  • split() 可以将字符串拆分成一个数组

    可以传递一个正则表达式作为参数

    不需要设置全局匹配模式

  • search() 可以搜索字符串中是否含有指定内容

    如果搜索到则返回第一次出现的索引,没有则返回-1

    可以接收一个正则表达式作为参数

    只会匹配第一个,即使设置了全局匹配模式

  • match() 可以根据正则表达式,将内容从字符串中提取出来

    默认只会找到第一个符合要求的内容,我们可以设置全局匹配模式以匹配所有内容

    该方法会将匹配到的内容封装到一个数组中返回,即使只有一个结果

  • replace() 可以将字符串中指定内容替换为新内容

    参数:1.被替换内容(可以接收正则表达式) 2.新的内容

    默认只会替换第一个匹配项

    将新内容设置为空串可以用于删除指定内容