一、变量提升
1、变量
var 表示是变量 存在提前申明 同时也会和window存在映射机制(只存在于全局变量和window之间)
console.log(a); // undefined 没报错,变量提升了
console.log(window.a, 'a' in window) // undefined true
var a = 5;
console.log(a); // 5
console.log(window.a, 'a' in window) // 5 true
没有var就不是变量,也就不存在变量提升,相当于 window.b=9
console.log(b) // Uncaught ReferenceError: b is not defined
console.log(window.b, 'b' in window) // undefined false
b = 9;
console.log(b) // 9
console.log(window.b, 'b' in window) // 9 true
var c = d = 10; 相当于
var c=10;
d=10;
2、函数
function和var变量得区别是:function不仅提前声明,还提前定义(赋值)
另外,function由于是引用类型,定义的时候其实是赋值了一个引用地址,这个引用地址指向某个堆内存,这个堆内存里存着这个函数字符串
var f1 = () => {
console.log(2)
}
function f1() {
console.log(1)
}
f1(); // 2 因为function f1提前声明定义了
函数内部变量提升, 私有变量不带var, 就向上级索取
console.log(a, b); // undefined undefined
var a = 12, b = 12;
function fn() {
console.log(a, b); // undefined 12 b不带var 向上级索取
// 这里b赋值,其实是window.b=13, 由于全局作用域变量b和 window.b存在映射关系,外部b变量也成了13
var a = b = 13;
console.log(a, b) // 13 13
}
fn();
console.log(a, b); // 12 13
条件语句中的变量提升
- 条件语句中的var变量仍然会变量提升
- 条件语句中的函数也会变量提升,但只是声明,不会定义
- 如果条件成立,条件中第一步就是先给之前预检查时只声明没定义的函数定义
console.log(c); // undefined
console.log(f1); // undefined 变量提升了 但只是声明,不定义
if (1) {
console.log(f1); // funciton f1(){} 如果条件成立,在块级作用域中,先把变量提升的函数赋值(因为之前只是声明了,没有赋值)
var c = 2;
function f1() {
console.log('f1');
}
}
console.log(f1); // funciton f1(){}
console.log(c); // 2
当然要是条件不成立,那if中也不会赋值
console.log(c); // undefined
console.log(f1); // undefined 变量提升了 但只是声明,不定义
if (0) {
console.log(f1);
var c = 2;
function f1() {
console.log('f1');
}
}
console.log(f1); // undefined
console.log(c); // undefined
下一题
f = function () { return true }
g = function () { return false }
~function () {
if (g() && [] == ![]) { // 行4 这里会报错 [] == ![] true
f = function () { return false }
function g() {
return true
}
}
}();
console.log('f()', f())
console.log('g()', g())
解析:在第四行就报错了,执行不下去了,为什么,因为在自执行函数中,if之前,function g提前声明了,但没有赋值;结果if判断条件g() && [] == ![]去调了,但此时g还是undefined呢
变换一下,if判断条件中g()换为f()
f = function () { return true }
g = function () { return false }
~function () {
if (f() && [] == ![]) { // 行4 [] == ![] true
f = function () { return false }
function g() {
return true
}
}
}();
console.log('f()', f()) // false
console.log('g()', g()) // false
解析:由于自执行函数中没有f函数和f变量,不存在提前申明,所以if判断中f()调用全局作用域的f,所以为true; 然后执行if语句中代码,全局作用域中的f被重新赋值(return false),而g函数只是在自执行函数中生效,不会影响全局作用域中的g
二、重命名
1、var和function名称重复会相互覆盖
ff() // ff
var ff =12;
ff() // 报错 因为此时ff是12,不是函数
function ff(){
console.log('ff')
}
ff() // 报错 因为此时ff是12,不是函数
2、js很懒,如果已经声明且赋值,再声明赋值,只会重新赋值
fn(); // 4
function fn() { console.log(1) }
fn(); // 4
function fn() { console.log(2) }
fn(); // 4
var fn = 2;
fn(); // not function error
function fn() { console.log(3) }
fn(); // not function error
function fn() { console.log(4) }
fn(); // not function error
三、let const
1、let const 不存在变量提升 且和window切断映射
console.log(l); // error
let l = 5;
console.log(window.l) // undefined
console.log('l' in window) // false
2、浏览器执行之前会检测使用let得变量, 会提前报错
let a=12;
a=12;
console.log(a) // 报错 从执行上这一行还没出错,但是提前检测了
let a=13; // 报错,已经声明了
var a=13; // 报错
其实第一行就报错了
var b=12; // 报错
let b=13;
3、暂时性死区
let a = 10, b = 10;
let fn = function () {
console.log(a) // 报错 因为函数作用域中定义了let a
console.log(b) // 10
let a = b = 20
console.log(a) // 20
console.log(b) // 20
}
fn()
console.log(a, b) // 10 20
var a = 12;
if (true) { // let会形成{}块级作用域
console.log(a); // 报错
let a = 13;
console.log(a) // 13
}
console.log(a) // 12
四、作用域
1、函数得上级作用域是谁,和执行时的位置没有关系,和该函数定义的位置有关系
var a = 12;
function fn2() {
console.log(a) // 12
}
function sum() {
var a = 20;
fn2();
console.log(a); //20
}
sum();
2、闭包
var n = 10;
function fn3() {
var n = 20;
function fn4() {
n++;
console.log(n)
}
fn4();
return fn4;
}
var x = fn3(); // 21
x(); //22
x(); //23
console.log(n); //10
`
堆内存:存储引用数据类型值(对象:键值对,函数:代码字符串)
- 1) 堆内存释放 让引用堆内存地址的变量赋值为null
- 栈内存:提供js代码执行环境,存储基本类型值
- 1) 栈内存释放:当函数执行完成, 所形成的私有作用域自动释放,
- 但如果栈内存某内容被栈内存以外的变量引用了,就不能释放
- 2) 全局栈内存只有页面关闭才能释放
`
五、鄙视题
私有作用域首先是形参赋值,然后是变量提升,再然后才是代码执行
var a = 12, b = 13, c = 14;
function fn(a) {
// 私有作用域 首先形参赋值 a=12
// 然后变量提升 var b;
// var function 形参 都是私有变量
console.log(a, b, c) // 12 undefined 14
var b = c = a = 20;
console.log(a, b, c) // 20 20 20
}
fn(a); // 传入实参12
console.log(a, b, c) // 12 13 20
衍生一下
var arr = [12, 23];
function fn1(arr) {
console.log(arr); // [12, 23]
// 这样赋值,改的不仅仅是实参,而且把全局中的arr[0]也改了,全局arr成了[100.23]
arr[0] = 100;
arr = [100];
arr[0] = 0;
console.log(arr); // [0]
}
fn1(arr);
console.log(arr); // [100,23]