一、作用域
【解释】: 规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问。
【分类】:
1.1 局部作用域
【分类】:
1、函数作用域
【解释】: 在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
【示例】:
<script>
function counter(x, y) {
let s = x + y;
console.log(s);
}
counter(10, 8);
console.log(s);
</script>
【总结】:
- 函数内部声明的变量,在函数外部无法被访问
- 函数的参数也是函数内部的局部变量
- 不同函数内部声明的变量无法互相访问
- 函数执行完毕后,函数内部的变量实际被清空了
2、块作用域
【解释】: 在 JavaScript 中使用 {} 包裹的代码称为代码块,代码块内部声明的变量外部将【有可能】无法被访问。
【示例】:
<script>
{
let age = 18;
console.log(age);
}
console.log(age);
let flag = true;
if(flag) {
let str = 'hello world!';
console.log(str);
}
console.log(str);
for(let t = 1; t <= 6; t++) {
console.log(t);
}
console.log(t);
</script>
【常量值】: JavaScript 中除了变量外还有常量,常量与变量本质的区别是 【常量必须要有值且不允许被重新赋值】,常量值为对象时其属性和方法允许重新赋值。
【示例】:
<script>
const version = '1.0';
const user = {
name: '小明',
age: 18
}
user = {};
user.name = '小小明';
user.gender = '男';
</script>
【总结之let、var、const】:
- let 声明的变量会产生块作用域,var 不会产生块作用域
- const声明的常量也会产生块作用域
- 不同代码块之间的变量无法互相访问
- 推荐使用
let 或 const - 开发中
let 和 const 经常不加区分的使用,如果担心某个值会不小被修改时,则只能使用 const 声明成常量。
关键字 | 块级作用域 | 变量提升 | 初始值 | 更改值 | 通过window调用 |
---|
let | √ | × √ | - | √ | × | const | √ | × √ | √ | × | × | var | × | √ | - | √ | √ |
1.2 全局作用域
【解释】: <script> 标签和 js 文件的【最外层】就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。
【示例】:
<script>
function sayHi() {
}
</script>
- 全局作用域中声明的变量,任何其它作用域都可以被访问
<script>
let name = '小明';
function sayHi() {
console.log('你好' + name);
}
let flag = true;
let x = 10;
if(flag) {
let y = 5;
console.log(x + y);
}
</script>
【总结】:
- 为
window 对象动态添加的属性默认也是全局的,不推荐! - 函数中未使用任何关键字声明的变量为全局变量,不推荐!!!
- 尽可能少的声明全局变量,防止全局变量被污染
1.3 作用域链
【解释】: 函数内部允许创建新的函数,f 函数内部创建的新函数 g ,会产生新的函数作用域,由此可知作用域产生了嵌套的关系。作用域链本质上是底层的变量查找机制,在函数被执行时,会优先查找当前函数作用域中查找变量,如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域
【示例】:
<script>
let a = 1;
let b = 2;
function f() {
let c;
function g() {
let d = 'yo';
}
}
</script>
<script>
let a = 1;
let b = 2;
function f() {
let c;
console.log(a);
console.log(d);
function g() {
let d = 'yo';
console.log(b);
}
g()
}
console.log(c);
console.log(d);
f();
</script>
【总结】:
- 嵌套关系的作用域串联起来形成了作用域链
- 相同作用域链中按着从小到大的规则查找变量
- 子作用域能够访问父作用域,父级作用域无法访问子级作用域(就近原则)
1.4、闭包
【解释】: 闭包是一种比较特殊和函数,使用闭包能够访问函数作用域中的变量。
【好处】: 可以把一个变量使用范围延伸
【示例】:
<script>
function foo() {
let i = 0;
function bar() {
console.log(++i);
}
return bar;
}
let fn = foo();
fn();
</script>
【总结】:
- 闭包本质仍是函数,只不是从函数内部返回的
- 闭包能够创建外部可访问的隔离作用域,避免全局变量污染
- 过度使用闭包可能造成内存泄漏‘
- 回调函数也能访问函数内部的局部变量。
1.5 变量提升
【解释】: 允许在变量声明之前即被访问
【示例】:
<script>
console.log(str + 'world!');
var str = 'hello ';
</script>
let和var都有提升,但是let定义的变量没有赋值之前是不可以使用、var可以使用是undefined
【总结】:
- 变量在未声明即被访问时会报语法错误
- 变量在声明之前即被访问,变量的值为 `undefined
- let
声明的变量不存在变量提升,推荐使用 let`【也有人认为具有提升但是不赋值不能使用】 - 变量提升出现在相同作用域当中
- 实际开发中推荐先声明再访问变量
二、函数
2.1、函数提升
【解释】: 函数在声明之前即可被调用
【示例】:
<script>
foo();
function foo() {
console.log('声明之前即被调用...');
}
bar();
var bar = function () {
console.log('函数表达式不存在提升现象...');
}
</script>
【总结】:
- 函数提升能够使函数的声明调用更灵活
- 函数表达式不存在提升的现象
- 函数提升出现在相同作用域当中
2.2、函数参数
1、默认参数
【示例】:
<script>
function sayHi(name="小明", age=18) {
document.write(`<p>我叫${name},我今年${age}岁了。</p>`);
}
sayHi();
sayHi('小红');
sayHi('小刚', 21);
</script>
【总结】:
- 声明函数时为形参赋值即为参数的默认值
- 如果参数未自定义默认值时,参数的默认值为
undefined - 调用函数时没有传入对应实参时,参数的默认值被当做实参传入
2、动态参数
【解释】: arguments` 是函数内部内置的伪数组变量,它包含了调用函数时传入的所有实参。
【示例】:
<script>
function sum() {
let s = 0;
for(let i = 0; i < arguments.length; i++) {
s += arguments[i];
}
console.log(s);
}
sum(5, 10);
sum(1, 2, 4);
</script>
【注意】:
- arguments是一个伪数组
- arguments的作用是动态获取函数的实参
3、剩余参数
【语法及示例】:
<script>
function config(baseURL, ...other) {
console.log(baseURL);
console.log(other);
}
config('http://baidu.com', 'get', 'json');
</script>
2.3、箭头函数
【解释】: 箭头函数是一种声明函数的简洁语法,它与普通函数并无本质的区别,差异性更多体现在语法格式上。
【示例】:
<script>
let foo = () => {
console.log('^_^ 长相奇怪的函数...');
}
foo();
let form = document.querySelector('form');
form.addEventListener('click', ev => ev.preventDefault());
</script>
【总结】:
- 箭头函数属于表达式函数,因此不存在函数提升
- 箭头函数只有一个参数时可以省略圆括号 `()
- 箭头函数函数体只有一行代码时可以省略花括号
{} ,并自动做为返回值被返回 - 箭头函数中没有
arguments ,只能使用 ... 动态获取实参 - 涉及到this的使用,不建议用箭头函数
|