饥人谷技术博客

JS 核心之函数

2019-04-14  本文已影响31人  养乐多__

函数是一段可以反复调用的代码块。
函数还能接受输入的参数,不同的参数会返回不同的值。

一、Function 对象

  1. 构造函数
    Function 构造函数可以创建一个新的 Function 对象,它的语法:
new Function (arg1, arg2, ...argN, functionBody)

用 Function 创建函数时加不加 new 效果是一样的:

var f = new Function('a', 'b', 'return a+b')
var f = Function('a', 'b', 'return a+b')
// f(1,2)    3
  1. 字符串之间加变量
var n = 1
var f = new Function('x','y','return x+' + n + '+y')
// f(1,2)   4
  1. functionFunction 的区别
    function 是关键字,与 var 用法相似,function 用来声明一个函数。
    Function 是全局对象,window.Function 用法与 window.Object 相似,也可以用来声明函数。

二、函数的声明

函数有五种声明方式。

  1. 具名函数
function f(x, y){
  return x+y
}

函数可以没有参数,但必须有返回值,不写的话则自动 return undefined.

  1. 匿名函数
 var f
 f = function(x, y){
     return x+y
 }

单独声明一个匿名函数会报错,需把它赋给一个变量才可以。

  1. 具名函数赋值
 var f
 f = function f2(x, y){ return x+y }
 console.log(f2) // Uncaught ReferenceError: f2 is not defined

注意:打印 f2 时报错。采用表达式声明函数时,function 命令后面若加上函数名,该函数名只在函数体内部有效,在函数体外部无效。

  1. window.Function
 var f = new Function('x','y','return x+y')
  1. 箭头函数
 var f = (x, y) => { return x+y }
 var sum = (x, y) => x+y
 var n2 = n => n*n

箭头函数没有函数名。
如果只有一个参数,可以省略()
如果函数体只有一句话,可以同时省略{}return,不能只省略一个。

三、函数的属性和方法

1. name 属性
function f1() {}
f1.name // "f1"
var f2 = function () {};
f2.name // "f2"
var f3 = function f4() {};
f3.name // 'f4'
f4.name // 报错
 var f5 = new Function('x','y','return x+y')
 f5.name // "anonymous"
2. length 属性

函数的 length 属性返回函数定义时的参数个数。无论调用时输入了多少个参数,length 属性始终不变。

function f(x, y) {}
f.length // 2
3. call() 方法

f.call() 方法用来调用函数,即执行这个函数的函数体。f只是代表这个函数,并不能执行它。

f.call(asThis, input1,input2)

其中 asThis 会被当做 this[input1,input2] 会被当做 arguments.

function f(x,y){ return x+y }
f.call(undefined, 1,2) // 3

四、函数其他知识点

1. 参数 thisarguments

call() 的第一个参数可以用 this 得到;
call() 的后面的参数可以用 arguments 得到。

  1. this
    普通模式下,如果 thisundefined,浏览器会自动把 this 变成window.
    严格模式下,this 传的参数的值不会改变。
    this
  1. arguments
2. call stack 调用栈
function a() { console.log('a1'); b.call(); console.log('a2'); return 'a' }
function b() { console.log('b1'); c.call(); console.log('b2'); return 'b' }
function c() { console.log('c'); return 'c' }
a.call()
console.log('end')
上面代码段调用栈的过程图解如下: call stack 调用栈
function sum(n){
  if(n === 1) { return 1 }
  else { return n + sum.call(undefined, n-1) }
}
sum.call(undefined, 3) // 6
// sum(3) = 3 + sum(2),sum(2) = 2 + sum(1),sum(1) = 1

n 不可以无限大,超过栈内存就会发生溢出 stack overflow.

3. 作用域
  1. 定义:作用域(scope)指变量存在的范围。
    全局作用域:变量在整个程序中一直存在,所有地方都可以读取;
    函数作用域:变量只在函数内部存在。
    函数外部声明的变量是“全局变量”(global variable),它可以在函数内部读取。
    在函数内部定义的变量,为“局部变量”(local variable),外部无法读取。
  1. 变量提升
    函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。
var a = 1
function f1(){
    f2.call()
    console.log(a)  // 断点 1
    var a = 2
    function f2(){
        var a = 3
        console.log(a)  // 断点 2
    }
}
f1.call()
console.log(a)

问:运行到断点1时,打印出 a 的值为?
答:由于变量提升,正确答案是 undefined.
技巧:看到题目后,第一件事是先做变量提升。

// 变量提升后
var a = 1
function f1(){
    var a  // f1 的变量提升
    function f2(){
        var a  // f2 的变量提升
        a = 3
        console.log(a)  // 断点 2
    }
    f2.call()
    console.log(a)  // 断点 1
    a = 2
}
f1.call()
console.log(a)
var a = 1
function f1(){
    console.log(a)
    var a = 2
    f4.call()
}
function f4(){
    console.log(a)  // 断点 3
}
f1.call()
console.log(a)

问:断点 3 处 函数 f4 打印出 a 的结果是?
答:a = 1。函数 f4 的变量只在它自己的作用域或它的父作用域里有效。

var a = 1
function f1(){
    console.log(a)
    var a = 2
    f4.call()
}
function f4(){
    console.log(a)  // 断点 4
}
a = 2
f1.call()
console.log(a)

问:断点 3 处 函数 f4 打印出 a 的结果是?
答:a = 2。因为函数的执行在语句a = 2之后发生。

4. 闭包

定义:如果一个函数,使用了它范围外的变量,那么(这个函数+这个变量)就叫做闭包。下面的一段代码就是闭包:

var a = 1
function f(){
  console.log(a)
}

五、相关知识

    • console.log() 默认打印出字符串,即使在 Chrome 中输出的结果不一定带双引号;
    • console.log() 返回 undefined,它的返回类型和打印出的结果没有 任何关系。源代码:
console.log = function(a){
  alert(a)
  return undefined
}
  1. window.eval():将字符串当作语句执行
eval('1+1') // 2
eval('1+"1"') // "11"

函数的调用过程即 “eval 函数体(字符串)”的过程。

上一篇下一篇

猜你喜欢

热点阅读