js高級
案例問題
案例一:
let a={name:"測試"};
function test(obj){
obj={name:"hehe"};
}
test(a);
console.log(a);//{name: "測試"}
說明:a傳入test中,obj重新賦值,obj的的形參的指向就變化了,此時其實(shí)obj={name:"hehe"};在函數(shù)執(zhí)行完畢就會被當(dāng)作垃圾回收,而a的指向是不變的。
案例二:
let a={name:"測試"};
function test(obj){
obj.name="qiang";
}
test(a);
console.log(a);//{name: "qiang"}
說明:該案例對比案例一,該案例,obj持有的也是a同樣的
地址索引,所以,改變obj的name就是修改a的name屬性。
案例:原型
function F(){
}
Object.prototype.a=function(){
console.log("a()")
}
Function.prototype.b=function(){
console.log("b()")
}
var f=new F()
f.a() //a()
f.b() //報錯
F.a() //a()
F.b() //b()
說明:Function也是一個對象,派生于Object對象。
f是對象,不屬于Function類型,所以f.b()在F中找沒有,然后去Object中找也沒有。
F.a()執(zhí)行:F本身沒有a方法,去它的原型找,原型是Function(也是一個對象,通過其__proto__可知最后到了Object上面),原型上還沒有a方法只有b方法,繼續(xù)去Function的原型上找,所以找到了Object上面。
作用域和作用域鏈
全局作用域也就是window和函數(shù)作用域,不考慮es6的塊級作用域前提下,作用域個數(shù)=函數(shù)定義個數(shù)+1.
作用域鏈就是內(nèi)部作用域找不到去外級作用域找,最后到全局作用域。
案例一:
var a=100;
function test(){
var a=200;
function test1(){
console.log(a)//200
}
test1();
}
test()
案例二:
var fn=function(){
console.log(fn)
}
fn()//? (){console.log(fn)}
var obj={
fn2:function(){
console.log(this.fn2)//? (){console.log(fn)}
console.log(fn2)// fn2 is not defined
}
}
obj.fn2()
說明:對比案例一發(fā)現(xiàn),理論上console.log(fn2)應(yīng)該是可以找到的,但是實(shí)際上,obj內(nèi)部不是作用域,所以fn2
函數(shù)內(nèi)部找不到fn2之后,直接去上一級作用域也就是此時的window找也沒有。而案例一test是函數(shù),其也是作用域
內(nèi)存溢出和泄露
- 內(nèi)存溢出:程序運(yùn)行需要的內(nèi)存超過了剩下的內(nèi)存時,就會拋出內(nèi)存溢出的錯誤
- 內(nèi)存泄露:占用內(nèi)存沒有用但是又沒有即時釋放
例如:意外的全局變量,沒有即時清理的定時器或回調(diào)函數(shù),閉包
閉包
內(nèi)部函數(shù)持有外部函數(shù)的引用
function test(){
let a=10;
function test1(){
console.log(a);
}
let t1=function(){
console.log(a);
}////////////
return test1;
}
let t=test();
t();
t=null;
說明:兩個內(nèi)部函數(shù)的定義是有區(qū)別的,因為函數(shù)聲明提示,test1在test函數(shù)內(nèi)部第一行就聲明和定義一起完成,
t1聲明是在第一行但是定義是在其執(zhí)行完畢才定義(也就“/////”所在的一行).
注意因為t的引用問題,所以test函數(shù)執(zhí)行完畢也不會釋放
test函數(shù)本身和閉包的相關(guān)引用,所以可能造成內(nèi)存泄漏,
所以如果確定閉包不使用了,可以把對象值設(shè)置為null。
閉包案例一:
var name="The WIndow";
var obj={
name:"obj",
getName:function(){
return function(){
return this.name;
}
}
}
console.log(obj.getName()())//The WIndow
說明:因為obj調(diào)用所以getName內(nèi)部的this就是obj,但是
執(zhí)行完畢返回的是一個函數(shù)然后在執(zhí)行,相當(dāng)于就是在window直接執(zhí)行了該方法,所以內(nèi)部匿名函數(shù)的this就是window。
閉包案例二:
var name="The WIndow";
var obj={
name:"obj",
getName:function(){
return ()=>{
return this.name;
}
}
}
console.log(obj.getName()())//obj
說明:obj的getName函數(shù)沒有用箭頭函數(shù)。其內(nèi)部this還是obj,但是內(nèi)部匿名函數(shù)是箭頭函數(shù),指向調(diào)用者,此時指向obj所以輸出obj。
閉包案例三:
var name="The WIndow";
var obj={
name:"obj",
getName:()=>{
return ()=>{
return this.name;
}
}
}
console.log(obj.getName()())//The WIndow
說明:都是用箭頭函數(shù),則指向相當(dāng)于為最終調(diào)用者,即window
閉包案例四:
var name="The WIndow";
var obj={
name:"obj",
getName:function(){
var that=this;
return function(){
return that.name;
}
}
}
console.log(obj.getName()())//obj
說明:that已經(jīng)指向getName函數(shù)作用域,也就是obj
瀏覽器內(nèi)部流程
瀏覽器一般都是多線程的,但是js是單線程執(zhí)行的,下圖是說明

QQ截圖20180608152428.png
初始化代碼都是在主線程執(zhí)行,且必須初始化代碼執(zhí)行完畢才能執(zhí)行其他代碼
setTimeout(()=>{
console.log("執(zhí)行")
},0);
for(var a=0;a<10000000000;a++){}
說明:setTimeut是初始化代碼,但是內(nèi)部的回調(diào)函數(shù)不是初始化代碼,所以即使時間是0也會在初始化代碼執(zhí)行完畢之后才執(zhí)行
事件循環(huán)模型
eventloop會每次從callback隊列中每次取出一個執(zhí)行,但是是分線程,所以主線程如果耗時,則回調(diào)隊列
內(nèi)部也不會順利的立刻執(zhí)行。

事件循環(huán)模型.png