闭包、柯理化函数、继承

118 阅读4分钟

闭包

函数执行空间

当js引擎解析到fn函数定义时,在堆区域开辟一块存储空间,将函数体内代码以字符串形式存储到堆区域,在栈区域存储fn函数变量,并将堆区域存储空间地址赋值给fn

function fn(){
    let num = 100
    console.log( 'fn函数' ,num)
}
fun()

当执行函数调用语句fn()时,根据fn找到堆区域函数体代码,复制一份到调用栈区域,执行调用栈函数体代码,执行完毕销毁调用栈区域

不摧毁函数执行空间

当执行函数调用语句fn()时,根据fn找到堆区域函数体代码,复制一份到调用栈区域,执行调用栈函数体代码,函数体代码返回一个复杂数据类型,赋值给变量f,f引用函数体复杂数据类型对象,调用栈空间不会销毁

image.png

概念

1.有一个A函数,在A函数内部返回一个B函数
2.在B函数内部,访间A函数私有变量
3.在A函数外部,有一个变量引用返回的B函数

function A(){
    let num = 100
    //1.A函数内部返回一个B函数(显示)
    return function B(){
        console. log(num) //2.B函数内部,访间A函数私有变量num
    }
}
let f = A()// 3.变量f引用返回的B函数

闭包形成后:生成一个不会被销毁的函数执行空间,内部函数叫做外部函数的闭包函数

形成条件

  • 有函数嵌套,内层函数被返回
  • 内层函数访问外层函数私有变量
  • 返回的内层函数被引用

三种写法

  • 显示写法

      function A(){
          let num = 100
          //1.A函数内部返回一个B函数(显示)
          return function B(){
              console.log(num) //2.B函数内部,访间A函数私有变量num
          }
      }
      let f = A() // 3.变量f引用返回的B函数
    
  • 隐示写法

      let B   //3.在A函数外部,有一个变量引用返回的B函数
      function A(){
          let num = 200
          //1.A函数内部返回一个B函数(隐示)B= function ( {
              console.log(num)//2.B函数内部,访问A函数私有变量num
          }
      }
      A()
      B()
    
  • 自调用函数

      let x= (function () {
          let num = 300
          return function B() {
              console. log(num)
          }
      })()
    

特点

  1. 作用域空间不销毁
    优点: 因为不销毁,变量不会销毁,增加了变量的生命周期
    缺点: 因为不销毁,会一直占用内存,多了以后就会导致内存溢出
  2. 可以利用闭包,在一个函数外部,访问函数内部的变量
    优点: 可以在函数外部访问内部数据
    缺点: 必须要时刻保持引用,导致函数执行栈不被销毁
  3. 保护私有变量
    优点: 可以把一些变量放在函数里面,不会污染全局
    缺点: 要利用闭包函数才能访问,不是很方便

柯理化函数

柯里化介绍

柯里化(Currying)是一种关于函数的高阶技术。它不仅被用于 JavaScript,还被用于其他编程语言。
柯里化是一种函数的转换,它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)。
柯里化不会调用函数。它只是对函数进行转换。

理解柯里化

创建辅助函数 currySum(f)
该函数将对两个参数的函数 f 执行柯里化。换句话说,对于两个参数的函数 f(a, b) 执行 currySum(f) 会将其转换为以 f(a)(b) 形式运行的函数

function sum(a,b){
    return a + b
}
//柯理化转换
function currysum(s){
    return function(a){
        return function(b){
            return s(a,b)
        }
    }
}

const f = currySum(sum)// f为转换后的柯里化函数
const s = f(10)(20)  // f(10)(20)<=> f1 = f(10),f1(20)
console.log(s)

高级柯里化实现

function curry (func) {
    return function curried(...args) {
        if (args.length >= func.length){
            return func.apply(this,args);
        } else {
            return function(...args2){
                return curried.apply(this, args.concat(args2));
            }
        }
    };
}

继承

继承是类与类之间的关系,子类继承父类子类就拥有父类的属性和方法。JS中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承

继承方式

  • 构造函数继承

      //学生-子类
      function Student() {
          Person.call(this) //构造函数继承,继承父类构造函数属性和方法
          this.num = 1001 //学生学号
      }
    
  • 原型拷贝继承

      //拷贝继承,继承父类原型对象上公共的属性和方法
      for(let key in Person.prototype){
          Student-prototype[key] = Person.prototype[key]
      }
    
  • 原型继承
    改变原型指向

  • 组合继承
    就是把原型继承和借用构造函数继承两个方式组合在一起

    function student() { Person.call(this) } student.prototype = new Person

示例

/**
*代参继承
*/
//人类-父类
function Person(){
    this.name = 'jack'
    this.age = 20
}

Person. prototype = {
    constructor: Person,//手动设置constructor指向构造函数
    say:function() {
        console.log('说话')
    }
}

//学生-子类
function Student() {
    Person.call(this)//构造函数继承
    this.num = 1001 //学生学号
}
Student.prototype = {
    constructor: Student,
    //读书
    readBook:function() {
        console.log('学生读书')
    }
}
//原型拷贝继承
for(let key in Person.prototype){
    Student.prototype[key] = Person.prototype[key]
}
     //测试-子类访间父类属性和方法
s1.readBook()
s1.say()
console.log(s1.name,s1.age,s1.num)

ES6的类与继承

  • class

      class类名{
          //构造函数
          constructor(name){
              this.name=name
          }
          //方法
          say() {}
      }
      
    
  • 语法

      //下面表示创造一个Student类,继承自Person类
      class Student extends Person {
          constructor () {
              //必须在constructor里面执行一下super()完成继承
              super()
          }
      }