1.什么是闭包?
闭包的概念:闭包就是函数嵌套时,让局部变量变成自由变量的环境,是一种让局部变量进化的方式
定义在一个函数内部的函数
垃圾回收机制:用过一次的东西,先放在一个内存中,不立即删掉,可以随时进行还原或再次使用,直到没有任何作用的时候再清除
如家用电器,电脑回收站
2.闭包的应用场景
一、for循环之中:
for循环之中的i变量会因为for的循环次数被覆盖,所以在for循环内部存在函数时,而且这个函数内会调用i变量,这种情况下就需要用到闭包。
for (var i = 0; i < 10; i++) {
console.log(i); //可以访问到每次的i
}
必须满足两个条件:
1.在for循环内存在函数
2.函数内会调用这个变量
var ali = document.getElementsByTagName("li");
for(var i=0;i<10;i++){
ali[i].onclick = function(){
console.log(i); //在函数内部就无法访问到外部变量
}
}
//如何形成闭包
var ali = document.getElementsByTagName("li");
for(var i=0;i<10;i++){
(function(a){
ali[a].onclick = function(){
console.log(a);
}
})(i)
}
一旦内部函数调用外部函数的局部变量,那么这个时候,这个局部变量就会变成内部函数的私有变量
二、当需要给setTimeout的回调函数传参时:
setTimeout(function (a){
console.log(a); //两秒后,undefined
}, 2000);
//或
setTimeout(fn(10), 2000);
function fn(a){
console.log(a); //没有延迟,直接打印10
}
//使用闭包:
function fn(a){
return function(){
console.log(a); //两秒后,10
}
}
var f = fn(10);
setTimeout(f, 2000);
3.闭包的特点
闭包是将函数内部和函数外部连接起来的桥梁
可以读取函数内部的变量
让这些变量的值,始终保存在内存中,不会在调用结束后被系统回收
避免全局变量命名空间的污染
内存消耗很大,不能滥用
闭包会在父函数外部,改变父函数内部变量的值
1.构造函数继承
在构造函数中,同样属于两个新创建的函数,也是不相等的
function Fn(name){
this.name = name;
this.show = function(){
alert(this.name);
}
}
var obj1 = new Fn("AAA");
var obj2 = new Fn("BBB");
obj1.show()
obj2.show()
此时,任何一个new出来的实例上都有了show方法,可以视为最基础的继承。
2.call/apply继承
function Father(skill){
this.skill = skill;
this.show = function(){
alert("我会"+this.skill);
}
}
function Son(abc){
//这里的this指向函数Son的实例化对象
//将Father里面的this改变成指向Son的实例化对象,当相遇将father里面所有的属性和方法都复制到了son身上
//Father.call(this,abc);//继承结束,call适合固定参数的继承
//Father.apply(this,arguments);//继承结束,apply适合不定参数的继承
}
var f = new Father("绝世木匠”);
var s = new Son("一般木匠");
f.show()
s.show();
优点:
创建子类实例时,可以向父类的构造器传参;
缺点:
只能继承构造器中定义的属性和方法,不能继承原型上定义的属性和方法
3.原型对象继承(注意深浅拷贝)
原型对象继承:继承原型
优点:简单,方便,可以继承原型身上的方式和属性
缺点:只能继承原型,不方便传参
function Parent(n){
this.name = n;
}
Parent.prototype.skill = function(){
console.log(this.name + "是鉴定师");
}
function Child(n){}
// 注意:对象的深浅拷贝
// Child.prototype = Parent.prototype;
for(var i in Parent.prototype){
Child.prototype[i] = Parent.prototype[i];
}
// Child.prototype.skill = function(){
// console.log("采矿");
// }
var p = new Parent("大老王");
console.log(p)
p.skill();
var c = new Child("小老王");
console.log(c)
c.skill();
4.原型链继承
原型链继承:通过给Child设置原型为Parent的实例的方式,给Child添加了一层原型链的方式
优点:既能继承原型,又能继承构造函数
缺点:复杂,不方便传参
function Parent(n){
this.name = n;
}
Parent.prototype.skill = function(){
console.log(this.name + "是鉴定师");
}
function Child(){}
Child.prototype = new Parent("小老王");
Child.prototype.skill = function(){
console.log(12312312)
}
var p = new Parent("大老王");
console.log(p)
p.skill();
// p是实例(对象),有__proto__属性,指向Parent.prototype
var c = new Child();
console.log(c);
c.skill();
c是实例(对象),有__proto__属性,指向Child.prototype,是Parent的实例(对象),有__proto__属性,指向Parent.prototype
5.混合继承1
混合继承:构造函数继承+原型对象继承
优点:既能继承构造函数又能继承原型,方便传参,多继承
缺点:复杂
常用的继承方式之一
function Parent(n){
this.name = n;
}
Parent.prototype.skill = function(){
console.log(this.name + "是鉴定师");
}
function P2(){
this.a = 10;
}
P2.prototype.init = function(){
console.log("hello")
}
function Child(n){
Parent.call(this,n)
P2.call(this)
}
for(var i in Parent.prototype){
Child.prototype[i] = Parent.prototype[i]
}
for(var i in P2.prototype){
Child.prototype[i] = P2.prototype[i]
}
Child.prototype.skill = function(){
console.log(123)
}
var p = new Parent("大老王");
console.log(p)
p.skill();
var c = new Child("小老王");
console.log(c)
c.skill();
c.init();
6.混合继承2
ES6的class继承:原理↓↓↓
混合继承:构造函数+原型链
优点:简单方便易操作,语法层面的继承,属性和方法都能继承,参数很好处理
缺点:兼容性
忽略兼容,常用的继承方式之一
class Parent{
constructor(n) {
this.name = n
}
skill(){
console.log(this.name + "是鉴定师")
}
}
class Child extends Parent{
constructor(n){
super(n)
}
}
var p = new Parent("大老王");
console.log(p)
p.skill();
var c = new Child("小老王");
console.log(c)
c.skill();
构造函数,原型,实例之间的关系:
1.构造函数Fn身上有属性prototype为原型对象,原型对象内有constructor属性指向当前prototype所在的构造函数Fn
2.在new执行构造函数Fn时,创造了一个实例对象f,实例对象f的__proto__指向构造函数Fn的原型prototype
3.因为实例对象f的__proto__指向构造函数Fn的原型prototype,所以实例对象f可以间接访问到Fn原型prototype的方法
查看实例对象f是否有指针指向构造函数Fn的原型:
isPrototypeOf()用于检测两个对象之间似乎否存在这种关系,使用方法如下:
Fn.prototype.isPrototypeOf(f) // 查看 Fn 的 prototype 对象,是否是 f 原型
类似的还有instanceof运算符,使用方法如下:
console.log(f instanceof Fn) // 查看 f 对象是否是构造函数 Fn 的实例
console.log(f instanceof Object)
两种使用,如果是返回ture,如果不是返回false
注意:instanceof运算符右侧为构造函数,并且js中所有原型都来自Object构造函数