闭包

Wenn 于 2022-09-21 发布

高阶函数

一个函数a接受另一个函数b作为自己的参数或者将一个函数c作为返回值返回,则称该函数a为高阶函数,或称为回调函数;

常用高阶函数:one:map :two:forEach :three:filter :four:find/findIndex :five:reduce

函数和方法的区别

==函数function:==

独立的function,那么称之为一个函数

function foo(){}  //函数

==方法method:==

当一个函数属于某一个对象时,则我们称这个函数为这个对象的方法

var obj = {
    foo: function(){}  //foo是obj的方法
}

闭包

闭包(closure)由两部分构成,==函数==+==可访问的自由变量==

function foo(){
    var name = 'foo'
    var age = 18;  //此时虽然形成闭包,但是v8引擎为了性能,依旧会销毁age
    function bar(){
        console.log(name)
    }
    return bar
}
var fn = foo()
fn();

image-20220921174803787

==甚至最简单的函数依旧属于闭包==

<script>
	var name = 'foo'
	function foo(){
        console.log(name)
    }
</script>

此时依旧构成闭包,虽然name声明在go中,但是按照mdn的解释,只要一个函数对其周围状态(lexical environment,词法分析)的引用捆绑在一起,那即是闭包

一个普通的函数function,如果==可以访问==外层作用域的自由变量,那么这个函数就是一个闭包;

从广义的角度:在javascript中任何一个函数都是闭包;

从狭义的角度,javascript中一个函数,如果==访问==了外层作用域中的自由变量,那这个函数一定是闭包。

function foo(){
    var name = 'foo';
    function bar (){
        conaole.log(name)
    }
    return bar
}
var fn = foo();
fn();

/*当代码编译时,
*在执行上下文栈ECS中创建GEC全局执行上下文,并创建vo,此时vo指向go;
*go内部创建foo,在堆内存中分配一块空间,并指向该地址;创建n=undefined
*
*当代码开始执行时,
*在ECS中创建函数执行上下文FEC,并在FEC中创建vo,此时vo指向Ao(活动对象)+parentScopr(GO),
*Ao此时创建name=undefined,创建bar,并在内存中分配一块空间,将bar指向该地址
*bar在堆内存中存在两块内容,一块为作用域,指向parentScope(foo-Ao),另一块为函数执行代码块
*将foo赋值给name,此时name=foo;并将bar函数地址返回给fn,此时fn=bar
*与此同时,foo正式结束,栈出,此时fn指向bar,所以bar不会被销毁;
*bar的vo指向(foo-Ao),所以foo-Ao依旧不会销毁,此时形成内存泄露,bar和foo-ao永远不会被销毁
*/

image-20220921184940197

this

  1. 函数在调用时,js会默认给this绑定一个值;
  2. this的绑定和定义的位置没有关系;
  3. this的绑定和调用方式以及调用的位置有关系;
  4. this实在运行时被绑定的。

==this绑定规则==

  1. 默认绑定
//默认绑定window,独立函数调用
function foo(){
    console.log(this); //window
}
foo();
function foo1(){
    console.log(this); // window
}
function foo2(){
    console.log(this); //window
    foo1()  //独立函数
}
function foo3(){
    console.log(this); //window
    foo2()  //独立函数
}
foo3()  //独立函数
var obj = {
    foo : function (){
        consoel.log(this)
    }
}
var bar = obj.foo;  //等同于 var bar = function foo(){}
bar(); //this => window  依旧为独立函数
function foo(){
    console.log(this)
}
var obj = {
    foo : foo
}
var bar = obj.foo
bar();  //this => window
function foo(){
    function bar (){
        console.log(this)
    }
    return bar
}
var fn = foo()
fn(); //this => window
var obj = {
    eat : fn
}
obj.eat(); //this => obj
  1. 隐式绑定 –> object.fn()

通过某个对象进行调用,必须在对象内部有一个属性对函数进行引用,

如果没有这样的引用,在进行调用时,会报找不到该函数的错误

object对象会被js引擎绑定到fn函数中的this里面

function foo(){console.log(this)}
var obj = {foo:foo}
obj.foo(); //this => obj
var obj1 = {foo:function (){console.log(this)}}
var obj2 = {bar:obj1.foo}
obj2.bar(); //this => obj2

==函数直接调用和使用call/apply的区别==

function foo(){
   console.log(this) 
}
var obj = {}
//foo直接调用指向的时全局对象(window)
foo(); //this => window

//call、apply是可以指定this的绑定
foo.call(obj); //this => obj
foo.apply(obj); //this => obj
function sum (num1,num2){
    console.log(num1+num2,this)
}
sum.call('call',20,30,40); //首先指定this,后参数使用逗号分隔即可
sum.apply('apply',[20,30,40]); //首先指定this,后参数放在数组中进行传参
  1. 显示绑定

call和apply在执行函数时,是可以明确的绑定this,这个绑定规则被称为显示绑定

function foo(){
    console.log(this)
}
var newFoo = foo.bind('aaa')
newFoo(); //this => 'aaa'  默认绑定和显示绑定bind冲突,优先级高的为bind
  1. new绑定
// 我们通过一个new关键字调用一个函数时(构造器,构造函数),这个时候this指向构造器创建出来的对象
// this => 创建出来的对象
function Person(){
    this.name = name
}
var p1 = new Person('hi') // this => p1
console.log(p1.name)