如何区分私有变量和全局变量?
1)在全局作用域下声明(预解释的时候上)的变量是全局变量
2)在“私有作用域中声明的变量”和“函数的形参”都是私有变量
在私有作用域中,我们代码执行的时候遇到了一个变量,首先我们需要确定它是否为私有的变量,如果是私有的变量,那么和外面的没有任何的关系;如果不是私有的,则往当前作用域的上级作用域进行查找,如果上级作用域也没有则继续查找,一直找到window为止 …(作用域链)
2.当函数执行的时候(直接目的:让函数体中的代码执行),首先会形成一个新的私有的作用域,然后按照如下的步骤执行:
1)如果没有形参,先给形参赋值
2)进行私有作用域中的预解释
3)私有作用域中的代码自上而下执行
…
4)函数形成一个新的私有的作用域保护了里面的私有变量不受外界干扰(外面修改不了里面的),私有的也修改不了外面的-》闭包
闭包
闭包是一种机制,函数执行的时候形成了一个私有的作用域,保护里面的私有变量不受外界干扰。
闭包最大的作用就是隐藏变量,闭包的一大特性就是内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后
- 基于此特性,JavaScript可以实现私有变量、特权变量、储存变量等
- 我们就以私有变量举例,私有变量的实现方法很多,有靠约定的(变量名前加_),有靠Proxy代理的,也有靠Symbol这种新数据类型的。
- 闭包的优点 - 可以隔离作用域,不造成全局污染
- 闭包的缺点 - 由于闭包长期驻留内存,则长期这样会导致内存泄露
- 如何解决内存泄露:将暴露全外部的闭包变量置为null
//预解释的时候不管你的条件是否成立,都要把带var的进行提前的声明
//window的预解释:var num; -> window.num
if(!('num' in window)){
var num = 12;
}
console.log(num) // undefined
3.执行函数定义的那个function在全局作用域下不进行预解释,当代码执行到这个位置的时候定义和执行一起完成了
自执行函数: 定义和执行一起完成了,不进行预解释:
(function(num){})(100)
4.函数体中的return下面的代码虽然不在执行了,但是需要进行预解释;return后面跟着的都是我们的返回值,所以不进行预解释。
function fn(){
console.log(num); // undefined
return function(){
};
var num = 10;
}
fn();
如何查找当前作用域上一级作用域
看当前函数是在哪个作用域下定义的,那么它的上级作用域就是谁->和函数在哪执行的没有任何关系
function fn(){
var num = 120;
return function(){
console.log(num);
};
}
var f = fn();
f(); //120
~function(){
var num = 1200;
f() //120
}
堆内存
对象数据类型或者函数数据类型在定义的时候首先会开辟一个堆内存,堆内存有一个引用的地址,如果外面有变量等引用了这个地址,我们就说这个内存被占用了,就不能销毁了。
我们要让堆内存释放/销毁,只需要把这个引用它的变量赋值为null。如果当前的堆内存没有任何东西被占用了,那么浏览器会在空闲的时候把它销毁。。。
销毁:
obj = null:
栈内存
1)全局作用域
只有当页面关闭的时候全局作用域才会销毁
2)私有的作用域(只有函数执行会产生私有的作用域
一般情况下,函数执行会形成一个新的私有作用域,当私有作用域中的代码执行完成后,我们当前作用域都会主动的进行释放和销毁
但是还是存在特殊情况的:
当前私有作用域中的部门内存被作用域以外的东西占用了,那么当前的这个作用域就不能销毁了
a,函数执行返回了一个引用数据类型的值,并且在函数的外面被一个其他的东西给接收了,这种情况下一般形成的私有作用域都不会销毁。
function fn(){
var num = 100;
return function(){
num++;
console.log(num);
}
}
var f = fn();
f();
下述情况属于不立即销毁 fn返回的函数没有被其他的东西占用,但是还需要执行一次呢,所以暂时不销毁,当返回的值执行完成后,浏览器会在空闲的时候把它销毁了。 “不立即销毁”
function fn(){
var num = 100;
return function(){
num++;
console.log(num);
}
}
fn()();
检测数据类型的四种方式:
1.typeof 用来检测数据类型的运算符
2.instanceof 检测某一个实例是否属于某个类
3.constructor 构造函数
4.Object.prototype.toString.call()
1.for,while 都是同步模式
2.异步:
不会立即执行,需要等一定时间,只有当下面的事情都处理完成了,才会回头处理之前的事情;如果下面的事情并没有处理完成,不管之前的事情有没有到时间,都等着。
3.在JS中异步编辑有四种情况:
(3.1)定时器
(3.2)事情绑定
(3.3)Ajax
(3.4)回调函数
定时器
每一个浏览器对于定时器的等待时间都有一个最小的值,谷歌5-6ms,IE:10-13ms,如果设置的等待时间小于这个值,不起作用,还需要等到最小时间才执行;0也不是立即执行。
var n = 0;
setTimeout(function(){
n++;
console.log(n)
},0)
console.log(n)
我们定时器设置的等待时间不一定是最后执行时间,如果定时器之后还有其他的事情正在处理中,不管定时器的时间有没有到,都是不会执行定时器的
var n = 0;
setTimeout(function(){
n++;
console.log(n) //不执行
},0)
console.log(n) //1
while(1){ //死循环
n++;
}
console.log(n); //不执行
所有的编程语言都是面向对象开发的->类的继承、封装、多态
继承:子类继承父类中的属性和方法
多态:当前方法的多种形态后台语言中:多态包含重载和重写
JS中不存在重载,方法名一样的话,后面的会把前面的覆盖掉。
JS中有一个操作类似重载但不是重载:“我们可以根据传递的参数不一样,实现不同的功能。
重写: 子类重写父类的写法
工厂模式:
function createPerson(){
var obj = new Object();
obj.name = 'zhl';
obj.getName = function(){
return this.name;
}
return obj;
}
构造函数模式:
function createPerson(){
this.name = 'zhl';
this.getName = function(){
return this.name;
}
}
构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立开的 实例识别
基于构造函数模式的原型模式解决了 方法或者属性公有的问题->把实例之间相同的属性和方法提取成公有的属性和方法 类.prototype = fn;
1.每一个函数数据类型(普通函数、类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值
2.并且在prototype上浏览器天生给它增加了一个属性constructor(构造函数),属性值是当前函数(类)的本身
3.每一个对象数据类型(普通对象、实例、prototype…)也天生自带一个属性:proto,属性值是当前实例所属类的原型(prototype).
1),f1 instanceof Object ->true 通过__proto__可以向上级查找,不管有多少级,最后总能找到Object.
2),在Object.prototype上没有__proto__这个属性
一、 JS中的所有的类都是函数数据类型,它通过new执行变成一个类,但是它本身也是一个普通的函数 JS中所有的实例都是对象数据类型的
二、构造函数中的this指当前new出来的实例对象
function Fn(){
//this->f1
this.x = 100;
this.getX = function(){
//this->需要看getX执行的时候才知道
console.log(this.x);
}
}
var f1 = new Fn;
f1.getX(); //->方法中的this是f1->100
var ss = f1.getX;
ss(); //->方法中的this是window -> undefined;
(1)、在构造函数模式中 new Fn() 执行,如果Fn不需要传递参数的话,后面的小括号可以省略
(2)、this的问题:在类中出现的this.xx = xxx中的this 都是当前类的实例,而某一个属性值(方法),方法中的this需要看方法执行的时候,前面是否有"."才知道this是谁。
匿名函数中的 this 是 window;
事件对象
e.pageX = e.pageX || (e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft))
e.pageY = e.pageY || (e.clientY + (document.documentElement.scrollTop || document.body.scrollTop))
e.preventDefault() // 阻止默认行为