• 函数


    函数的概念:

      函数包含一组数据,它们是JavaScript的基础模块单元,用于代码复用、信息隐藏(封装)和组合调用(继承)。

      函数用于指定对象的行为。

      所谓编程,就是将一组需求分解成一组函数与数据结构的技能。

    函数对象:

      对象的角度(__proto__):JavaScript中的函数就是对象。对象是“键/值”对的集合并拥有一个连到原型对象的隐藏连接(这个隐藏连接就是__proto__)。对象字面量产生的对象连接到Object.prototype。函数对象连接到Function.prototype(该原型对象本身连接到Object.prototype)。每个函数在创建时会附加两个隐藏属性:函数的上下文(this:指向window)和实现函数行为的代码注释1

      函数的角度(prototype):每个函数对象在创建时也随机配有一个prototype属性,它的值是一个拥有constructor属性并且值为该函数的对象。这和隐藏连接到Function.prototype完全不同。

       因为函数是对象,所以他们可以像任何其他的值一样被使用。函数可以保存在变量、对象和数组中。函数可以被当做参数传递给其他函数,函数也可以再返回函数。而且,因为函数是对象,所以函数拥有方法(静态方法和动态方法)。

        function fn() {
          // 动态方法,需要new一个实例对象来调用
          this.play = function () {
            console.log('玩耍')
          }
        }
        let f = new fn()
        f.play() // 玩耍
        fn.play() // Uncaught TypeError: fn.play is not a function
    
        // 静态方法,不需要new一个实例,直接调用
        fn.eat = function () {
          console.log('吃饭')
        }
        fn.eat(); // 吃饭

    函数字面量:

      函数对象可以通过函数字面量来创建:

        let add = function fn(a, b) {
          return a + b
        };
    
        console.log(add(10, 20))
        console.log(add(101, 201))

      函数字面量包括4个部分:

        1、保留字function

        2、函数名,就是fn,它可以被省略。函数可以用它的名字来递归地调用自己,这个名字可以被调试器和开发工具用来识别函数。如果没有给函数命名,就是匿名函数

        3、函数的参数,多个参数用逗号分隔。这些参数被定义为函数中的变量。它们不像普通的变量那样被初始化为undefined,而是在函数被调用时初始化为实际提供的参数的值

        4、函数体,函数被调用的时候执行

      函数字面量可以出现在任何允许表达式出现的地方。函数也可以被定义在其他函数中。一个内部函数除了可以访问自己的参数和变量,同时它也能自由访问把它嵌套在其中的复函数的参数与变量(作用域链:内部函数可以访问到外部函数的变量,而外部函数不可以访问内部函数的变量)。通过函数字面量创建的函数对象包含一个连到外部上下文的连接,这就是闭包。

    调用:

      调用一个函数会暂停当前函数的执行,传递控制权和参数给新函数(JavaScript是单线程,当前函数执行完了再去执行下一个函数,就是入栈和出栈的概念)。除了声明时定义的形式参数,每个函数还接收两个附加的参数:this和arguments。参数this在面向对象编程中非常重要,它的值取决于调用的模式。在JavaScript中一共有4中调用模式:方法调用模式、函数调用模式、构造器调用模式、apply调用模式。这些模式在如何初始化关键参数this上存在差异。

      调用运算符就是函数名后面的小括号。小括号里可以有零个或多个用逗号隔开的表达式(表达式:有可能是别的函数运行的结果放在这里当做参数使用,所以这里叫表达式),每个表达式产生一个参数值。每个参数值被赋予函数声明时定义的形式参数名。当实参的个数和形参的个数不一致时,不会导致运行错误。如果实参个数多于形参,超出的实参就相当于没传;如果实参比形参少,那剩下的形参默认为undefined。对参数值不会进行类型检查:任何类型的值都可以被传递给任何参数(JavaScript是弱类型的语言)。

    方法调用模式:

      当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到该对象。如果调用表达式包含一个提取属性的动作(即包含一个.点表达式或[]下标表达式),那么它就是被当做一个方法来掉调用。

        let obj = {
          value: 100,
          eat: function (val) {
            console.log(this.value += typeof val === 'number' ? val : 1)
          }
        }
        obj.eat()
        document.writeln(obj.value)
    
    
        obj.eat(3000)
        document.writeln(obj.value)

      方法可以使用this访问自己所属的对象,所以它能够从对象中取值或对对象进行修改。this到对象的绑定发生在调用的时候(JavaScript的作用域在执行的时候确定)。这个“超级”延迟绑定使得函数可以对this高度复用。通过this可取得对它们所属对象的上下文的方法称为公共方法

    函数调用模式:

      当一个函数不是一个对象的方法时,那么它就是被当做一个函数来调用:

        function add(a, b) {
          return a + b
        }
        let sum = add(3, 4)
        console.log(sum)

      以此模式调用函数时,this被绑定到全局对象。外部函数被调用的时候,this应该绑定到外部函数的this变量,但是内部函数的this在JavaScript语言设计的时候没有按照这样的思路来,所以不能共享该方法对对象的访问权(this指向)。

      解决方案:给该函数定义一个that变量,赋值为this,内部函数可以访问到外部函数的变量,就可以访问到that,其实就是访问到this。

        let obj = {
          value: 0,
          increment: function (val) {
            this.value += typeof val === 'number' ? val : 1
          }
        }
        obj.increment()
        document.writeln(obj.value) // 1
        obj.increment(2)
        document.writeln(obj.value) // 3
    
        function add(a, b) {
          return a + b
        }
    
        obj.double = function () {
          let that = this
          let helper = function () {
            that.value = add(that.value , that.value)
          }
          helper()
        }
        obj.double()
        document.writeln(obj.value) // 6

      定义在double方法中的helper函数就是私有方法,它只能被double方法调用。helper函数中的this指向window,它是无法通过this直接访问到obj对象中的value值的,但是double方法是obj对象调用的,所以该方法中的this指向obj对象,然后在该函数中定义that赋值为this,就可以在helper函数中通过that,拿到obj中的value值并进行操作,这是非常常用的方法。如果这里不想使用that变量,还可以用将helper函数定义为箭头函数,箭头函数的this正好指向外层函数的this,这里就正好指向obj了。还有call方法可以改变this指向,在调用helper函数的时候,调用call方法,即helper.call(this),这里传入的this就是赋给that的this,此时helper函数中的this指向为obj。

    构造器调用模式:

    注释1:JavaScript创建一个函数对象时,会给该对象设置一个“调用”属性。当JavaScript调用一个函数时,可理解为调用此函数的“调用”属性。

  • 相关阅读:
    Java动态规划实现将数组拆分成相等的两部分
    动态规划解决hdu龟兔赛跑
    Eclipse上将maven项目部署到tomcat,本地tomcat下,webapps下,web-inf下lib下没有jar包决绝方案
    【转】spring IOC和AOP的理解
    Eclipse创建一个普通的java web项目
    linux服务器自动备份与删除postgres数据库数据
    开启Linux服务器vnc远程桌面详细步骤
    设计模式---JDK动态代理和CGLIB代理
    菜谱
    网络协议-dubbo协议
  • 原文地址:https://www.cnblogs.com/wuqilang/p/13732442.html
Copyright © 2020-2023  润新知