闭包
首先,我们先明确一下闭包的概念
闭包是从函数内部返回一个数据出来,然后在外部接收这个数据。
先写一个最简单的闭包:
function bibao(){
return "1";
}
var re = bibao();
console.log(re)// "1"
或者直接
console.log(bibao())// "1"
闭包的原则是:
你return的是什么,外面拿到的就是什么。
假如你没有写return 值,它默认返回undefined
function bibao(){}
console.log(bibao())// undefined
作用域链
提到闭包一定会涉及到作用域链
什么是作用域?
作用域有两种:
1、全局作用域:window,直接暴露在window中的一切,都是在window的作用域下,此时产生了一个GO
2、局部作用域:如果你在window的作用域下写了一个函数,那么这个函数内部会自己形成一个自己的作用域,这个作用域就叫做局部作用域
注意:局部作用域或者说函数作用域只有在它被调用时才会产生这个作用域,或者说是AO
补充:
函数A有函数A的作用域或者说函数A的AO
函数B有函数B的作用域或者说函数B的AO
什么是作用域链?
举个例子:
A函数中的变量或者函数,假如在A函数中对变量名或函数有定义,那么它们就在A函数的作用域中。
当出现一种情况:
A函数中有一个变量或者函数在被使用,但是在当前作用域(即A函数的作用域)中找不到它。那么这时候就会往它的外部作用域中去找,直到找到全局作用域为止。假如还是没有找到,就会报错。这种一层一层作用域去查找的过程中,这些作用域就形成了一个作用域链。
注意:作用域链是由内至外的
举个例子:
var num = 1;
function fun() {
console.log(2);
}
function a() {
function b() {
function c() {
console.log(num) // 1
fun(); // 2
}
c()
}
b()
}
a()
我来解释一下这个例子:
a函数中包裹了一个b函数,在调用a函数时b函数也被调用了;
当b函数被调用时,其内部的c函数被调用。
所以,这个执行的顺序是 a函数——>b函数——>c函数
当c函数在执行时,浏览器发现,c函数中,没有变量num和函数fun
那么,它就会找到c的上一级也就是函数b
当它发现里面也没有变量num和函数fun时
它又会找到b的上一级,也就是a,发现a也没有
最终找到全局作用域,如果还是没有,那么,就会报错。
恰好这里找到全局时,找到了,那么ok。
打印num,执行函数fun
注意:此时的这二者都是全局的,而非c自身的,当你在c中对他们做出改变时,改的是去全局中的。
最后说一点:找变量是,找到哪里,就停在哪里,变量也属于那里。
如果还看不懂,请百度预编译。
this
提到this,我们就要提到对象了,
对象有两种:
构造对象(由构造函数new出来的对象)
内置对象(系统构造出来的对象)
function A(){
// var this = {}
// return this;
}
var obj = new A();
当我们在使用new关键字时,系统会自动帮我们做两件事情:
首先,它会帮我们定义一个this变量作为一个空对象
注意:this是关键字,不要随便使用
然后,在函数执行到最后将this这个变量返回出来。
关于this执向的问题:可以参考下面这篇博客
函数和obj以及定时器中的this指向
闭包与this的差异
1.返回值的差异
闭包是你返回什么,就可以在外部拿到什么
而使用new方法去构造对象,系统帮我们定义的this对象则不同。
注意哈!new方法也是拿返回值。默认拿到的是构造函数中系统帮我们创造的this对象。
所以这就是它们的第一个差异。
当然,并非new 关键字出来的返回值只能是this对象
来看看例子:
这是默认情况
function A() {
}
var b = new A();
console.log(b)
// A__proto__: Object
当我们自己写了一个返回值,看看我们写的返回值是否可以改变系统自动给我们返回的值
首先拿一个基本数据类型作为返回值来试试:
function A() {
return "1"
}
var b = new A();
console.log(b)
// A __proto__: Object
我们会发现不行
当然,这里我就直说了,只有我们返回引用类型的返回值才能改变系统帮我们返回出来的值,即this对象。
function A() {
return []
}
var b = new A();
console.log(b)
// [] length: 0__proto__: Array(0)
此时的b的值就和我们用闭包是一样的,得到了一个空数组[]
2.编程时的差异
变量定义和函数对this对象毫无影响
举个例子:
function A() {
var num = 0;
function fun(){
}
}
var b = new A();
console.log(b.num) // undefined
console.log(b.fun) // undefined
变量与this不冲突
举个例子:
function A() {
var num = 0;
this.num = 1;
console.log(num); // 0
}
var b = new A();
console.log(b.num) //1
最后写几个题目吧。
习题1
var num = 1;
var obj = {
num: "11",
b: function() {
console.log(num)
console.log(this.num)
}
}
obj.b() // 1 "11"
var n = obj.b;
console.log(n()) // 1 1 undefined
这里我来解释一下:
首先:是闭包的原理,函数b中没有num变量或者num形参,那么就会往外层去找,直接就找到了window,刚好window中有,ok,打印1
其次:obj.b() b这个函数被obj调用,所以obj函数中的this指向obj,就是"11"了
var n = obj.b;
n拿到到的仅仅是obj对象中b这个函数的函数体。
n直接处在window的作用域中
所以n是指向window的函数,而不是被obj对象调用的。
所以n中的this指向window
console.log(n()) // 1 1 undefined
num是window中的 1
this.num是指向window的,num 为 1
n这个函数没有返回值,所以最后还会打印 undefined
打印函数的同时会打印它们的返回值,默认返回undefined(构造函数除外);
习题2
var num = 1;
var obj = {
num: "11",
b: function() {
return function() {
console.log(num);
console.log(this.num)
}
}
}
console.log(obj.b()()) // 1 1 undefined
解释一下:
这里的obj.b()()相当于:
var o = obj.b();这里是闭包,得到obj.b函数中的返回值
即:function() {
console.log(num);
console.log(this.num)
}
然后o就是一个window作用域中的函数了,this指向window