这些天看了JavaScript的一些基础,想把所学习的都总结一下,毕竟了解是一回事,表达清楚又是另一回事,所以以后会坚持把所学习的知识做一个总结,理清思路,训练表达能力,每天进步一点点(#^.^#)。
什么是作用域?
作用域就是代码运行时,各个变量、函数及对象的可访问性,换句话说,就是作用域决定了你代码中变量和其他资源在各个区域的可见性。
什么是执行环境?
执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境的关联了一个变量对象,这个变量对象包含了这个环境中所有的变量和函数。
我们可以理解成作为一个可执行的程序,把它抽象的理解成一个任务,当函数被调用时就立刻创建执行环境,然后被压入环境栈中执行。
什么是作用域链?
当代码在一个环境中执行时,就会创建变量对象的一个作用域链,这个作用域链保证了执行环境对有权访问的变量和函数的有序访问。
作用域链是向上访问的,它的最前端是当前执行环境的变量对象,接着向上访问,是其包含环境(外部环境)的变量对象,以此访问下去,最后是全局环境的变量对象。
实际上,作用域链本质上是一个指向变量对象的指针列表,它只引用而不包含实际变量对象。
在我的理解中,闭包的本质,就是将函数内部和函数外部连接起来的一座桥梁。
简单点来说,就是内部函数能够访问外部函数的变量,即使外部函数已经执行完毕。
因为JavaScript中只存在全局作用域和局部作用域,没有块级作用域(当然,在ES6中 let,const支持块级作用域了),那么在某些情况下,我们需要在外部获取局部变量怎么办呢,这就涉及到闭包的用处了:利用闭包可以在函数外部读取函数内部的变量,并且可以让这些变量一直保存在内存中
请看下面的例子:
function f1(){
//f1将f2作为它的内部函数,并将其返回
var n = 1;
sum = function(){n+=1}
function f2(){
console.log(n);
}
return f2;
}
//调用f1时返回函数f2,此时函数f1已经执行完毕
//但此时函数f1的执行环境并不会被回收,因为在函数f2中还保持对外部函数f1中的变量n的引用
var result = f1();
result();//输出1
//变量n依然存在内存中
sum();
result(); //输出2
在其他编程语言中,可以通过protect,public,private来设置类中变量和方法的可见性,那么在JavaScript中要怎么实现公有作用域和私有作用域呢?我们可以使用闭包来实现这一特性:利用闭包实现公有作用域和私有作用域
在下面的例子中,用一个对象中的公有作用域和私有作用域来划分函数,将公有函数return,私有函数并没有暴露出去,但是可以公有函数可以访问私有函数:
var Module = (function() {
//私有函数
function privateMethod() {
console.log(1);
}
return {
//公有函数
publicMethod: function() {
console.log(2);
privateMethod();
}
};
})();
由于闭包能让函数中的变量一直保存在内存中,会造成内存消耗,滥用闭包会造成网页的性能问题,可能会导致内存泄漏。
在理解原型链之前,我们首先需要了解的是:每个函数都是一个对象,而对象又是由函数创建的
function f() { };
//函数也是一个对象,下面会打印true
console.log(f instanceof Object);
//对象是由函数创建的,下面会打印function
console.log(typeof (Object));
其次是原型和隐式原型
原型:每一个函数都有一个prototype属性,这个属性的属性值是一个对象,这个对象默认有一个constructor属性,其属性值又指向这个函数本身。
隐式原型:每个对象都有一个隐藏的属性__proto__,这个属性引用了创建这个对象的函数的prototype
function f(){}
var f1 = new f();
console.log(f1.__proto__ === f.prototype);//true
然后需要提到的是,所有的对象都是由Object()函数创建的,所有的函数都是由Function()函数创建的,Function()函数由自身创建。
接下来上图:
垃圾回收机制原理:找出那些不再继续使用的变量,然后释放它们占用的内存,垃圾收集器会按照固定的时间间隔,周期性的执行这一操作。
垃圾回收的算法
内存泄漏:不再用到的内存,没有及时释放
解除引用:将不再使用的变量设置为null
我们应该及时解除对不再使用的变量(全局变量、全局变量属性已经循环引用变量)的引用
常见的导致内存泄漏的情况:
function foo(arg) {
bar = "some text";//相当于window.bar
}
解决方式:可以在js文件开头使用 'use strict' 严格模式,在严格模式下可以防止意外的全局变量
function assignHandler(){
var element=document.getElementById("someElement");
element.onclick=function(){
return false;
}
}
在上面的代码中,从表面上看,没有任何循环引用,但是存在闭包,内部函数有权访问外部函数的变量,DOM元素的属性onclick指向内部函数的引用,内部函数又与element之间存在隐藏的关联(作用域链),形成了一个循环引用。
解决方式:手动设置DOM变量为null
function assignHandler(){
var element=document.getElementById("someElement");
element.onclick=function(){
return false;
}
element.onclick = null;
}