ES 函数基础 —— 属性与方法

published

ES 中的函数也是对象,因此也有属性和方法。

属性

name [ES2015]

函数的 name 属性,返回该函数的函数名。

这个属性早就被浏览器广泛支持,但是直到 ES2015,才被写入标准。

function foo() {}
foo.name // "foo"

如果一个将匿名函数赋值给一个变量:

  • ES5 的 name 属性,返回空字符串
  • ES2015 的 name 属性返回实际的函数名
const foo = function () {}

// ES5
foo.name // ""

// ES2015
foo.name // "foo"

如果将一个有函数名的函数赋值给一个变量,ES5 和 ES2015 的 name 属性都会返回这个函数本来的名字。

const foo = function bar() {}

// ES5
foo.name  // "bar"

// ES2015
foo.name  // "bar"

Function 构造函数返回的函数实例,其 name 属性的值为 'anonymous'

(new Function).name  // "anonymous"

Function.prototype.bind() 返回的函数, name 属性值会加上 'bound' 前缀。

function foo() {};

foo.bind({}).name  // "bound foo"

(function(){}).bind({}).name  // "bound "

length

length 属性表示函数预期接受的参数的个数。

function sayHi() {
}

function sayName(name) {
}

function sum(num1, num2) {
}

console.log(sayHi.length);      // 0
console.log(sayName.length);    // 1
console.log(sum.length);        // 2

方法

apply() / call()

apply() 接受两个参数:

  1. 函数将要运行的作用域
  2. 函数的参数数组(也可以是 arguments 对象)
function sum(num1, num2) {
  return num1 + num2
}

function callSum1(num1, num2) {
  return sum.apply(this, arguments) // 传入 arguments 对象
}

function callSum2(num1, num2) {
  return sum.apply(this, [num1, num2]) // 传入数组
}

console.log(callSum1(10, 10))
console.log(callSum2(10, 10))

在上面的代码中, callSum1() 在执行 sum() 函数时传入了 this(因为是在全局作用域中调用的,所以传入的就是 global 对象)和 arguments 对象。而 callSum2() 同样也调用了 sum() 函数,但它传入的则是 this 和一个参数数组。这两个函数都会正常执行并返回正确的结果。 call() 除了接收的参数不同,其他都相同。call() 的第一个参数还是 this,但是其他参数都直接传递,比如:

function sum(num1, num2) {
  return num1 + num2
}

function callSum(num1, num2) {
  return sum.call(this, num1, num2)
}

console.log(callSum(10, 10))

结果与使用 apply() 没有什么不同。

使用 apply() 还是 call()

使用哪个方法,取决于你传递参数的方式:

  • 如果打算直接传入 arguments 对象或数组,用 apply()
  • 否则,就用 call()
  • 如果不向函数传递参数,用哪个都无所谓

apply() / call() 存在的意义

apply() / call() 仅仅是用来传传参数,实在是大材小用。它真正存在的意义是更改函数的执行环境。

function sayColor() {
  console.log(this.color)
}

global.color = 'red'
const o = { color: 'blue' }

sayColor()             // red
sayColor.call(global)  // red
sayColor.call(o)       // blue

sayColor() 是作为全局函数定义的。以上代码使用了三种调用方式:

  • 在全局作用域中调用时,this 会指向全局对象,输出 red
  • sayColor.call(global) 显式地在全局作用域中调用函数,和前一种情况一样。
  • sayColor.call(o) 使函数体内的 this 指向了 o,输出 blue apply() / call() 的好处多多,其中一点 —— 对象不需要与方法有任何耦合关系。

之前,是需要将 sayColor() 函数放到对象 o 中,然后再通过 o 来调用它的:

function sayColor() {
  console.log(this.color)
}

const o = { color: 'blue' }
o.sayColor = sayColor

o.sayColor()  // blue

bind()

bind() 的作用是函数绑定 —— 创建一个保持了执行环境的函数。

被绑定的函数与普通函数相比有更多开销,必要时再用。

bind() 会创建一个函数的实例,并将传给 bind() 的参数绑定到这个实例的 this 上,比如:

function sayColor() {
  console.log(this.color)
}

global.color = 'red'
const o = { color: 'blue' }

const objectSayColor = sayColor.bind(o)
objectSayColor()  // blue

上面的代码中,sayColor() 调用其自身的 bind() 方法,并传入对象 o,这样创建出了 objectSayColor() 函数。 objectSayColor() 函数的 this 绑定了 o 。所以,即便是在全局作用域中调用这个函数,也会显示 blue

实现细节

function bind(fn, context) {
  return function () {
    return fn.apply(context, arguments)
  }
}

bind() 中创建了一个闭包,闭包使用 apply() 调用传入的函数,并给 apply() 传递 contextarguments

toLocaleString() / toString()

每个函数继承的 toLocaleString()toString() 方法始终都返回函数的代码。返回的格式因引擎而异:

  • 有的返回函数源代码
  • 有的返回函数源代码的内部表示(由解析器删除了注释并对某些代码做了改动后的代码)

由于存在差异,所以也没法根据这两个方法返回的结果来实现什么重要的功能。不过这些信息在调试的时候还是挺有用的。