js 编写位置

  1. html 属性中(不推荐)
  2. script 元素中(常见)
  3. 外部引入独立的 js 文件
    通过src属性引入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 1.编写位置一:在html内部(了解) -->
<a href="#" onclick="alert('百度一下')">点我百度</a>
<a href="javascript:alert('哈哈哈')">google一下</a>
<!-- 2.写在script元素内(常见) -->
<a href="#" class="bing">bing一下</a>
<script>
var bingEl = document.querySelector('.bing');
bingEl.onclick = function () {
alert('bing一下');
};
</script>
<!-- 3.编写独立的js文件(代码多) -->
<a href="#" class="demo">我是demo</a>
<script src="./demo.js"></script>

noscript

对于不能处理 js 的浏览器,我们需要给用户一个提示,页面的优雅降级的处理方案

1
2
3
4
5
6
<noscript>
<h1>你的浏览器不支持js,请打开对应的渲染或者更换浏览器</h1>
</noscript>
<script>
alert('你的浏览器正在运行js代码');
</script>

编写 js 的注意事项

  • 注意一:script 元素不能写成单标签,而且外部引入的 js 标签内不要再写
  • 注意二:type=”text/javascript”现在可以不写,js 是所有现代浏览器和 html5 默认的脚本语言
  • 注意三:把 script 标签放在 body 最后面

script 还有 defer、async 属性,我们后续再讲解

交互方式

1
2
3
4
5
6
7
8
9
10
11
12
13
//1.交互方式一:alert函数
// alert('hello world');

//2.console.log函数 (最常用)
console.log('hello world');
// message.length;

//3.交互方式三 document.write()
document.write('hello alex');

//4.交互方式4 prompt函数 接受用户输入的内容
var result = prompt('请输入你的名字');
alert(result + '是个瓜皮');

console.log 一定要掌握

js 语句和分号

  • 语句(statements)是向浏览器发出的指令,通常表达一个操作或者行为(Action)
  • 分号(semicolon)

推荐:
前期对 js 语法不熟练的情况下 写分号
后期熟练以后 任意

js 注释

  • 单行注释 ctrl+斜杠 mac:command+斜杠
  • 多行注释 alt+shift+a mac:option+⬆️+a
  • 文档注释 单独的 js 文件才能生效 在函数上方输入/**回车即可

注意:js 不支持多行注释的嵌套

变量(variable)和数据类型(data-type)

  • 程序中数据会改变的量
  • 盒子,存储某一个东西

变量的定义

  1. 变量的声明:使用 var 关键字(后续 es6 还有 let const)
  2. 变量的赋值:使用 = 给变量进行赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//第一步:变量的声明
// var关键字
// 第二步:变量的赋值 用 = 即可
var currentTime = '14:46';

//其他的写法
var nextTime;
nextTime = '16:00';
// 其他的写法二 同时声明多个变量(不推荐,阅读性比较差)
var name, age, height;
name = 'alex';
age = 99;
age1 = '99';
height = 1.7;
console.log(age, age1, height, currentTime, nextTime);
//补充
// 1.当我们实际使用/打印变量的时候,实际上是在使用里面的值
// 2.console.log(参数1,参数2,参数3,.......)

语法:var 名字 = 值

变量的命名规范

规则

必须遵守,不然就报错

  1. 一个变量只能有数字,字母,美元符号$,以及下划线_组成
1
2
3
4
5
6
var num123
var $123
var q_q
var $0_0$
var $$$$
var $# //报错
  1. 一个变量不能由数字开头
1
2
var n1=100
var 1n//报错
  1. 严格区分大小写
1
2
var num = 100;
var nUm = 200;
  1. 不要使用关键字和保留字 (https://developer.mozilla.org/zh-CN/docs/web/javascript/reference/lexical_grammar)
  2. 不要使用中文

规范

建议你遵守

  1. 变量语义化
    userName,password

  2. 赋值符号(=)左右两边都加上空格

  3. 驼峰命名 userName✅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 交换两个变量的值
var a = 10;
var b = 20;
console.log('交换前', a, b);
// //方法一:借助第三个变量
// var temp = a; //a:10 b:20
// a = b; //a:20 b:20
// b = temp; //a:20 b:10
// console.log('交换后', a, b);

// //方法二:不允许使用第三个变量
// a = a + b; //a:30 b:20
// b = a - b; //a:30 b:10
// a = a - b; //a: 20 b:10
// console.log('交换后', a, b);

// 方法三:
[a, b] = [b, a];
console.log(a, b);

变量的注意事项

  1. 注意一:如果一个变量未声明就直接使用,会报错
  2. 注意二:如果一个变量有声明,但是没有赋值,那么默认值是 undefined
  3. 注意三:如果没有使用 var 声明变量也可以,但是禁止(实际上是会添加到 window 上的)

数据类型

在 js 中一共有 8 种基本的数据类型(7 种原始类型和一种复杂类型)
Number
String
Boolean
Undefined
Null
Object

BigInt(后续了解):Symbol 指的是独一无二的值。每个通过 Symbol() 生成的值都是唯一的。
Symbol(后续了解):一种数字类型的数据,它可以表示任意精度格式的整数。而在其他编程语言中,可以存在不同的数字类型,例如:整数、浮点数、双精度数或大斐波数。

typeof 操作符

确定任意变量的数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1.typeof 基本操练
var info = 'jack';
info = 18;
console.log(typeof info);

//2. typeof其他类型
var age;
console.log(typeof age); //undefined

//3. null的操作
var address = null;
console.log(typeof address); //object

//4. ()的作用
//4.1调用函数
// alert('123')
//4.2 将某一个表达式当作一个整体
var res = (2 + 3) * 4;
console.log(res);
  • 对一个值使用 typeof 操作符 会返回下列字符串之一
    “undefined”表示值未定义
    “boolean”表示为布尔值
    “string”表示为字符串
    “number”表示为数字
    “object”表示为对象或 null
    “function”表示为函数
    “symbol”表示值为符号
  • typeof 的用法
    可能还会遇到 typeof(xxxx),他与 typeof xxxx 一样只是把后面当作整体,并不是函数调用

js 数据类型

Number 类型

  • number 类型代表整数和浮点数

  • 数字 number 可以做很多操作,比如+ - * /等等

  • 除了常规的数字,还有所谓的特殊的数值:(-)Infinity、NaN、Number.MAX_VALUE、Number.MIN_VALUE

  • isNaN 判断是不是 NaN

    1
    2
    3
    4
    5
    // isNaN 用于判断是不是NaN
    var res = 123;
    var res1 = NaN;
    // 123->false NaN->true
    console.log(isNaN(res), isNaN(res1));
  • 后续还会讲解

String 类型

  • js 的字符串必须被括在引号内,有三种方式
    双引号,单引号,反单引号(ES6)
  • 转义字符(实际开发用的少,要用的时候去查) ' " \t \n
  • 字符串本身还有一些操作

拼接字符串+

1
2
3
4
var nickName = 'alex';
var info = 'my name is';
console.log(info + nickName);
console.log(`my name is${nickName}`); //babel 自动帮我们做处理

获取字符串的长度

1
2
var msg = 'hello world!';
console.log(msg.length); //12 有12个字符,空格也算

Boolean 类型

  • 布尔类型用于表示真假
    仅包含两个值:true(真),false(假)
1
2
3
4
5
6
7
8
//是否登陆
var isLogin = false;
//是否是管理员
var isAdmin = true;
var flag = true;
//这里的 = = 后续讲,他是一个比较运算
var result = 1 == 1;
console.log(result);

Undefined

Undefined 类型只有一个值,就是 undefined
如果我们声明一个变量,但是没有对其进行初始化操作,它默认就是 undefined

1
2
3
4
5
6
7
8
9
10
11
var name;
console.log(name); //空字符串,因为name比较特殊,window上有一个默认的name:""
var message;
console.log(message); //undefined

//可以这样,但是不推荐
var info = undefined;
//一般初始化的时候
var num = 0;
var str = '';
var obj = {};

Object 类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//1.Object类型的基本使用
// var name = 'alex';
// var age = 18;
// var height = 2.9;
var person = {
name: 'alex',
age: 18,
height: 2.9,
isAdult: true,
};
console.log(person);
//2.对象类型中的某一个属性
console.log(person.name);
var msg = 'hello world!';
console.log(msg.length); //包装成一个对象 .去拿到他的length属性值
  • Object 类型是一个特殊的类型,我们通常把它称为引用类型或者复杂类型
  • 语法:var 变量名={key:value,key:value...}

Null 类型

1
2
3
4
5
6
7
8
9
10
//1.其他类型的初始化
var age = 0;
var str = '';
var isAdmin = false;
var person = null;
// 在初始化对象的时候,我们不建议初始化为{},建议初始化为null
//Null 存在的意义 就是对对象来初始化的,而且在转成布尔值的时候会转成false
if (person) {
console.log('分支语句不会执行');
}
  • Null 类型只有一个值 null
    通常用来初始化对象/释放内存

数据类型的转换

隐式转换:大多数情况下,运算符和函数会自动转换类型
显示转换:我手动做一些操作来转换

字符串 String 的转换

1
2
3
4
5
6
7
8
9
10
11
var num1 = 123;
var age = 18;
var isAdmin = true;
// 1.隐式
var num1Str = num1 + '';
var ageStr = age + '';
var isAdminStr = isAdmin + '';
console.log(typeof num1Str);
//2.显示
var numStr2 = String(num1);
console.log(numStr2, typeof numStr2);

隐式:
一个字符串和另一个进行+操作

显式:
String(你要转换的变量)
toString() 后续学

数字 Number 的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//隐式
var num1 = '8';
var num2 = '4';
var res = num1 + num2;
console.log(typeof res); //string
var res2 = num1 * num2;
console.log(typeof res2); //number
//显示 Number()
console.log(Number(undefined)); //NaN
console.log(Number(true), Number(false)); //1 ,0
console.log(Number(null)); //0
console.log(Number('abc123')); //NaN
console.log(Number(' 123 ')); //234
console.log(Number('')); //0
  • 隐式:在算数运算中 除开+运算以及一边有字符串 其他都是转数字
  • 显式:Number()以及规则

Boolean 的转换

1
2
3
4
5
6
7
8
//隐式
//1.分支语句
//显示 Boolean()
console.log(Boolean('')); //false
console.log(Boolean('0')); //true
//个人技巧 - 双取反转布尔
var info = 'hahahah';
console.log(!!info);

显示:Boolean()
0,null,undefined,NaN,””->false
其他->true

运算符

运算符(operators)和运算元

计算机最基本的操作就是执行运算,执行运算就需要使用运算符
比如 console.log(20 + 30),+就是一个运算符
算数运算符/赋值运算符/比较运算符/逻辑运算符/(位运算符)

运算元 – 运算符的应用对象
5 * 2
左运算元 5,右运算元 2

算数运算符

  • 算数运算符在数学表达式中,+ - * /,在 js 里面和在数学中的使用一样
  • 取余%
  • 求幂(es7 也叫 es2016) ** 2**3 = 8

赋值运算符

  • =也是一个运算符,被称为赋值(assignments)运算符
  • 链式赋值(chaining assignments)
1
2
3
var a, b, c;
a = b = c = 2 + 2;
console.log(a, b, c); //4 4 4

链式赋值是从右到左计算
先对 2+2 进行求值,然后将它赋给:c,b 和 a
最后 abc 三个变量共享一个值

不推荐这种写法

原地修改(Modify-in-place)

对一个变量做运算,并将新的结果存储在同一个变量中

1
2
var blood = 10000;
blood = blood + 100;

上述的操作可以用+=来表示

1
2
var blood = 10000;
blood += 100;

所有的算术运算符(和位运算符)都可以有简短的修改并赋值的运算符
+= -= /= *= **= %=

自增和自减

  • 对一个数进行加一,减一是实际开发中非常常见的操作
  • 因此,有了专门的运算符
  • ++将会让变量+1 --将会让变量 -1
1
2
3
var currentIndex = 5;
currentIndex--;
console.log(currentIndex); //4

++和–的位置

  • 可以放到变量前面,也可以放到变量后面
    当运算符置于变量前:前置形式(prefix form) ++count
    当运算符置于变量后:后置形式(postfix form) count++
  • 有区别?
    有,但是只有当我们使用++/–的返回值时才能看到区别
    如果不使用,那没什么区别
    如果你想要对变量自增,而且需要立即使用自增的结果那就用前置
1
2
3
4
5
6
7
8
9
10
11
12
var currentIndex = 5;
// //自己 ++或者-- 放在前/后 没有任何区别
// ++currentIndex;
// console.log(currentIndex);
//-----elegant hr-----
//如果自增或者自减的表达式本身参与了其他运算,那就有区别
// var res1 = 100 + currentIndex++;
// console.log(currentIndex); //5+1=6
// console.log(res1); //100+5=105
var res2 = 100 + ++currentIndex;
console.log(currentIndex); //6
console.log(res2); //100+(5+1)=106

先用后加,先加后用

运算符的优先级

1
2
3
var res = 2 + 3 * 5; //17
var num = 5;
var result = 2 + 3 * ++num; //20

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence

比较运算符

  • 比大小
    大于/小于:a>b,a<b
    大于等于/小于等于:a>=b,a<=b
    检查两个值是否相等:a == b a === b
    检查两个值不等:a!=b a !== b
  • 比较运算符的结果永远是布尔值

=====的区别

===类型也要一致,==不用

1
2
3
4
5
6
7
8
9
var foo1 = 0;
var foo2 = '';

// == 在类型不相同的情况下会先把运算元转成Number值,再进行比较--隐式转换
console.log(Number(foo1));
console.log(Number(foo2));
console.log(foo1 == foo2); //true
// === 运算符 在类型不同的情况下,直接返回false
console.log(foo1 === foo2); //false

== 的补充(了解)

https://262.ecma-international.org/5.1/#sec-11.9.3

1
2
3
4
5
6
7
8
9
10
var info = {
name: 'jack',
age: 18,
//重写对象的函数
//用[]表明是一个计算属性
[Symbol.toPrimitive]() {
return 456;
},
};
console.log(info == '456'); //true

分支语句

理解代码块

  • 代码块是多行执行代码的集合,通过一个花括号{}放在一起
1
2
3
4
5
6
7
8
9
10
11
//是代码块
{
var name = 'alex';
var message = 'my name is ' + name;
console.log(message);
}
//是对象
var info = {
name: 'haha',
age: 18,
};

什么是分支结构

在开发中,我们经常需要根据一定的条件,来决定代码的执行方向
如果条件满足,执行某些代码(块)
如果条件不满足,执行另外的代码(块)

分支结构的代码就是让我们根据条件来决定代码的执行

别名:判断结构或者选择结构

js 中:if 和 switch

if 分支语句

if 的分支的结构有三种
if...
if...else...
if...else if...else...

if…

if(…)计算括号里的条件表达式,如果计算结果是 true,那就执行相应的代码块

1
2
3
4
//如果条件成立,那么执行代码块
if (条件判断) {
//执行的代码块
}

案例:

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
if (10 > 3) {
console.log('10比3大');
}
//1.超过90分这周五不晚修
//声明变量保存分数
var score = 80;
if (score > 90) {
console.log('这周五不晚修');
}
//2.苹果单价5元/斤 如果购买数量超过3斤,那就立减3元
var price = 5;
var weight = 7;
var totalPrice = price * weight;
//根据购买的数量来决定是否-3
if (weight > 3) {
totalPrice -= 3;
}
//3.播放列表 currentIndex '0 1 2' 3首歌 如果已经是第三首歌 currentIndex=0
// ['bad girl','只因你太美','情人']
// 0 1 2
var currentIndex = 2;
//这里是播放的代码块
{
}
currentIndex++;
if (currentIndex === 3) {
currentIndex = 0;
}

if 语句的细节补充

  1. 如果代码块中只有一行代码,那么{}可以省略
  2. if(…)会计算圆括号内的表达式,并且将结果转换为布尔类型
    1. 转换规则和 Boolean 函数一样
    2. 0 '' undefined null NaN都会被转成 false,其他值被转成 true(真值)

if…else…语句

多分支语句:if…else…
if 语句包含了一个可选的”else 块”
如果条件判断不成立,就会执行 else 块的代码

1
2
3
4
5
if (条件判断) {
//条件成立,执行的代码块
} else {
//条件不成立,执行的代码块
}

案例

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
var m = 20;
var n = 10;
if (m > n) {
console.log('m比n大');
} else {
console.log('m不比n大');
}

//1.分数超过90去长隆,否则去补习班
var score = 89.9;
if (score > 90) {
console.log('去长隆happy');
} else {
console.log('去上补习班~');
}
//2.比较num1和num2的大小,获取较大的数字
var num1 = 12 * 6 + 7 * 123 + 3 ** 2;
var num2 = 789 * 2 + 123 ** 2;

var res = 0;
if (num1 > num2) {
res = num1;
} else {
res = num2;
}
console.log(res);

if…else if…else…语句

判断多个条件
else if

1
2
3
4
5
6
7
8
9
if (条件) {
//代码块1
} else if (条件) {
//代码块2
} else if (条件) {
//代码块3
} else {
//代码块4(所有条件都不成立)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// >90 优秀
// 90~>80 良好
// 80~60 及格
// <60 不及格
var score = prompt('请输入你的分数');
score = Number(score);
//edge deal 分数越界
// if (score > 100 || score < 0) alert('不是一个合法成绩数值');
if (score > 90) {
alert('优秀');
} else if (score > 80) {
alert('良好');
} else if (score >= 60) {
alert('及格');
} else {
alert('不及格');
}

三元运算符

1
2
var result = condition ? value1 : value2;
计算结果,如果condition为真则返回value1,如果为假则返回value2

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 案例一:比较两个数字
var num1 = 123;
var num2 = 123212;
//三元运算符
var result = num1 > num2 ? num1 : num2;
console.log(result);
//案例二:输入年龄判断是否成年
var age = prompt('请输入您的年龄');
age = age - 0;
var message = age >= 18 ? '成年人' : '未成年';
alert(message);
//案例三(了解) 给变量赋默认值
var info = {
name: 'alex',
};
var obj = info ? info : {};

逻辑运算符

认识逻辑运算符

  • 主要是三个符号
    ||(或) &&(与) !(非)

    运算符 运算规则 范例 结果
    && 与:同时为真 true&&true True
    || 或:一个为真 true||false True
    ! 非:取反 !false True

    有了逻辑运算符,我们就可以在判断语句中书写多个条件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var chineseScore = 88;
    var mathScore = 99;
    //1.逻辑与:&& 并且
    //条件1&&条件2&&条件3...
    if (chineseScore > 90 && mathScore > 90) {
    console.log('去长隆');
    }
    //2.逻辑或 || 或者
    if (chineseScore > 90 || mathScore > 90) {
    console.log('打一个小时游戏~');
    }
    //3.逻辑非 ! 取反
    var isLogin = true;
    if (!isLogin) {
    console.log('引导他跳去登陆页登陆');
    }
    console.log('正常访问页面');

逻辑或的本质

  • ||表示或运算符(短路或)
    res = a||b
    从左到右计算操作数
    处理每一个操作数的时候,都将其转化为布尔值
    如果结果是 true,就停止计算,返回这个操作数的初始值
    如果所有的操作数的结果都是 false,则返回最后一个操作数的初始值

  • 注意:返回的值是操作数的初始值,不是 Boolean 之后的

  • 一个||链,将返回第一个真值,如果不存在真值,就返回该链的最后一个值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 
1.把运算元转成Boolean类型
2.对转成Boolean类型进行判断
如果为true,直接把结果(primitive Value)返回
如果为false,进行第二个运算元的判断
以此类推
*/
var chineseScore = 95;
var mathScore = 99;
//chineseScore > 90 为true,那么后续的所有判断不再走
if (chineseScore > 90 || mathScore > 90) {
}
//获取第一个有值的结果/兜底默认值处理
var info = null;
var message = info || '我是默认值';
console.log(message.length); //3

console.log(true || false); //true
console.log(NaN || 1 || true || false || 1 > 2); //1
console.log('abc' || 123); //'abc'
console.log(0 || ' '); //" "
10 > 20 || console.log('haha'); //'haha'

遇真短路

逻辑与的本质

  • &&表示与运算(短路与)
  • 从左到右依次计算操作数
  • 在处理每个操作数时,都将其转成布尔值
  • 如果结果是 false,就停止计算,返回该 Operators 的 primitive value
  • 如果所有的操作数都被计算过,则返回最后一个操作数的原始值
  • 与运算 返回第一个假值,如果没有 false value,返回最后一个值
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
// 运算元1 && 运算元2 && 运算元3...
/*
1.拿到第一个运算元,将运算元转成布尔值
2.对布尔值进行判断
如果false,返回运算元的原始值
如果true,继续下一个运算元
依此类推
3.如果所有的运算元都为true,那就返回最后一个运算元的初始值
遇假短路
*/
var chineseScore = 80;
var mathScore = 99;
console.log(chineseScore > 90 && mathScore > 90); //false

//应用场景
var obj = {
name: 'jack',
// friend: {
// name: 'alex',
// say: function () {
// console.log('giao~');
// },
// },
};
// obj.friend.say();
//实际开发我们也就写两个或者三个,不会写这么长
obj && obj.friend && obj.friend.say && obj.friend.say();

console.log('abc' && null); //null
console.log(null && 'abc'); //null
console.log(null && undefined); //null
console.log(undefined && null); //undefined
10 > 20 && console.log('haha'); //false

非运算

双取布尔反转
求任意变量的布尔值

1
2
var obj = null;
console.log(Boolean(obj), !!obj);

switch 语句

switch 开关 岔路
case 方案 情况
break 打断、断开
default 默认(缺省)

1
2
3
4
5
6
7
8
9
10
switch (常量 / 变量) {
case 常量1:
//语句1
break;
case 常量2:
//语句2
break;
default:
//语句三
}

switch 是全等的(===)判断

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
// switch (常量 / 变量) {
// case 常量1:
// //语句1
// break;
// case 常量2:
// //语句2
// break;
// default:
// //语句三
// }
//上一首0 播放/⏸️ 1 下一首 2
// var btnIndex = 0
// if(btnIndex===0){
// console.log("点击了上一首按钮");
// }else if(===1){

// }else if(===2){

// }else{
// //123
// }
var btnIndex = 0;
switch (btnIndex) {
case 0:
console.log('上一首');
//不加break会穿透
break;
case 1:
console.log('play/pause');
break;
case 2:
console.log('next');
break;
default:
console.log('有问题');
}
  1. 月份案例
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
//月份案例
//已知1~12 表示1~12月
//2月统一按照28天来算
// 根据月份判断当月天数
var month = 12;
//28 30 31
//2 4 6 9 11 1 3 5 7 8 10 12
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
console.log('31天');
break;
case 4:
case 6:
case 9:
case 11:
console.log('30天');
break;
case 2:
console.log('28天');
break;
default:
console.log('请输入正确的月份');
}
  1. 天数案例
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
43
44
45
46
47
48
49
//天数
//求2023 3月 31日是2023 的第几天
var year = 2023;
var month = 3;
var date = 31;
/*
不管年份,2月统一按照28天去算
到最后判断 是否是3月及以上 去看要不要补上一天

先不管日期,计算完月份之后,几号我就多加几天
month ->1
2 加上整个1月的天数
3 加上1,2月的天数
...
12 1+2+3+...+11
*/
var result = 0;
switch (month) {
case 12:
result += 30;
case 11:
result += 31;
case 10:
result += 30;
case 9:
result += 31;
case 8:
result += 31;
case 7:
result += 30;
case 6:
result += 31;
case 5:
result += 30;
case 4:
result += 31;
case 3:
//判断闰年?
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
result += 29;
} else {
result += 28;
}
case 2:
result += 31;
case 1:
//几号就加几天
result += date;
}

循环语句

循环是一种重复运行同一代码的方式
如果是对某个列表进行循环,我们通常也会称为遍历(traversal)或者迭代(iteration)

在 js 中支持三种循环方式
while
do…while
for

1.什么时候开始循环 2.什么时候结束循环 3.步长(step)每次循环变量改变了多少

while 循环

while 循环的语法如下:
当条件成立的时候,执行循环代码
当条件不成立的时候,跳出代码块

1
2
3
while (循环条件) {
//循环的代码 循环体
}

2023-04-03-14-55-21.png

如果条件一直为真,那么就会产生死循环(dead loop)
开发中一定要避免死循环

练习

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//练习一 打印10次hello world
// var count = 0;
// while (count < 10) {
// console.log('hello world');
// count++;
// }
//练习二 打印0-99的数字
// var count = 0;
// while (count < 100) {
// console.log(count);
// count++;
// }
//练习三 计算0~99的数字和
// var count = 0;
// var totalSum = 0;
// while (count < 100) {
// totalSum += count;
// count++;
// }
//练习四 计算0~99的所有odd 奇数和
// if (num % 2 !== 0) {
// console.log('num是odd数');
// }
// var count = 0;
// var totalSum = 0;
// while (count < 100) {
// if (count % 2 !== 0) {
// totalSum += count;
// }

// count++;
// }
// console.log('所有奇数和', totalSum);
//练习五 计算0~99的所有even 偶数和
// var count = 0;
// var totalSum = 0;
// while (count < 100) {
// if (count % 2 === 0) {
// totalSum += count;
// }

// count++;
// }
// console.log('所有偶数和', totalSum);
//优化
var count = 0;
var totalSum = 0;
while (count < 100) {
//0 是偶数
totalSum += count;
count += 2;
}
console.log('所有偶数和', totalSum);

//1~100内所有偶数 打印出来
// var n = 1;
// while (n <= 100) {
// if (n % 2 === 0) console.log(n);
// n++;
// }
//优化
// var n = 2;
// while (n <= 100) {
// console.log(n);
// n += 2;
// }

do…while 循环

do…while 的特点是不管条件成立与否,do 循环体都会先执行一次

1
2
3
4
5
6
7
8
9
do {
//循环体
} while (循环条件);

var num = 1000;
do {
console.log('我执行了一次');
num++;
} while (num < 10);

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//打印10次hello world
var n = 0;
do {
console.log('hello world');
n++;
} while (n < 10);
//计算0~10的数字和
var count = 0;
var sum = 0;
do {
sum += count;
count++;
} while (count <= 10);
console.log(sum); //1+...+10 55
/*
count 0 1 2 ... 8 9 10
sum 0+0 0+0+1 1+2 ... 1+2+...+8 1+..+9 1+...+10
*/

for 循环(重点)

for 循环看上去更加复杂,但是他是最常用的循环形式
语法for(初始变量;条件判断;步长){代码}

1
2
3
for (begin; condition; step) {
//循环代码块
}

注意:循环是条件、步长、代码三者在循环

练习

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
// for (var i = 0; i < 3; i++) {
// console.log(i);
// }
//练习一 打印10次hello world
//练习二 打印0-99的数字
//略
//练习三 计算0~99的数字和
// var totalCount = 0;
// for (var i = 0; i < 100; i++) {
// // totalCount=totalCount+i ;
// totalCount += i;
// }
// console.log(totalCount);
//练习四 计算0~99的所有odd 奇数和
// var totalCount = 0;
// for (var i = 0; i < 100; i++) {
// // totalCount=totalCount+i ;
// if (i % 2 !== 0) {
// totalCount += i;
// }
// }
// console.log(totalCount);
//练习五 计算0~99的所有even 偶数和
// var totalCount = 0;
// for (var i = 0; i < 100; i++) {
// // totalCount=totalCount+i ;
// if (i % 2 === 0) {
// totalCount += i;
// }
// }
// console.log(totalCount);
//优化
var totalCount = 0;
for (var i = 0; i < 100; i += 2) {
// totalCount=totalCount+i ;
if (i % 2 === 0) {
totalCount += i;
}
}
console.log(totalCount);

循环控制语句

  1. break 直接跳出循环,循环结束

  2. continue 跳过本次迭代,继续下一次

1
2
3
4
5
6
7
8
9
//吃包子
for (var i = 1; i <= 10; i++) {
console.log('我吃了一个包子');
if (i === 3) {
console.log('第三个掉地上了,但是剩下的我还要吃');
// break;
continue;
}
}

循环的嵌套

1
2
3
4
5
6
7
8
//for的嵌套
for (var i = 1; i <= 3; i++) {
//外层循环走一步, i增加1
//内层循环走全程,j从1到2
for (var j = 1; j <= 2; j++) {
console.log('i是', i, 'j是', j);
}
}

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//在屏幕上显示一个❤️
// document.write('❤️');
// for (var i = 0; i < 9; i++) {
// document.write('<div>');
// for (var j = 0; j < 9; j++) {
// document.write('❤️');
// }
// document.write('</div>');
// }
//三角❤️
for (var i = 0; i < 9; i++) {
document.write('<div>');
for (var j = 0; j < i + 1; j++) {
document.write('❤️');
}
document.write('</div>');
}

作业相关

1
2
3
4
5
//随机生成数字
// Math.random() [0,1)
//生成一个0~99的随机数 0,1 -> [0,100) 99
var randomNum = Math.floor(Math.random() * 100);
console.log(randomNum);

总结

  • 三种循环
    • while
    • do…while
    • for(;;)
  • break/continue

函数

程序里面的 foo、bar、baz

通常被用来作为函数、变量、文件的命名
目前已经变成了编程术语的一部份
他们本身并没有特殊的用途和意义
被称为“伪变量”

1
2
3
4
//stackover flow
var foo = xxxxxx;
function bar() {}
var baz = {};

函数的定义

函数其实就是某种特定功能的代码的封装

函数的使用:

  1. 声明函数–封装 独立的功能
    在 js 中也可以称为定义函数
    声明函数的过程是对某些功能的封装
    在后续开发中,我们会根据不同的需求定义很多函数

  2. 调用函数–享受封装的成果
    也称为函数调用

函数的作用:提高开发效率,复用代码

1
2
3
4
5
function foo() {
//实现非常复杂的功能
}

foo();

函数的声明和调用

1
2
3
4
5
6
7
8
//声明一个函数
//制作好一个工具,但是这个工具默认情况下是不会被使用的
function sayHello() {
console.log('你好');
}
//函数的调用
//使用工具
sayHello();

语法:

1
2
3
4
5
function 函数名() {
//函数体
//函数封装的代码
}
函数名();

注意:

  1. 函数的命名规则和之前的变量命名规则一样
  2. 函数要尽量做到见名知意(函数通常使用动词更多)
  3. 函数定义完后,里面的代码不会执行,必须调用才会执行

函数的参数

增加函数的通用性,针对相同的逻辑处理,能适应更多变的数据就有了函数的参数

  • 在函数的内部,把参数当作变量使用,进行你要的处理
  • 函数调用时,按照函数参数的定义时的顺序,传递进去
  • 形参(param):定义函数时,小括号中的参数,是用来接受实际参数用的,在函数内部作为变量使用
  • 实参(argument):调用函数时,小括号中的参数,是用来把真实的数据传递到函数内部用的

函数的返回值

return 关键字来定义返回值
return 除了返回值以外还能终止函数的执行

arguments 实参列表

函数内部存在特别的对象:arguments 对象
里面存放的是函数接受到的所有实际参数
他的数据类型是一个 object 类型,伪数组(array-like)

递归

函数自己调用自己有一个专业的名词,叫递归(Recursion)

将一个复杂的任务,转化成重复执行的相同(小)任务。

局部和全局变量

作用域

在 js(es6 之前)中没有块级作用域的概念的,但是函数是可以定义自己的作用域的

  • 作用域(Scope):表示一些标识符的作用有效范围(那块代码这个变量可以使用)
  • 函数作用域:表示在函数内部定义的变量,只有函数内部可以访问到

局部和全局变量

  • 局部变量:定义在函数内部的变量,称为局部变量
  • 外部变量:定义在函数外部的变量,称为外部变量
  • 全局变量:在任何函数中/函数外都能访问到的变量,就是全局变量(var 声明的全局变量是在 window 上添加一个属性)

变量的访问顺序

在函数中访问变量时,优先找自己,如果自己没有就去外部找,最终找到 window 都没有那就报错

  • 块级作用域,作用域链,变量提升,AO,VO,GO 高阶学习

函数的头等公民

函数的表达式(Function Expressions)
在 js 中函数并不是神奇的语法结构,而是一种特殊的值(是一种特殊的对象 Object 类型)
之前我们定义函数的方式–函数的声明(Function Declaration)
其实,还有另一种写法是函数表达式

1
2
3
var foo = function () {
console.log('foo函数');
};

头等函数(first-class-function:第一级函数),指在程序设计语言中,函数被当作头等公民

  • 函数可以作为其他函数的参数、函数的返回值、赋值给变量、或者存储在其他的数据结构中
  • 通常对头等函数的编程方式,称为函数式编程。

回调函数(callback function)

高阶函数必须满足以下两个条件之一

  • 接收一个/多个函数作为参数
  • 输出一个函数(返回一个函数)

立即执行函数

Immediately-Invoked Function Expression(IIFE)

预解析

  • 在所有代码执行之前,对代码进行通读并解释

预解析 var 关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
console.log(num); //undefined
var num = 100;
//解析var关键字
//1. var num
//2. num=100
console.log(num); //100

/*
打开页面
预解析
=> var num
->告诉浏览器我定义一个变量叫num,但是没有赋值
执行代码
= 第一行代码 在控制台打印num
- 因为预解析的时候,已经声明过num变量,但是没有赋值
- num存在,但是没有值
- undefined
= 第二行代码
- 给已经声明的num赋值为100
= 第三行代码
- 打印num变量
- 由于第二行代码的执行 num已经被赋值为100
- 打印100
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn();
var fn = function () {
console.log('我是fn');
};
fn();
/*
1 fn()
2 var fn = funciton(){}
3 fn()
打开页面
预解析
= var fn
- 告诉浏览器我定义了一个变量叫fn,但是没有赋值
执行代码
= 第一行代码 fn()
- 把fn变量名,当作函数名来调用
- 但是fn只是声明了变量,并没有赋值,所以fn是undefined
- 我们就相当于在做 undefined()
- 报错 fn is not a function
*/

解析 声明式 函数

  • 在所有代码执行前,把函数名进行声明提前,并且赋值为一个函数
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
fn();
function fn() {
console.log('我是fn');
}
fn();
/*
1 fn()
2 function fn(){}
3 fn()

打开页面
预解析
= function fn(){}
- 告诉浏览器,我定义了一个fn变量,并且这个fn变量里面保存的是一个函数
代码执行
= 第一行代码 fn()
- 拿到fn变量保存的值,当作一个函数来使用
- 因为预解析阶段,fn里面就是一个函数
- 正常调用 不报错
= 第二行代码
= 第三行代码
- 拿到fn变量保存的值,当作一个函数来使用
- 因为预解析阶段,fn里面就是一个函数
- 正常调用 不报错
*/

预解析的无节操

  1. 不管 if 是否为 true,if 里面的代码依旧会预解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log(num);
if (false) {
//var num会提升到最顶上,但是num=100赋值操作还是留在原地
var num = 100;
}
console.log(num);

//相当于如下代码
var num;
console.log(num); //undefined
if (false) {
num = 100;
}
console.log(num); //undefined
  1. 函数体内,return 后面的代码虽然不执行,但是还是会预解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function fn() {
console.log('我是fn内的代码');
console.log(anum); //undefined 不会报错,因为var anum提升到函数作用域内的顶部

return;
var anum = 100;
console.log(anum);
}
fn();

//相当于如下代码
function fn() {
var anum;
console.log('我是fn内的代码');
console.log(anum);

return;
var anum = 100;
console.log(anum);
}
fn();
  1. 重名问题
  • 当你使用 var 定义的变量 和 声明式的函数 重名的时候 以函数为准
  • 只在预解析阶段以函数为准
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
43
44
num();
var num = 100;
num();
function num() {
console.log('我是num');
}
num();
//相当于如下代码

function num() {
console.log('我是num');
}
var num;
num();
num = 100;
num();
num();

/*
1 num();
2 var num = 100;
3 num();
4 function num() { console.log('我是num');}
5 num();
打开页面
预解析
= var num
- 告诉浏览器我定义了一个变量叫做num,但是没有赋值
= function num(){}
- 告诉浏览器,我定义了一个变量叫num,里面放的是头等公民--函数
= 预解析结束
- num里面放的是函数
执行代码
1 num()
- 拿到num内的值,当作函数调用
- 因为预解析,所以正常调用
2 num=100
- 把变量num内的值,我不管你之前里面是什么,现在是100
- 从此以后,num里面就是100
3 num()
- 拿到num内的值,当作函数调用
- 但是num里面已经是数字100而不是函数
- 报错
*/

面试题

1
2
3
4
5
var a = (b = 10);
a = 20;
b = 20;
console.log(a); //20
console.log(b); //20
1
2
3
4
5
6
7
8
9
var a = b; //报错
//拿到b保存的值赋值给a
//访问b
//自己没有 全局也没有
//访问一个全局都不存在的变量 报错
a = 20;
b = 20;
console.log(a);
console.log(b);

对象

认识对象

  • 在数据类型中一种特别的类型:对象类型
    • 对象类型是一种存储键值对(key-value)的更复杂的数据类型
    • 键值对可以是属性也可以是方法(key 是字符串,value 可以是任意数据类型)
1
2
3
4
var num = {
key: value,
属性名: 属性值,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 
两个术语:函数/方法
函数(function):在js 代码中通过function默认定义的一个结构,称为函数.
方法(method):如果把一个函数放到对象中,作为对象的一个属性,那么将这个函数称为方法.
*/
function foo() {}
foo();
var person = {
//key:value
//key是字符串,但是在定义对象的属性名的时候,大部份情况下可以省略
name: 'alex',
age: 99,
isSingle: false,
hobby: ['唱', '跳', 'rap'],
//key比较复杂一点,""是不能省略的
'my friend': {
name: 'tom',
age: 30,
},
eat: function () {
console.log('嘎嘎香~');
},
};

创建和使用对象

有三种

  1. 对象字面量(Object Literal)
  2. new Object+动态添加属性
  3. new 其他类
1
2
3
4
5
6
7
8
9
10
11
var obj = {
name: 'alex',
};

//2.new Object()
var obj1 = new Object();
obj1.name = 'tom';

//3.new 其他类
function Person() {}
var obj2 = new Person();

对象的常见操作

  • 访问对象的属性 Read
  • 修改对象的属性 Update
  • 添加对象的属性 Create
  • 删除对象的属性 Delete
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.定义一个对象
var person = {
name: 'alex',
age: 18,
friend: {
name: 'chicken',
age: 30,
},
eat: function () {
console.log('嘎嘎香');
},
};
// console.log(person);
//2.访问对象的属性
console.log(person.name);
console.log(person.friend.name);
person.eat();
//3.修改对象中的属性
//一旦你在浏览器的控制台打印整个复杂类型的变量
//你点了展开的小三角,就会显示成这个变量最终的样子
person.age = 81;
// console.log(person);
person.eat = function () {
console.log('滂臭');
};
person.eat();
//4.添加对象中的属性
person.study = function () {
console.log('没日没夜的卷');
};
console.log(person);
//5.删除对象中的属性
// delete 操作符
delete person.study;
delete person.age;
console.log(person);

数组关联语法

当对象的 key 是无效的变量标识符时,可以使用这种语法来获取键值
不能包含空格、不能以数字开头,不能包含特殊字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var obj = {
name: 'jack',
'my friend': {
name: 'alex',
sing: function () {
console.log('嘉宾~');
},
},
'eat something': function () {
console.log('嘎嘎炫~');
},
};
//点访问符就不行啦
// console.log(obj.my friend)
console.log(obj['my friend']);
console.log(obj.name, obj['name']);
//名字太长 单独拿一个变量装起来
var eatKey = 'eat something';
// obj['eat something']();
//千万不要写成 obj.eatKey ,因为这样是访问obj的eatKey属性
// obj.eatKey();
//访问对象不存在的属性
// console.log(obj.age);
obj[eatKey]();

对象的遍历(for…in)

  • 获取对象中所有的属性和方法
  • Object.keys()
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
//我们在开发的时候,可能会要去挨个的拿到里面的每个key以及对应的value
var person = {
name: 'alex',
age: 18,
height: 2.88,
};
// console.log(`key:${'name'},value:${person.name}`);
// console.log(`key:${'age'},value:${person.age}`);
//Object.keys(person)返回对象的自己的可枚举属性组成的数组
//Object.definePorperty Proxy
console.log(Object.keys(person));
//对对象进行遍历
var personKeys = Object.keys(person);
for (var i = 0; i < personKeys.length; i++) {
//personKeys[i]通过索引去拿数组里面的东西
var key = personKeys[i];
//通过数组关联语法去拿对象的东西
var value = person[key];
console.log(`key:${key},value:${value}`);
}
//2.for...in遍历对象
for (var k in person) {
console.log(k);
//key是键名
// console.log(key);
// console.log(`key:${key},value:${person[key]}`);
}
//对象无序 数组有序
//for...of... 默认是不能遍历对象的
for (var foo of person) {
console.log(foo);
}

js 的内存分配(!important)

  • 程序是需要加载到内存中来执行的,内存就可以划分为两个区域:栈内存,堆内存

    • 原始类型占据的空间是在栈内存中分配的
    • 对象类型占据的空间是在堆内存中分配的
  • 原始类型的保存方式:保存的是值本身

  • 对象类型的保存方式:保存的是对象的”引用”

函数本身是放到堆内存的,但是函数的执行是放到栈里面的。

1
2
3
4
5
6
7
8
9
10
11
12
var name = 'jack';
var age = 18;
var obj = {
foo: 'foo',
bar: 123,
};
var message = 'hello' + name;
var info = obj;
info.bar = 456;
//我们想要修改的是info的bar属性
//结果发现obj的bar也变了
console.log(obj);

思考如下现象

  1. 两个对象的比较
1
2
3
4
5
6
7
8
9
var num1 = 123;
var num2 = 123;
console.log(num1 === num2); //true
//两个对象的比较
var obj1 = {};
var obj2 = {};
console.log(obj1 === obj2); //false
console.log(obj1 == obj2); //false
//从内存的角度去解释
  1. 引用传递的内存表现
1
2
3
4
5
6
7
8
9
10
//2.引用的赋值
var info = {
name: 'jack',
friend: {
name: 'alex',
},
};
var friend = info.friend; //{name:'alex'}
friend.name = 'dog';
console.log(info.friend.name); //
  1. 值传递
1
2
3
4
5
6
7
8
9
10
11
12
13
//3.值传递
function foo(a) {
a = 200;
}
var num = 100;
foo(num);
console.log(num); //100

// function sum(a, b) {
// console.log(a + b);
// }
// sum(10, 20);
// sum(100, 200);
  1. 引用传递
1
2
3
4
5
6
7
8
9
10
11
12
//4.引用传递,在函数中创建了一个新对象,没有对传入的对象进行修改
function foo(a) {
a = {
name: 'jack',
};
console.log(a); //'jack'
}
var obj = {
name: 'alex',
};
foo(obj);
console.log(obj); //'alex'
  1. 引用传递
1
2
3
4
5
6
7
8
9
//5. 引用传递,但是对传入的对象进行了修改
function foo(a) {
a.name = 'dog';
}
var obj = {
name: 'alex',
};
foo(obj);
console.log(obj); //dog

this

目前掌握两个 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
//情况一:如果普通的函数被默认调用,this->window
function foo(name, age) {
// console.log(arguments);
// console.log(this);
}
foo('alex', 18); //window
function sayHello() {
// console.log(this);
}
sayHello(); //window
//情况二:如果函数被某一个对象引用,并且调用它,this->对象
var obj = {
name: 'alex',
friend: {
running: function () {
console.log(this);
},
},
running: function () {
console.log(this);
// console.log(this == obj);
// console.log(this === obj);//true
},
};
// obj.friend.running(); //friend
// obj['friend']['running']()

//题目一
var fn = obj.running;
fn(); //window

//题目二
function bar() {
console.log(this);
}
var obj = {
name: 'jack',
bar: bar,
};
obj.bar(); //obj
bar(); //window

this 的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//需要有一个类,类中的方法,实例的方法中,this指向当前的调用对象 new 类()->对象 this
var info = {
name: 'alex',
age: 18,
//1.传递参数
running: function (name) {
console.log(name, '在疯狂的跑');
},
//2.不传递参数,直接写一个info.name
eating: function () {
console.log(info.name, '在疯狂的吃');
},
//3.对象中的函数中的this永远指向该对象
jumpping: function () {
console.log(this.name, '疯狂的跳');
},
};

类和对象的思维方式 (如何创建一系列的对象)

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//创建一系列对象的方法一:
//手动书写很多对象... ❌
var p1 = {
name: '张三',
age: 18,
height: 0.5,
address: '北京',
};
//创建一系列对象的方法二:工厂函数
//for可以创建多个对象,但是这些对象都是tom,不够灵活
for (var i = 0; i < 2; i++) {
var stu = {
name: 'tom',
age: 18,
};
}
//工厂函数(工厂生产stu对象)->设计模式
// function createStudent(name, age) {
// //虽然我只写了一个stu = {}但是每次调用这个函数
// //都会开辟一块新的内存空间
// var stu = {};
// stu.name = name;
// stu.age = age;
// return stu;
// }
// var stu1 = createStudent('张三', 18);
// var stu2 = createStudent('李四', 28);
// var stu3 = createStudent('王五', 12);
//方式三:构造函数创建对象
/*
js默认提供给我们的,更加符合js思维方式,
在函数中this一般指向某个对象
new做了那四件事?
如果一个函数被new操作符 调用
1. 创建出一个新的空的对象
2. 让this指向这个新的空对象
3. 执行函数体
4. 返回该对象
*/
function coder(name, age) {
this.name = name;
this.age = age;
// return {};
//return是原始数据类型 没用
//return 是Object类型 返回值就变成你return的数据
}
//new是keyword 也是operator
var stu1 = new coder('张三', 123); //this->张三
console.log(stu1);
var stu2 = new coder('李四', 19); //this->李四
console.log(stu2);

//补充
//构造函数的名称:使用大驼峰
function Coder() {}
//平时创建普通对象
var person = {};
var person = new Object();

//普通函数:使用小驼峰
function sayHello() {}

/*
eat gaga
大驼峰:EatGaga
小驼峰:eatGaga
*/

类和对象的关系

  • 什么是类(构造函数)?
    • 现实生活中往往是根据 一份描述/一个模版 来创建一个实体对象
    • 编程语言也一样,也必须先有这样一份描述,说明将来创建出来的对象会有哪些属性和方法
  • 在 js 中类的表现形式就是构造函数
    • 构造函数也是函数
    • 如果一个普通的函数被 new 操作符调用了,我们就称他为构造函数
  • 如果一个函数使用 new 操作符调用,那么他会执行如下步骤
    • 在内存中创建一个新的空对象
    • 这个对象内部的[[Prototype]]属性会被赋值为该构造函数的 prototype 属性
    • 构造函数内部的 this,会指向这个创建出来的新对象
    • 执行函数的内部代码
    • 如果构造函数没有返回非空对象,则返回创建出来的新对象

额外补充-全局对象 window

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//浏览器存在的一个全局对象 window
//作用一:查找变量时,最终会找到window上
//作用二:将一些浏览器提供给我们的变量/函数/对象,放在window上
//作用三(了解):使用var定义的变量会默认添加到window上,为什么我们后面不用var而是let/const es6->es5
// console.log(msg); //因为没有 报错
console.log(window); //不会报错
var aname = '123';
function foo() {
console.log(alert === window.alert);
//Object
//new Object()
console.log(Object === window.Object);

console.log(document);

console.log(console === window.console);
var obj = '123';
}
foo();

额外补充-函数也是对象

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
//定义原始类型的变量
var name = 'alex';
var age = 18;
//定义对象类型的变量
var obj = {}; //堆内存 本质上new Object()在堆里面开辟空间给他
var foo = function () {}; //堆内存 函数本身是放在堆内存的 具体调用我们先不管
var foo1 = new Function(); //能这样创建说明函数也是对象
console.log(typeof obj); //object
console.log(typeof foo); //function -> object 更具体
//Object 是Function的父类
//动物类 狗类

//函数是一个对象
var info = {};
info.name = 'abc';

function sayHello() {}
//函数是对象,也可以往对象里面添加属性
sayHello.age = 18;
console.log(sayHello.age);

//有什么用?
function Dog() {}
//构造函数(类) 方法 称为 类方法
//暂时做个了解
Dog.running = function () {
console.log('跑~');
};
Dog.running();

数组 Array

1.认识数组

  • 是 js 的一个复杂数据类型,叫 Array
  • 有序的存放多个数据的一种数据结构

2.创建数组

2.1 字面量创建

空数组:var arr =[]
带有数据的:var arr = [数据 1,数据 2,…]

1
2
3
4
5
6
7
8
9
var arr = [];
var names = ['alex', 'jack', 'lyan', 'tom', 'kern'];
var product1 = { name: '苹果', price: 10 };
var products = [
{ name: '西瓜', price: 20 },
{ name: '键盘', price: 300 },
product1, //开发不推荐这样
//studentName student-name StudentName
];

2.2 构造函数(Array)创建

语法 var arr = new Array(参数)
参数个数为 1:数组的长度,empty 填充
参数个数>1:数组的数据

1
2
3
4
5
var arr1 = new Array(); // []
var arr2 = new Array('abc', 'cba', 'nba'); //['abc','cba','nba']

//传入一个数字,它会默认当成我们想要创建一个对应长度的数组
var arr3 = new Array(5); //[empty*5]

2.3 索引的问题

数组元素索引(index)从 0 开始
js 不支持负索引

1
2
3
var names = ['alex', 'jack', 'lyan', 'tom', 'kern'];
//想访问最后一个元素 js不能写names[-1]
names[names.length - 1];

3 数组的基本操作

3.1 访问数组中的元素

  • 通过中括号[]来访问
  • arr.at(i)
    i>=0 的时候和[]没有区别
    i<0,它是从数组的尾部向前数
1
2
3
4
5
6
var names = ['abc', 'cba', 'nba'];
console.log(names[1]); //'cba'
console.log(names.at(1)); //'cba'
console.log(names[-1]); //undefined
console.log(names.at(-1)); //'nba'
//实际开发很少用这种负数,阅读性太差

3.2 修改数组中的元素

1
2
var names = ['abc', 'cba', 'nba'];
names[0] = 'jack';

3.3 增加数组中的元素/删除数组中的元素(了解)

1
2
3
4
5
6
var names = ['abc', 'cba', 'nba'];
// 0 1 2
console.log('改动前', names);
// names[3] = 'jack';
delete names[0];
console.log('改动后', names);

4.数组的添加、删除方法(一)

push-pop shift-unshift

  • 在数组的尾端添加或者删除元素
    push 添加
    pop 删除
  • 在数组的首端添加或删除元素
    shift 删除
    unshift 添加
1
2
3
4
5
6
7
8
9
10
11
12
13
var names = ['abc', 'cba', 'nba', 'mba', 'abcd'];
//1.在尾部添加和删除元素
// names.push('jack', 'alex', 'tom');
//没有参数,而且一次只能删除一个
//如果需要拿到被删除的可以拿到,不需要的话 不管他
// var a = names.pop();
// console.log(a);
//2.头部
//一次添加多个
names.unshift('jack', 'alex');
//一次只能删除一个
names.shift();
console.log(names);

5.数组的添加、删除方法(二)

splice 可以添加,删除,替换元素
语法
array.splice(start[,deleteCount[,item1[,item2[,...]]]])

中括号不是数组的意思,可选的意思

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// var names = ['abc', 'cba', 'nba', '15-5'];
//参数一:start,从什么位置开始操作元素
//参数二:deleteCount,删除的元素个数
// //1.删除
// names.splice(1, 2);
// //splice方法会直接改变原数组
// console.log(names);
//2.新增
//deleteCount:0,后面可以再跟上其他你要添加的元素
// var names = ['abc', 'cba', 'nba', '15-5'];
// names.splice(1, 0, '麦当劳', 'KFC', '汉堡王');
// console.log(names);
//3.替换元素
var names = ['abc', 'cba', 'nba', '15-5'];
names.splice(1, 1, 'alex', 'jack');
console.log(names);

一般情况下头部和尾部的操作我们还是用那两对,只是在处理中间的情况下更多的用到 splice

6.length 属性

  • length 属性用于获取数组的长度
    当我们修改数组的时候,他的长度会自动更新
  • length 属性另一个特点是可写的
    如果我们修改为一个大于原来的值,会用 empty 填充
    如果小于,数组被截断
  • 清空数组最快办法:arr.length =0

7 数组的遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var dogs = ['二哈', '边牧', '柯基'];
//1. 普通的for遍历
for (var i = 0; i < dogs.length; i++) {
//i索引
//dogs[i]元素
console.log(dogs[i]);
}
//2. for...in遍历
for (var index in dogs) {
console.log(dogs[index]);
}
//3. for...of遍历
for (var item of dogs) {
console.log(item);
}

8 数组方法

8.1 slice

  • arr.slice 用于对数组进行截取(和字符串的类似)
    arr.slice(begin[,end])
    包含 begin 元素但是不包含 end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var dogs = ['二哈', '边牧', '柯基', 'alex', 'jack', 'tom'];
//slice方法,不回修改原数组
//splice会修改原有的数组
//start从什么地方开始
//end结束为止
var res = dogs.slice(2, 4);
console.log(res);
console.log(dogs);
//不传任何参数,把原数组复制一份给我们
var res1 = dogs.slice();
console.log(res1);
//填负数
var res2 = dogs.slice(1, -2);
console.log(res2); // ['边牧', '柯基', 'alex']
//故意找茬
var res3 = dogs.slice(1, 1);
console.log(res3); // []

8.2 concat

创建一个新的数组,其中包含其他数组/其他项的值
进行数组的拼接
arr.concat(value1[,value2[...[,valueN]]])

1
2
3
4
5
6
7
var arr = ['hello', 'world'];
var res = arr.concat('新来的');
var res1 = arr.concat('又一个新来的', [100, 200]);
console.log(res, res1);
var name1 = ['abc', 'cba'];
var name2 = ['alex', 'tom'];
console.log(name1.concat(name2));

8.3 join

把数组的所有元素连接成一个字符串并返回这个字符串
arr.join([连接符])

功能相反的一个字符串方法str.split([连接符])

1
2
3
4
5
6
7
8
9
10
11
12
13
var arr = [10, 20, 30, 40];
console.log('原始数组', arr);
//不传任何连接符,默认使用逗号
var res = arr.join();
console.log(res);
var res1 = arr.join('-');
console.log(res1);
var res2 = arr.join('');
console.log(res2); //10203040
var res3 = arr.join('^.^');
console.log(res3);
var str = 'get-element-by-id';
console.log(str.split('-')); //['get', 'element', 'by', 'id']

9 数组方法-查找元素

9.1 indexOf

arr.indexOf(searchElment[,fromIndex])
从 fromIndex 开始查找,如果找到返回索引,没有找到返回-1,也有对应的 lastIndexOf 方法

9.2 includes

从 fromIndex 开始搜索 searchElment,如果找到了返回 true,找不到的话返回 false
arr.includes(searchElment[,fromIndex])

9.3 find 和 findIndex

都是 es6 新增的

9.3-1 findIndex

1
2
3
4
5
6
7
var names = ['abc', 'cba', 'nnd'];
//item是元素 index索引 arr当前数组
var res = names.findIndex(function (item, index, arr) {
// console.log(item, index, arr);
return item === 'nnd';
});
console.log(res); //2 'nnd'的索引

9.3-2 find
arr.find(function(item,index,arr){})

  • 返回值:你找到的数据
  • 查找条件以 return 的形式来书写
  • 往往用在复杂数据类型中查找
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
var students = [
{ id: 100, name: 'alex', age: 18 },
{ id: 101, name: 'jack', age: 38 },
{ id: 102, name: 'tom', age: 8 },
{ id: 103, name: 'kern', age: 88 },
];
//查找id为101的学生的所有信息
var stu = null;
//自己用for循环配合if寻找
for (var i = 0; i < students.length; i++) {
// students[i]
if (students[i].id === 101) {
stu = students[i];
break;
}
}
console.log(stu);
// if (stu) {
// console.log('展示stu');
// } else {
// console.log('查无此人');
// }
var stu2 = students.find(function (item) {
if (item.id === 101) return true;
});
console.log(stu2);
// //简单的
// var arr = [100, 200, 301, 400, 500];
// //我要查找arr中的奇数
// var res = arr.find(function (item) {
// //以return的形式书写条件
// return item % 2 === 1;
// });
// console.log(res);

//往往会用在复杂类型中查找
var objArr = [
{
id: 1,
name: '迪丽热巴',
age: 18,
},
{
id: 2,
name: '刘亦菲',
age: 19,
},
{
id: 3,
name: 'alex',
age: 29,
},
];
//要求接受用户输入的id,根据id查找🐟的所有信息
var inputId = Number(prompt('请输入你要查询的🐟的id'));
var fish = objArr.find(function (item) {
//条件
return item.id === inputId;
});
alert(fish); //他只能识别字符串,当你想把一个对象强行识别为字符串 就会遇到[object Object]
console.log('你的🐟的完整信息:', fish);

10 数组的排序- sort/reverse

10.1 sort

按照你制定的规则来排序
参数:两个

  • arr.sort() 把数据一位一位的看待
  • arr.sort(a-b) 从小到大排
  • arr.sort(b-a) 从大到小

返回值:排序后的数组

arr.sort[compareFunction]
如果 compareFunction(a,b)<0 那么 a 会排到 b 的前面
如果 compareFunction(a,b)=0 那么 a b 的相对位置保持不变

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
var arr = [1, 3, 100, 10, 5, 4, 2];
console.log('原始的数组', arr);
//不写参数 把每个数据 一位一位的看待
// var res = arr.sort();
var res = arr.sort(function (a, b) {
/*
a=3 b=1 -2<0 3,1
a = 100 b=3 <0 100,3,1
a=10 b=100 >0 100,10,3,1
a=5 b=10 >0 100,10,5,3,1
a=4 b=5 >0 100,10,5,4,3,1
a=2 b=4 >0 100,10,5,4,2,3,1
a=3 b=2 <0 100,10,5,4,3,2,1
*/

//a是下一位数,b是上一位数
// console.group();
// console.log('a', a);
// console.log('b', b);
// console.groupEnd();
//从小到大排
// return a - b;
return b - a; // [100, 10, 5, 4, 3, 2, 1]
});
console.log(res);
console.log('调用方法之后的数组', arr);

//复杂类型排序
var good = [
{ id: 100, name: '麦当劳', price: 25 },
{ id: 101, name: 'KFC', price: 99 },
{ id: 102, name: '华莱士', price: 10 },
{ id: 103, name: '奈雪的茶', price: 28 },
];
good.sort(function (a, b) {
//a.price b.price
return a.price - b.price;
});

10.2 reverse

颠倒数组

1
2
var nums = [1, 2, 3, 4];
nums.reverse(); //[4,3,2,1]

返回值:颠倒之后的数组

11 数组的其他高阶方法

11.1 forEach

遍历数组,并且让数组中的每一个元素都执行一次对应的方法
语法:arr.forEach(function(item,index,arr){})

  • item 数组内的每一项
  • index 每一项的索引
  • arr 被遍历的数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var arr = [1, 2, 35, 66, 10, 6];
//讲过三种数组的遍历 ,新增的一种遍历方式
//a以后都没有必要去写,只是为了记笔记方便
//a函数我们定义 但是不由我们调用 -- 回调函数
var res = arr.forEach(function a(item, index, arr) {
/*
forEach的原理
根据数组成员的数量来调用a函数
*/
console.log(item, index, arr);
});
console.log(res);
var arr1 = [1, 2, 3, 4];
var sum = 0;
arr1.forEach(function (item) {
sum += item;
});
console.log(sum); //10

11.2 every

自带遍历,对每个元素进行条件判断,如果每个元素都成立返回 true,只要有一项不成立返回 false
参数:function(item,index){
item 每个元素
index 元素的索引
}

11.3 some

自带遍历,对每个元素进行条件判断,只要有一个成立返回 true,都不成立才返回 false
参数:function(item,index){
item 每个元素
index 元素的索引
}

1
2
3
4
5
6
7
8
9
10
var arr = [1, 2, 3, 4, 1, 5];
var res = arr.every(function (item) {
return item > 0;
});
console.log(res);

var res1 = arr.some(function (item) {
return item > 4;
});
console.log(res1);

11.4 filter

语法:arr.filter(function(item,index,arr){})
item 每个元素
index 元素对应的下标
arr 当前过滤的数组
过滤条件也是以 return 的形式书写
返回值:过滤后的新数组

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
var arr = [111, 222, 333, 444, 555];
var res = arr.filter(function a(item) {
//以return形式书写过滤的条件
return item > 200;
/*
filter的原理
1.准备一个的空数组 []
2.开始根据数组内的成员调用对应次数的a函数
2-1 a(111) 返回false 111不加到第一步的数组中 []
2-2 a(222) ....

五次都走完之后 [222,333,444,555]
3.返回[222,333,444,555]给res
*/
});
console.log(res); //得到的是条件成立的每一项

var nums = [11, 20, 55, 100, 88, 32];
//只要奇数
//for...of
var newNums = [];
for (var item of nums) {
if (item % 2 !== 0) {
newNums.push(item);
}
}
//filter实现
var res2 = nums.filter(function (item) {
return item % 2 !== 0;
});
// 1 true 0 false
// var res3 = nums.filter(item=>item%2)

11.5 map

  • 语法:arr.map(function(item,index,arr){})
  • 作用:映射数组
  • 返回值:
    • 是一个新数组,并且长度和原数组一致
    • 新数组里面的每一个元素都是根据原数组的每个元素映射出来的
    • 映射的条件以 return 形式书写
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
var good = [
{ id: 100, name: '麦当劳', price: 25, sold: 10 },
{ id: 101, name: 'KFC', price: 99, sold: 2 },
{ id: 102, name: '华莱士', price: 10, sold: 50 },
{ id: 103, name: '奈雪的茶', price: 28, sold: 5 },
];
// 外卖平台 要抽成100

var arr = [100, 200, 300, 400, 500];
//开始映射
var res = arr.map(function a(item) {
return item * 0.1;
});
console.log(res); //[10, 20, 30, 40, 50]
/*
原理
1.准备一个新数组[]
2.根据原始的数据进行遍历
=>第一次调用a(100,0,arr) 返回值10 把10放到新数组里面 [10]
=>第二次调用a(200,1,arr) 20 20 [10,20]
...
[10,20,30,40,50]
3.把最终的数组作为map函数的返回值给到res
*/
var goodNewPrice = good.map(function (item) {
return item.price + 100;
});
console.log(goodNewPrice);

11.6 reduce

  • 语法:arr.reduce(function(prev,item,index,arr){},intial)
    • prev:初始值或者每一次叠加后的结果
    • item
    • index
    • arr
    • intial:默认是 0,表示从什么位置开始叠加
  • 返回值:累加后的结果
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
43
44
45
46
47
48
49
50
51
52
53
54
55
var good = [
{ id: 100, name: '麦当劳', price: 25, sold: 10 },
{ id: 101, name: 'KFC', price: 99, sold: 2 },
{ id: 102, name: '华莱士', price: 10, sold: 50 },
{ id: 103, name: '奈雪的茶', price: 28, sold: 5 },
];
var res1 = good.reduce(function (prev, item) {
return prev + item.price * item.sold;
});

var arr = [100, 200, 300, 400, 500];
var res = arr.reduce(function a(prev, item) {
//以return的形式书写每次叠加的操作
console.log(prev + item);
return prev + item;
/*
reduce的原理
1.准备一个初始值,按照你传递的第二个参数来定
2.根据arr来调用a函数
第一次 a(0,100,0,arr) return 0+100 把这个返回值赋给intial 100
第二次 a(100,200,1,arr) return 100+200 把这个返回值赋给intial 300
第三次 a(300,300,2,arr) return 300+300 把这个返回值赋给intial 600
第四次 a(600,400,3,arr) return 600+400 把这个返回值赋给intial 1000
第五次 a(1000,500,4,arr) return 1000+500 把这个返回值赋给intial 1500
3.把intial的结果当作reduce的返回值 给到res
*/
}, 0);
console.log(res);
//面试题 统计数组中每个元素出现的次数
var arr1 = ['a', 'a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd', 'd'];
var obj = arr1.reduce(function haha(prev, item) {
// if (prev[item]) {
// prev[item] += 1;
// return prev;
// } else {
// prev[item] = 1;
// return prev;
// }
prev[item] = prev[item] + 1 || 1;
return prev;
}, {});
console.log(obj);
/*
1. prev = {} 调用haha({},"a") return {a:1} 并且赋给prev
2. prev= {a:1} 调用haha({a:1},"a") return {a:2} 并赋值给prev
...
最后一次
prev={
a:2,
b:2,
c:3
d:5
}
把最终的prev当作reduce的返回值给到obj
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//综合练习
var nums = [11, 20, 55, 100, 88, 32]; //19168
//过滤得到所有的偶数,映射所有偶数的平方 最后计算他们的和
var total = nums
.filter(function (item) {
return item % 2 === 0;
})
.map(function (item) {
return item * item;
})
.reduce(function (prev, item) {
return prev + item;
}, 0);
// var toal = nums
// .filter(item=>item%2===0)
// .map(item=>item*item)
// .reduce((prev,item)=>prev+item,0)

12 数组塌陷

12.1 概念

  • 当你在数组中删除内容的时候,就会产生塌陷
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, 4];
console.log('原来的数组', arr);
for (var i = 0; i < arr.length; i++) {
arr.splice(i, 1);
}
console.log('删除过后的数组', arr);
/*
[1,2,3,4]
当你把1删除以后
原来的2会往前移动,原来2索引是1 现在索引0
[1,2,3,4] len=4
i=0
arr.splice(0,1)
[2,3,4] len=3
i=1
arr.splice(1,1)

[2,4] len=2
i=2 退出循环
*/

12.2 解决办法

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//方法一:倒着循环
// var arr = [1, 2, 3, 4];
// console.log('原来的数组', arr);
// for (var i = arr.length - 1; i >= 0; i--) {
// arr.splice(i, 1);
// }
// console.log('删除过后的数组', arr);
/*
代码执行
[1,2,3,4] len=4
i=3
arr.splice(3,1)

[1,2,3] len=3
i=2
arr.splice(2,1)

[1,2] len=2
i=1
arr.splice(1,1)

[1] len=1
i=0
arr.splice(0,1)

[] len=0
i=-1
退出循环

*/
//方法二:每次让i跟着变化
var arr = [1, 2, 3, 4];
console.log('原来的数组', arr);
for (var i = 0; i < arr.length; i++) {
//删除一个
arr.splice(i, 1);
i--;
}
console.log('删除过后的数组', arr);
/*
[1,2,3,4] len=4
i=0
arr.splice(0,1)
i-- i=>-1 i=>0

[2,3,4] len=3
i=0
arr.splice(0,1)

[3,4] len=2
i=0
arr.splice(0,1)

[4] len=1
i=0
arr.splice(0,1)

[] len=0
i=0
循环退出
*/

13 数组去重(重点)

13.1 排序后删除相邻

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var arr = [1, 2, 3, 4, 1, 1, 2, 3, 2, 3, 1, 2, 3, 4, 5];
//方案一
//1.排序
arr.sort(function (a, b) {
return a - b;
});
//2.把重复的删除
for (var i = 0; i < arr.length; i++) {
//判断当前的和下一个是否相等
if (arr[i] === arr[i + 1]) {
//删除
arr.splice(i, 1);
//解决塌陷
i--;
}
}
/*
[1(0),1(1),1(2),1(3)] len=15
i=0
arr[i]=== arr[i+1]
i->-1 i++->0
[1(1),1(2),1(3)] len=14
arr[i] === arr[i+1]
*/

13.2 push 时判断是否重复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var arr = [1, 2, 3, 4, 1, 1, 2, 3, 2, 3, 1, 2, 3, 4, 5];
/*
方案2:
1. 准备新数组
2. 遍历原来的数组,一个个添加进去
添加的时候我们需要判断是否已经存在

=>判断数组是否已经存在某个元素
indexOf
=> -1 没有
=> 只要不是-1 有
*/
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) === -1) {
newArr.push(arr[i]);
}
}
console.log(newArr);

13.3 利用对象键名不重复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var arr = [1, 2, 3, 4, 1, 1, 2, 3, 2, 3, 1, 2, 3, 4, 5];
/*
方案:利用对象的key不能重复
1.准备好一个空对象
2.遍历原来的数组把item当作对象的key 添加到对象内
3.把对象变成数组
*/
var obj = {};
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
//item拿到的就是每个元素
//把item作为对象的key添加进去 value不管
obj[item] = '好痛苦';
}
console.log(obj);
console.log(Object.keys(obj));
var newArr = [];
for (var key in obj) {
//键名 key obj[key]
newArr.push(key - 0);
}
console.log(newArr);

13.4 set

1
2
3
4
5
6
7
8
var arr = [1, 2, 3, 4, 1, 1, 2, 3, 2, 3, 1, 2, 3, 4, 5];
/*
es6 有一个数据结构叫set 他天生不允许重复
new Set 创建一个set
... 就是把set里面的东西全部倒出来
[]声明数组
*/
console.log([...new Set(arr)]);

13.5 挨个比较

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
var arr = [1, 2, 3, 4, 1, 1, 2, 3, 2, 3, 1, 2, 3, 4, 5];
/*
去重方法:
1.循环遍历原数组
2.每一个item去和后面所有item挨个比较
如果相同 把后面的item删除splice(?,1)
如果不同 什么都不做
*/
for (var i = 0; i < arr.length; i++) {
//arr[i]就是item
/*
2.判断重复
var arr = [1, 2, 3, 4, 1, 1, 2, 3, 2, 3, 1, 2, 3, 4, 5];
0 1
如果我拿到的是数据1,去检查是否和后面的1相同?
arr.indexOf(1,0+1)// 0数据本身的索引+1 跳过自己往后查找

arr.indexOf(2,1+1)
...
arr.indexOf(数据,数据索引+1)
*/
var index = arr.indexOf(arr[i], i + 1);
if (index != -1) {
//表明后面有重复,删除它
arr.splice(index, 1);
i--;
}
}
console.log('处理完的数组', arr);

总结

  • 数组常用方法通用语法:数组名.xxx()

    • 方法名不同
    • 参数不同
  • 方法
    push-pop shift-unshift
    splice
    slice
    concat
    join - split
    indexOf、lastIndexOf
    includes
    find、findIndex
    sort
    reverse
    forEach(function(item,index,arr){})
    every
    some
    filter
    map
    reduce

  1. 会修改原数组的方法
    push-pop、unshift-shift、splice
    sort、reverse
  2. 不会修改原数组的方法
    slice、concat、join、indexOf、lastIndexOf、
    includes、find、findIndex、forEach、every、some
    filter、map、reduce

常见内置类

包装类型

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
//奇怪现象🤔
var str = 'Hello world';
var num = 18;
console.log(str.length); //拿到字符串的长度 11
console.log(str.split(' ')); // ['Hello', 'world']
//因为js为了可以使得它能够获取属性以及调用方法,自动对它封装了对应的包装类型
//实际上它有自己的构造函数
//他的构造函数的用处,默认情况下直接使用原始数据类型的值的时候 你可以把它的构造函数忽视
//但是 一旦你 访问它的属性/调用它的方法 这个时候js引擎会帮我们做一些事
/*
str = new String(str)
str.length str.xxx()
js引擎就会把它 回收掉 把你的str再次变成原始类型的hello world
*/

//模拟包装的过程
function String(str) {
this.str = str;
this.length = 11;
this.xxx = function () {};
}
var age = 18;
var obj = {
name: 'alex',
eat: function () {
console.log('吃嘛嘛香~');
},
};
//对象调用自身方法
obj.eat();
//对象访问自身属性
console.log(obj.name);
//常见的包装类型 String Number Boolean Symbol BigInt

因为 js 为了可以使其获取属性和调用方法对其封装了对应的包装类型
注意:null、undefined 没有任何方法,也没有对应的“对象包装类”

数字类型 Number 类属性

1
2
3
4
5
6
7
8
//1. Number上的类属性
//Number构造函数->window.Number
var num = 123;
console.log(Number.MAX_VALUE);
console.log(Number.MIN_VALUE);
//integer 整数
console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.MIN_SAFE_INTEGER);

toString 实例方法

toString(base)

  • 将数字转成字符串,并且按照 base 进制
  • base 是进制,默认是 10
  • 如果直接对一个数字操作,需要使用..
1
2
3
4
5
6
7
//2-1 toString(base) 把数字转成字符串
//默认是十进制,可以填入你要的进制,返回的是一个字符串
//base进制
console.log(num.toString(2)); //11
console.log(num.toString(8));
console.log(num.toString(16)); //字符串 没加0x
console.log((123).toString(2));

toFixed 实例方法

toFixed(你要的数字)
toFixed(digits)
格式化一个数字,保留 digits 位的小数
保留方式是四舍五入,返回的是一个字符串

1
2
var pi = 3.1415926;
console.log(pi.toFixed(3)); //3.142

parseInt parseFloat 类方法

Number.parseInt()
parseInt()
这两个方法都没有四舍五入

1
2
3
4
5
6
7
var num1 = '123.521';
console.log(Number(num1).toFixed(0)); //124
console.log(Number.parseInt(num1)); //123
console.log(Number.parseFloat(num1)); //123.521
//其实也有全局的对应方法 放在window上
console.log(parseInt(num1)); //123 不报错
console.log(parseInt === Number.parseInt); //true
  • 开发中常用的是:toFixed,parseInt,parseFloat

数学对象 Math

Math 是一个对象,不是构造函数
属性:Math.PI 圆周率
方法:
Math.floor 向下取整
Math.ceil 向上取整
Math.round 四舍五入
Math.random 随机数 包括 0 不包括 1
Math.pow 求幂

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
// console.log(typeof Number); //function
// console.log(typeof Math); //object
// var num = new Number(123);
//var math = new Math();
//属性 圆周率
console.log(Math.PI); //
//方法 Math.random() [0,1)包括0 不包括1
//其他方法一起使用 [0,1] [a,b]
var num = 3.55;
console.log(Math.floor(num)); //3 向下取整
console.log(Math.ceil(num)); //4 向上取整
console.log(Math.round(num)); //4四舍五入
//生成[5,50)随机数
//[0,45) 50-5=45
//[5,50)
console.log(Math.floor(Math.random() * 45) + 5);
//[5,50] 包括5,包括50
console.log(Math.floor(Math.random() * 46) + 5);
//(5,50) (n,m)
function fullOpen(n, m) {
var res = Math.random() * (m - n) + n;
while (res === n) {
res = Math.random() * (m - n) + n;
}
return res;
}
//求幂
console.log(Math.pow(2, 4)); //2^4=16

String 的补充

String 常见的属性

str.length 返回字符串的长度

字符串的访问

  1. str[索引] str[index]
  2. str.charAt(索引) str.charAt(pos)
    这两种方法正常访问没有区别,但是访问不存在的
    []返回 undfined chatAt 返回空字符
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
var message = 'hello world';
console.log(message.length); //11
//访问字符
console.log(message[0]);
console.log(message.charAt(4));
//区别
console.log(message[20]); //undefined
console.log(message.charAt(20)); //空

//喇叭花数 100~999的一个数,abc = a!+b!+c!
//我们首先需要一个阶乘器
function jc(n) {
var result = 1;
for (var i = 1; i <= n; i++) {
result *= i;
}
return result;
}
console.log(jc(0));
//穷举100~999的所有数字
for (var i = 100; i <= 999; i++) {
var str = i.toString(); //转成字符串
var a = Number(str[0]); //第一位
var b = Number(str[1]);
var c = Number(str[2]);
if (jc(a) + jc(b) + jc(c) === i) {
console.log(i);
}
}

字符串的遍历

方式一:for 循环
方式二:for…of

1
2
3
4
5
6
7
8
9
10
11
var msg = 'alex';
//for遍历
for (var i = 0; i < msg.length; i++) {
console.log(msg[i]);
}
//for...of->可迭代
//两个可迭代:字符串 数组
//String对象内部是将字符串变成了一个可迭代的
for (var char of msg) {
console.log(char);
}

修改字符串

字符串的不可变

1
2
3
4
5
6
7
8
var msg = 'Alex';
console.log(msg[0]); //a
//字符串的不可更改性
msg[0] = '1232131231231231';
console.log(msg); //blex
//大小写转换
console.log(msg.toLowerCase()); //小写
console.log(msg.toUpperCase()); //大写 ALEX

查找字符串

方法一:indexOf
indexOf(你要查找的字符,开始查找的位置)
indexOf(searchString,position)
position 是可选的,不写的时候,从索引为 0 的位置开始查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//方法一 查找字符串的位置
/*
str.indexOf(serachString,[position])
*/
var msg = 'my name is alex,我喜欢ikun';
console.log(msg.indexOf('alex')); //11 返回的是索引
var name = 'alex';
console.log(msg.indexOf(name, 18)); //-1
console.log(msg.indexOf('dog')); //-1 找不到给你返回-1
var index = msg.indexOf(name);
if (index !== -1) {
console.log('变量msg中有变量name');
} else {
console.log('没有找到');
}

方法二:includes
es6 新增的,是否包含字符串
str.includes(searchString,position)

1
2
3
4
5
6
7
8
9
//方法二 es6新增的 是否包含字符串
/*
str.includes(searchString,position)
position可以省略
*/
var msg = 'my name is alex,我喜欢ikun';
var name = 'alex';
console.log(msg.includes(name)); //true
console.log(msg.includes('alex.')); //false

开头和结尾

  • startsWith(你要的字符)
  • endsWith(你要的字符)
1
2
3
4
5
6
7
8
9
var msg = 'my name is alex,我喜欢ikun';
var name = 'alex';
if (msg.startsWith('my')) {
console.log('msg是以my开头的');
}
if (msg.endsWith('ikun')) {
console.log('msg是以ikun结尾的');
}
console.log(msg.startsWith(name)); //false

替换字符串

str.replace(regexp|substr,newSubStr|function)
查找对应的字符串,并且用新的字符串替代
这里可以传入一个正则表达式(regexp)来查找,也可以传入一个函数来替换

1
2
3
4
5
6
7
8
9
var msg = 'my name is alex';
console.log(msg.replace('alex', 'ikun'));
msg = msg.replace('alex', 'ikun');
var name = 'tom';
//函数的情况
var msg2 = msg.replace('ikun', function () {
return name.toUpperCase(); //TOM
});
console.log(msg2);

截取字符串

  • slice(start,end) 从 start 到 end (不包含 end) 允许负值
  • substring(start,end) 从 start 到 end (不包含 end) 不允许负值(负值代表 0)
  • substr(start,length) 从 start 开始截取长度为 length 的字符串 允许 start 为负值
  • 开发推荐 slice
1
2
3
4
5
6
var msg = 'Hello world';

//获取子字符串
console.log(msg.slice(3, 7)); //lo w
console.log(msg.slice(3, -1)); //lo worl
console.log(msg.slice(3)); //lo world

其他方法

  1. str.concat(str,[,…strN]) 拼接字符串
1
2
3
4
5
6
7
8
9
10
var str1 = 'my';
var str2 = 'name';
var str3 = 'kern';
//1.学过的拼接方式 +
var newStr = str1 + str2 + str3;
//2.concat的链式调用
var newStr2 = str1.concat(str2).concat(str3);
console.log(newStr, newStr2);
var newStr3 = str1.concat(str2, str3, 'abc', 'bca', 'nba');
console.log(newStr3);
  1. 删除首尾空格
    str.trim()
1
2
var ipt = '    alex       handsome            '.trim();
console.log(ipt);
  1. 字符串分割
    str.split([separator[,limit]])
    separator:以什么字符进行分割,也可以是一个正则
    limit:限制返回的数量
1
2
3
4
var msg = 'abc-cba-nba-mba';
var arr = msg.split('-');
//把字符串转成数组
console.log(arr);

Array

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
43
44
45
46
47
48
49
50
51
52
53
54
var arr = [10, 20, 30, 40, 50];
//1. 求数组内所有元素的和
var sum = 0;
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
sum += arr[i];
}
console.log(sum);
//2. 求arr内的最大的数
//假设max是最大的
var max = arr[0];
for (var i = 0; i < arr.length; i++) {
//一次和max进行比较,比他大就赋值给max,其他情况不变
if (arr[i] > max) {
max = arr[i];
}
//简写
// max = arr[i] > max ? arr[i] : max;
}
console.log(max);
//3. 求arr1内最小的数字的索引
var arr1 = [100, 200, 1994, 1, 12, 23];
//设minIndex是最小数的索引 arr1[minIndex]
var minIndex = 0;
for (var i = 0; i < arr1.length; i++) {
if (arr1[i] < arr1[minIndex]) {
minIndex = i;
}
}
console.log(minIndex);
//4. 循环得到一个10~20的所有整数,并且放到数组里面
var newArr = [];
for (var i = 10; i <= 20; i++) {
//10 11 12 13 14 15...20
//0 1 2 3
//方法一:利用i和索引的关系
// newArr[i - 10] = i;
//方法二:利用索引添加
newArr[newArr.length] = i;
/*
[] 0 10
[10] 1 11 newArr[1]=11
[10,11]
*/
}
console.log(newArr);
//5.把1000~2000之间的所有的闰年放到数组里面
var year = [];
for (var i = 1000; i <= 2000; i++) {
if ((i % 4 === 0 && i % 100 !== 0) || i % 400 === 0) {
year[year.length] = i;
}
}
console.log(year);

Date 日期

get 获取
year 年
month 月
day 日
date 日期
hour(s) 小时
minute(s) 分钟
second(s) 秒
millSeconds 毫秒
time 时间

new Date()

  • new Date()在不传递参数的情况下返回的是当前时间
1
2
var time = new Date();
console.log(time); //Tue Apr 18 2023 10:13:04 GMT+0800 (中国标准时间)
  • new Date 传递参数的多种情况
  1. 传递两个数字
    第一个数字表示年份,第二个数字表示月份
1
2
3
4
var time = new Date(2023, 10);
console.log(time); //Wed Nov 01 2023 00:00:00 GMT+0800 (中国标准时间)

//月份的范围0~11 0表示1月 11表示12月
  1. 传递三个
    前两个不变,第三个表示该月的第几天 1~31
1
2
var time = new Date(2023, 3, 18);
console.log(time); //今天
  1. 传递四个
    前三个不变,最后一个是小时数 0~23
1
2
var time = new Date(2023, 3, 18, 20);
console.log(time);
  1. 传递五个
    前四个不变,最后一个表示分钟 0~59

  2. 传递六个
    前五个不变,最后一个表示秒 0 ~ 59

  3. 传递字符串

1
2
3
4
5
6
7
var time = new Date('2023');
var time1 = new Date('2023-02');
var time2 = new Date('2023-02-18');
var time3 = new Date('2023-02-18 13:');
var time4 = new Date('2023-02-18 13:13');
var time5 = new Date('2023-02-18 13:13:13');
console.log(time, time1, time2, time3, time4, time5);

日期格式化

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
var time = new Date();

// function Date(){
// this.getMonth=function(){
// return 具体月份
// }
// }
console.log(time);
// Date - time 类(构造函数)-实例对象

//获取年份 2023
console.log(time.getFullYear());
//获取月份 0~11表示
console.log(time.getMonth()); //3
//获取那一天 18号
console.log(time.getDate());
//小时 10
console.log(time.getHours());
//分钟
console.log(time.getMinutes());
//秒钟
console.log(time.getSeconds());
//周几 0~6 0表示周日
console.log(time.getDay());
//获取执行时间到`格林威治时间的`毫秒数
console.log(time.getTime());
//编程中比较特殊的一个时间`格林威治时间的`
//2023 xx xx
//1970年1月1日00:00:00

案例

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
var time = new Date();

//格式化时间
//我们需要从时间中拿到 年 月 日...
//月份 需要+1
//判断 上午/下午
//调整为12小时
//调整星期 0~6
//格式化的结果打印
var year = time.getFullYear();
var month = time.getMonth();
var date = time.getDate();
var hours = time.getHours();
var minutes = time.getMinutes();
var seconds = time.getSeconds();
var day = time.getDay();
//处理月份
month += 1;
//上午/下午
var str = hours >= 12 ? '下午' : '上午';
//12小时
hours %= 12;
//星期
var s = '日一二三四五六';
var week = `星期${s[day]}`;
// m >10?m:0+m
//拼起来
var res = `${year}${month}${date}${str}--${hours}${minutes}${seconds}秒--${week}`;
console.log(res);

获取时间差

封装版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
*
* @param {Date} curr 当前时间
* @param {Date} tar 目标时间
* @return 处理好的时间差对象
*/
function diffTime(curr, tar) {
//curr表示当前时间
//tar表示目标时间
var diffMs = Math.ceil((tar.getTime() - curr.getTime()) / 1000);
console.log(diffMs); //秒
//推算出hour minute second
var day = Math.floor(diffMs / (24 * 60 * 60)); //1.5 1天 xx小时
var hours = Math.floor((diffMs % (24 * 60 * 60)) / (60 * 60));
var minutes = Math.floor((diffMs % (60 * 60)) / 60);
var seconds = diffMs % 60;
return { day: day, hours: hours, minutes: minutes, seconds: seconds };
}

推导和使用

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
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
//时间差
/*
1. 两个时间都转成毫秒数
2. 相减
3. 转回你要的格式
*/
//准备两个时间
var current = new Date();
var target = new Date(2023, 3, 28, 18, 00, 00);
console.log(current, '-----', target);
//求相差的毫秒数 -> 秒
var diffMs = Math.ceil((target.getTime() - current.getTime()) / 1000);
console.log(diffMs); //秒
//推算出hour minute second
var day = Math.floor(diffMs / (24 * 60 * 60)); //1.5 1天 xx小时
var hours = Math.floor((diffMs % (24 * 60 * 60)) / (60 * 60));
var minutes = Math.floor((diffMs % (60 * 60)) / 60);
var seconds = diffMs % 60;
console.log(`${day}${hours}${minutes}${seconds}秒`);
//封装📦到函数里
/*
=>参数 几个参数?
一个是当前时间 一个是目标时间
=>返回值
字符串 `xx天xx时xx分xx秒`
数组 [10,6,59,33] [10,10,10,10]
对象 {day:10,hours:6,minutes:3,seconds:33}
*/

function diffTime(curr, tar) {
//curr表示当前时间
//tar表示目标时间
}
</script>
<script src="./utils.js"></script>
<script>
var time1 = new Date();
var time2 = new Date('2023-11-11 00:00:00');
var res = diffTime(time1, time2);
console.log(res);
</script>
</body>
</html>

定时器

setTimeout(fn,时间)

定时一段时间后执行 fn
时间是毫秒数,写 0/不写 都会“立即执行”
定时器还可以有更多的参数,时间到了以后会把多余的参数按照顺序依次传递给 fn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// setTimeout(function () {
// console.log('时间到了');
// });
setTimeout(
function (a, b, c) {
console.log(a, b, c);
},
1000,
'周末',
'放假',
'去正佳玩'
);
function add(a, b) {
console.log(a + b);
}
//一秒后计算10+20
//add()直接调用了,函数在传递的时候 add
setTimeout(add, 1000, 10, 20);
//清除定时器
var res = setTimeout(add, 0, 10, 20);
console.log(res); //他是一个数字,表示当前页面的第几个定时器是用来清除定时器的
clearTimeout(res); //清除定时器

setInterval(fn,时间)

反复调用定时器
语法和 setTimeout 类似
清除定时器用 clearInterval(res)