Appearance
Function 函数
需要理解的概念有:
- 函数的 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. 函数本身的属性
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)
我们发现其结果就是 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)
输出的结果为
[ 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]
重点
- 这是一个不可访问的内部属性 无法通过
x.[[Class]]
去获取到这个属性 - 任何类型的变量都存在(基础数据类型访问 toString 的时候会自动转换成相对应的包装类)