注:
log語句打印的是結果,直接顯示信息;
dir語句打印的是內容,對顯示對象的所有屬性和方法。
1. 對象屬性名 & 堆棧內存
a指向一個對象,對象存儲在堆中
=> 數組和對象的區別
b是對象
把b作爲obj的的屬性名:obj[b]=100,即 [object,object]:100
(是把對象b…toString了)
a[b] 存的是[object,object]
a[c]也是,屬性名相同,所以覆蓋 a[b]= ‘培訓’
=>Object.prototype.toString / valueOf
2. Symbol() 創建唯一值
結果:a[b] :珠峯
=>自己實現一個Symbol函數
3. 閉包
‘4’
alert結果都要toString()
立即執行函數
函數也是引用類型,存儲在堆中,test函數中沒有形參
函數每次執行,都會創建一個執行上下文(執行環境)
作用域鏈:test函數在定義時,自己是在哪個執行上下文中創建的,也就是上級作用域(test是立即執行函數的返回結果,所以是在這個立即執行函數裏面定義的,這個結果是一個函數,所以這個函數也是在立即執行函數裏定義的)執行的時候,會去上級作用域裏找
分析:
- 預編譯階段,發生變量 | 函數聲明提升,test是自執行函數的返回結果,是一個函數,指向該函數所在堆內存地址引用
- 執行階段:自執行函數先執行,創建自己的執行上下文,返回一個函數,所以這個函數是在EC函數裏定義的,形成作用域鏈
- 執行階段:test(5),創建自己的執行上下文,當前上下文沒有i定義,去上級作用域找到 ,i.toString(),輸出
- 銷燬階段:test(5)執行完後,執行上下文內部沒有被其他人佔用的東西(看函數的地址啊,有沒有函數在自己裏定義的),就一定會銷燬
i=2所在的執行上下文不能被銷燬,因爲是右邊函數的上級作用域,i 也就保存下來了,閉包
var a = 0;
b=0
function A(a){
A = function(b){console.log(a+b++)}
console.log(a++)
}
A(1)//1
A(2)//4
A被重寫了 GO代表全局變量
全局的a和b不變,閉包保護了全局不被污染
- 先是預編譯階段的函數,變量提升,A指向函數的堆內存地址
- 函數執行:
調用A(1),進入A(1)函數的運行環境,創建A函數執行上下文,推入stack棧中:創建變量對象,作用域鏈,this
(1)參數a=1, 自由變量A,是在父執行環境,也就是全局裏面定義的,此時重寫了全局裏面的A的指向(新A是在A(1)EC裏定義的)
(2)alert(a++),A(1)裏的a++【輸出=>1】,a=2
調用A(2)是,A是已經被重寫過的,參數b = 2,(傳入的),a去作用域鏈上找,此時A是在A(1)EC裏定義的,所以a=2【輸出=>4, 然後b++】 - 銷燬階段:此時A(最右邊堆內存)是在A(1)EC裏定義的,A所以(1)EC不銷燬
4. 面向對象
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2)
}
Foo.prototype.getName = function () {
console.log(3)
}
var getName = function () {
console.log(4)
}
function getName() {
console.log(5)
}
Foo.getName();//2
getName()//4
Foo().getName()//1
getName()//1
new Foo.getName()//2,先是成員調用Foo.getName(), new xx
new Foo().getName()//3 先是new Foo(),創建實例,this變了
new new Foo().getName()//3 先是new Foo(),然後new xxx.getName()
Foo()普通函數,在這裏也是構造函數
Foo.getName 添加屬性(執行過程中吧,按順序加上屬性,畫原型鏈)
作爲普通函數調用時,看有沒有自由變量,看執行環境
有function , var,是ES5的語法,所以會有JavaScript 變量提升
- JavaScript 中,函數及變量的
聲明
都將被提升到函數(當前作用域)的最頂部。 - var 提前聲明,function提前聲明
- 所以變量可以在使用後聲明,也就是變量可以先使用再聲明。
function getName(){console.log(5)} 聲明,但在當前作用域下已經有getName了,就不重新聲明,直接賦值
函數也是對象,原型也是對象,在堆裏
函數參數是私有的,變量在哪裏定義,哪裏就是上級作用域
實例調用方法,一定是原型上的方法
箭頭函數不能被new =>箭頭函數和普通函數的區別
js運算符優先順序表 :new Foo.getName()無參數的new;new Foo().getName() 有參數的new
圓括號 > (成員,new(),函數調用) > new xx無參
5. js 異步
async function async1(){
console.log('async1 start')
await async2();
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
async1()
new Promise(function(resolve){
console.log('promise1')
resolve()
}).then(function(){
console.log('promise2')
})
console.log('script end')
/*script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/
EC函數:立即執行函數
主棧 - EventQuene(微任務,宏任務)
代碼走到異步時,會放到事件隊列去,先把主棧代碼執行完,纔會去事件隊列裏找
(輸出start的時候,已經是開始執行Js了)
await async2(); 執行async2,等待返回的結果(這是要現在執行async2,把等待放在微任務隊列)
promise ,asyn,定時器都是異步的
瀏覽器是多進程的,渲染 & http請求。。
js是單線程=>瀏覽器只給js一個線程來渲染
事件隊列 event queue
棧裏的主線程代碼執行完了,纔會到事件隊列裏找(優先執行微任務【promise ,asyn,await】,然後宏任務【定時器,事件綁定,ajax】),放在主棧裏執行
var a = ?
if(a == 1 && a == 2 && a == 3){
console.log('條件成立');
}
6.轉換數據類型
= = 轉換數據類型
轉換規則:
對象 = = 字符串:對象.toString()變爲字符串之後,再比較
null = = undefined相等,但是和其他值比較,就不再相等了
NaN == NaN 不相等
剩下的都是轉化爲數字
例如 [10] = =10
[10].toString() -> “10” ->Number(“10”) -> 10
=== 絕對相等
方法1. 變成字符串,重寫原型的toString()方法,利用==比較規則
或者valueof()
方法2. 劫持函數
獲取obj.name :觸發get()
設置obj.name屬性:觸發set()
defineProperty GETER攔截器中不能再次獲取當前屬性,所以使用++i,否則報錯:棧溢出
a.shift 刪除第一項並返回該項