1. 三种输出方式
// 单行注释
/*
shift+alt+a
*/
// 三种输出
console.log("在控制台打印"); //弱类型语言,可不加分号
alert("弹窗");
document.write("在网页显示");
// 可解析标签类型字符串
document.write("<h1>巴巴</h1>");
2. 声明变量
1. 常量(字面量) 一些不可改变的数据
变量 记录一些可变化的数据
3. 变量声明规则
// 1.数字 字母 下划线 $组合,不能数字开头,不能汉字
// 2. 不能使用关键字
// 3. 不超过255字符
// 4. 可使用小驼峰方式 见名知义
// 5. var my_name = "张三";双引号 单引号 反撇号
var _123a = 2;
var my_name = "张三";
var myName = "张三";
var myAge = 12, mytel = 110;
//变量状态:声明变量 给变量赋值 使用变量
var let const
var
1.声明的变量不具备块级作用域的特性,它在{}外依然能被访问到;
3.循环中,使用了var关键字,它声明的变量是全局的,包括循环体内与循环体外。
4.声明的全局作用域变量属于 window对象
5.使用 var 关键字声明的变量在任何地方都可以修改
6.变量提升时,变量可以先使用再声明
let
1.声明的变量只在let命令所在的代码块{}内有效,在{}之外不能访问;
3.Internet Explorer 11 及更早版本的浏览器不支持 let关键字;
4.循环中,使用let关键字,它声明的变量作用域只在循环体内,循环体外的变量不受影响。
5.声明的全局作用域变量不属于 window 对象
6.在相同的作用域或块级作用域中,不能使用 let 关键字来重置 var 关键字声明的变量 ,不能使用 let 关键字来重置 let 关键字声明的变量 ,不能使用 var 关键字来重置 let 关键字声明的变量,let 关键字在不同作用域,或不同块级作用域中是可以重新声明赋值的:
const
const 用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改,块级作用域,需要先声明再使用
3. 数据类型
//1. 基本数据类型:
// String字符串
// Number数值类型(整数 小数 负数)
// Boolean(true false)
// Undifind未定义(没赋值) null是一个表示"无"的对象,转为数值时为0;
// Null(赋了空值) undefined是一个表示"无"的原始值,转为数值时为NaN
//2. 引用数据类型:
// 除了基本数据类型 都是引用数据类型
// 比如 (对象 数组 日期)对象类型 函数
var name = "悟空"; //String
var age = 2000; //number
var isGirl = false; //Boolean
//3. 对象
var obj_wukong = {
name:"孙悟空",
age:"2000",
address:"花果山",
brother:{
name:"猪八戒",
age:3000,
address:"高老庄",
wife:{
name:"高翠兰",
address:"高老庄"
}
}
}
//4. 数组和对象相互嵌套
var citys = [
{
name:"郑州",
areaList:["经开区","高新区","金水区"]
},
{
name:"周口",
areaList:["川汇区","淮阳区"]
}
];
console.log(citys);
5. 数据类型转换
其他类型变Number Number() parseInt() parseFloat() 2+ +2 2- -2
-
(Number类型,用于编写程序逻辑运算)
string转number:
1.数字字符直接转
2.空字符以及纯空格字符转为0
3.其他字符NaN
4.NaN不能进行运算,如果运算结果要么NaN要么false,NaN与任何数都不相等,与自身也不相等
var res = Number(‘0’);//0
var res = Number(’’);//0
5.parseInt()将其他类型转为数值型的整数,除字符串外其他都是NaN
( parseInt()的转换规则:
1.忽略字符串前面的空格,直到找到第一个非空格字符
2.如果第一个字符不是数字或者是负号,返回NaN
3.空字符串,返回NaN
4.如果第一个字符是数字,会继续解析第二个字符,直到解析完所有后续字符或遇到了一个非数字字符。
5.如果字符串以0x开头且后面跟数字字符,就会将其转为10进制数,同样,八进制也一样。
)
var res = parseInt(’’);//NaN
var res = parseInt(‘0’);//0
var res = parseInt([]);//NaN
var res = parseInt([ ,012]);//NaN
var res = parseInt([‘9’]);//9
var res = parseInt({});//NaN
var res = parseInt(’ 012fg’);//12
var res = parseInt(‘0x222222’);//2236962
6.parseFloat()也是从第一个字符开始解析每个字符,而且也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。规则如下:
(1.第一个小数点是有效的,第二个小数点无效
2.始终忽略前面的0,不分进制数
3.十六进制会被转换成0
4.空字符串转换为NaN)boolean转number:
true 1 false 0数组转number:
由于没有对应的API,先转字符串(默认触发toString()),再转number
var res = Number([]);//0
var res = Number([1,2]);//NaN对象转number:
也是先转字符串’[Object,Object]’
var res = Number({});//NaN
var res = Number({name:“li”});//NaN总结:
数组和对象直接运算,先转字符串,再转其他类型,最后再运算
基本类型间的转换只转一次,引用类型转基本类型先变字符串再变其他
其他类型变String String() +’’ ‘’+ toString() join()
-
(String类型,用于展示,建立与数组联系)
console.log(String([]));//'' console.log(String([1,2]));//'1,2' console.log(String({a:1,b:2}));//[Object,Object] console.log(String(undefined));//'undefined' console.log(String(null));//'null' console.log(String([123]+{}));//'null'
其他类型变Boolean Boolean() 除了下边这写是true其余都是false ==比较运算符个别关系运算符时
-
(BOOlean类型,用于条件句,或循环语句)
console.log(Boolean(false));//false console.log(Boolean(0));//false console.log(Boolean(''));//false console.log(Boolean(undefined));//false console.log(Boolean(null));//false console.log(Boolean(NaN));//false console.log(Boolean([]));//true console.log(Boolean({}));//true
-
null类型,用于 清理内存
-
undefined类型,表示某个变量已声明但未赋值用以判断函数是否有结果
-
array
-
function
6. 数据类型验证
var a =1;
// 验证是否是number
console.log(typeof(a) ==='number');
console.log(a instanceof Number);
**arr instanceof Object验证的可能是数组也可能是对象**
console.log(a.constructor===Number);
//验证是否是数组
console.log(Array.isArray(Date))
// 验证是否是NaN
console.log(isNaN(a));
// 验证是否是undefined
console.log(typeof(a) ==='undefined');
console.log(a===undefined);
// 验证是否是null
console.log(!a && !isNaN(a) && typeof(a) != 'undefined' && a != 0)
//判断思路:所有数据转化false只有undefined null 0 NaN false
//如果一个数据不是undefined 0 NaN false ,那么根据排除法 返回true就是null 返回false就是上面其中一个
7. 运算符
//1. 算术运算符 + - * %
//2. 比较运算符(关系运算符)> < >= <= ==
var str1 = "z张";
var res = str1 + 123; //这里加是字符串拼接的意思
console.log(res);
console.log(str1 + res);
//3. 逻辑运算符 ||或 &&且 !非
// && 叫逻辑与,在其操作数中找到第一个虚值表达式并返回它,如果没有找到任何虚值表达式,则返回最后一个真值表达式。它采用短路来防止不必要的工作。
// || 叫逻辑或,在其操作数中找到第一个真值表达式并返回它。这也使用了短路来防止不必要的工作。在支持 ES6 默认函数参数之前,它用于初始化函数中的默认参数值。
// ! 运算符可以将右侧的值强制转换为布尔值,这也是将值转换为布尔值的一种简单方法。
var ab = false;
var ac = false;
console.log(ab || ac);
//4. 赋值运算符 =
//5. 判断数据类型的运算符
var a = null;
console.log(typeof a); //object
//NaN 的数据类型是数值
// 数组的数据类型是对象
// 日期的数据类型是对象
// null 的数据类型是对象
// 未定义变量的数据类型是 undefined
// 尚未赋值的变量的数据类型也是 undefined
//6. constructor 属性返回所有 JavaScript 变量的构造器函数。
"Bill".constructor
//返回"function String(){[native code]}"
8. 渲染页面
var arr = singers.data.artistList;
var htmlStr = "<div class='boxall'>";
for(var i=0; i<arr.length; i++){
htmlStr += "<div class='box'><img src="+arr[i].pic+"><br> <b>"+arr[i].name+"</b><p>"+arr[i].artistFans +"</p></div>";
}
htmlStr += "</div>";
document.write(htmlStr);
//var a = ```'a'''""`;
//不能嵌套相同的引号,只有``能换行能添加变量
var b = `sgdg${arr[i].name}gshghg`;//必须用``
var b = "fds"+arr[i].name+"sgfdgs";//里外引号要一样
var c = `dsds`+arr[i].name+`ffhhh`;//里外引号要一样
9. js内置对象
Math
Date
时间戳
// Math内置API
alert(Math.random());//0-1随机数不包括1
console.log(Math.random()*5);//0-5随机数不包括5
console.log(Math.ceil(1.5));//向上取证
console.log(Math.floor(1.5));//向下取整
console.log(Math.round(3.33));//四舍五入 结果3
console.log(Math.ceil(Math.random()*5));
console.log(Math.floor(Math.random()*8+4));
// console.log(Math.random*(b-a+1)+a);
console.log(Math.PI);
console.log(5*5*Math.PI.toFixed(3));//保留三位小数,如果不传参数四舍五入
console.log(12.58.toFixed());
console.log(Math.pow(2,10));//2的10次方
console.log(Math.pow(2));//NAN not a number
console.log(Math.sqrt(9));//对9开平方根 结果3
console.log(Math.abs(-3));//绝对值
console.log(Math.max(1,2,3,4,5,6));//最大值
arr=[6,7,8];
console.log(Math.max(...arr));//...拓展arr数组,不能直接放arr数组
console.log(Math.min(...arr));
// data日期对象API
var date = new Date();//日期构造函数
console.log(date);
var year = date.getFullYear();//年
var month = date.getMonth();//月 返回0-11 比月份实际小1
var day = date.getDate();//日
var week = date.getDay();//星期几 周日0 1-6
console.log(year +"-" + month + "-" + day + "-" + week);
var hour = date.getHours();//时
hour = hour<10 ? "0" + hour : hour;
var minutes = date.getMinutes();//分
minutes = minutes<10 ? "0" + minutes : minutes;
var seconds = date.getSeconds();//秒
seconds = seconds<10 ? "0" + seconds : seconds;
console.log(hour+ ":" + minutes + ":" + seconds );
var milliseconds = date.getMilliseconds();//毫秒
// 设置日期对象
var date = new Date();
date.setFullYear(2000,1,1);//设置年 2000年1月1日
date.setMonth(1,6); //设置月 1月6日
date.setDate(22);
date.setHours(3);
date.setMinutes(3);
date.setSeconds(3);
date.setMilliseconds(999);
console.log(date);
//传入字符串 如果不写时分秒 默认08:00:00
console.log(new Date('2021-02-02 12:00:00'))
;
//除了2021-02-02 其他的如果不写时分秒 默认00:00:00
console.log(new Date('2021 02 02 12:00:00'));
console.log(new Date('2021,02,02 12:00:00'));
console.log(new Date('2021*02*02 12:00:00'));
console.log(new Date('2021.02.02 12:00:00'));
console.log(new Date('2021/02/02 12:00:00'));
// 时间戳
// 计算机元年1970年1月1日
function Timer(){
var date = new Date();
console.log(Date.now());//当前时间距1970年1.1毫秒数
var date1 = new Date('2023.2.21');
console.log(date1,date1.getTime(), date1.valueOf());//2023年距1970年毫秒数
var time = date1.getTime() - date.valueOf();//2023年2.21距当前毫秒数
var d = parseInt(time/1000/60/60/24);
var h = parseInt(time/1000/60/60%24);
var m = parseInt(time/1000/60%60);
var s = parseInt(time/1000%60);
setTimeout(Timer,1000);
document.getElementById('timer').innerHTML=('2023.2.21距当前时间' + d + '天' + h + '小时' + m + '分钟' + s + '秒');
};
Timer();
// 将时间戳转为标准时间
var chuo = new Date(555555555);
console.log(chuo);
</script>
10. 数组
.slice(start,end)截取 (开始索引,结束索引不包括) 返回截取下来的数组 对原数组无影响
只写一个值的话就从这个数字截取到最后
var res9 = arr7.slice(0,3);//134
console.log(res9,arr7);
.splice(start,count,newItem) 删除数组中某些项 返回删除的数 改变原数组
(开始值,要删几个 非必填 不填则删除后面所有 填0则不删 , 从开始删除的位置添加新的项)
arr7=[7,8,7,8]
var res10 = arr7.splice(1,2,6);
var res10 = arr7.splice(1,2);
var res10 = arr7.splice(1);
var res10 = arr7.splice(1,2,6,7,7,8);
console.log(res10,arr7);
.filter()
过滤数组
参数:函数
回参1:表示数组的元素
回参2:数组索引
return 过滤条件
返回值:返回满足条件所有元素的新数组
var res = arr.filter(function (item,index){
return item.age>13;
})
console.log(res);
.forEach() 遍历数组
var arr = [
{age:12},
{age:13},
{age:15},
{age:123},
]
arr.forEach(function(item,index) {
console.log(item,index);
});
.some()api 检查数组中是否存在 符合条件的元素
作用:检查数组中是否存在 符合条件的元素
参数:回调函数
返回值:true存在 false不存在
一旦存在满足条件的元素函数就不循环执行
var array = [1,4,3,9,5];
var even = function(element){
return element % 2 === 0;//验证的条件
}
var res = array.some(even);
console.log(res);
//模拟api
Some(array,function(element){
return element%2===0;
})
function Some(array,cb){
var flag=false;//初始化结果
for(var i=0;i<array.length;i++){
if(cb(array[i])){
flag = true;
break;
}
if(i===array.length-1){
flag=false;
}
}
console.log(flag);
return flag;
}
.map()api
作用:创建新数组
参数:回调函数,arr多长,函数就执行多少次
返回值:新的数组map map.length ===array.length
新数组中的元素来自于回调函数的return
用于:数组重组,数据处理
var arr = [1,2,3];
var map_arr=map(arr,function(element){
// console.log(element);
return element;
})
console.log(map_arr);
function map(arr,cb){
var res=[];
for(var i=0 ; i<arr.length ;i++){
res.push(cb(arr[i]));
}
console.log(res);
return res;
}
.ruduce()一般用于数学运算,累加累乘
参数一:回调函数
参数二:回参一的初始值
回参一pre:上一次函数执行的return运算结果
回参二current:是arr数组的元素
返回值:运算结果,一个单一的数
var arr = [1,2,3,4,5];
var initialValu = 0;
var res = reduce(arr, function (pre,current) {
console.log('-------------');
console.log(pre,current);
return pre+current
},initialValu)
console.log('运算结果------',res);
// arr 是做什么的 init 是干什么的
// 写什么东西可以完成reduce的功能
function reduce(arr,cb,init){
for(var i = 0;i<arr.length;i++){
init = cb(init,arr[i]);
}
return init
}
js内置处理数组方法
1.合并数组concat 有返回值 返回一个新数组 对原数组没有影响
var arr1=[1,2];
var arr2 = [3,4];
var res = arr1.concat(arr2);
console.log(res);
5.shift删除数组中的第一个 返回删除的项 改变原数组
var res4 = arr3.shift();
console.log(res4);
4.unshift 向数组头部添加 返回添加后的长度 改变原数组
var arr3 = [1,2,3];
var res3 = arr3.unshift(1,1);
console.log(res3);
3.pop删除数组中最后一个 有返回值 返回删除的项 改变原数组
var res2 = arr3.pop();
console.log(res2);
2.push 向数组尾部添加项 有返回值 返回添加项之后的长度 改变原数组 同时向添加多项 用逗号隔开即可
var res1 = arr3.push(5,6);
console.log(res1);
练习1. 将两数组合并 并将合并后的数组首尾交换
var arr4 = [1,2,3,4];
var arr5 = [5,6,7,8];
var res = arr4.concat(arr5);
console.log(res);
var one = res.shift();
var last = res.pop();
res.push(one);
res.unshift(last);
console.log(res);
6.toString(); 将数组转换为字符串 不改变原数组
var arr6 = [1,2,3,4,5,6];
var res5 = arr6.toString();
console.log(res5,arr6);
7.join()将数组转为字符串 不添加值默认逗号隔开 添加值则由添加的值隔开 原数组不变
var res6 = arr6.join(6);
console.log(res6 , arr6);
8.reverse 翻转数组 改变原数组值
console.log(arr6.reverse(),arr6 );
9.indexOf() 获取收个指定元素在数组中的位置的索引,若不存在返回-1
var arr7 = [1,3,4];
var res7 = arr7.indexOf(4);//2
console.log(res7);
lastIndexOf()从后往前
var res8 = arr7.lastIndexOf(1);//0
console.log(res8);
练习2.不带7
arr7 = [];
for(var i=1 ;i<=100 ;i++){
if(i%7==0 || i%10==7 || i-(i%10)/10==7 ){
continue;
}
arr7.push(i);
}
delete 删除某个 可以删除 但位置会保留 数组长度不会变
delete arr7[0];
console.log(arr7);
练习3.数组去重
法一双重for循环判断(从后边删除的)
var arr11 = [1,2,412,4,51,312,4551,2,31,51,1,88];
for (let i = 0; i < arr11.length; i++) {
for (let j = i+1; j < arr11.length; j++) {
if(arr11[j]=arr11[i]){
arr11.splice(j,1);
j--;//arr11.length变化
}
}
}
console.log(arr11);
法二根据indexOf返回值是否为-1判断
var arr11 = [1,2,412,4,51,312,4551,2,31,51,1,88];
var newArr11=[];
for(var i=0 ;i<arr11.length ; i++){
var res11 = newArr11.indexOf(arr11[i])
if(res11 == -1){
newArr11[newArr11.length]=arr11[i]
continue;
}
}
console.log(newArr11);
法三前后索引对比(从前边删除的)
var arr11 = [1,2,2,3 ];
for (let i = 0; i < arr11.length; i++) {
if(arr11.indexOf(arr11[i]) != arr11.lastIndexOf(arr11[i])){
arr11.splice(i,1);
}
}
console.log(arr11);
法四indexOf索引与下标对比
var arr11 = [1,2,2,3 ];
for (let i = 0; i < arr11.length; i++) {
if(arr11.indexOf(arr11[i]) != i){
arr11.splice(i,1);
i--;
}
}
console.log(arr11);
11. 字符串
.split();将字符串分割为数组 返回分割后的数组 不会改变原来的数组
console.log(str2.split("人"), str2);//以"人"分割
console.log(str2.split(), str2);
模拟split():
var str='fdj,kggd';
var arr=split(str,',')
console.log(arr,str);
function split(str,substr){
var res=[];
var str_init = '';//空字符串
str=str + substr;//给最后加逗号
for(var i=0;i<str.length;i++){
if(str[i]==substr){
console.log(str_init);
res.push(str_init);
str_init = '';
continue;
}
str_init = str_init + str[i];
}
return res;
}
.slice(start,end);截取 不包括end 返回截取的字符串 不影响原字符串 第一个数如果为负数,从后往前查
console.log(str2.slice(0, 2), str2);
console.log(str2.slice(2), str2);
console.log(str2.slice(-4), str2);
模拟slice():
var str = slice('dfsfsdfdsfs',1,3);
function slice(str,start,end){
var res = '';
for(var i=0 ; i<str.length ; i++){
if(i>=start && i<end){
res = res + str[i];
}
}
return res;
}
.substr(from,count) 截取 (从哪里开始 , 截取几个) 返回截取下来的字符串 不影响原字符串
console.log(str2.substr(2, 3), str2);
.substring(start,end) 截取 不包括end 第一个参数如果为负数还是从0开始
console.log(str2.substring(1,3));
console.log(str2.substring(-1000));
.sort排序 会改变原数组 有返回值 返回拍好的数组 默认从小到大升序
var res=arr.sort();//把数组里面的元素当成了字符串 按unicode码 ascii码 英文字符 数字 英文小写 汉字 中文字符
console.log(res);
for循环遍历字符串
var str = '123456789';
for (let i = 0; i < str.length; i++) {
console.log(str[i]);
}
字符串拼接
var str1 = "1"; var str2 = "2"; var str3 = "3";
console.log(str1 + str2 + str3);
.concat 拼接 连接 字符串
var res1 = "你好".concat(str1, str2, str3);
console.log(res1);
indexOf和lastIndexOf 跟数组中的用法是一致的
var str1 = "撒娇女人最好命";
console.log(str1.indexOf("女女"));//-1
console.log(str1.lastIndexOf("撒娇"));//2
.replace()替代 替换 有返回值 返回替换后的字符串 替换多个相同的字符的话 智能替换第一个 不改变原来的字符串
var str2 = "男人得有钱";
console.log(str2.replace("有钱", "^^"), str2);//只替换一个$,别的符号例外 且只能替换第一个匹配的"有钱""
.replaceAll()存在兼容问题
console.log(str2.replaceAll("钱", "//"), str2);//只替换一个$,别的符号例外
//建议用for循环一个个替换 , 或使用正则表达式替换
// i不区分大小写 g全局匹配 m多行匹配
console.log(str2.replace(/钱/g, "$"), str2);
小练习:
1.加密电话
var num = "15188470181";
var num1 = num.replace(num.substr(3, 4), "****");
console.log(num1, num);
2.大小写转换 原字符串不变
var str4 = "sdggfgg";
console.log(str4.toUpperCase(), str4);//变大写
var str5 = "HJUYHIUIUIU";
console.log(str5.toLowerCase(), str5);//变小写
3.互相转换
var str6 = "JAVAsCRIPT";
var res = "";
for (let i = 0; i < str6.length; i++) {
var code = str6[i];
if (code == str6[i].toLowerCase()) {
res += code.toUpperCase();
} else {
res += code.toLowerCase();
}
}
console.log(res);
.trim(); 去除字符串两边空白符 不影响原字符
var str7 = " 今天周三 ";
console.log(str7.trim());//去掉两边空格
console.log(str7.trimLeft());//去掉左边空格
console.log(str7.trimRight());//去掉右边空格
es6 .includes(); 搜索字符是否存在 true存在 false不存在
console.log(str7.includes("周三"));//false
.charAt(index) 通过索引值 找到指定位置的字符
console.log(str7.charAt(3));//周
.starsWith() 和 .endsWith()判断字符串是否是以某个字符开头和结尾的 返回值true false
console.log(str7.startsWith("周"), str7.endsWith("三"));//false
isNaN判断是不是数字 数字:false 不是数字:true
//NaN:not a number 属于number类型
//NaN不等于任何值,包括他本身
console.log(typeof ("1"));
console.log(typeof ("g"));
console.log(isNaN("1"));//false 1是个数字
console.log(isNaN("g"));//true g不是个数字
练习: 长度11,开头1,全是数字,不符合则提示输入有误,正确则把手机号中间四位换成****
var tel = prompt("请输入电话号码");
if (isNaN(tel)) {//true不是数字
alert("请输入数字");
} else {//false是数字
if (tel.charAt(0) == 1) {//true是1开头 charAt和startsWith能判断字符串,但不能判断数字
if (tel.length == 11) {
var tel1 = tel.replace(tel.slice(3, 7), "****");
alert(tel1);
} else {
alert("长度不是11");
}
} else {
alert("数字开头不是1");
}
}
12. Object对象
获取对象中所有键 返回一个由键组成的数组
var keys=Object.keys(obj);
console.log(keys);
获取值
var values = Object.values(obj);
console.log(values);
小练习:判断obj1中是否存在eat这个属性
var obj1 =
{
name:"明",
eye:2,
hair:"black"
}
flag=false;//假设不存在
for(var key in obj1){
//var keys=Object.keys(obj1);//key数组
// for (let i = 0; i < keys.length; i++) {
if(key=="eat"){
flag=true;
break;
// console.log("yes");
}
}
}
if (!flag) {
console.log("不存在");
}
else{
console.log("cunzai");
}
Object.keys(ogj1).indexOf('eat') == -1 ? alert("不存在") : alert("存在");
声明对象
var obj={
name:"老王"
}
var obj3= new Object(8);
console.log(obj3);
取属性值
console.log(obj.name) ;
console.log(obj["name"]) ;
添加属性值
obj.age="1";
obj["hair"]="black";
删除属性
delete obj.hair;
// console.log(obj);
更改属性值
obj.age=18;
//对象有可扩展性 默认扩展性为true
检查是否有扩展性的方法: Object.isExtensible(obj);
Object.isExtensible(obj);
阻止拓展 禁止给对象添加新属性 但可以修改和删除属性 Object.preventExtensions()
Object.preventExtensions(obj);
冻结 Object.freeze() 无法添加修改删除
Object.freeze(obj);
13. 函数
1. 函数的定义
// 函数:也叫做方法,它的作用主要就是用来封装重复的代码,来达到代码复用的效果
function func1 () {
alert('我是func1')
}
func1();
// 匿名函数/函数表达式 通过一个变量来接收函数
var fn2 = function () {
alert('我是fn2');
};
console.log(fn2); //此时fn2是一个函数表达式
fn2();
2. 函数的参数
// 函数中的参数:实参和形参,实际参数、形式参数
// 形式参数:在定义函数时,写在括号中的自己定义,每个形参之间用逗号隔开
function add (a, b) {
// 在函数中就要使用这两个形式参数;
return a + b;
}
// 实际参数:在调用函数时,写在括号中的参数;
console.log(add(1, 2));
console.log(add(123, 4124));
3. 作用域
变量状态:声明变量 给变量赋值 使用变量
判断使用的是那一个变量:在当前作用域中查找,是否声明,如果声明,找最近一次的赋值
// 作用域:全局作用域和局部作用域
// 全局作用域:范围指的整个script代码块中
var a = 1;
// 局部作用域/函数作用域:声明在函数内部的变量,只能在函数中使用,函数内的变量在函数执行时才声明
function f1 () {
var b = 2;
console.log(a);
console.log(b);
}
f1();
//console.log(b); //Uncaught ReferenceError: b is not defined
function f2 (params) {
var a = 10;
// 当访问一个变量时,会优先在自己的作用域中寻找,如果没有,就往上找;
console.log(a);
}
f2();
console.log(a);
// 全局作用域(整个script标签或一个单独的js文件 或 函数内部没有声明直接定义的变量)
// 局部作用域/函数作用域(函数内部的名字只在函数内部起作用 函数形参也是局部变量 function max (a,b)这种函数默认是全局变量)
console.log(qqq);//访问不了
function jisuan(){
console.log(ppp);
}
var qqq = 1;
console.log(ppp);//访问不了
// 变量会提升声明但不提升值,函数如果是变量形式也会同样提升,函数如果是函数命名形式会将内容一并提升
4. 变量提升
// 变量提升
var a;
console.log(a); // undefined
var a = 100;
// 函数提升
f1();
function f1 (params) {
alert('我是f1')
}
// add(3,4);//变量命名的函数前面不能调用,函数不提前 匿名形式的提前能调用
//函数表达式(匿名函数) add是变量不是函数名
function add(a,b){//形参可看做不用声明的变量
console.log(a+b);
}
add(1,2,3);//实参多于形参的不参与计算
add(2);//2+undifind 结果是NaN
5. arguments
argumnets对象,接收所有传入的实参,是一个类数组,可以通过索引去获取到传入的参数,只能在函数体内使用 与数组的区别是没有数组的一些方法如pop
function f1 (a, b, c) {
console.log(a, b, c);
// argumnets对象,接收所有传入的实参,是一个类数组,可以通过索引去获取到传入的参数,只能在函数体内使用
console.log(arguments, arguments[3]);
// instanceof 用来判断引用类型Function Object Array
console.log(arguments instanceof Math);
var arr = [1, 2, 3];
console.log(arr instanceof Array);
}
f1(1, 2, 3, 4, 5, 6, 7, 8);
f1(1);
// 形式参数的作用主要就是为了接受调用函数时,传入的实际参数
/*
当实际参数的个数<形式参数时,从左开始接受,多余的显示为undefined
当实际参数的个数>形式参数时,那么形式参数就按照实际参数从左到右的顺序接收,多余的则不再接收
*/
6. 回调函数
将一个函数当成另一个函数的实参,这个作为参数的函数就叫做回调函数,callback;
// sayName为回调函数
function sayName (name) {
alert('Hi,' + name);
}
function f1 (func, value) {
console.log(func);
func(value);
}
f1(sayName, 'Jack');
sayName('jack');
7. 函数构造对象
//函数构造对象
function Star(uname,uage){
this.name = uname;
this.age = uage
}
var ldh = new Star("刘德华",18);
console.log(ldh.age);
8. 定时器和延时器
setInterval(callback,time);
定时器:每隔多少秒,执行一次callback中的代码;
一直执行
var res = setTimeout(callback, timeout);
延时器:设置一个时间delay,推迟多少秒执行这个代码;
只执行一次,写在函数里面也可实现setInterval的效果
慢执行程序,不阻塞下面程序
clearTimeout(res),注意是否全部清空
返回值 执行的第几个setTimeout
性能优化:把所有延时程序的返回值加到Timer数组中,在程序结束之前找到一个点,遍历Timer清空所有的延时程序
//性能优化:
var timesr = []//保存所有setTimeout的id值
var id = setTimeout(function () {
console.log(2);
}, 1000)
timesr.push(id)
for (var i = 0; i < 3; i++) {
var res = setTimeout(function () {
console.log(i) // 3 3 3
}, 5)
timesr.push(res)
}
function foo() {
timesr.push(setTimeout(function () {
}, 10))
}
if (true) {
var res_ = setTimeout(function () {
}, 10)
timesr.push(res_)
}
for (var j = 0; j < 91; j++) {
var id_= setTimeout(function () {
}, 10)
timesr.push(id_)
}
console.log(timer)
timesr.push(setTimeout(function(){
for(var i=0;i<timer.length;i+++){
clearTimeout(timer[i])
console.log(1)
}
},1001))
for(var i=0;i<3;i++){
setTimeout(function(){
console.log(i);
},1000)
//打印3 3 3
//变量污染
}
//程序是自上而下的快执行,慢的程序默认在快的程序后面执行,注意当慢程序执行时,快程序中变量的值是什么
//解决变量污染,使用闭包
for(var i=0;i<3;i++){
(function(i){
setTimeout(function(){
console.log(i);
},1000)
})(i)
//打印0 1 2
}
//每秒页面上输出一次:距离2022年3月1日还有xx小时xx分钟xx秒,小时可以超过24,分钟不能超过60,秒不能超过60;
//法一 setInterval(function(){},1000);
var h;
var m;
var s;
var aaa='2022/3/1';
setInterval(function(){
var now = new Date();
var future = new Date(aaa);
var nowSecond = Date.now();//当前时间戳
var futureSecond = future.getTime(future);//2022.3.1时间戳
var timeDeffierence = futureSecond - nowSecond//时间戳差值
h =parseInt(timeDeffierence/1000/60/60) ;//时
m =parseInt(timeDeffierence/1000/60%60) ;//分
s =parseInt(timeDeffierence/1000%60) ;//秒
num.innerText = `距离2022年3月1日还有${h}小时${m}分钟${s}秒`;
},1000);
//法二 setInterval("time('2022/3/1')",1000);
var h;
var m;
var s;
setInterval("time('2022/3/1')",1000); //回调函数有参数时需要用引号包括 没有参数时直接setTimeout(Timer,1000);
function time(aaa){
var now = new Date();
var future = new Date(aaa);
var nowSecond = Date.now();//当前时间戳
var futureSecond = future.getTime(future);//2022.3.1时间戳
var timeDeffierence = futureSecond - nowSecond//时间戳差值
h =parseInt(timeDeffierence/1000/60/60) ;//时
m =parseInt(timeDeffierence/1000/60%60) ;//分
s =parseInt(timeDeffierence/1000%60) ;//秒
num.innerText = `距离2022年3月1日还有${h}小时${m}分钟${s}秒`;
}
//法三 setTimeout("time('2022/3/1')",1000);
var h;
var m;
var s;
function time(aaa){
var now = new Date();
var future = new Date(aaa);
var nowSecond = Date.now();//当前时间戳
var futureSecond = future.getTime(future);//2022.3.1时间戳
var timeDeffierence = futureSecond - nowSecond//时间戳差值
h =parseInt(timeDeffierence/1000/60/60) ;//时
m =parseInt(timeDeffierence/1000/60%60) ;//分
s =parseInt(timeDeffierence/1000%60) ;//秒
num.innerText = `距离2022年3月1日还有${h}小时${m}分钟${s}秒`;
setTimeout("time('2022/3/1')",1000);
}
time('2022/3/1');
14. 深拷贝 浅拷贝
//深拷贝 复制一个变量 当改变一个变量时 对另一个的变量没有影响
var a=1;
var b=a;//a或b改变 另一个不改变
a=3;
b=4;
console.log(a,b);
//浅拷贝 复制一个引用类型的变量 当改变一个变量中的属性时 另一个变量也会跟着变化
var obj={
name:"王"
};
var obj1=obj;//改变obj或obj1 另一个也改变
obj.age=20;
//对象数组深拷贝:
//若果想要实现引用类型的深拷贝 就可以使用遍历把一个对象或数组中每一个值复制到另一个变量中
var obj2={};
for(var key in obj){
obj2[key] = obj[key];
}
obj.age = 30;
obj2.name = "李";
//基本数据类型变量和值都存在栈中,
//引用数据类型变量和值的指针存在栈中,真正的值存在堆中 指针指向堆中值的地址
15. unll undefined区别
undefined在内存中占据了空间,没有赋值
null没有占据空间
优化内存,吧不使用的变量使用null清空
16. 有var变量和没var变量的区别
var声明的变量可以在当前作用域中任意位置都能使用,其作用是将变量提升到当前作用域顶端
没用声明的变量需要先有再使用
17. not defind和undefined
is not defined 报错原因:没有发现变量
eg: console.log(a); a=2;
undefined 有var变量但没有值
eg:console.log(a);var a=2;
18. 数据结构
- 栈
- 队列
- 集合
- 链表
- 字典
- 树
- 图
- 堆
当一个数组或者对象,具备某种特殊规律的时候,就形成了数据结构
栈(基本类型数据)
先进后出(进,将数据添加到数据中;出,将数据从中删除)存基本类型的变量和值,引用类型的变量和指针
基本数据类型是独立的
堆(引用类型数据)
存引用类型的值
拷贝
让一个变量给另一个变量赋值的过程叫做拷贝
拷贝结果:两个变量的数据一模一样
深拷贝:(基本类型都是深拷贝)
浅拷贝:(默认情况,引用类型都是浅拷贝)
对象数组深拷贝:
队列
先进先出
// 队列 模拟等待执行的任务
var quen=[];
quen.push('任务一')
quen.push('任务二')
quen.push('任务三')
//先执行完毕的 先推出
quen.shift()
quen.shift()
quen.shift()
执行上下文
执行上下文栈(执行:执行的代码,上下文:理解为对象,栈:数组)
模拟js引擎执行过程:
1.创建执行上下文栈,运行js代码
2.创建全局的执行上下文 global
3.全局的执行上下文添加到 [执行上下文栈]中
4.全局的开始运行
5.遇到函数代码执行,创建一个函数执行上下文
6.将函数执行上下文 添加到 执行上下文栈中
7.函数执行完毕 将执行上下文 推出 执行上下文
8.函数销毁 js通过进栈与出栈方式完成程序运行 浏览器关闭, global出栈 --> golbal销毁 --> 栈销毁
预编译
语言分析
检查是否有语法错误 (SynataxError unexpected token)
预编译(针对函数执行)
简单理解就是在内存中开辟一些空间,采用南方一些变量与函数
-
- 创建go(global object)对象 也是 全局执行上下文
-
- 将全局中 var function 定义的变量和函数,作为go的属性,也叫做变量提升,函数声明提升
函数执行时
- 创建AO
- 形参 和 变量提升, 值都为undefined
- 将实参赋值给形参(形参和实参相统一)
- 在函数体内找函数声明, 值赋予AO函数名
function foo(){
var a = 213
// ao:{
// arguments:{
// length:0
// }
// a:123
// b:function
// d:unfined
// }
function b (){}
var d = function(){}
// ao:{
// arguments:{
// length:0
// }
// a:123
// b:function
// d:function
// }
}
foo() // 执行完毕 AO销毁
/*
ao:{
arguments:{
length:0
}
a:undefined
b:function
d:unfined
}
*/
// 练习 编译过程
// 5
// 代码开始执行前
// 代开浏览器
// 创建执行上下文栈
/*var EackStack= [];*/
// 创建全局上下文GlobalContext
/*var GlobalContext = {
scope
this
}*/
// 添加到执行上下文栈中
/*EackStack.push(GlobalContext)*/
// 变量提升 函数声明提升
/*
GlobalContext = {
scope
this
a:undefined
b:undefined
foo:function
f : undefined
d:undefined
}
*/
// 开始执行:
var a = 123;
/*
GlobalContext = {
scope
this
a:123 (undefined-->123)
b:undefined
foo:function
f : undefined
d:undefined
}
*/
b = 234
/*
GlobalContext = {
scope
this
a:123 (undefined-->123)
b:234 (undefined-->234)
foo:function
f : undefined
d:undefined
}
*/
var b = 123;
/*
GlobalContext = {
scope
this
a:123 (undefined-->123)
b:132 (234-->123)
foo:function
f : undefined
d:undefined
}
*/
function foo() {
// 创建 fooContext
// 形参 函数 变量提升
/*
var fooContext = {
scope
this
c:undefine
}
*/
// 添加到执行上下文栈中
/* EackStack.push(fooContext) */
c = 456;
/*
var fooContext = {
scope
this
c:456
}
*/
var c = 123;
/*
var fooContext = {
scope
this
c:123
}
*/
b = 456
/*
GlobalContext = {
scope
this
a:123
b:456 (234--->456)
foo:function
f : undefined
d:undefined
}
*/
if (b) {
c = a;
/*
var fooContext = {
scope
this
c:123 (123-->123)
}
*/
}
}
// 函数执行完毕 fooContext 移除 执行上下文栈 函数中变量销毁
/*EackStack.pop()*/
foo(1,2) // 开始执行前创建fooContext
var f = d;
/*
GlobalContext = {
scope
this
a:123
b:456 (234--->456)
foo:function
f : undefined (undefined ---> undefined)
d:undefined
}
*/
var d = function () {
console.log(f)
}
/*
GlobalContext = {
scope
this
a:123
b:456
foo:function
f : undefined
d: function (undefined --->function)
}
*/
d() // 开始之前 创建 dContext
解析执行
对上下文 对象属性重新赋值的过程
面试:预编译
预编译发生在函数执行前,创建AO对象,形参,变量,函数 声明提升,形参实参相统一,最后解析执行
19. 验证一个字符是否存在
for if
str.indexOf()
20. 验证一个字符串片段是否存在
indexOf()
正则表达式
21. js为什么基于对象
-
- 声明的变量或函数,都变成了window属性,window是一个对象
- 学习的都是js内置对象,以及对象下的api
- 函数 模拟api方式实现函数应用
- 常见数据处理 封装函数
- 学习api思路:
- 知道是哪个对象下的
- api的作用效果
- 复制示例代码查看运行结果,推测作用
- api实参是做什么的
- 参数是回调函数
- api中回调函数自动执行,可能多次可能单次
- 回参的值是什么
- return 的作用是什么(相对api来说意义是什么)
- api的反回值
- 总结什么情况下可能用到
- 深入 完成api模拟
22. 封装函数思路
- 0.确定函数名
- 1.确定作用是什么
- 2.确定是否有返回值 有( 确定返回值是什么类型的数据)
- 告诉外界,函数运算结果是什么
- 如果是字符串,初始化空字符串 return
- 数组,初始化空数组 return
- 对象,初始化空对象 return
- num,初始化0 return
- bool,初始化true false return
- 不知道,初始化null return
- 3.确定形参
- 4.根据外界提供的数据以及预期输出的结果 完成方法体中编码
- 分析原始值是什么 输出什么
- 怎样从你能输出预其
- 在做业务中,缺内容就声明变量
- 先写一个静态的
- 在用代码代替静态的
- 在想多个的时候,怎么输出多个结果
- 在想多个静态的,怎么动态
- 再重复的代码找规律 ,用代码实现找到的规律
23. 闭包
- 闭包语法
function foo(){
var i=0;
i+=1;
bar()
//bug问题:为什么return 可以调用bar函数 ; 有了return bar is not defined
//return可以将bar属性从fooContext中删除 , 相当于只有匿名函数
return function bar(){
console.log('bar run');
}
}
- 闭包特点
//程序一
function foo(){
var i=0;
var b = 0;//没有销毁
return function(){
i+=1;
console.log(i);
}
}
//1.函数内部的变量没有被销毁 AO 对象没有被销毁
var a = foo()
a()
a()
//2.外面的函数执行多次 彼此独立
//3.执行多次 形成多个独立 AO 对象
var b = foo()
b()
b()
//i没有销毁 因为在未来程序中 随时可能需要i变量
//已知 函数执行结束函数中的变量销毁
//原因:接下来不需要使用了,所以就销毁了
//程序二
function foo1(){
var i=0;
function bar(){
i+=1;
console.log(i);
}
bar()
bar()
bar()
}
foo1()
foo1()
//程序1中内部函数未来随时可能被触发
//程序二中内部函数 当外部函数执行结束 内部函数销毁
//重点:闭包相似程序的区分
//一个变量在什么情况下会被销毁: 用不到时销毁
闭包 : 当一个函数a return返回另一个函数,那么这个函数a就是闭包函数
优点:
1.防止变量污染
---> 闭包在执行时彼此独立互不干扰
2.外部函数可以访问内部函数的变量(错误的观点)
---> 外部的变量只是引用了闭包函数内部的retrun函数,在作用域链的角度上,没有违背,函数变量有化的原则
缺点:容易内存泄漏
--->正常情况函数执行结束 变量释放, 因为闭包函数中变量未来可能你会被其他程序引用 ,所以不会销毁, 占据了内存
- 解决变量污染 ,就是让return 返回的函数在全局下标记清除法 实现零引用
function foo(){
var i=0;
var b = 0;
return function(){
i+=1;
console.log(i);
}
}
var a = foo()
a()
a()
a=null
//或者让闭包函数是一个立即执行函数
(function(){
return function(){
}
})()
24. this
this是一个关键字 正常情况下 赋值对象
在不同情况下赋值不同
this判断
1.看this写在哪里
--->全局 this===window
--->函数 看第二步
2.是什么函数
--->如果是function函数
3.看函数是如何触发的
--->call apply bind this===三个函数的实参一
--->new this===new 创建的对象那个
--->事件 this===出发时间的dom元素
--->难点: 普通对象触发 this===普通对象
//this 练习1
var o={
a:10,
b:{
name:function(){
return this;
},
zzz:'1'
}
}
var x=o.b.name();
console.log(x); //{zzz: '1', name: ƒ}
//练习2
function Animal(name,age){
console.log(this);
//Animal {}
// age: 2
// name: "mao"
// 构造函数中的this指向的是实例对象
this.name = name;
this.age = age;
}
var cat = new Animal('mao',2);
25. new
构造函数 构造的函数叫构造函数(实例化对象)
作用:创建对象
构造函数不能有return 因为new自带了return一些效果
function Animal(name,age){
console.log(this);
// 构造函数中的this指向的是实例对象
this.name = name;
this.age = age;
}
new
作用:触发函数
返回对象
var cat = new Animal('mao',2);
console.log(cat);
// Animal {name: 'mao', age: 2}
// 获取构造函数
console.log(cat.constructor);
// ƒ Animal(name,age){
// console.log(this);
// // 构造函数中的this指向的是实例对象
// this.name = name;
// this.age = age;
// }
console.log(Animal.constructor);
// ƒ Function() { [native code] }
// 函数的构造函数是Function