Skip to content

Function 函数

需要理解的概念有:

  1. 函数的 3 种创建方式(函数申明、函数表达式)。
  2. 没有重载。
  3. 函数的 内部属性 和 属性、方法。

函数的创建方式( 3 种 )

1. 函数字面量

js
var fun4 = function () {}

2. 申明函数

js
function fun1() {}
js
fun4() // 报错 Chrome

if (true) {
  function fun4() {
    alert("true")
  }
} else {
  function fun4() {
    alert("false")
  }
}
js
var fun4 = null

if (true) {
  fun4 = function () {
    alert("true")
  }
} else {
  fun4 = function () {
    alert("false")
  }
}
fun4() //true;

1.3 函数构造函数

js
var fun3 = new Function(["函数参数"], "函数体") //不推荐使用
1、申明函数与函数表达式的区别是 : 函数申明提升(即读取代码前会读取函数的申明,所以可以在函数申明前调用)

函数名

主要看一下几个题目

js
var func = 1

;(function func() {
  func = 2
  console.log(func)
})()

// 结果为什么?   func() { func = 2;console.log(func) }
js
func() // Uncaught TypeError: func is not a function
func1() // Uncaught TypeError: func1 is not a function
var func = function func1() {
  console.log(func1)
}

// 修改成
var func = function func1() {
  console.log(func1)
}
func() // 输出func1(){xxxx}
func1() // Uncaught TypeError: func1 is not a function

对于使用 function 申明的函数对于其名称, 如果在函数外使用 字面量重新赋值那么外部就无法访问了; 在函数体里面还是可以访问的,但是需要注意的是函数名变量是无法被修改的,只读

没有重载

跟 java 等有很大的区别,函数名相同,参数不同,调用函数时可以根据不同的参数调用不同的方法。但是 JS 中 相同函数名称的函数只能有一个

js
function fn1(arg1) {
  return arg1
}
function fn1(arg1, arg2) {
  return arg1 + "----" + arg2
}

fn1("name") //不是我们想象的调用第一个 返回name   而是返回 name----

函数的内部属性、属性、方法

  • arguments
    • arguments.callee
    • arguments.caller
    • arguments.length
  • this
  • length
  • prototype
  • proto

1. 函数的内部属性

入参 arguments

函数不会在意你传递了几个参数,或者传递的申明类型。

arguments 保存的是执行函数式传递的入参数据,是一个类数组

  • arguments.length 执行时入参的长度

  • arguments.callee 返回当前正在指向的函数 参考 54321 阶乘

  • arguments.caller 返回当前指向函数的函数名

js
var arguFun = function (arg1, arg2) {
  console.log(arg1) // 1
  console.log(arg2) // undefined
  console.log(arguments) // [1]
  console.log(arguments.length) // 1
  console.log(arguments.callee) // 返回当前正在指向的函数  此处即arguFun
  console.log(arguments.caller) // 1
}
function fn2() {
  arguFun(1) // 此时的 arguments.callee.caller === null
}
arguFun(1) //  此时
fn2(1) //  此时就可以访问到arguments.callee.caller == fn2
阶乘 -- 获取 54321 的值
js
var factorial = function (n) {
  if (n <= 1) {
    return 1
  } else {
    return n * arguments.callee(n - 1)
  }
}
console.log(factorial(5)) //120
js
var factorial = function (n) {
  if (n <= 1) {
    return 1
  } else {
    return n * factorial(n - 1)
  }
}
console.log(factorial(5)) //120
// 接受参数5 输出[1,2,3,4,5]
var get = function (n) {
  var arr = []
  return (function (n) {
    arr.unshift(n)
    if (n > 1) {
      arguments.callee(n - 1)
    }
    return arr
  })(n)
}
console.time("get")
get(10000) //[1,2,3,4,5]
console.timeEnd("get")
// get: 2.364990234375ms   get(5)
// get: 27.068115234375ms   get(10000)
// 不要使用arguments.callee
var get1 = function (n) {
  var arr = []
  return (function jiechen(n) {
    arr.unshift(n)
    if (n > 1) {
      jiechen(n - 1)
    }
    return arr
  })(n)
}
console.time("get1")
get1(10000) //[1,2,3,4,5]
console.timeEnd("get1")
// get: 0.514892578125ms   get(5)
// get: 13.31396484375ms   get(10000)

new.target

在函数的内部 如果当前函数是 new 命令调用,则 new.target 指向当前函数,否则为 undefined

js
var Dog = function (name) {
  if (!new.target) {
    return new Dog(...arguments)
  }
  this.name = name || "小黑"
}

var dog1 = Dog()

2. 函数本身的属性

image-20230612161410311

2.1. length

保存了函数定义时需要的参数数量

js
function fn1(arg1, arg2) {
  console.log(arg1 + arg2)
}
// 此时函数不会在意你传递了几个参数  或者1个  fn1(1),或者两个 fn1(1,2);
// 那我们如何知道函数定义的时候需要传递的参数数量  fun.length
console.log(fn1.length) // 2  函数定义时参数数量
console.log(fn1.name) // fn1
console.dir(fn1)

2.2. name

保存了函数的名称

2.3. prototype

保存函数对象原型链上的数据,如特殊的 constructor

2.4. 特殊属性 [[Prototype]]

保存了函数对象其构造函数(Function),其跟 prototype 不是一个对象

js
Function.prototype.mGetName = () => {
  return "Function.prototype.mGetName"
}

function func(funcp) {
  var funcName = 1
  function func1(i) {
    var funcPrams = funcName
    var func2 = function (...args) {
      console.log(funcPrams)
    }
    console.dir(func2)
  }
  func1()
}

func(2)

image-20230612163706277

我们发现其结果就是 Function

[[Prototype]] 和 prototype 的区别

  • [[Prototype]] 保存的是其构造函数(由什么创建的,函数对象那就是通过 Function 创建)
  • prototype 保存的是原型对象(用来创建对象的时候生成其原型链)
js
Function.prototype.FunctionGetName = () => {
  return "Function.prototype.mGetName"
}

var func = () => {}

func.prototype.funcGetName = () => {
  return "func.prototype.mGetName"
}

console.dir(func)

// func.prototype = { constructor : func (){} , funcGetName : () =>{} , }
// func.[[Prototype]] = Function { FunctionGetName : () =>{} , ...}

2.4. 特殊属性 [[Scope]]

保存了函数定义时函数作用域链数据,其本身是一个数组,最少有一个为 Global,如存在多个为保存作用域链上的闭包数据 Closure,如 [Closure , Global]

  • Closure 闭包
  • Global 全局作用域

如下述例子

js
function func(funcp) {
  var funcName = 1
  function func1(i) {
    var funcPrams = funcName
    var func2 = function (...args) {
      console.log(funcPrams)
    }
    console.dir(func2)
  }
  func1()
}

func(2)

输出的结果为

image-20230612163113560

[ Closure { funcPrams : 1} , Closure { funcName : 1} , Global {...}]

2.5. 不可见属性 [[Class]]

数据的内部属性[[Class]]是一个不可访问的字符串类型,保存了数据的类型,我们可以 通过 Object.prototype.toString()间接得到这个内部属性的值

js
console.log(Object.prototype.toString.call(1)) //[object Number]
console.log(Object.prototype.toString.call("string")) //[object String]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call(null)) //[object Null]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(Symbol())) //[object Symbol]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(new Date())) //[object Date]
console.log(Object.prototype.toString.call(/\d+/)) //[object RegExp]
console.log(Object.prototype.toString.call(fun1)) //[object Object]
console.log(Object.prototype.toString.call(function () {})) //[object Function]

重点

  1. 这是一个不可访问的内部属性 无法通过 x.[[Class]] 去获取到这个属性
  2. 任何类型的变量都存在(基础数据类型访问 toString 的时候会自动转换成相对应的包装类)