JS-一定要了解的function函数

234 阅读6分钟

这里只讨论function(){}类型的函数

1. 在js中,function函数是什么

  1. 是对象,一种特殊的对象,可以通过在后面添加括号来调用

  2. 有三个隐含参数:

    1. name:函数的名字。如果是匿名函数,那name的值就是anonymous;

    在实际环境中打印匿名函数的name,会输出''

    1. length:函数形参的个数

    2. code:函数逻辑的代码,以字符串的形式储存在内存中。 当执行到一个函数的调用语句时,V8便会从函数对象中取出code的属性值,也就是函数代码,然后再解释执行这段函数的代码

    在实际环境中打印函数的code,会输出undefined

2. 形参、实参,分别是什么意思

  1. 形参就是在声明函数的时候,所填入的参数。该参数会在函数实际调用的时候,被赋予真正的值。可以理解为形参就是占位用的,形式参数
//此处声明了一个函数foo
//a, b 两个就是这个函数的形参
function foo( a, b ){
    return a + b;
}
  1. 实参是在函数调用的时候,所填入的参数,此处填入的参数具有真实值,所以被叫做实参--实际参数;

在下面的例子中,我们可以知道

  • 形参和实参的名字可以不一样
  • 实参可以为变量,也可以为常量
//此处调用了上面声明的foo,并且填入了两个实参
const param1 = 12;
foo(para1, 23);

3. 如何获取形参,或者实参的个数

  1. 形参的个数可以通过函数的名字来访问
//下面,变量len的值就是函数的形参个数了
const len = foo.length;
console.log(len);
  1. 实参的个数,可以通过在函数内部访问arguments来获取
function foo( a, b ){
    //在函数foo被调用的时候,此处就在控制台打印了实参的个数
    console.log('length:', arguments.length); 
    return a + b;
}

4. 匿名函数和非匿名函数

  1. 下面的例子中,foo就是非匿名函数,也就是又名的函数,又叫具名函数。
  2. bar等式右边就声明了一个匿名函数,然后将这个匿名函数赋给了bar这个变量,之后bar所指向的就是该函数,最后得到了一个具名函数。即bar(), 就是调用该函数
  3. 第三个例子,直接获取匿名函数的name,得到了一个空字符串''
function foo( a, b ){ 
    return a + b;
}
console.log(foo.name);	//'foo'

const bar = function(){
  return a + b;
}
console.log(bar.name);	//'bar'

const name_3 = (function(){
})().name;
console.log(name_3);	//''

5. 匿名函数的缺点

  1. 在非匿名函数内部调用自身,可以直接引用函数的名字。
  2. 而匿名函数调用自身的时候,就没有名字可以引用了。

在之前的javascript中,只能使用已经过期的arguments.callee引用自身

6. 剩余参数、默认参数、结构赋值是什么意思

  1. 剩余参数可以实现定义数量不定的形参
function sum(...theArgs) {
  return theArgs.reduce((previous, current) => {
    return previous + current;
  });
}

console.log(sum(1, 2, 3));
// expected output: 6

console.log(sum(1, 2, 3, 4));
// expected output: 10
  1. 默认参数,当我们声明一个函数的时候,填入了两个形参,当用户没有传入其中一个参数的时候,我们要赋予其默认值
function foo( a, b = 0 ){
    return a + b;
}

foo(12);	//12
foo(12,11);	//23
  1. 结构赋值。我们在声明函数的时候,形参可能是一个对象,或者是数组,我们想快速的获取对象当中某个字段的值,这个时候我们可以用结构赋值
function foo( { name } ){
    console.log(name);
}

const Bob = {
  name:'bob name';
}

foo(Bob);		//'bob name'

还可以嵌套解构

function foo({ name, brithDate: { time } }) {
    console.log('name: ', name);
    console.log('day: ', time);
}

const Bob = {
    name: 'bob name',
    brithDate: {
        day: '2021-09-09',
        time: '20:00'
    }
}

foo(Bob);
//name:  bob name
//day:  20:00

7. 我们在函数的执行的时候,是否可以改变形参的值呢?

函数中是否包含剩余参数、默认参数、结构赋值,决定了arguments对象是否会跟踪参数的值

  1. 在严格模式下,剩余参数默认参数解构赋值参数的存在不会改变 arguments对象的行为,但是在非严格模式下就有所不同了。
  2. 当非严格模式中的函数
function func(a) {   
	arguments[0] = 99;   // 更新了arguments[0] 同样更新了a   
	console.log(a); 
} 
func(10); // 99 

function func(a) {   
	a = 99;              // 更新了a 同样更新了arguments[0]  
	console.log(arguments[0]); 
} 
func(10); // 99


const foo = function(n){
	if(n===1){
		return;
	}
	console.log(n);
	foo(n-1);
}
  • 包含剩余参数默认参数解构赋值,那么arguments对象中的值不会跟踪参数的值(反之亦然)。相反, arguments反映了调用时提供的参数:
function func(a = 55) {   
    arguments[0] = 99; // updating arguments[0] does not also update a            
    console.log(a); 
} 
func(10); // 10

function func(a = 55) {   
    a = 99; // updating a does not also update arguments[0]   
    console.log(arguments[0]); 
} 
func(10); // 10

8. 文档中的特别要求

arguments不允许被泄露。可能是因为里面有涉及到代码安全的信息

  1. 下面的例子中,返回arguments,在arguments上面调用slice,将arguments包含在返回函数的闭包中,都算泄露

为什么在arguments上面调用slice也不行呢?

  • 对参数使用slice会阻止某些JavaScript引擎中的优化 (比如 V8 - 更多信息)。如果你关心性能,尝试通过遍历arguments对象来构造一个新的数组。另一种方法是使用被忽视的Array构造函数作为一个函数:
  • 估计有泄漏的风险,所以不建议
function leaksArguments1() {
    return arguments;
}

function leaksArguments2() {
    var args = [].slice.call(arguments);
}

function leaksArguments3() {
    var a = arguments;
    return function() {
        return a;
    };
}
  1. 创建一个新的数组不算是泄露
function doesntLeakArguments() {
    //.length is just an integer, this doesn't leak
    //the arguments object itself
    var args = new Array(arguments.length);
    for(var i = 0; i < args.length; ++i) {
        //i is always valid index in the arguments object
        args[i] = arguments[i];
    }
    return args;
}

结尾小技巧:快速创建html列表的方法

定义创建HTML列表的方法

这个例子定义了一个函数通过一个字符串来创建HTML列表。这个函数唯一正式声明了的参数是一个字符。当该参数为 "u" 时,创建一个无序列表 (项目列表);当该参数为 "o" 时,则创建一个有序列表 (编号列表)。该函数定义如下:

function list(type) {   
    var result = "<" + type + "l><li>";   
    var args = Array.prototype.slice.call(arguments, 1);   
    result += args.join("</li><li>");   
    result += "</li></" + type + "l>"; // end list   return result;
}

你可以传递任意数量的参数到该函数,并将每个参数作为一个项添加到指定类型的列表中。例如:

var listHTML = list("u", "One", "Two", "Three"); 
/* listHTML is: "<ul><li>One</li><li>Two</li><li>Three</li></ul>" */

总结:

  1. 在js中,function函数是什么?

    • 函数就是一个特殊的对象
  2. 形参,实参分别是什么意思?

    • 一个声明函数时期的函数,一个调用函数时期的函数
  3. 如何获取形参或者实参的个数?

  4. 匿名函数和非匿名函数表示什么意思?

  5. 匿名函数的缺点?

    • 无法调用自身
  6. 剩余参数、默认参数、结构赋值表示什么意思?

    • 第一个是功能性的优化,第二,第三个是写法上的简化
  7. 在函数执行的时候,是否可以改变形参的值?

    • 非严格模式下,没有剩余参数,默认参数,结构赋值的话,可以改变
  8. 文档中特别要求,谨防arguments泄露

    • 可能是基于安全考虑吧,我也不太懂
  9. 快速创建html列表。原始html开发中,相同的功能的代码。包装成函数特别有效

    • 思想很重要