昨天学长说可以放缓一下学习进度,刚好最近期末复习也不是很紧张,所以来重新复习一下js的一些知识点。
一:变量
(1)变量声明
来简单看一下变量的一些知识点。首先是变量声明:变量声明尽量使用数组字母下划线 来举几个例子:
//声明变量尽量使用 字母数字下划线
const web ='1',name='2';
const qianduan =houduan = quanhzan='money'
console.log(qianduan);
console.log(houduan);
console.log(quanhzan);
//js是弱数据类型语言
// typeof 可以查看变量的类型
console.log(typeof web);
我们可以连续声明多个变量 也可以使用typeof来检查变量的数据类型,来看看输出结果
(2)变量提升
变量提升的问题出现在使用var关键词声明的变量,来看个例子
//通过var声明的变量会变量提升
console.log(web)//在运行代码,会执行一个解析,会将var声明的变量做一个提前 但是提前的只是声明
var web='houdunren'
运行之后我们发现结果没有报错 也是undefined 这说明var造成的变量提升 只是提升了声明过程,但是忽略了赋值过程。那么下面我将展示一段错误代码:
function hd(){if(false){var web='111'}console.log(web)
}
hd()
来运行一下看看
发现结果是undefined 本质上就是变量提升造成的问题,在执行代码之前var就将代码的声明提前到最上方 所以函数直接就将未赋值的web打印出来了。所以现在的前端代码为了避免变量提升造成的困扰,建议使用let或const来声明变量或常数。
(3)let与const声明变量与TDZ时间死区
鉴于var声明所带来的缺陷 现在使用let与const 这类具有临时性死区(tdz)的关键词避免变量提升的问题。(知道就行 不过还是建议了解一下时间死区)时间死区:是指变量从进入作用域到被实际声明的一段时间,在这段时间里面 访问该变量会导致错误从而不被提前使用。简单来说就是变量提升被一个错误(ReferenceError)阻断了。这里我就不举实例了。
(4)全局污染
我们先来看一个例子:
function show() {web='houdunren'
}
show()
console.log(web);
来看看运行结果:
神奇的发现我们并未实际声明web这个变量 但是web变量确实被输出了 并且输出的作用域是全局作用域 这就是全局污染。那么这就会造成一些问题 比如 我们在编写代码之前已经提前引入了第三方库或者其他js文件 这些文件中如果存在同名的变量就会被污染
所以js中所有的变量一定要先声明后使用。
(5) 变量冻结
继续先看一个例子:
const HOST={url:'https://www.houdunren.com/api',port:443
};
// 对象冻结 冻结之后代码不再可以修改
Object.freeze(HOST);
HOST.port=80;
console.log(HOST);
声明了一个HOST对象变量 使用了Object.freeze方法之后对象里面的数据就不可被修改了
(6)传值与传址
对于简单的数据类型 在传递值的时候是直接把数值传递过去的;但是对于复杂数据类型(对象,数组等),在传递值的时候是将自身数据的内存地址赋值过去的 问题就是当其中一个值发生变动的时候 同地址的另一个变量的值也会变化
//这里是传值 因为简单数据类型占用空间小 所以会直接开辟一块空间
let a=1;
let b=a;
//这里是传址 因为对象等复杂数据类型占用空间很大 所以会赋值前者的地址给后者
// 造成的问题是两者之间有一个人出现改动 后者的值也会被改动
// 解决方法是使用深浅拷贝来避免
let e={};
let f=e;
解决办法也很好解决 就是使用深浅拷贝。对于深浅拷贝另见我的对象那篇文章(附图)
(7)null与undefined
对于简单数据类型来说 声明未赋值 输出的就是undefined 说明值的没有定义的;对于复杂数据(数组 对象)来说声明之后没有具体值 输出之后就是null 可以理解成复杂数据类型就是一个书包 书包没东西那肯定就是null了
二:运算符与逻辑控制
(1)短路逻辑
短路逻辑常备用来设定默认值 与简单的条件执行语句 我们直接来看看例子吧
let a=0;
let b=1
if(a||b){console.log('nihao');
}
只有a或b同时为0 的时候才不会输出。
let f=a||b;
console.log(f);
当a是0的时候 f 就被赋值成 b
function star(num){return '*'.repeat(num||1)//repaat(重复次数)
}
console.log(star(123));
如果没有输入参数的时候 函数只返回一个 *
(2)do while循环
do while循环是一定会执行一次循环体的 简单来看几个例子
打印三角形
let start=0;
do{let n=0;let line = ""; // 创建一个空字符串来存储当前行的星号do{line += "*"; // 将星号添加到字符串中}while(++n<=start)console.log(line); // 打印完整的行
}while(++start<=5)
// 但是以我个人主见 使用for循环来完成这个效果是更好的
(3)打印正三角型
for(let i=0;i<10;i++){for(let j=10-i;j>0;j--){process.stdout.write(' ')}for(let j=i*2-1;j>0;j--){process.stdout.write('*')}process.stdout.write('\n')}
这里的 process.stdout.write()是在控制台单行输出不自动换行的一种方法 不过基本被弃用了
(4)label(类似于goto 但不同)
当函数运行到break outerLoop的时候 直接跳出指定的循环 直接看例子吧
// 标签(label)配合break使用的例子
outerLoop:
for(let i = 0; i < 3; i++) {innerLoop:for(let j = 0; j < 3; j++) {console.log(`i=${i}, j=${j}`);if(i === 1 && j === 1) {break outerLoop; // 直接跳出外层循环}}
}
因为inner标签来第一层循环里面 所以当运行到breakj outerloop的时候就直接跳出了第一层循环(被标记的循环) ,来看看输出结果
(5)for in 与 for of
for in可以遍历数组和对象 同样的for of也可以遍历数组 也可以遍历字符串 如果使用for of来遍历对象 必须先获取键名或者值 可能说的很抽象 那直接来看例子
定义一个数组和对象
let obj={uname:'123',age:12,gender:'女'
}
const arr=[1,2,3,45,5,6]
1.使用for in 遍历对象的属性
// 遍历对象属性
for(let k in obj){console.log(obj[k]);
}
2.使用for in遍历数组的值
// 遍历数组
for (const key in arr) {console.log(arr[key]);
}
3.推荐使用for of遍历数组
for (const value of arr) {console.log(value); // 输出: 1, 2, 3, 45, 5, 6
}
4.使用for of遍历字符串
const str = "hello";
for (const char of str) {console.log(char); // 输出: h, e, l, l, o
}
5.使用for of遍历Map集合(键值对集合)
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {console.log(key, value); // 输出: a 1, b 2
}
6.for of遍历对象的三种类型
// 6. 如果要遍历对象,可以先获取其键、值或条目
// 方法1: 遍历对象的键
for (const key of Object.keys(obj)) {console.log(key, obj[key]);
}// 方法2: 遍历对象的值
for (const value of Object.values(obj)) {console.log(value);
}// 方法3: 遍历对象的键值对
for (const [key, value] of Object.entries(obj)) {console.log(key, value);
}
o而k之,感谢阅读,拜拜