数据类型
1、undefined 与null的区别?
undefined代表定义未赋值。
null定义并赋值了,只是值为null。
2、什么时候给变量赋值为null呢?
初始赋值,表明将要赋值为对象。
结束前,让对象成为垃圾对象(被垃圾回收器回收)。
3、严格区别变互类型与数据类型?
数据的类型:基本类型、对象类型。
变量的类型(变量内存值的类型):
基本类型:保存就是基本类型的数据。
引用类型:保存的是地址值。
数据、变量、内存
1、什么是数据?
存储在内存中代表特定信息的,本质上是0101…
数据的特点:可传递、可运算
2、什么是内存?
内存条通电后产生的可存储数据的空间(临时的)
内存产生和死亡:内存条(电路板)>通电> 产生内存空间==> 存储数据==> 处理数据==> 断点==> 内存空间和数据都消失
一块小内存的2个数据:内部存储的数据、地址值(并非对象内存有地址值,基本变量也有只是没用)
内存分类:
栈:全局变量、局部变量
堆:对象
3、什么是变量?
可变化的量,由变量名和变量值组成。
每个变量都对应一块小内存,变量名用来查找对应的内存,变量值就是内存中保存的数据。
4、内存、数据、变量三者之间的关系?
内存是用来存储数据的空间、变量是内存的标识
5、关于赋值和内存问题
问题:var a = xxx,a内存中到底保存的是什么?
xxx基本数据时,保存的就是这个数据。
xxx是对象时,保存的是对象的地址值。
xxx是一个变量时,保存的xxx的内存内容(可能是基本数据,也可能是地址值)。
引用变量赋值问题:
1、n个变量指向同一个对象时,通过一个变量修改对象内部数据,其它所有变量看到的是修改后的数据。
2、2个引用变量指向同一个对象时,让其中一个引用变量指向另一个对象,另一引用变量依然指向前一个对象。
6、关于数据传递问题
问题:在js调用函数时传递变量参数时,是值传递还是引用传递?
理解1:都是值(变量的内容,基本/地址值)传递。
理解2:可能是值传递,也可能是引用传递(当传递的是地址值时)。
7、内存管理
问题:JS引擎如何管理内存?
1、内存生命周期
分配小内存空间,得到他的使用权。
存储数据,可以反复进行操作。
释放小内存空间。
2、释放内存
局部变量:函数执行完自动释放(自动释放是瞬间)
对象:成为垃圾对象==> 垃圾回收器回收(对象释放内存是个过程)
对象
1、什么是对象?
多个对象的封装体、用来保存多个对象的容器、一个对象代表现实中的一个事物。
2、为什么要用对象?
统一管理多个数据
3、对象的组成
属性:属性名(字符串)和属性值(任意)组成。
方法:一种特别的属性(属性值是函数)。
函数
如何调用函数?
1、test():直接调用
2、obj.test():通过对象调用
3、new test(): new调用
4、test.call/apply(obj):临时让test成为obj的方法进行调用
var obj = {}
function test () {
this.xxx = "wlh";
}
// obj.test() 不能直接调,obj没有test方法
test.call(obj); //obj.test() //可以让一个函数成为指定任意对象的方法进行调用
console.log(obj.xxx); //wlh
匿名函数自调用
(IIFE:Immediately-Invoked Function Expression)
作用:隐藏实现、不会污染外部(全局)命名空间。
(function () {
var a = 3
console.log(a + 3)
})()
var a = 4
console.log(a)
;(function () { //匿名函数自调用
var a = 1
function test () {
console.log(++a)
}
window.$ = function () { //向外暴露一个全局函数
return {
test: test
}
}
})()
$().test() //1.$是一个函数 2.$执行后返回的是一个对象
this的不同的情况:
1、以函数的形式调用时,this永远都是window。
2、以方法的形式调用时,this就是调用方法的对象。
3、以构造函数的形式调用时,this就是新创建的对象。
4、使用call和apply调用时,this就是指定的那个对象。
函数高级
原型与原型链
原型prototype
1、函数的prototype属性
每个函数对象
都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象)。
原型对象中有一个属性constructor,它指向函数对象。
该属性在函数被创建的时候(定义函数的时候)添加到函数
2、给原型对象添加属性(一般都是方法)
作用:函数的所有实例对象自动拥有原型中的属性(方法)
function Fun () {
}
console.log(Fun.prototype);
console.log(Fun.prototype.constructor === Fun) //true
// 给原型对象添加属性(方法)
Fun.prototype.test = function () {
console.log("test()")
}
var fun = new Fun();
// 实例可以访问
fun.test(); //test()
显式原型与隐式原型
1、每个函数function都有一个prototype,即显式原型(属性)
function Fn () { // 内部语句:this.prototype = {}
}
console.log(Fn.prototype) // {constructor: ƒ}
2、每个实例对象都有一个__proto__
,可称为隐式原型(属性)
var fn = new Fn(); // 内部语句:this.__proto__ = Fn.prototype
console.log(fn.__proto__) // {constructor: ƒ}
3、实例对象的隐式原型的值为其对应构造函数的显式原型的值
console.log(Fn.prototype === fn.__proto__); // true
4、内存结构(图)
https://www.processon.com/view/link/61c173325653bb2eb0245140
5.总结:
函数的prototype属性:在定义函数时自动添加的,默认值是一个空object对象。
对象的__proto__
属性:创建对象时自动添加的,默认值为构造函数的prototype属性值。
程序员能直接操作显式原型,但不能直接操作隐式原型(ES6之前)。
原型链
1、原型链(图解:https://www.processon.com/view/link/61c18522f346fb21618ce9d4)
访问一个对象的属性时,
现在自身属性中查找,找到返回。
如果没有,再沿着__proto__
这条链向上寻找,找到返回。
如果最终没找到,则返回undefined。
别名:隐式原型链。
作用:查找对象的属性(方法)。
方法一般定义在原型中,属性一般通过构造函数定义在对象本身身上。
console.log(Function.prototype === Function.__proto__) //true
console.log(Function.__proto__.__proto__===Function.prototype.__proto__)// true
所有函数都是Object的实例,Object除外,Object也是函数(Object是Function实例)
//面试题1 var A = function () {} A.prototype.n = 1 var b = new A(); A.prototype = { n: 2, m: 3 } var c = new A() console.log(b.n,b.m, c.n, c.m) //1 undefined 2 3 //面试题2 var F = function () {} Object.prototype.a = function() { console.log("a()"); } Function.prototype.b = function() { console.log("b()"); } var f = new F(); f.a() //a() f.b() //f.b is not a function F.a() //a() F.b() //b()
执行上下文与执行上下文栈
1、变量提升与函数提升
变量提升:在变量定义语句之前就能访问到这个变量,值为undefined。
函数提升:在函数定义语句之前就可以调用执行这个函数。
变量先提升
2、执行上下文
上下文产生的个数:n+1(n:函数调用次数、1:全局执行上下文)
(1)代码分类(位置)
全局代码。
函数(局部)代码。
(2)全局执行上下文
在执行全局代码前将window确定为全局执行上下文。
对全局数据进行预处理:
var定义的全局变量==> undefined,添加为window的属性。
function声明的全局函数==> 赋值(fun),添加为window的方法。
this==> 赋值(window)。
开始执行全局代码
(3)函数执行上下文
在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象。
对局部数据进行预处理:
形参变量==>赋值(实参)==> 添加为执行上下文的属性。
arguments==> 赋值(实参列表),添加为执行上下文的属性。
var定义的局部变量==>undefined,添加为执行上下文的属性。
function声明的函数==> 赋值(fun),添加为执行上下文的方法。
this==>赋值(调用该函数的对象)。
(4)开始执行函数体代码
3、执行上下文栈
在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象。
在全局执行上下文(window)确定后,将其添加到栈中(压栈)。
在函数执行上下文创建后,将其添加到栈中(压栈)。
在当前函数执行完后,将栈顶的对象移除(出栈)。
当所有的代码执行完后,栈中只剩下window。
栈的结构特点:后进先出
console.log("global begin: "+ i);
var i = 1;
foo(1);
function foo(i) {
if (i == 4) {
return;
}
console.log("foo() begin: " + i)
foo(i+1); //递归调用:在函数内部调用自己
console.log("foo() begin: " + i)
}
console.log("global end: "+ i)
/*
依次输出:
global begin: undefined
foo() begin: 1
foo() begin: 2
foo() begin: 3
foo() begin: 3
foo() begin: 2
foo() begin: 1
global end: 1
整个过程产生了几个执行上下文? 5个( window、foo(1)、foo(2)、foo(3)、foo(4) )
*/
//面试题: function a() {} var a = 2 console.log(typeof a) //"function" if (!(b in window)) { var b = 1 //全局作用域,变量提升 } console.log(b) //undefined var c = 1 function c() { console.log(c) var c = 3 } c(2); //c is not a function
作用域与作用域链
作用域
作用域产生的个数:n+1(n:定义的函数个数、1:全局执行上下文)
理解:
作用域就是一块"地盘",一个代码段所在的区域。
它是静态的(相对于上下文对象),在编写代码时就产生了。
分类:
全局作用域。
函数作用。
块作用域(ES6)。
作用:
隔离变量,不同作用域下同名变量不会有冲突。
作用域与执行上下文
区别1:
全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时。
全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建。
函数执行上下文是在调用函数时,函数体代码执行之前创建。
区别2:
作用域是静态的,只要函数定义好了就一直存在,且不会再变化。
执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放
。
联系:
上下文环境(环境)是从属于所在作用域
全局上下文环境==> 全局作用域
函数上下文环境==> 对的应函数作用域
作用域链
什么叫作用域链:嵌套的作用域产生的由内向外的一个(由下向上)过程。
var a = 1;
function fn1 () { //产生fn1作用域
var b = 2;
function fn2 () { //产生fn2作用域
var c = 3;
console.log(c); //当前作用域找,有c输出3
console.log(b); //先在当前作用域找没有,向上个作用域找,输出2
console.log(a); //同理,输出1
console.log(d); //从下往上一直找到全局作用域都没有,报错:d is not defined
}
fn2(); //此处产生fn1函数执行上下文
}
fn1(); //此处产生fn1函数执行上下文
多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外),查找变量时就是沿着作用域链来查找的。
查找一个变量的查找规则:
在当前作用域下的执行上下文中查找对应的属性,如果有就直接返回,否则进入2。
在上一级作用域的执行上下文中查找对应的属性,如果有就直接返回,否则进入3。
再次执行2的相同操作,直到全局作用域,如果还找不到就抛出找不到的异常。
//面试题1 var x = 10 function fn () { console.log(x); } function show (f) { var x = 20; f(); //10 } show(fn); //面试题2 var fn = function () { console.log(fn); } fn(); //function () {console.log(fn);} var obj = { fn2:functon () { //console.log(fn2); //先找当前函数作用域没有,再找全局作用域也没有,报错:fn2 is not define console.log(this.fn2); //先找当前函数作用域没有,再找obj作用域有,输出 fn2... } } obj.fn2();
闭包
理解闭包
1、如何产生闭包?
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。(调用几次外部函数就产生几个闭包)
2、闭包到底是什么?
使用chrome调试查看
理解一:闭包是嵌套的内部函数(绝大部分人)。
理解二:包含被引用变量(函数)的对象(极少数人)。
注意:闭包存在于嵌套的内部函数中(闭包也是个对象)。
3、产生闭包的条件?
函数嵌套。
内部函数引用了外部函数的数据(变量/函数)。
function fn1() {
var a = 2;
function fn2() {
a++;
console.log(a);
}
return fn2;
}
var f = fn(); //闭包存在因为外部有变量指向它,所以执行完后不会释放
f(); //3
f(); //4
//通过闭包在外部通过fn2间接操作fn1里的a,且只能能对fn1作用域里的a进行+1操作
闭包的作用
1、使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)。
2、让函数外部可以操作(读写)到函数内部的数据(变量/函数)。
问题:
1、函数执行完后,函数内部声明的局部变量是否还存在?
一般是不存在,存在于闭包中的变量才可能存在。
2、在函数外部能直接访问函数内部的局部变量吗?
不能,但我们可以通过闭包让外部操作它。
闭包的生命周期
产生:在嵌套内部函数定义执行完时就产生了(不是在调用)。
(定义执行完只是创建了函数对象,函数体并没有执行。)
死亡:在嵌套的内部函数成为垃圾对象时。
function fn1() {
//此时闭包就已经产生了(函数提升,内部函数对象已经创建了)
var a = 2;
function fn2() {
a++;
console.log(a);
}
return fn2;
}
var f = fn();
f(); //3
f(); //4
f = null; //闭包死亡(包含闭包的函数对象成为垃圾对象)
闭包的应用_自定义js模块
具有特定功能的js文件。
将所有的数据和功能都封装在一个函数内部(私有的)。
只向外暴露一个包含n个方法的对象或函数。
模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能。
写法一:
js文件:
function myModule() {
//私有数据
var msg = "My module";
//操作数据的函数
function doSomething() {
console.log("doSomething()" + msg.toUpperCase());
}
function doOtherthing() {
console.log("doOtherthing()" + msg.toLowerCase());
}
//向外暴露对象(给外部使用的方法)
return {
doSomething: doSomething,
doOtherthing: doOtherthing
}
}
html文件:
<script>
var module = myModule();
module.doSomething(); //doSomething()MY MODULE
module.doOtherthing(); //doSomething()my module
</script>
写法二:
js文件:
(function (window) {
//私有数据
var msg = "My module";
//操作数据的函数
function doSomething() {
console.log("doSomething()" + msg.toUpperCase())
}
function doOtherthing() {
console.log("doOtherthing()" + msg.toLowerCase())
}
window.myModule2 = {
doSomething: doSomething,
doOtherthing: doOtherthing
}
})(window); //传window防止代码压缩,可不传
html文件:
<script>
myModule2.doSomething(); //doSomething()MY MODULE
myModule2.doOtherthing(); //doSomething()my module
</script>
闭包的缺点及解决
缺点:
函数执行完后,函数的局部变量没有释放,占用内存时间会变长。
容易造成内存泄露。
解决:
能不用闭包就不用。
及时释放。(如下)
function fn1() {
var arr = new Array[100000];
function fn2() {
console.log(arr.length);
}
return fn2;
}
var f = fn1()
f = null //让内部函数成为垃圾对象--> s回收闭包
面试题1:
var name = "The Window";
var object = {
name: "My Object";
getNameFunc: function() {
return function() {
return this.name;
};
}
};
alert(object.getNameFunc()()); //The Window
var name2 = "The Window";
var object2 = {
name2: "My Object",
getNameFunc: function() {
var that = this;
//此时这个this是调用getNameFunc的对象object2,将它保存到that
return function() {
return that.name; //引用了that产生了闭包
};
}
};
alert(object2.getNameFunc()()); //My Object
面试题2:
function fun(n,o) {
console.log(o);
return {
fun: function(m) {
return fun(m,n);
}
};
}
var a = fun(0); //undefined
a.fun(1); //0
//产生闭包,闭包里n=0,但因为没有东西接收它所以执行完闭包会消失
a.fun(2); //0
a.fun(3); //0
var b = fun(0).fun(1).fun(2).fun(3);
//undefined 0 1 2
var c = fun(0).fun(1); //undefined 0
//此时闭包里n为1
c.fun(2); //1
c.fun(3); //1
面向对象高级
对象创建模式
object构造函数模式
var p = new Object();
适用场景:起始时不确定对象内部数据。
问题:语句太多。
对象字面量
var p = {};
适用场景:起始时对象内部数据是确定的。
问题:如果创建多个对象有重复代码。
工厂模式
**套路:**通过工厂函数动态创建对象并返回
function createPerson(name,age) { //返回一个对象的函数==> 工厂函数
var obj = {
name: name;
age: age;
setName: function(name) {
this.name = name;
};
}
return obj;
}
var p1 = createPerson("tom", 12);
var p2 = createPerson("bob", 13);
适用场景:需要多个对象。
问题:对象没有具体类型,都是Object类型。
自定义构造函数模式
**套路:**自定义构造函数,通过new创建对象。
//定义类型
function Person(name,age) {
this.name = name;
this.age = age;
this.setName = function(name) {
this.name = name;
};
}
var p1 = new Person("tom", 12);
p1.setName("jack");
适用场景:需要创建多个类型确定的对象。
问题:每个对象都有相同的数据,浪费内存。
构造函数+原型的组合模式
**套路:**自定义构造函数,属性在函数中初始化,方法添加到原型上。
//定义类型
function Person(name,age) { //在构造函数中只初始化
this.name = name;
this.age = age;
}
Person.prototype.setName = function(name) {
this.name = name;
}
var p1 = new Person("tom", 12);
p1.setName("jack");
适用场景:需要创建多个类型确定的对象。
继承模式
原型链继承
套路:
1、定义父类型构造函数。
2、给父类型的原型添加方法。
3、定义子类型的构造函数。
4、创建父类型的对象赋值给子类型的原型。
5、将子类型原型的构造属性设置为子类型。
6、给子类型原型添加方法。
7、创建子类型的对象:可以调用父类型的方法。
关键: 子类型的原型为父类型的一个实例对象。
//父类型
function Supper() {
this.supProp = "Supper property"
}
Supper.prototype.showSupperProp = function() {
console.log(this.supProp);
}
//子类型
function Sub() {
this.subProp = "Sub property";
}
Sub.prototype = new Supper(); //子类型的原型为父类型的一个实例对象
//必须在给原型添加方法之前,让子类型的原型为父类型的一个实例对象
Sub.prototype.constructor = Sub; //让子类型的原型的constructor指向子类型
Sub.prototype.showSubProp = function() {
console.log(this.subProp);
}
var sub = new Sub();
sub.showSupperProp(); //能输出证明sub为supper的子类型
//"Sub property"
sub.showSubProp(); //"Sub property"
console.log(sub) //Sub
借用构造函数继承
套路:(假继承)
1、定义父类型构造函数
2、定义子类型构造函数
3、在子类型构造函数中调用父类型构造函数
关键:在子类型构造函数中通用call()
调用父类型构造函数。
function Person(name, age) {
this.name = name;
this.age = age;
}
function Student(name, age, price) {
Person.call(this, name, age);
//相当于:this.Person(name, age); this为子类型的一个实例
/*this.name = name;
this.age = age;*/
this.price = price;
}
组合继承
原型链+借用构造函数的组合继承
1、利用原型链实现对父类型对象的方法继承。
2、利用call()
借用父类型构建函数初始化相同属性。
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.setName = function(name) {
this.name = name;
}
function Student(name, age, price) {
Person.call(this, name, age);
this.price = price;
}
Student.prototype = new Person(); //为了能看到父类型的方法
Student.prototype.constructor = Student; //修正constructor属性
Student.prototype.setPrice = function(price) {
this.price = price;
}
var s = new Student("Tom", 24, 15000);
s.setName("Bob");
s.setPrice(16000);
console.log(s.name, s.age, s.price) //Bob 24 16000
new一个对象背后做了些什么?
1、创建一个空对象。
2、给对象设置proto,值为构造函数对象的prototype属性值。
3、执行构造函数体(给对象添加属性/方法)。
线程机制与事件机制
进程与线程
进程
1、程序的一次执行,它占有一片独有的内存空间
2、可以通过windows任务管理器查看进程
线程
1、是进程内的一个独立执行单元
2、是程序执行的一个完整流程
3、是CPU的最小的调度单元
相关知识
1、应用程序必须运行在某个进程的某个线程上
2、一个进程中至少有一个运行的线程:主线程,进程启动后自动创建
3、一个进程中也可以同时运行多个线程,我们会说程序是多线程运行的
4、一个进程内的数据可以供其中的多个线程直接共享
5、多个进程之间的数据是不能直接共享的
6、线程池(Thread Pool):保存多个线程对象的容器,实现线程对象的反复利用
浏览器是单进程还是多进程
1、单进程:firefox、老版IE
2、多进程:Chrome、新版IE
浏览器内核
内核模块
JS引擎模块:负责js程序的编译与运行
html,css文档解析模块:腐恶页面文本的解析
DOM/CSS模块:负责DOM/CSS在内存中的相关处理
布局和渲染模块:负责页面的布局和效果的绘制(内存中的对象)
(运行在主线程上)
——————————————————————————————————
(运行在分线程上)
定时器模块:负责定时器的管理
事件响应模块:负责事件的管理
网络请求模块:负责ajax请求
定时器引发的思考
定时器真是定时执行的吗?
1、定时器并不能保证真正定时执行
2、一般会延迟一丁点(可以接受),也有可能延迟很长时间(不能接受)
定时器回调函数是在分线程执行的吗?
1、在主线程执行的,js是单线程的
2、定时器是如何实现的?
事件循环模型(后面讲)
document.getElementById('btn').onclick = function(){
var start = Date.now()
console.log('启动定时器前...')
setTimeout(function(){
console.log('定时器执行了',Date.now()-start) //582!
},200)
console.log('启动定时器后')
//时间长的任务
for(var a = 0; a < 1000000; a++){
}
}
JS是单线程执行的
如何证明js执行时单线程的?
1、setTimeout() 的回调函数是在主线程执行的
2、定时器回调函数只有在运行栈中的代码全部执行完之后才有可能执行
为什么js要用单线程模式,而不是多线程模式?
Javascript的单线程,与它的用途有关作为,浏览器脚本语言,Javascript的主要用途是与用户交互,以及操作DOM,这决定了它只能是单线程,否则会带来很复杂的同步问题。
代码的分类
1、初始化代码
2、回调代码
js引擎执行代码的基本流程
1、先执行初始化代码:包含一些特别的代码
比如:设置定时器、绑定事件监听、发送ajax请求
2、后面在某些时刻才会执行回调代码
浏览器的事件循环(轮询)模型
模型原理图:https://www.processon.com/view/link/61e130571e085306c96bc179
1、所有代码分类:
初始化执行代码(同步代码):包含绑定dom事件监听,设置定时器,发送ajax请求的代码回
调执行代码(异步代码):处理回调逻辑
2、js引擎执行代码的基本流程:
初始化代码==> 回调代码
3、模型的2个重要组成部分:
事件管理模块
回调队列(先进先出先执行)
4.模型的运转流程:
(1)执行初始化代码,将事件回调函数交给对应模块管理
(2)当事件发生时,管理模块会将回调函数及其数据添加到回调列队中
(3)只有当初始化代码执行完后(可能要一定时间),才会遍历读取回调队列中的回调函数执行
H5 Web Workers
Web Workers是HTML5提供的一个javascript多线程解决方案
我们可以将一些大量的代码交由web Worker运行而不冻结用户界面
但是子线程完全受主线程控制,且不得操作DOM,所以这个新标准并未改变javascript单线程的本质。
示例
test.html
<input type="text" placeholder="数值" id="number">
<button id="btn">计算</button>
<script type="text/javascript">
// 1 1 2 3 5 8 f(n) = f(n-1) + f(n-2)
var input = document.getElementById('number')
document.getElementById('btn').onclick = function () {
var number = input.value
//创建一个worker对象
var worker = new Worker('worker.js')
//绑定接收消息监听
worker.onmessage = function (event){
console.log('主线程接收分线程返回的数据'+event.data)
alert(event.data)
}
//向分线程发送消息
worker.postMessage(number)
console.log('主线程向分线程发送数据'+number)
}
worker.js
function fibonacci(n) {
return n<=2 ? 1 : fibonacci(n-1) + fibonacci(n-2)
}
var onmessage =function (event){ //不能用函数声明
var number = event.data
console.log('分线程接收到主线程发送的数据:'+number)
var result = fibonacci(number)
postMessage(result);//将获取到的数据发送会主线程
console.log('分线程向主线程返回的数据:'+result)
// alert(result) aLert是window的方法,在分线程不能调用
// 分线程中的全局对象不再是window,所以在分线程中不可能更新界面
}
图解:https://www.processon.com/view/link/62202b1107912966cc4accfd
不足
1、慢
2、不能跨域加载JS
3、worker内代码不能访问DOM(更新UI) (因为全局对象不再是window)
4、不是每个浏览器都支持这个新特性
其他补充
内存泄露与溢出
1、内存溢出
一种程序运行出现的错误
当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误
//1、内存溢出
var obj = {};
for (var i = 0; i < 10000; i++) {
obj[i] = new Array[10000000];
console.log("-----");
}
2、内存泄露
占用的内存没有及时释放。
内存泄露积累多了就容易导致内存溢出。
常见的内存泄露:
意外的全局变量。
没有及时清理的计时器或回调函数。
闭包。
//2、内存泄露
//意外的全局变量
function fn() {
a = 3; //定义成全局变量
console.log(a);
}
fn(); //函数执行完释放后,a还在
//没有及时清理的计时器或回调函数
var intervalId = setInterval(function () { //启动循环定时器后不清理
console.log( '----');
},1000);
// clearInterval(intervalId); //清理循环定时器
//闭包
function fn1() {
var a = 4;
function fn2() {
console.log(++a);
}
return fn2;
}
var f = fn1();
f();// f = null; //释放闭包
mber)
var result = fibonacci(number)
postMessage(result);//将获取到的数据发送会主线程
console.log(‘分线程向主线程返回的数据:’+result)
// alert(result) aLert是window的方法,在分线程不能调用
// 分线程中的全局对象不再是window,所以在分线程中不可能更新界面
}
图解:https://www.processon.com/view/link/62202b1107912966cc4accfd
##### 不足
1、慢
2、不能跨域加载JS
3、worker内代码不能访问DOM(更新UI) (因为全局对象不再是window)
4、不是每个浏览器都支持这个新特性
### 其他补充
#### 内存泄露与溢出
##### 1、内存溢出
一种程序运行出现的错误
当程序运行需要的内存超过了剩余的内存时,就出抛出内存溢出的错误
```javascript
//1、内存溢出
var obj = {};
for (var i = 0; i < 10000; i++) {
obj[i] = new Array[10000000];
console.log("-----");
}
2、内存泄露
占用的内存没有及时释放。
内存泄露积累多了就容易导致内存溢出。
常见的内存泄露:
意外的全局变量。
没有及时清理的计时器或回调函数。
闭包。
//2、内存泄露
//意外的全局变量
function fn() {
a = 3; //定义成全局变量
console.log(a);
}
fn(); //函数执行完释放后,a还在
//没有及时清理的计时器或回调函数
var intervalId = setInterval(function () { //启动循环定时器后不清理
console.log( '----');
},1000);
// clearInterval(intervalId); //清理循环定时器
//闭包
function fn1() {
var a = 4;
function fn2() {
console.log(++a);
}
return fn2;
}
var f = fn1();
f();// f = null; //释放闭包