变量提升与函数提升
- 声明变量
var a = 1; var a //变量声明 // a=1变量赋值;
所有用到的变量在开始位置全部声明之后,再到变量的定义的地方进行赋值,变量的提前声明的过程是变量提升
var value1=1
加载时会提前声明 - 声明函数
函数的提升是直接将整个函数整体提升到作用域的最开始位置
function name(){};
加载时会提前声明
匿名函数不提前声明
函数的提升后的位置是在变量提升后的位置之后的。
数据类型
String、Number、Boolean、Null、Undefined、object
一个变量可以存放两种类型的值,基本类型的值(primitive values)和引用类型的值(reference values)
JavaScript 深入了解基本类型和引用类型的值
- 栈:原始数据类型/基本值类型primitive values
Null、Undefined、String、Number、Boolean、Symbol
按值访问、值不可变、值的比较、值存放在栈内存(Stack) - 堆: 引用数据类型reference values
统称Object类型
,细分有Object 、Array 、Date 、RegExp 、Function 等
按引用访问、值可变、引用的比较、栈区内存保存:变量标识符+指向堆内存对象的指针,对象保存在堆内存(Heap)
栈区内存保存变量标识符和指向堆内存中该对象的指针
JavaScript 不能直接操作对象的内存空间(堆内存)
null 和 undefined
-
JS最初
null是一个表示"无"的对象,转为数值时为0;
undefined是一个表示"无"的原始值,转为数值时为NaN。 -
目前的用法
null表示"没有对象",即该处不应该有值.用法是:(1) 作为函数的参数,表示该函数的参数不是对象。 (2) 作为对象原型链的终点。Object.getPrototypeOf(Object.prototype)// null
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。用法是:
(1)变量被声明了,但没有赋值时 var i // i undefined (2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。 function f(x){} f()//x undefined (3)对象没有赋值的属性 //obiect.p undefined (4)函数没有返回值时,默认返回undefined。let a = f() //a undefined
-
typeof
typeof null;//object
typeof undefined;//undefined
检测数据类型
typeof 操作符来检测变量的数据类型
instanceof 判断constructor.prototype 是否存在于参数 object 的原型链上
typeof value;返回类型
instanceof Object;返回布尔值
函数 isNaN(value); value是非数字值NaN返回true。如果其他值,返回 false
this的指向
- js中的this取决于当前执行对象
- 当前执行对象为window时 this一般为函数内部
- 时间内部时 this指代当前事件的执行对象
同步异步
• JavaScript是单线程的语言,所谓的单线程呢就是指如果有多个任务就必须去排队,前面任务执行完成后,后面任务再执行。
• 同步:事件在主线程等待执行
函数返回结果的时候,调用者能够拿到预期的结果,那么这个函数就是同步的.
如果函数是同步的,即使调用函数执行任务比较耗时,也会一致等待直到得到执行结果。
• 异步:事件在任务队列等待执行, 主线程执行完会后会去执行
如果在函数返回的时候,调用者还不能购得到预期结果,而是将来通过一定的手段得到(例如回调函数),这就是异步。例如ajax操作。
如果函数是异步的,发出调用之后,马上返回,但是不会马上返回预期结果。调用者不必主动等待,当被调用者得到结果之后会通过回调函数主动通知调用者。
解决异步
我们都知道js是单线程执行代码,导致js的很多操作都是异步执行(ajax)的,以下是解决异步的几种方式:
1.回调函数(定时器)。
2.事件监听。
3.发布/订阅。
4.Promise对象。(将执行代码和处理结果分开)
5.Generator。
6.ES7的async/await。
闭包
闭包就是当一个函数返回了一个函数后,其内部的局部变量被另一个函数使用,闭包可以保存当前数据不被清除 重复使用。
- 闭包使用
循环给页面上多个dom节点绑定事件
var btns=document.getElementBysTagName("button");
for(let i=0,len=btns.length; i<len; i++){
(function (n){ //循环内闭包 i //创建一个匿名函数并立刻执行
btns[n].onclick = function (){ alert (n); }
}(i));
}
- 返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
- 解决方法:用创建函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变
- 封装s私有变量:闭包可以将一些不希望暴露在全局的变量封装成“私有变量”
function create_counter(initial) {
var x = initial || 0;
return {
inc: function () {
x += 1; //私有变量,外部访问不到
return x;
}
}
}
在返回的对象中,实现了一个闭包,该闭包携带了局部变量x,并且,从外部代码根本无法访问到变量x。换句话说,闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。
4. 闭包可以大量使用吗?
闭包不可以大量使用,造成内存泄漏
在退出函数之前,将不使用的局部变量全部删除。可以使变量赋值为null
这段代码会导致内存泄露
window.onload = function(){
var el = document.getElementById("id"); //占用el
el.onclick = function(){
alert(el.id);
}
}
解决方法为
window.onload = function(){
var el = document.getElementById("id");
var id = el.id; //解除循环引用 el
el.onclick = function(){
alert(id);
}
el = null; // 将闭包引用的外部函数中活动对象 el 清除
}
内存泄露
指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束
- 造成原因
- 无意的全局变量
function f(){ i=0;}//i为全局变量
- 变量闭包
- 没有清理的DOM元素引用
- 被遗忘的定时器或者回调
- 子元素存在引起的内存泄露
- IE7/8引用计数使用循环引用产生的问题
- 怎样避免内存泄露
- 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;
- 注意程序逻辑,避免“死循环”之类的 ;
- 避免创建过多的对象 原则:不用了的东西要及时归还。
js垃圾回收机制
找出不再使用的变量,然后释放掉其占用的内存
但是这个过程不是实时的,因为其开销比较大,所以垃圾回收系统(GC)会按照固定的时间间隔,周期性的执行
- 判别变量是否还要使用
用于标记的无用变量的策略可能因实现而有所区别,通常情况下垃圾回收方式有两种实现方式:标记清除和引用计数
- 标记清除
“进入环境”的变量占用的内存,而当变量离开环境时,则将其标记为“离开环境”可释放。 - 引用计数?
引用计数的含义是跟踪记录每个值被引用的次数;当垃圾回收器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。