文章序
本文就标题的问题,小结一下本人的观点与想法,如有错误欢迎指正交流.
作用域
在ES6之前只有两个作用域,全局作用域和函数内的局部作用域,for循环内部,对象,{}等内部是没有作用域,使用var定义的变量都是全局变量。在ES6之后,使用let定义的变量只能在大括号{}内部有效,比如循环内部,if内部,包括单独的{}内,是块级元素,{}内部作用域也就称为块级作用域,但是对象的{}内部仍然是没有作用域的.
闭包
闭包,通俗的讲就是可以在外部访问局部变量,通过函数可以根据链式作用域原则取到外层局部作用域的原理,将外层局部作用域的变量通过return返回出去,这样就可以在局部作用域外部使用局部作用域内部的变量了.
优点
可以在外部使用局部作用域的变量.
缺点
使局部作用域变量不能被内存释放,变量就会占用内存,内存消耗大,可能会导致内存泄露.
闭包的使用方法
简单来讲就是在局部作用域内通过函数将需要的局部作用域变量通过return返回出去,代码如下:
var hobby = 'football'
{
let hobby = 'games';
window.getHobby = function () {
return hobby;
}
}
let hpfHobby = window.getHobby();
console.log(hobby); //football
console.log(hpfHobby); // games
可以看到,我们通过闭包的方式,在全局环境下拿到了{}内部局部作用域的games,这便是闭包。其实闭包没必要必须通过函数套函数的方式去使用,只是说函数内部创造了一个局部作用域,我们也可以通过其他方式创造局部作用域,比如我这里就是通过单纯的{}创造了局部作用域并通过设置window对象的getHobby方法,在外部拿到了局部作用域的变量.
如果通过函数嵌套的方式去调用的话,代码如下:
var hobby = 'football'
var getHobby = function () {
let hobby = 'games'
return function () {
return hobby;
};
}
console.log(hobby); //football
console.log(getHobby()()); // games
可以看到,这种写法就跟我们在网上能够找到的关于闭包的主流写法比较一致了,但我是不推荐谈及闭包就是函数套函数,return出去这种说法,这只是一种写法,而不应该一传十十传百好像只有这种写法才对一样.
箭头函数
对象内部函数
ES6新增箭头函数,funName () => {}
箭头函数内部的this指针是指向箭头函数被创建时外部作用域的this指向的对象,而不是调用时指定this指向,普通函数则是根据调用时外部作用域内的this的指向来确定函数内部this的指向。这样看起来比较晦涩难懂,请看如下代码:
var name = 'xx';
let obj1 = {
name: 'hpf',
normalGetName: function () {
console.log('name: ', name); // name: xx
console.log('this.name: ', this.name); // this.name: hpf
console.log(this); // {name: "hpf", normalGetName: ƒ, arrowGetName: ƒ}
},
arrowGetName: () => {
console.log('name: ', name); // name: xx
console.log('this.name: ', this.name); // this.name: xx
console.log(this); // Window {window: Window, self: Window, document: document, name: "xx", location: Location, …}
}
};
obj1.normalGetName();
obj1.arrowGetName();
这里普通函数因为调用时是通过obj1.normalGetName()去调用,所以this是指向obj1,但是箭头函数的this是根据函数定义时的上下文作用域去确定,而定义时因为对象内部是没有作用域的,所以根据链式作用域原则,向上去找到全局window作为作用域.
函数内部函数
当函数外部有作用域时,比如放在函数内,代码和结果如下:
var name = 'xx';
let fun1 = function () {
let name = 'lmm';
console.log(this); // Window {window: Window, self: Window, document: document, name: "xx", location: Location, …}
let normalGetName = function () {
console.log('name: ', name); // name: lmm
console.log('this.name: ', this.name); // this.name: xx
console.log(this); // Window {window: Window, self: Window, document: document, name: "xx", location: Location, …}
}
let arrowGetName = () => {
console.log('name: ', name); // name: lmm
console.log('this.name: ', this.name); // this.name: xx
console.log(this); // Window {window: Window, self: Window, document: document, name: "xx", location: Location, …}
}
normalGetName();
arrowGetName();
};
fun1();
这里很显然,因为函数内部是有作用域的,所以name均取到了函数内部变量,所以显示lmm,this.name因为普通函数是在函数内部调用,箭头函数也是在函数内部定义,所以两者的this指向和函数fun1内部的this指向相同,而又因为fun1是在全局下调用,所以两种函数的this均为全局变量winodw了.
总结
那么我觉得,到现在为止,你应该已经很清晰JS中作用域,闭包,普通函数和箭头函数以及this指向的问题了.
JS中查找变量是按照链式作用域来查找,即先找自己的作用域是否有该变量,如无继续向父作用域找,直到全局作用域window,如果都没有则报错抛出异常.
闭包就是利用在函数内部可以访问到函数外部作用域的变量,通过return的形式返回出去,这样就可以在外部得到局部作用域内部的变量了.
箭头函数的this指向定义时外部作用域内的this指向,普通函数的this指向调用时根据上下文取确认.