javascript数据类型分为基本数据类型,引用数据类型和Symbol。javascript
基本数据类型
String,Number,Boolean,undefined,nulljava
引用数据类型
Object
这里的Object包括带编号的有序集合Array,包含key/value的无序集合和另外一种特殊对象Function。算法
要点:基本数据类型的值是不可变的,引用数据类型值是可变的
基本数据类型的复制
基本数据类型变量复制是分配新的地址,新值是被复制变量的一个副本,变量之间是独立的 ,互不影响。数组
var num1 = 5; var num2 = num1;
引用数据类型的复制
引用数据类型复制时候也会分配新的地址,不一样的是新地址中存储的是引用数据在堆内存中的指针。复制操做结束后,二者都指向同一个引用地址。所以,修改其中一个会影响另一个。浏览器
var obj1 = new Object(); var obj2 = obj1; obj1.name = "Nicholas"; alert(obj2.name); //"Nicholas"
类型检测
一、typeof闭包
typeof是操做符不是函数。使用typeof会返回以下值:ide
注意:在使用typeof检测数组和null都会返回object,所以typeof并不能精确判断某一数据类型。
二、instanceof
若是某个变量是指定引用类型的实例,则instanceof会返回true。固然数组的判断可使用Array.isArray()。函数
注意:instanceof也并不是彻底可靠,1.因为变量的原型并非一层不变的,一旦原型被修改,就可能返回false。2.没法判断多全局对象,好比在多窗口之间进行原型判断,多窗口意味着多全局对象,拥有不一样的内置构造函数,好比[] instanceof window.frames[0].Array会返回false
三、constructor
“javascript中一切都是对象”,全部对象都会在其原型上继承一个constructor属性指向其构造函数。spa
注意:null和undefined没有constructor。一样,对象的constructor也是能够改变的,好比:
var a=[]; a.constructor=new Number(); console.log(a.constructor);//Number
四、Object.prototype.toString操作系统
Object.prototype.toString会返回一个表示该对象的字符串
Object.prototype.toString.call()
JavaScript执行环境决定了变量或函数是否有权访问,每一个执行环境都有一个变量对象(variable object),执行环境中全部变量和函数都保存在该对象中,虽然咱们没法直接访问该对象,JavaScript解析器执行时会使用到它。某个执行环境全部代码执行完毕后随之销毁。
全局执行环境
全局执行环境是最外围的一个执行环境,在浏览器中,全局执行环境被认为是window对象,全部变量和函数都是window下的属性或者方法。
局部执行环境
每一个函数都有本身的局部执行环境,当执行流进入函数时候,函数的执行环境就会被推入到执行栈中,函数执行完毕后就会被弹出执行栈。函数的参数也是局部变量,其优先级高于外部变量。
函数做用域和变量声明提高
每一个函数内部变量只能在函数体内访问,函数外是无权访问的,包括函数的参数。
函数内定义的变量在整个函数体内均可访问,即便变量声明在变量使用以后,这就是变量声明提高。最典型的例子以下:
var a=1; function test(){ console.log(a);//1 a=2; console.log(a);//2 } test();
换一下
var a=1; function test(){ console.log(a);//undefined var a=2; console.log(a);//2 } test();
for(var i=0;i<10;i++){ console.log(i);//输出1~9 } console.log(i);//输出10
ES6以前JavaScript没有块级做用域
理解这句话能够看以下例子:
function test(o){ if(typeof o==='object'){ var i="test"; for(var k=0;k<10;k++){ console.log(k); } console.log(k) } console.log(i); } test();//undefined test({});//输出0~9,10,test
可见,即便加了if条件判断或者循环,变量i和k均可以在大括号代码块外访问。
做用域链
每段JavaScript代码或者函数都有一个与之关联的做用域链(scope chain),这个做用域链是一个对象列表或者链表,这组对象定义了这段代码“做用域”中的变量。
当JavaScript须要查找变量a的时候,它会从链中的第一个对象开始查找,若是这个对象包含变量a属性,则会直接使用该对象中的a属性,若是不存在,则继续向上查找第二个对象,若是第二个对象也没有,则继续查找下一个,以此类推。若是整个做用域链上都没有a属性,则会抛出异常。
注意:在JavaScript顶层代码中,做用域链由一个全局对象组成,在函数体内,做用域链有2个,一个是定义函数参数和函数局部变量的对象,一个是全局对象,若是函数内找不到某个变量,会继续在全局对象中查找。
声明变量就得在内存中给它分配存储地址,基本数据类型存储在栈中,引用数据类型存储在堆中。
内存的生命周期
内存的分配
let myNumber = 23
JavaScript在执行上面代码时候流程以下:
基本数据类型的复制是分配新的地址,是被复制对象的副本,所以会互不干扰;
引用数据类型的复制是堆地址指针的复制,复制后它们指向同一地址,所以修改其中一个会影响另一个。
内存的使用
在JavaScript中使用分配的内存意味着在其中读写,这能够经过读取或写入变量或对象属性的值,或者将参数传递给函数来实现。
内存释放
这里最困难的地方是肯定什么时候再也不须要分配的内存,它一般要求开发人员肯定程序中哪些地方再也不须要内存的并释放它。
垃圾收集
引用计数
这是最简单的垃圾收集器算法。若是没有引用指向这个对象的时候,这个对象就被认为是“能够做为垃圾收集”。
var o = { a: { b:2 } }; // 两个对象被建立,一个做为另外一个的属性被引用,另外一个被分配给变量o // 很显然,没有一个能够被垃圾收集 var o2 = o; // o2变量是第二个对“这个对象”的引用 o = 1; // 如今,“这个对象”的原始引用o被o2替换了 var oa = o2.a; // 引用“这个对象”的a属性 // 如今,“这个对象”有两个引用了,一个是o2,一个是oa o2 = "yo"; // 最初的对象如今已是零引用了 // 他能够被垃圾回收了 // 然而它的属性a的对象还在被oa引用,因此还不能回收 oa = null; // a属性的那个对象如今也是零引用了 // 它能够被垃圾回收了
循环引用的问题
当遇到循环的时候就会有一个限制。在下面的实例之中,建立两个对象,而且互相引用,所以就会产生一个循环。当函数调用结束以后它们会走出做用域以外,所以它们就没什么用而且能够被释放。可是,基于引用计数的算法认为这两个对象都会被至少引用一次,因此它俩都不会被垃圾收集器收集。
function f(){ var o = {}; var o2 = {}; o.a = o2; // o 引用 o2 o2.a = o; // o2 引用 o return "azerty"; } f();
标记清除
JavaScript 中最经常使用的垃圾收集方式是标记清除(mark-and-sweep)。当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,由于只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。这个算法由如下步骤组成:
四种常见的JavaScript泄露
1. 全局变量
一个未声明变量的引用会在全局对象内部产生一个新的变量。在浏览器的状况,这个全局变量就会是window。
function foo(arg) { bar = "some text"; } 等同于: function foo(arg) { window.bar = "some text"; }
2.被遗忘的计时器和回调
setInterval 在 JavaScript 中是常常被使用的。大多数提供观察者和其余模式的回调函数库都会在调用本身的实例变得没法访问以后对其任何引用也设置为不可访问。 可是在setInterval的状况下,这样的代码很常见
var serverData = loadData(); setInterval(function() { var renderer = document.getElementById('renderer'); if(renderer) { renderer.innerHTML = JSON.stringify(serverData); } }, 5000); //每5000ms执行一次
renderer所表明的对象在将来可能被移除,让部分interval 处理器中代码变得再也不被须要。然而,这个处理器不可以被收集由于interval依然活跃的(这个interval须要被中止从而表面这种状况)。若是这个interval处理器不可以被收集,那么它的依赖也不可以被收集。这意味这存储大量数据的severData也不可以被收集。
3. 闭包
闭包的特性是内部函数可以访问外部函数的做用域。
var sayName = function(){ var name = 'jozo'; return function(){ alert(name); } }; var say = sayName(); say();
sayName返回了一个匿名函数,该函数又引用了sayName的局部变量name,sayName 调用后变量name应该被回收,可是因为say继续引用,致使没法回收。
小结: 一、JavaScript基本数据类型:string,number,boolean,null,undefined,引用类型,包括Object,Array,function,ES6新增的symbol。 二、判断数据类型的方法有typeof,instanceof,constructor,Object.prototype.toString。 三、JavaScript分为全局做用域和局部做用域,做用域链向上层层查找。 四、基本数据类型占据固定大小空间,所以存储在栈内存中,引用类型占据空间不肯定,存储在堆内存中。复制基本数据类型会分配新地址,新旧互不影响,引用类型复制是复制指针,新旧变量会互相影响。 五、JavaScript垃圾回收方法包括引用计数和标记清除。 六、JavaScript常见的内存泄漏包括全局变量,循环引用,计时器,闭包。