作用域和闭包
作用域和自由变量
作用域
全局作用域
函数作用域
块级作用域(es6新增) if/for/while...中的{}
自由变量
- 一个变量在当前的作用域没有定义,但被使用了
- 向上级作用域查找,知道找到为止
- 如果直到全局作用域都没有找到,则报错 xxx is not defined
闭包
什么是闭包?
作用域应用的特殊情况,有两种表现:
- 函数作为参数被传递
- 函数作为返回值被传递
- js
// 函数作为返回值 function create() { const a = 100; return function () { console.log(a); }; } const a = 200; let fun1 = create(); fun1(); // 函数作为参数 function print(fun) { const b = 200; fun(); } const b = 100; function fun2(){ console.log(b); } print(fun2)
- 答案都为100
- 结论:所有的自由变量的查找,是先在函数定义的地方,再向上级查找,不是在执行的地方!bi
闭包的应用
- 隐藏数据,封装私有变量
- 制作工具, 如防抖节流,结果缓存等等
- 绑定函数上下文,如bind
闭包的优点
- 包含函数内变量的安全,防止变量流入其他环境发生命名冲突,造成环境污染
- 在适当的时候,可以在内存中维护变量并缓存,提高执行效率。
闭包的缺点
- 增加了内存消耗量,影响网页性能可能出现问题,过度的使用闭包可能会导致内存泄漏
this
this取什么样的值是在函数执行的时候确定的,不是在定义的时候确定的
作为普通函数
- js
function fun1() { console.log(this); } fun1(); //window or global
使用call apply bind
- js
fun1.call({ x: 100 }); //{ x: 100 } // bind返回一个函数,稍后调用才会执行,call和apply则是立即调用 const fun2 = fun1.bind({ x: 200 }); fun2(); //{ x: 200 }
作为对象方法被调用
- js
const zhangsan = { name: "张三", sayHi(){ // this 指向当前对象 console.log(this); }, wait(){ setTimeout(function(){ // this === window console.log(this); }) }, waitAgain(){ setTimeout(()=>{ // this 再次指向当前对象,因为箭头函数取上级作用域的this console.log(this); }) } };
在class方法中调用
- js
class People{ constructor(name){ this.name = name } sayHi(){ console.log(this); } } const lisi = new People('李四') lisi.sayHi() // lisi 对象
箭头函数
- 箭头函数本身没有,取上级作用域的this
手写bind函数
- js
// 模拟bind Function.prototype.bind2 = function(self,...args){ // 此处的this为执行bind2的Function const context = this return function(){ return context.apply(self,args) } }