一、變量提升
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]