JavaScript散乱(四、细节)

js高级

案例问题

案例一:
let a={name:"测试"};
function test(obj){
    obj={name:"hehe"};
}
test(a);
console.log(a);//{name: "测试"}
说明:a传入test中,obj重新赋值,obj的的形参的指向就变化了,此时其实obj={name:"hehe"};在函数执行完毕就会被当作垃圾回收,而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()执行:F本身没有a方法,去它的原型找,原型是Function(也是一个对象,通过其__proto__可知最后到了Object上面),原型上还没有a方法只有b方法,继续去Function的原型上找,所以找到了Object上面。

作用域和作用域链

全局作用域也就是window和函数作用域,不考虑es6的块级作用域前提下,作用域个数=函数定义个数+1.
作用域链就是内部作用域找不到去外级作用域找,最后到全局作用域。

案例一:
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()
说明:对比案例一发现,理论上console.log(fn2)应该是可以找到的,但是实际上,obj内部不是作用域,所以fn2
函数内部找不到fn2之后,直接去上一级作用域也就是此时的window找也没有。而案例一test是函数,其也是作用域

内存溢出和泄露

  • 内存溢出:程序运行需要的内存超过了剩下的内存时,就会抛出内存溢出的错误
  • 内存泄露:占用内存没有用但是又没有即时释放

例如:意外的全局变量,没有即时清理的定时器或回调函数,闭包

闭包

内部函数持有外部函数的引用

function test(){
    let a=10;
    function test1(){
        console.log(a);
    }
    let t1=function(){
        console.log(a);
    }////////////
    return test1;
}
let t=test();
t();
t=null;
说明:两个内部函数的定义是有区别的,因为函数声明提示,test1在test函数内部第一行就声明和定义一起完成,
t1声明是在第一行但是定义是在其执行完毕才定义(也就“/////”所在的一行).
注意因为t的引用问题,所以test函数执行完毕也不会释放
test函数本身和闭包的相关引用,所以可能造成内存泄漏,
所以如果确定闭包不使用了,可以把对象值设置为null。
闭包案例一:
var name="The WIndow";
var obj={
    name:"obj",
    getName:function(){
        return function(){
            return this.name;
        }
    }
}
console.log(obj.getName()())//The WIndow
说明:因为obj调用所以getName内部的this就是obj,但是
执行完毕返回的是一个函数然后在执行,相当于就是在window直接执行了该方法,所以内部匿名函数的this就是window。
闭包案例二:
var name="The WIndow";
var obj={
    name:"obj",
    getName:function(){
        return ()=>{
            return this.name;
        }
    }
}
console.log(obj.getName()())//obj
说明:obj的getName函数没有用箭头函数。其内部this还是obj,但是内部匿名函数是箭头函数,指向调用者,此时指向obj所以输出obj。
闭包案例三:
var name="The WIndow";
var obj={
    name:"obj",
    getName:()=>{
        return ()=>{
            return this.name;
        }
    }
}
console.log(obj.getName()())//The WIndow
说明:都是用箭头函数,则指向相当于为最终调用者,即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已经指向getName函数作用域,也就是obj

浏览器内部流程

浏览器一般都是多线程的,但是js是单线程执行的,下图是说明

QQ截图20180608152428.png

初始化代码都是在主线程执行,且必须初始化代码执行完毕才能执行其他代码

setTimeout(()=>{
    console.log("执行")
},0);
for(var a=0;a<10000000000;a++){}
说明:setTimeut是初始化代码,但是内部的回调函数不是初始化代码,所以即使时间是0也会在初始化代码执行完毕之后才执行

事件循环模型

eventloop会每次从callback队列中每次取出一个执行,但是是分线程,所以主线程如果耗时,则回调队列
内部也不会顺利的立刻执行。

事件循环模型.png

你可能感兴趣的:(JavaScript散乱(四、细节))