JavaScript
一、初始JavaScript
- JavaScript是一种运行在客户端的脚本语言
- 脚本语言:不需要编译(源代码->预处理器->编译器->目标代码->),运行过程中js引擎逐条解析执行成机器语言
- 所以vscode这一类也不是编译器,而是代码编辑器
- 浏览器分成:渲染引擎和JS引擎
1.渲染引擎:解析HTML+CSS,俗称内核,webkit
2.JS引擎:解读JavaScript代码,对其进行运行的,chrome浏览器的V8
1.1、js的组成
二、变量
- 变量:是内存中用来存放数据的空间
- 变量的使用:先声明
var age;
,再赋值age=12
;当只声明不赋值是未定义undefined
;不声明只赋值也可以因为JS是弱类型语言;不声明不赋值直接报错 - 声明变量后,系统自动为变量分配内存空间;通过变量名来访问分配的空间
var age='12';
数字加引号是字符串类型的
2.1、变量命名规范
- 数字,字母,下划线,$组成
- 严格区分大小写
- 遵守驼峰命名法
- 不能数字开头
- 中间不允许空格
- 不能是关键字 尽量不使用name,在一些浏览器中有特殊含义
三、数据类型
3.1、为什么需要数据类型
- 根据数据占有的存储空间不同,可以将数据分为不同的数据类型
3.2、变量的数据类型
- JS的变量数据类型只有在运行中才能确定是什么类型的(在声明的时候不知道是什么类型的;只有在赋值的时候才知道是什么类型的)
var age ;
不知道是什么类型的;var age=12;
是数值型- 变量的类型,可以变化,是动态语言
var age=123;
age='小明'
;所以JS的数据类型,完全取决于最后的赋值
3.3、数据类型的分类
-
操作系统将内存分为堆和栈
-
栈:简单数据类型放在栈里,存放的是值
-
堆:首先在栈中存放地址,栈里的地址会指向堆里面的数据
3.3.1、简单数据类型与运算
数据类型 | 描述说明 |
---|---|
Number | 整形和浮点型(数字型)Infinity:无穷大 ;-Infinity:无穷小 ;NaN:无法计算,非数值 |
String | 字符串类型,带引号'' |
Boolean | true 1/false 0 可以参与数值加法运算的 |
Undefined | 声明了变量,但没赋值;可以和字符做拼接,但是不能和数字相加(NaN) |
Null | var a=null;声明了变量a为空值 ;即为0 |
symbol | ES6新引入,表示独一无二的值 |
- 八进制:以0打头
- 十六进制:以0x打头
- isNaN()用来判断非数字,如果是数字返回false;非数字返回true
console.log(isNaN(12));
返回false - JS推荐使用单引号(也可以外单内双///外双内单)
- 字符串拼接“+”号
- NaN:not a number;非数值
1.
console.log(undefined+'pink')
结果为undefinedpink
2.console.log(undefined+1)
结果是NaN
3.console.log('undefined' + undefined);
结果是undefinedundefined
4.console.log(undefined + undefined);
结果是NaN
5.console.log('null' + null);
结果是nullnull
6.console.log(null + null);
结果是0
7.console.log('null' + undefined);
结果是nullundefined
8.console.log(null + undefined);
结果是NaN
由此可见,加引号的都是拼接;只要有undefined都是NaN;null既是0
3.3.2、复杂数据类型
- 复杂数据类型通过new关键字创建对象
数据类型 | 描述说明 |
---|---|
Object | 对象 |
Array | 数组 |
Function | 函数 |
3.4、检测变量的数据类型
<script>
// 结果输出的是Object,所以null是object类型的
var str = null;
console.log(typeof str);
</script>
- prompt取过来的类型是String类型的
3.5、数据类型转换
3.5.1、转换为字符串
方式 | 案例 |
---|---|
toString | alert(变量.toString()) |
String()强制类型转换 | alert(String(变量)) |
和字符串拼接的都是字符串(+) | alert(变量名+‘字符串’) |
3.5.2、转换为数值型
- parseInt和parseFloat
console.log(parseInt('3.94')); //不会进位
console.log(parseInt('3.14pxsds')); //会去掉非数字
console.log(parseInt('regd3.14fd')); //NaN
// =============================================
console.log(parseFloat('3.152'));
console.log(parseFloat('3.152hjhj')); //去掉字母
console.log(parseFloat('gdshf3.152dsd')); //NaN
实例年龄计算:
var year = prompt("请输入出生年份");
year = parseInt(year);
alert('您现在的年龄是 ' + (2021 - year));
实例计算器:
var num1 = prompt("请输入第一个数值");
var num2 = prompt("请输入第二个数值");
var sum = parseInt(num1) + parseInt(num2);
alert('总和为' + sum);
3.5.3、转换为布尔类型
- 如下都是false,其余是true:
console.log(Boolean(''));//String
console.log(Boolean(0));//Number
console.log(Boolean(undefined));
console.log(Boolean(null));
四、运算符
- 浮点数有精度的问题:所以不要直接判断两个浮点数是否相等
var sum = 0.1 + 0.2; console.log(sum == 0.3);//结果为false
- 取余目的(%)判断数值是否能够被整除
- 自增自减运算符如下:
var num = 1;
console.log(num++);//输出结果为1,先赋值再加
console.log(++num);//输出结果为2,先加再赋值
- == 只要求两侧的值相等即可
- ===全等于,要求两侧的值和数据类型完全一样
- 当两个对象判断是否相等,看是否指向同一个对象地址
逻辑与&&
两侧都为true,结果才为true;逻辑或||
只要一个为true,结果就为true;逻辑非!
- 优先级
!>&&>||
4.1、逻辑中断(逻辑与&&)
- 当表达式1可以得出结果时,还需计算表达式2
4.2、短路中断(逻辑或||)
- 当表达式1可以得出结果时,不再计算表达式2
console.log(213||num++)
不再计算num++
五、流程控制
- 流程控制有三种结构:顺序结构,分支结构
if
,switch
,循环结构 - 三元表达式:简化版的
if..else..
,做分支选择var result = num > 5 ? '是的' : '不是的'
- switch语句多选一 ,针对特定值做选项 ,不写break就会接着向下执行case,直到遇到break或default为止
- 表达式与value做匹配,要求数据类型和值都要一样(全等匹配)
- switch和if else if的区别:
1.switch通常是确定值的情况下使用;if else if则是范围判断的情况下使用
2.switch是判断条件后,直接执行程序的条件语句,效率更高;而if else if是一条一条执行
3.分支比较少的情况下,使用if else if语句效率更高一些
switch (表达式) {
// 表达式与value做匹配,要求数据类型和值都要一样
case value1:
执行语句1;
break;
case value2:
执行语句2;
break;
...
default:
执行最后的语句
}
================================
直接输出8
switch (8) {
case 1:
console.log(1);
// break;
case 2:
console.log(2);
// break;
case 8:
console.log(8);
break;
}
=================================
输出1,2,8
switch (1) {
case 1:
console.log(1);
// break;
case 2:
console.log(2);
// break;
case 8:
console.log(8);
break;
}
六、数组
- 创建数组的两种方式:(1)利用数组字面量(2)利用new Array();
var arr1=new Array(2)
表示数组长度为2
2.var arr1=new Array(2,3)
等价于数组【2,3】两个值以上的才是数组元素
- 将数组中大于10的挑选出来组成新的数组
方式一:
<script>
var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
var newarr = [];
var j = 0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] > 10) {
newarr[j] = arr[i];
j++;
}
}
console.log(newarr);
</script>
===========================================================
方式二:
<script>
var arr = [2, 0, 6, 1, 77, 0, 52, 0, 25, 7];
var newarr = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] > 10) {
newarr[newarr.length] = arr[i];
}
}
console.log(newarr);
</script>
- 翻转数组:
<script>
var arr = ['red', 'green', 'pink', 'purple'];
var newarr = [];
for (var i = arr.length - 1; i >= 0; i--) {
newarr[newarr.length] = arr[i];
}
console.log(newarr);
</script>
- 冒泡排序:嵌套for循环,第一次循环(交换多少趟,数组长度减一),第二次循环(一趟交换的次数,数组长度减趟数)
<script>
var arr = [12, 3, 48, 52, 1, 3, 26];
var temp;
for (var i = 0; i < arr.length - 1; i++) { //要交换多少趟
for (var j = 0; j < arr.length - 1 - i; j++) { //一趟交换多少次
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
console.log(arr);
</script>
七、函数
- 函数:就是封装一段可以重复执行的代码块
- 当实参个数大于形参个数时,会取形参个数其余忽略
- 当实参个数小于形参个数时,结果是NaN
- 函数只有在被调用的时候,才被执行
1.return后面的代码不会被执行;
2.return只能返回一个值,所以当需要返回多个值时,需要将其放到数组中return [num1 + num2, num1 - num2];
3.当函数没有return时,返回undefined
- break,continue,return的区别:
1.break是结束当前循环
2.continue是跳出本次循环,继续执行下次循环
3.return可以退出循环,还可以结束当前函数体中的代码
7.1、arguments
-
当不确定有几个参数传递时,可以用arguments(当前函数的内置对象)来获取,arguments存储了传递的所有实参
-
arguments的简单使用(求任意个数的最大值):
function getmax() {
var max = arguments[0];
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
console.log(getmax(1, 5, 63, 13));
console.log(getmax(111, 5, 63, 13));
7.2、函数的两种声明方式
function 函数名(){}
自定义函数var 变量名=function(){}
函数表达式
八、JavaScript两种作用域
- 作用域:代码名字在某个范围内起作用
- js的作用域(ES6之前):
1.全局作用域:(整个scripted标签或者单独的js文件)注意:在函数内未声明直接赋值的变量是全局变量
2.局部作用域:(在函数内部)注意:函数的形参也可以看作是局部变量
3.全局变量:只有在浏览器关闭的时候才销毁,比较占内存
4.局部变量:程序执行完毕时就会被销毁
- 作用域链:内部函数访问外部函数的变量,采取的是链式查找方式(就近原则)
九、JavaScript预解析(变量提升)
- js引擎运行js代码分为:预解析 代码执行(按照代码书写的顺序从上到下执行)
- 预解析:js引擎会把所有的
var
function
的声明提到作用域的最前面,而不提升赋值操作 - 严格模式下不会有变量提升
let
,const
也不会有变量提升
如下结果输出直接报错:
console.log(num);
=============================================================================
如下结果输出undefined(代码顺序执行,预解析将变量声明提升到最前面,而不提升赋值操作)
console.log(num);
var num=10;
3. 函数声明注意点
如下会正常输出(会把函数的声明提到最前面):
fu();
function fu() {
console.log(1121);
}
相当于:
function fu() {
console.log(1121);
}
fu();
====================================================================================================
如下会输出报错:
fun();
var fun = function() {
console.log(1212);
}
相当于:(这种用了变量名的方式,所以不会将函数声明提到最前面)
var fun;
fun();
fun = function() {
console.log(1212);
}
面试题:(在函数内没有var声明的变量,相当于全局变量)
结果输出9,9,9,报错
fun();
console.log(a);在这一步报错,之后不会执行
console.log(b);
console.log(c);
function fun() {
var a = b = c = 9;
相当于var a=9,b=9,c=9;
有var是声明了,没有var就是相当于全局变量了
console.log(a);
console.log(b);
console.log(c);
}
十、对象
- 对象是一个具体的事物,对象包括属性和方法
- 对象可以将数据表达的更加清晰
- 变量和属性,函数和方法的区别:
1.变量和属性都是来存储数据的,变量是单独声明和使用的;而属性封住在对象里,不能单独调用,需要
对象.属性
2.函数和方法都是实现某种功能的,函数是单独声明和调用的;而方法是封住在对象里的,不能单独调用,需要对象.方法
10.1、创建对象的三种方式
10.1.1、利用字面量创建对象
对象属性方法的调用 1.对象.属性名 2.对象[‘属性名’]
对象创建
var obj = {
uname: '小明',
age: 18,
address: '清河街道',
fun: function() {
console.log('测试创建对象方法');
}
}
对象使用
obj.uname;
obj['uname']
obj.fun();
10.1.2、使用关键字new
new Object()
;
var obj = new Object();
obj.uname = '小明';
obj.age = 12;
obj.fun = function() {
console.log('测试new创建对象');
}
obj.fun();
10.1.3、使用构造函数创建对象
- 构造函数:把对象中一些相同的属性和方法抽取出来封装到这个构造函数里面
- 构造函数名首字母大写
- 构造函数不需要
return
- 构造函数泛指一大类;而对象特指某一个事物
声明函数
function Star(uname, age, gender) {
this.name = uname;
this.age = age;
this.gender = gender;
}
调用函数
var oll = new Star('小明', 18, '男');
console.log(oll);
- new关键字:
1.在内存中创建了新的对象
2.用this指向这个对象
3.给空对象添加属性和方法
4.返回对象(不需要return)
10.2、遍历对象
- for…in…对数组或对象中的属性进行遍历循环
for(变量 in 对象)
log(变量)输出的是属性名/方法名;log(对象[变量])输出的是属性值/方法
11、内置对象
- 对象:自定义对象,内置对象(JS自带的),浏览器对象
- JS提供了多个内置对象:Array,Date,Math,String等
- MDN学习网站
Math | 方法 |
---|---|
max(‘123’) | 隐式转换为数值型 |
floor | 向下取整 1.9=1 |
ceil | 向上取整 1.1=2 |
round | 四舍五入 |
abs | 绝对值/abs(‘12’)隐式转换为数值型 |
Math.random()
会返回一个【0,1)间的一个随机小数Math.floor(Math.random()*(max-min+1))+min
返回的是随机数
function getrandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var arr = ['小明', '小红', '小刚', '小黑'];
//随机点名
console.log(arr[getrandom(0, arr.length-1)]);
输出拼接年月日周
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth() + 1;
var date = date.getDate();
var day = date.getDay();
var arr = ['周天', '周一', '周二', '周三', '周四', '周五', '周六'];
console.log(year + '年' + month + '月' + date + '日 ' + arr[day]);
输出拼接时分秒
var date = new Date();
var h = date.getHours();
h = h < 10 ? '0' + h : h;
var m = date.getMinutes();
m = m < 10 ? '0' + m : m;
var s = date.getSeconds();
s = s < 10 ? '0' + s : s;
console.log(h + ':' + m + ':' + s);
- 获得总的毫秒数(H5新增的,但是需要考虑兼容性的问题)
Date.now()
11.1、数组对象
- 检测是否为数组instanceof:
var arr = [];console.log(arr1 instanceof Array);
输出的是布尔类型 - 检测是否为数组isArray():
var arr = []; console.log(Array.isArray(arr));
输出的是布尔类型(H5新增的方法,优先于instanceof) - 检测数据类型(由此可见,typeof判断数据类型除了array,null返回的是Object,别的都返回自己的类型)
console.log(typeof(123)); //数字是number
console.log(typeof('123')); //字符串是String
console.log(typeof(true)); //布尔是boolean
console.log(typeof(undefined)); //undefined是undefined
console.log(typeof(null)); //null是object
var obj = {
name: '小明'
}
console.log(typeof(obj)); //对象是object
console.log(typeof([1, 2, 3])); //数组是Object
function Fun() {
console.log(123);
}
console.log(typeof(Fun)); //函数是function
添加删除数组元素的方法
方法 | 描述 |
---|---|
push | 在数组的末尾添加元素 |
unshift | 在数组的开头添加元素 |
pop | 在数组中删除最后一个方法arr.pop() 会输出删除的元素 |
shift | 删除数组的第一个元素 |
数组排序,替换,字符串转为数组,数组转换为字符串
方法名 | 描述 |
---|---|
reverse | 数组翻转 |
sort | 冒泡排序arr.sort(function(a,b){return a-b;}) |
replace | 替换字符,只能替换一个,str.replace('旧','新') |
splice | 将字符串转换为数组str.aplice('分隔符') |
toString | 数组转换为字符串 |
join | 数组转换为字符串arr.join("连接符") |
根据字符返回位置
方法 | 描述 |
---|---|
indexOf() | 求数组的索引号,只能返回一个索引号,如果没有返回-1 arr.indexOf('要查找的字符',[起始的位置]) |
lastindexOf() | 简单了解 |
根据位置返回字符
方法 | 描述 |
---|---|
charAt(index) | 返回指定位置的字符串str.charAt(index ) |
charCodeAt(index) | 返回指定位置的字符串的ASCII |
str[index] | 获取指定位置的字符串 HTML5(兼容性问题) |
数组去重(面试常问)
var arr = ['a', 'b', 'c', 'd', 'd', 'c', 'e'];
function unique(arr) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
// 根据索引判断新数组里是否有重复的值
if (newArr.indexOf(arr[i]) === -1) {
// 在数组末尾添加元素
newArr.push(arr[i]);
}
}
return newArr;
}
console.log(unique(arr));
数组转换为字符串
方法 | 描述 |
---|---|
toString() | |
join(分隔符) | arr.join("&") |
splice和substr对数组进行操作(删除,添加,替换)
方法 | 描述 |
---|---|
删除arr.splice(index,num) | index起始项,num删除个数 |
插入arr.splice(index,0,insertValue) | index起始项,insertValue插入项 |
替换arr.splice(index,num,insertValue) | index起始项,num插入个数,insertValue替换值 |
截取arr.substr(index,num) | index截取的开始位置,num截取字符的个数 |
- 字符串的不可变,当改变字符串的值时,其实改变的不是值,变得是指向地址,原来的值是不会消失的(所以不要大量的字符串拼接)
- 字符串的所有方法,都不会修改字符串本身,会返回一个新的字符串
- 实例:查找字符串 "abcdeadb"中b出现的位置和次数
var str = "abcdeadb";
var index = str.indexOf('b');
var num = 0;
while (index !== -1) {
console.log(index);
num++;
index = str.indexOf('b', index + 1);
}
console.log("个数为" + num);
- 实例:将字符串中出现的字符统计出来///统计出现次数最多的字符
var str = "abcdefadcadcaa";
var obj = {};
//将字符串中出现的字符统计出来
for (var i = 0; i < str.length; i++) {
var chars = str.charAt(i);
if (obj[chars]) {
obj[chars]++;
} else {
obj[chars] = 1;
}
}
console.log(obj);
//统计出现次数最多的字符
var ch = '';
var max = 0;
for (var k in obj) {
if (obj[k] > max) {
max = obj[k];
ch = k;
}
}
console.log(max);
console.log(ch);