javascript 函数

函数的定义方式

  • 函数声明 function foo () {}
  • 函数表达式 var foo = function () {}
  • new 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 指向的不同:

调用方式非严格模式备注
普通函数调用window严格模式下是 undefined
构造函数调用实例对象原型方法中 this 也是实例对象
对象方法调用该方法所属对象紧挨着的对象
事件绑定方法绑定事件对象
定时器函数window

函数也是对象

  • 所有函数都是 Function 的实例

call、apply、bind

call

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

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

语法:

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

参数:

  • thisArg

    • 在 fun 函数运行时指定的 this 值
    • 如果指定了 null 或者 undefined 则内部 this 指向 window
  • arg1, arg2, ...

    • 指定的参数列表
apply

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

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

语法:

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); // 不用bind,会直接执行declare
};

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

var flower = new LateBloomer();
flower.bloom();  // 一秒钟后, 调用'declare'方法
小结
  • 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) // 形参的个数 3
  console.log(arguments) // 伪数组实参参数集合
    // Arguments(3) [10, 20, 30, callee: ƒ fn(x, y, z), Symbol(Symbol.iterator): ƒ]
  console.log(arguments.callee === fn) // 函数本身 true
  console.log(fn.caller) // 函数的调用者  ƒ f() { fn(10, 20, 30) }
  console.log(fn.name)   // 函数的名字    fn
}
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
Object.prototype.toString.call([])   // '[object Array]'

函数闭包

作用域、作用域链、预解析
  • 全局作用域
  • 函数作用域
  • 没有块级作用域
{ var foo = 'bar' }
console.log(foo) // bar ???

if (true) { var a = 123 }
console.log(a)   // 123 ???

作用域链示例代码:

var a = 10

function fn () {
  var b = 20

  function fn1 () {
    var c = 30
    console.log(a + b + c) // 60
  }

  function fn2 () {
    var d = 40
    console.log(c + d) // c is not defined
  }
  fn1()
  fn2()
}
fn() // 调用函数
  • 内层作用域可以访问外层作用域,反之不行
什么是闭包
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 name = "The Window";
var object = {
  name: "My Object",
  getNameFunc: function () {
    return function () {
      return this.name;
    };
  }
};

console.log(object.getNameFunc()()) // The Window

思考题 2:

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

函数递归

递归执行模型
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()  // 111 222 333 444 fn4 fn3 fn2 fn1
举个栗子:计算阶乘的递归函数
function factorial (num) {
  if (num <= 1) {
    return 1
  } else {
    return num * factorial(num - 1)
  }
}
递归应用场景
  • 深拷贝
  • 菜单树
  • 遍历 DOM 树

函数其它

匿名函数

匿名函数:没有名字的函数

匿名函数如何使用:

将匿名函数赋值给一个变量,这样就可以通过变量进行调用
匿名函数自调用

关于自执行函数(匿名函数自调用)的作用:防止全局变量污染。

自调用函数

匿名函数不能通过直接调用来执行,因此可以通过匿名函数的自调用的方式来执行

(function () {
  alert(123);
})();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值