JavaScript语言精粹学习笔记之函数(一)

函数

函数包含一组语句,用于代码复用、信息隐藏和组合调用。函数用于指定对象的行为。

函数对象

在JavaScript中,函数也是对象。
因为函数也是对象,所以函数可以存放在变量,对象和数组中;函数可以当作参数传递给其他函数,函数也可以返回函数;函数也可以拥有方法。

函数的两个附加隐藏属性:函数的上下文和实现函数行为的代码。

函数的上下文:每当一个函数被调用是,都会为该函数创建一个新的上下文。函数对该上下文的变量是可见的,函数可以直接访问到。(函数要通过连接访问外部上下文的变量)

函数字面量

  1. 保留字 function
  2. 函数名(可以被省略),函数可以递归调用自己
  3. 参数列表。每个参数用逗号隔开;参数是被定义在函数内部的变量;参数不会像普通变量一样初始化为undefined,而是在函数调用时,初始化为实际提供的参数的值
  4. 函数体(花括号中的语句),在函数被调用的时候执行。

声明一个函数

var f = function(a, b) {
    
}
function f(a, b) {
    
}

// 省略函数名 匿名函数 会立即执行
(function(){

})

调用

调用一个函数将暂停当前函数的执行,传递控制权和参数给新函数。除了声明时定义的参数,每个函数接收两个附加参数:this和arguments。

arguments(实际参数):当实际参数(arguments)与形式参数(parameters)个数不匹配时,不会导致错误。超出的部分将会被忽略,缺失的部分替换为undefined。由于JavaScript是弱语言,对参数的类型不会进行类型检查,函数能够接收任何类型的参数。

function f(a, b) {
    console.log(a);
    console.log(b)
}

// 超出
f(1,2,3);// 1 2  不报错 

// 少了
f(1);// 1 undefined
  1. 方法调用模式

    当一个函数被保存为一个对象的属性时,称它为一个方法。当方法被调用时,this被绑定到该对象(引用该对象)。

  2. 函数调用模式
    当函数不通过对象调用,而是直接调用时,this绑定全局。

function f(){
    console.log(this.a);
}
var a = '全局'
var obj = {
    a: 'obj',
    f
}

f();	// 全局 函数调用,this绑定全局
obj.f() // obj 对象调用函数f,this绑定对象(obj)
  1. 构造器调用模式
    如果在一个函数前面带上一个new来调用,那么函数会创建一个空对象,然后再执行函数体,最后将返回这个对象。this绑定这个对象。
function F() {

}
var obj = new F();
console.log(obj);// f {} 

function FUNC(a,b) {
    this.a = a;
    this.b = b;
}
var o = new FUNC('a','b');
// this绑定对象 执行函数体 返回了该对象
// 这种函数成为构造器函数,约定以大写格式命名
console.log(o); // func { a: 'a', b: 'b' }
  1. Apply调用模式
    因为函数也是对象,所以函数也能拥有方法。apply方法让我们构建一个参数数组并用其去调用函数。第一个参数是被绑定给this的值,第二个参数是参数数组
function add(a, b) {
    return a + b;
}
var array = [3, 4];
var sum = add.apply(null,array)
console.log(sum);// 7

// 如果改为
function add(a, b) {
    return this.a + this.b;
}

var sum = add.apply(null,array)
console.log(sum);
// 此时输出的值是NaN 因为this为空 this.a和this.b都是为undefined,undefined + undefined 返回NaN

// 可以绑定为其它对象
var obj = {
    a: 10,
    b: 20
}
var sum = add.apply(obj,array)
console.log(sum);// 30 此时this绑定ojb

参数

函数被调用时,有一个默认参数arguments(类似数组的对象,先当成数组理解)。函数通过arguments访问所有传递给他的参数(包括那些未定义的多余参数)。

function f (a, b, c) {
    console.log(arguments);
}


f(1,2,3,4);// [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4 } 全部接收

这使得可以编写无需指定参数个数的函数

function sum() {
    var sum = 0;
    for(var i = 0; i < arguments.length; i++) {
        sum += arguments[i];
    }
    return sum;
}

console.log(sum(1,2,3,4,5,6,7,8,9));// 45

返回

调用的函数结束或return提前结束时,函数会把控制权还给调用该函数的程序部分。
函数总会返回一个值,如果未指定,默认返回undefined。
new调用一个函数如果返回的不是一个对象,则会返回this(新的对象)

function f() {
    return o
}
var o = {
    a: 'a'
}
var obj = new f();
console.log(obj);// { a: 'a' } 返回的是一个对象,将该对象返回

function fun() {
    return '123'
}
var o = new fun();
console.log(o);// f {} 返回的不是对象,返回this(新创建的空对象)

异常

异常是干扰程序的正常流程的非正常的事故,但是并不是预料之外的个事故。JavaScript可以检查出这样的事故,并抛出一个异常。

throw:如果检查出异常,可以用throw抛出一个异常对象exception,该对象包含可识别异常类型的name属性和一个描述性的message属性。

if(检查异常的判断条件) {
	// 抛出异常
	throw {
		name: 'TypeError',
		message: 'describe'
	}
}

try和catch:被抛出的异常对象将会传递到一个try语句的catch从句

try {
    // 捕获异常
} catch (error) {
    // 处理异常,也可不处理
} 

给类型增加方法

JavaScript允许给语言的基本类型增加方法。
举个栗子:
使用Number.prototype给所有number增加一个取整方法integer。

Number.method('integer', function() {
    return Math[this < 0 ? 'ceiling' : 'floor'](this);
})

递归

递归函数会直接或间接的调用自己的函数。
汉诺塔问题(hanoi)
游戏规则:将A柱上的环移动到C柱上,并且只能小环放在大环上,大环不能放在小环上。
在这里插入图片描述

// n 为环的个数
// 首先n = 1时,一步到位,从A放到C上
// n = 2 时, 只需要将将小环放在B上(A -> B),再将大环放在C上(A -> C),最后将小环(B -> C)
// n = 3 时,将上面两个环通过C放到B上,将最大的环放到C上,最后将上面两个环通过A放到C上
// 如此递推下去,环的个数为n时,将上面n-1个环通过C放到B上,将最大的环放到C上,最后将上面n-1个环通过A放到C上


// 将n个环从A通过B放到C上
var hanoi_path = (n, A, B, C) => {
    // 结束条件,递归函数调用必须有结束条件,否则函数会一直执行不会结束
    // n = 1 时,一部到位, 不再递归
    if(n == 1) {
        console.log(A + '->' + C)
    }else{
        // 将上面n-1个环从A通过C放到B上
        hanoi_path(n-1, A, C, B);
        // 将最大的环从A放到C上
        console.log(A + '->' + C)
        // 将上面n-1个环从B通过A放到C上
        hanoi_path(n-1, B, A ,C)
    }
}
hanoi_path(3, 'A', 'B', 'C')
// A->C
// A->B
// C->B
// A->C
// B->A
// B->C
// A->C
// 感兴趣的朋友可以自己试一下更高的汉诺塔,这里不再展示

递归函数的调用不会结束外部函数,所以当递归深度过深时,会占用大量的空间,可能会导致空间的不足

作用域

在变成语言中,作用域控制着变量与参数的可见性以及生名周期。

var a = 1, b = 2;

var fun = () => {
    // 对函数内部定义的变量可见 及花括号内部
    var a = 3, b =4;
    console.log(a, b)
}
fun();// 3 4
// 函数结束后释放该作用域,内部变量声明周期结束
console.log(a, b)// 1 2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值