1. 脚本和模块
js有两种源文件:脚本(语句)和 模块(语句,import声明,export声明)
脚本可由浏览器或者node环境引入执行;模块只能由import引入执行
从概念上:脚本是主动性JavaScript代码段,控制宿主完成一定任务的代码;模块是被动性的JavaScript
代码段,是等待被调用的库;
现代浏览器可用script引入模块,加上type=“module”,如果不加这个,默认引入的是脚本,如果我们在脚
本中使用export,会抛错
<script type="module" src="xxx.js"></script>
import声明
- import x from “./a.js” //引入模块中导出的默认值
- import { a as x,modify} from “./a.js” //引入模块中的变量
- import * as x from “./a.js” //把模块中的所有变量以对象属性的方式引入
- import d, {a as x, modify} from “./a.js” //组合
- import d, * as x from “./a.js” //组合
模块a
export var a = 1;
export function modify(){
a = 2;
}
模块b
import {a, modify} from "./a.js";
console.log(a); // 1
modify();
console.log(a); // 2
//调用修改后,b模块变量也跟着变了,说明导入和一般赋值不同,导入后的变量和原变量使用的是同一个
export声明
-
export {a,b,c}
-
可以在以下声明语句前加export
- var, function(含async 和generator) , class, let, const
-
export default 导出默认变量值,可用于function 和 class
-
export default 表达式
//这里和导出变量不一样,这里就是导出a的值,以后a的变化和导出的值无关了
var a = {};
export default a;
- export a from “a.js” //从其他模块中导出
2. 预处理和指令语言(js语法全局机制)
js执行前,对脚本,模块,函数体中的语句进行预处理,预处理过程会提前处理var,函数声明,class,
const,和let这些语句,以确定其中变量的意义
- var 声明
var a = 1;
function foo() {
console.log(a);
var a = 2;
}
foo(); //预处理时有函数级别的a,就不会访问外层的a 了,而函数级别的a此时没赋值,所以是undefined
// with中修改了 o中a的值,with中的var a 会预处理,所以得到结果是 2,undefined
var a = 1;
function foo() {
var o= {a:3}
with(o) {
var a = 2;
}
console.log(o.a);
console.log(a);
}
foo();
因为早年js没有let和const,只能用var,又因为var除了脚本和函数体都会穿透,于是大家发明了
“立即执行函数表达式(IIFE)” 这一用法,用于产生作用域
//为页面添加了20个div,绑定事件,打印序号
//立即执行函数在循环内构造了作用域,每个循环都产生一个新环境,每个div中的i互不干扰
for(var i=0;i<20;i++){
void function(i){
var div = document.creatElement("div");
div.innerHTML = i;
div.onclick = function(){
console.log(i);
}
}(i);
}
如果不用IIFE
//最后所有的div都打印20
for(var i = 0; i < 20; i ++) {
var div = document.createElement("div");
div.innerHTML = i;
div.onclick = function(){
console.log(i);
}
document.body.appendChild(div);
}
3. JavaScript有哪些语句?
语句块
//语句块会产生作用域
{
let x = 1;
}
console.log(x); //报错
if语句
if(a < 10) {
//...
} else if(a > 10 && a < 20) {
//...
} else if(a > 20) {
//...
} else {
//...
}
switch语句
switch(num) {
case 1:
print 1;
break;
case 2:
print 2;
break;
case 3:
print 3;
break;
}
循环语句
let a = 100
while(a--) {
console.log("*");
}
//do while 至少执行一次
let a = 101;
do {
console.log(a);
} while(a < 100)
for循环
for(i = 0; i < 100; i++)
console.log(i);
for(var i = 0; i < 100; i++)
console.log(i);
for(let i = 0; i < 100; i++)
console.log(i);
for in 循环
//输出得到a 和 b,c是不可枚举的
let o = { a: 10, b: 20}
Object.defineProperty(o, "c", {enumerable:false, value:30})
for(let p in o)
console.log(p);
for of 循环和 for await of
背后的机制是iterator
for(let e of [1, 2, 3, 4, 5])
console.log(e);
可以给任何一个对象添加iterator,使他可以用for of语句
let o = {
[Symbol.iterator]:() => ({
_value: 0,
next(){
if(this._value == 10)
return {
done: true
}
else return {
value: this._value++,
done: false
};
}
})
}
for(let e of o)
console.log(e);
实际操作,一般不需要定义iterator;可以用generator function
function* foo(){
yield 0;
yield 1;
yield 2;
yield 3;
}
for(let e of foo())
console.log(e);
return语句
function squre(x){
return x * x;
}
break和continue
outer:for(let i = 0; i < 100; i++)
inner:for(let j = 0; j < 100; j++)
if( i == 50 && j == 50)
break outer;
outer:for(let i = 0; i < 100; i++)
inner:for(let j = 0; j < 100; j++)
if( i >= 50 && j == 50)
continue outer;
with语句
//把对象属性在它内部作用域中变成变量
let o = {a:1, b:2}
with(o){
console.log(a, b);
}
try
//即使try有return,finally也会执行
try {
throw new Error("error");
} catch(e) {
console.log(e);
} finally {
console.log("finally");
}