• 函数


    函数的定义方式

    • 函数声明

    • 函数表达式

    • new Function

    函数声明


    function foo () {

    }

    函数表达式


    var foo = function () {

    }

    函数声明与函数表达式的区别

    • 函数声明必须有名字

    • 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用

    • 函数表达式类似于变量赋值

    • 函数表达式可以没有名字,例如匿名函数

    • 函数表达式没有变量提升,在执行阶段创建,必须在表达式执行之后才可以调用

    下面是一个根据条件定义函数的例子:


    if (true) {
     function f () {
       console.log(1)
    }
    } else {
     function f () {
       console.log(2)
    }
    }

    以上代码执行结果在不同浏览器中结果不一致。

    不过我们可以使用函数表达式解决上面的问题:


    var f;

    if (true) {
     f = function () {
       console.log(1);
    }
    } else {
     f = function () {
       console.log(2);
    }
    }

    函数的调用方式

    • 普通函数

    • 构造函数

    • 对象方法

    函数内 this 指向的不同场景

    函数的调用方式决定了 this 指向的不同:

    调用方式this指向
    普通函数调用 window
    构造函数调用 实例对象
    对象方法调用 该方法所属对象
    事件绑定方法 绑定事件对象
    定时器函数 window

    这就是对函数内部 this 指向的基本整理,写代码写多了自然而然就熟悉了。

    函数也是对象

    • 所有函数都是 Function 的实例

    call、apply、bind

    那了解了函数 this 指向的不同场景之后,我们知道有些情况下我们为了使用某种特定环境的 this 引用,这时候时候我们就需要采用一些特殊手段来处理了,例如我们经常在定时器外部备份 this 引用,然后在定时器函数内部使用外部 this 的引用。然而实际上对于这种做法我们的 JavaScript 为我们专门提供了一些函数方法用来帮我们更优雅的处理函数内部 this 指向问题。这就是接下来我们要学习的 call、apply、bind 三个函数方法。

    call

    call() 方法调用一个函数, 其具有一个指定的 this 值和分别地提供的参数(参数的列表)。

    <p class="danger"> 注意:该方法的作用和 apply() 方法类似,只有一个区别,就是 call() 方法接受的是若干个参数的列表,而 apply() 方法接受的是一个包含多个参数的数组。</p>

    语法:


    fun.call(thisArg[, arg1[, arg2[, ...]]])

    参数:

    • thisArg

      • 在 fun 函数运行时指定的 this 值

      • 如果指定了 null 或者 undefined 则内部 this 指向 window

    • arg1, arg2, ...

      • 指定的参数列表

    apply

    apply() 方法调用一个函数, 其具有一个指定的 this 值,以及作为一个数组(或类似数组的对象)提供的参数。

    <p class="danger"> 注意:该方法的作用和 call() 方法类似,只有一个区别,就是 call() 方法接受的是若干个参数的列表,而 apply() 方法接受的是一个包含多个参数的数组。</p>

    语法:


    fun.apply(thisArg, [argsArray])

    参数:

    • thisArg

    • argsArray

    apply()call() 非常相似,不同之处在于提供参数的方式。apply() 使用参数数组而不是一组参数列表。例如:


    fun.apply(this, ['eat', 'bananas'])

    bind

    bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

    语法:


    fun.bind(thisArg[, arg1[, arg2[, ...]]])

    参数:

    • thisArg

      • 当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。

    • arg1, arg2, ...

      • 当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。

    返回值:

    返回由指定的this值和初始化参数改造的原函数拷贝。

    示例1:


    this.x = 9;
    var module = {
     x: 81,
     getX: function() { return this.x; }
    };

    module.getX(); // 返回 81

    var retrieveX = module.getX;
    retrieveX(); // 返回 9, 在这种情况下,"this"指向全局作用域

    // 创建一个新函数,将"this"绑定到module对象
    // 新手可能会被全局的x变量和module里的属性x所迷惑
    var boundGetX = retrieveX.bind(module);
    boundGetX(); // 返回 81

    示例2:


    function LateBloomer() {
     this.petalCount = Math.ceil(Math.random() * 12) + 1;
    }

    // Declare bloom after a delay of 1 second
    LateBloomer.prototype.bloom = function() {
     window.setTimeout(this.declare.bind(this), 1000);
    };

    LateBloomer.prototype.declare = function() {
     console.log('I am a beautiful flower with ' +
       this.petalCount + ' petals!');
    };

    var flower = new LateBloomer();
    flower.bloom();  // 一秒钟后, 调用'declare'方法

    应用

    • 让伪数组使用数组的方法


      var obj = {
       0: 1,
       1: 8,
       2: 10,
       3: 3,
       4: 2,
       length: 5
      };
      Array.prototype.splice.call(obj, 0, 1);
      Array.prototype.push.call(obj, 100);
      console.log(obj);
    • 对象内部使用使用定时器


      var o = {
       name: 'hanmeimei',
       func1: function () {
         console.log(this.name);
      },
       func2: function () {
         setInterval(function () {
           console.log(this);
        }.bind(this), 2000);
      }
      };
      o.func2();
    • 让数组的每一项作为方法的参数


      var arr = [2, 1, 8, 9, 10, 3];
      console.log(Math.max.apply(Math, arr));
      console.log.apply(console, arr);

    小结

    • call 和 apply 特性一样

      • 都是用来调用函数,而且是立即调用

      • 但是可以在调用函数的同时,通过第一个参数指定函数内部 this 的指向

      • call 调用的时候,参数必须以参数列表的形式进行传递,也就是以逗号分隔的方式依次传递即可

      • apply 调用的时候,参数必须是一个数组,然后在执行的时候,会将数组内部的元素一个一个拿出来,与形参一一对应进行传递

      • 如果第一个参数指定了 null 或者 undefined 则内部 this 指向 window

    • bind

      • 可以用来指定内部 this 的指向,然后生成一个改变了 this 指向的新的函数

      • 它和 call、apply 最大的区别是:bind 不会调用

      • bind 支持传递参数,它的传参方式比较特殊,一共有两个位置可以传递

          1. 在 bind 的同时,以参数列表的形式进行传递

          1. 在调用的时候,以参数列表的形式进行传递

        • 那到底以谁 bind 的时候传递的参数为准呢还是以调用的时候传递的参数为准

        • 两者合并:bind 的时候传递的参数和调用的时候传递的参数会合并到一起,传递到函数内部

    函数的其它成员

    • arguments

      • 实参集合

    • caller

      • 函数的调用者

    • length

      • 形参的个数

    • name

      • 函数的名称


    function fn(x, y, z) {
     console.log(fn.length) // => 形参的个数
     console.log(arguments) // 伪数组实参参数集合
     console.log(arguments.callee === fn) // 函数本身
     console.log(fn.caller) // 函数的调用者
     console.log(fn.name) // => 函数的名字
    }

    function f() {
     fn(10, 20, 30)
    }

    f()

    高阶函数

    • 函数可以作为参数

    • 函数可以作为返回值

    作为参数


    function eat (callback) {
     setTimeout(function () {
       console.log('吃完了')
       callback()
    }, 1000)
    }

    eat(function () {
     console.log('去唱歌')
    })

    作为返回值


    function genFun (type) {
     return function (obj) {
       return Object.prototype.toString.call(obj) === type
    }
    }

    var isArray = genFun('[object Array]')
    var isObject = genFun('[object Object]')

    console.log(isArray([])) // => true
    console.log(isArray({})) // => true

    函数闭包


    function fn () {
     var count = 0
     return {
       getCount: function () {
         console.log(count)
      },
       setCount: function () {
         count++
      }
    }
    }

    var fns = fn()

    fns.getCount() // => 0
    fns.setCount()
    fns.getCount() // => 1

    什么是闭包

    闭包就是能够读取其他函数内部变量的函数,由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成 “定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

    闭包的用途:

    • 可以在函数外部读取函数内部成员

    • 让函数内成员始终存活在内存中

    一些关于闭包的例子

    示例1:


    var arr = [10, 20, 30]
    for(var i = 0; i < arr.length; i++) {
     arr[i] = function () {
       console.log(i)
    }
    }

    示例2:


    console.log(111)

    for(var i = 0; i < 3; i++) {
     setTimeout(function () {
       console.log(i)
    }, 0)
    }
    console.log(222)

    闭包的思考题

    思考题 1:


    var name = "The Window";
    var object = {
     name: "My Object",
     getNameFunc: function () {
       return function () {
         return this.name;
      };
    }
    };

    console.log(object.getNameFunc()())

    思考题 2:


    var name = "The Window";  
    var object = {    
     name: "My Object",
     getNameFunc: function () {
       var that = this;
       return function () {
         return that.name;
      };
    }
    };
    console.log(object.getNameFunc()())

    小结

    函数递归

    递归执行模型


    function fn1 () {
     console.log(111)
     fn2()
     console.log('fn1')
    }

    function fn2 () {
     console.log(222)
     fn3()
     console.log('fn2')
    }

    function fn3 () {
     console.log(333)
     fn4()
     console.log('fn3')
    }

    function fn4 () {
     console.log(444)
     console.log('fn4')
    }

    fn1()

    举个栗子:计算阶乘的递归函数


    function factorial (num) {
     if (num <= 1) {
       return 1
    } else {
       return num * factorial(num - 1)
    }
    }

    递归应用场景

    • 深拷贝

    • 菜单树

    • 遍历 DOM 树


  • 相关阅读:
    函数的重载 C++快速入门06
    PE格式详细讲解8 系统篇08|解密系列
    《零基础入门学习汇编语言》检测点,实验,课后题答案
    PE格式详细讲解9 系统篇09|解密系列
    C++输出输入小结 C++快速入门05
    使用XML生成菜单
    DNS解析过程详解
    Windows Azure 2.5天深度技术训练营 和 微软公有云发现之旅
    使用单例模式实现自己的HttpClient工具类
    android 反编译和防止被反编译。
  • 原文地址:https://www.cnblogs.com/xj1111/p/13817423.html
Copyright © 2020-2023  润新知