1. 作用域
const a = 3;
switch (a) {
case 1:
const result = a * 2;
console.log(result);
break;
case 2:
const result = a * 2;
console.log(result);
break;
case 3:
const result = a * 2;
console.log(result);
break;
}
在switch的case中,它们并不是单独作用域,所以如果一旦重复声明变量则会报错:
Uncaught SyntaxError: Identifier 'result' has already been declared
在WebStorm中也会有错误提示:
貌似在VSCode中没有对应的错误提示。
2. in
判断一个对象中是否有某个属性,你会使用什么方法?
const person = {
name: "张三",
age: 18,
};
if (person.name) {
console.log("person.name 名字存在");
} else {
console.log("person.name 名字不存在");
}
// 输出:person.name 名字存在
还是:
if ("name" in person) {
console.log("name in person 名字存在");
} else {
console.log("name in person 名字不存在");
}
// 输出:name in person 名字存在
那么这两种方法有什么区别呢?
众所周知,在JavaScript中undefined
和null
在条件判断中会被判断为false
,也正是因为JavaScript有这个特性,带来一些方便之处也带来一些不方便之处,我们可以看看上面的代码中当name
的值为undefined
,最终输出的结果是什么?
const person = {
name: undefined,
age: 18,
};
if (person.name) {
console.log("person.name 名字存在");
} else {
console.log("person.name 名字不存在");
}
// 输出:person.name 名字不存在
显然,在上面的代码中name
是有值的,它的值就是undefined
,我们希望它输出**”name有值”**这个结果,但是使用第一种方法它默认判断person.name=false
,所以也就无法进行输出。
而使用"name" in person
这种语法,就可以正确的进行条件判断。同理name
为null
值的时候也一样。
const person = {
name: undefined,
age: 18,
};
if ("name" in person) {
console.log("name in person 名字存在");
} else {
console.log("name in person 名字不存在");
}
// 输出:name in person 名字存在
3. 模板语法
所谓的模板语法就是如下所示:
const name = "张三";
const age = 18;
console.log(`${name}今年${age}岁`);
// 输出:张三今年18岁
但是该模板语法还可以进行骚操作。
function introduction(strings, ...values) {
// 这里返回的是什么,模板字符串的结果就是什么
return values;
}
const name = "张三";
const age = 18;
console.log(introduction`${name}今年${age}岁`); // 输出:["张三", 18]
- strings:模板中的普通字符串合成的数组。
- …values:模板
${}
中变量合成的数组。
知道这个特性后可以操作的空间就多了,剩下的留给大家自己发挥。
4. Generator
async await
的实现原理就是使用了Generator和Promise,Generator函数现在已经不常用了(反正我是不常用),不过在React中,有一个库叫做Redux-saga
,它就使用到了Generator。
Generator的作用是让函数分步执行,当你调用一个Generator函数时,函数并不会立即执行而是返回一个这个生成器的 迭代器( iterator )对象。当这个迭代器的next()
方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield
的位置为止,yield
后紧跟迭代器要返回的值。或者如果用的是 yield*
(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。
可能看起来很懵,我们直接举一个例子来看一眼就大致明白了:
function* initID() {
let id = 0;
while (true) {
yield id;
id++;
}
}
const getId = initID();
console.log(getId.next().value); // 0
console.log(getId.next().value); // 1
console.log(getId.next().value); // 2
console.log(getId.next().value); // 3
console.log(getId.next().value); // 4
这里还可以在next()
中传入参数,如下所示:
function* initID() {
let id = 0;
while (true) {
const x = yield id;
console.log(x); // 从第二次开始,只有传入的那次才会获得值,其它都为undefined
id++;
}
}
const getId = initID();
console.log(getId.next());
console.log(getId.next(10));
console.log(getId.next());
console.log(getId.next());
console.log(getId.next());
5. 动态引入模块
现在浏览器已经支持了ES module
引入模块的方式,但是仅限主流浏览器有效。
在index.html
的引入js文件的script标签中要添加type="module"
属性。
<script src="./index.js" type="module"></script>
下面我们有一个模块module.js
。
console.log("这是默认加载的语句");
export default function module() {
console.log("这是模块");
}
然后在index.js
中进行调用:
import module from "./module.js";
module();
但是上面的导入方式有一个问题,就是当module.js
这个文件比较大的话,可能会影响页面加载速度,带来不好的体验,于是就有了动态加载的方法,可以客户在有用到这个模块的时候才进行加载。
const a = Math.random() * 10;
// a小于5时才进行加载
if (a < 5) {
import("./module.js").then((res) => {
res.default();
});
} else {
console.log("不加载");
}
5.1 兼容性
不得不说IE真是业界毒瘤。
说到这里就不得不提到Vite,它的原理就是使用了type="module"
,然后在现代浏览器上面开发,所以在开发环境下的启动和热更新速度就非常快。
在webpack中也有类似的语法,一旦在webpack中使用了动态引入,则被引入的模块会被进行单独打包,其中React和Vue的页面懒加载也是使用了该原理。
6. 最后
其实JavaScript还有更多的有趣的特性我们并不知道,编程本来就是一个积累过程,积累的越多,就更容易看懂别人代码中的一些高级写法。
参考链接:
- 5 MORE Must Know JavaScript Features That Almost Nobody Knows
- 模板字符串
- Generator function