05-参数默认值、递归、预编译、暗示全局变量

本文详细介绍了JavaScript中的参数默认值,包括设置方式、问题及兼容性解决方案。接着讨论了递归的概念,核心要素及注意事项。然后,文章探讨了预编译的初步知识,包括JS引擎的解析步骤和函数、变量提升的原理。此外,还讲解了如何暗示全局变量,以及在不同场景下的行为。最后,提供了预编译的进一步理解及作业题供读者实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、参数默认值

1、指定默认值的方式,

在形参后加等号(=)即可

function test(a = 1, b = 2) {};

2、一些问题

1、第一个参数使用默认值,第二个参数使用实参传递过来的值,该怎么实现?

// 需求:a使用默认值,b使用实参传递过来的值
function test(a = 1, b) {
	
}
test(undefined, 2);  // 在第一个实参那里传递过去undefined就可以做到。

为什么会这样呢?我们再看一个例子

function test(a = 1, b = undefined) {
	console.log(a, b); // 结果为 1, 2
}
test(undefined, 2); 

得出结论:形参与实参都有值的话,尽量不去选择undefined的值,如果实参传递undefined,形参默认值不为undefined,则用形参默认值;如果实参传递的不是undefined,形参默认值是undefined,那么则选用实参传递的值。

所以我们例1可以通过实参传递undefined,形参有默认值这样子,实现第一个参数用默认值,第二个参数用实参。

2、形参默认值和实参的值都不是undefined,那么用实参的值

3、解决兼容性问题

因为参数默认值是es6之后才有的,所以如果是不支持es6的浏览器该怎么做呢?

核心都是使用arguments来判断传递的实参是不是undefined。以下展示两种简单合适的写法。

最好还是使用typeof来判断一个实参的值是不是undefined,直接判断false有可能会出问题,比如我就是想要0这个值,而不是默认值。

// 第一种,通过逻辑运算符来实现
// a的默认值是1,b的默认值是2
function test(a, b) {
  	// 这里最好使用typeof来判断一下,不然有可能我想要的是0,结果给我整成默认值了
    var a = arguments[0] || 1;
    var b = arguments[1] || 2;
    console.log(a, b);
}
test(undefined, 5);
// 第二种,通过三目运算符来实现
function test(a, b) {
  	// 使用三目运算符来判断传递过来的实参是不是undefined最合适
    var a = typeof(arguments[0]) !== 'undefined' ? arguments[0] : 1;
    var b = typeof(arguments[1]) !== 'undefined' ? arguments[1] : 2;
    console.log(a, b);
}
test(undefined, 5);

二、递归(函数自己调用自己)

1、递归两个核心要素

1、找到递归的规律

2、找到出口

2、递归的一些小案例

// 定义一个函数,从wp接收一个n,算出n的阶乘
var num = parseInt(window.prompt('请输入一个需要阶乘的n'));

function fact(n) {
  	// 出口
    if (n === 1) {
        return 1;
    }
  	// 规律
    return n * factorial(n - 1);
}
console.log(factorial(num));
// 定义一个函数,从wp接收一个n,算出斐波那契数列的第n位
var num = parseInt(window.prompt('请输入需要查找第几位斐波那契数:'));

function fib(n) {
  	// 出口
    if (n === 2 || n === 1) {
        return 1;
    }
  	// 规律
    return fib(n - 1) + fib(n - 2);
}
console.log(fib(num));

3、递归的问题

递归因为自己调用自己,所以函数一直在嵌套,所以性能、效率方面不是很好。所以递归慎用

三、预编译前奏

1、JS引擎解析代码步骤

        1、检查通篇的语法错误。如果某个地方语法错误,全篇都不执行。

        2、预编译的过程。

        3、解释一行,执行一行。

2、一些现象引起我们的思考

1、函数可以写在执行语句的后面。

test();  // 正常执行
function test() {};

2、变量声明如果在使用之前,变量值是undefined。

console.log(a);  // 打印undefined
var a = 10; 

所以我们得出结论:

函数声明整体提升,变量只有声明提升,赋值是不提升的。

我们之前强调,var a = 10;是有两个步骤的,第一步先声明变量a,第二步为a赋值10。提升只有第一步。

但是上面的结论太浅显了,我们抛出一个问题:

console.log(a);
function a(a) {
	var a = 10;
    var a = function() {};
}
var a = 2;
// 请问上面的console.log(a)打印什么?

很明显,只使用上面的知识我们解决不了问题。所以我们得继续深入学习预编译。但是现在我们先学习暗示全局变量

四、暗示全局变量(imply global variable)

1、在script标签里直接声明的变量

在script声明的变量中,我们可以使用var a = 1;,也可以不使用var关键字,如a = 1;这两种方式声明都是会在window上面挂载a变量

var a = 1;
b = 2;
console.log(window.a);   // 1
console.log(window.b);   // 2

2、在函数体里声明的变量

在函数体里声明的变量,如果使用var关键字声明,那就是该函数的局部变量,如果没有关键字,那就是全局变量了,挂载在window上面。不用关键字声明变量是暗示全局变量的一种体现。

function test() {
	var a = b = 1;
}
test();
console.log(a);   // 报错,因为a在test函数中,外面访问不到里面的变量
console.log(b);   // 打印1,因为b已经为全局变量了,挂载在window上面,所以可以访问
console.log(window.b); //打印1

五、预编译开始

我们直接开始讲步骤,然后看一道案例题。

1、创建AO对象(activation object,活跃对象,也叫函数上下文)

2、寻找函数中所有的形参与变量声明,然后添加到AO对象中,值为undefined

3、形参与实参相统一

4、寻找函数中所有的函数声明,注意是普通的函数声明function a(){}字面量函数声明不算var a = function(){},如果有与变量重名的,函数覆盖变量

5、开始一步一步执行代码

// 预编译案例
function test(a) {
    console.log(a); // function a(){},
    var a = 1;
    console.log(a); // 1

    function a() {};
    console.log(a); // 1
    var b = function() {};
    console.log(b); // function(){}
    function d(){};
  	console.log(d);  // function d(){};
}
test(2);

// 预编译过程
AO = {
  a: undefined, -> 2, -> function a(){}, 
  b: undefined, 
  d: function d(){},
}

练习一道题:

function test(a, b) {
    console.log(a); // 1
    c = 0;
    var c;
    a = 5;
    b = 6;
    console.log(b); // 6
    function b(){}
    function d(){}
    console.log(b); // 6
}
test(1);

// 编译过程
AO ={
	a: undefined -> 1 -> 5,
  b: undefined -> function b(){} -> 6,
  c: undefined -> 0,
  d: function d(){}
}

如果是在全局作用域下,我们的过程也是差不多的,只有没有形参了而已。

1、创建GO对象(global object,全局上下文)

2、寻找所有的变量声明,并且添加到GO中,值为undefined

3、寻找函数中所有的函数声明,注意是普通的函数声明function a(){}字面量函数声明不算var a = function(){},如果有与变量重名的,函数覆盖变量

4、一步一步执行代码

注意:GO就是window

案例:

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

console.log(a); // 1

// 预编译过程
GO = {
	a: undefined -> function a(){} -> 1,
}

练习一道题:

var b = 3;
console.log(a); // function a(){}

function a(a) {
    console.log(a); // function a(){}
    var a = 2;
    console.log(a); // 2

    function a() {}
    var b = 5; 
    console.log(b); // 5
}

a(1);

// 编译过程:
GO = {
	b: undefined -> 3,
  a: function a(){},
}
AO = {
	a: undefined -> 1 -> function a(){} -> 2,
  b: undefined -> 5,
}

六、作业题

第一题:

function test() {
    console.log(b); // undefined
    if (a) {
        var b = 2; // 不执行,以为a要拿到外面的a,是undefined
    }
    c = 3; // window.c = 3;
    console.log(c);	// 3;
}
var a;
test();
a = 1;
console.log(a); // 1

// 预编译过程
GO = {
	a: undefined -> 1,
  test: function test(){},
  c: 3
}
AO = {
	b: undefined,
}
// 最终结果 undefined 3 1

第二题

function test() {
    return a;
    a = 1;
    function a(){}
    var a = 2;
}
console.log(test());  // function a(){};

// 预编译过程
AO = {
	a: undefined -> function a(){},
}

第三题

function test() {
    a = 1;
    function a() {}
    var a = 2;
    return a;
}
console.log(test());  // 2

// 预编译过程
AO = {
	a: undefined -> function a(){} -> 1 -> 2,
}

第四题

a = 1;
function test(e) {
    function e(){}
    arguments[0] = 2; // 这一步会影响到e吗?应该是会的。前面好像有讲过
    console.log(e); // 2
    if(a) {
        var b = 3;
    }
    var c;
    a = 4;
    var a;
    console.log(b); // undefined
    f = 5;
    console.log(c); // undefined
    console.log(a); // 4
}
var a;
test(1);

// 预编译过程
GO = {
  a: undefined -> 1,
  f: 5
}
AO = {
  e: undefined -> 1 -> function e(){} -> 2,
  b: undefined,
  c: undefined,
  a: undefined -> 4,
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值