Javascript 两种 function 定义的本质区别

JS函数定义与提升
本文探讨JavaScript中两种常见函数定义方式的区别,分析函数声明与赋值给变量时的行为差异,并解释变量与函数声明的提升机制。

两种常见的function定义:

1⃣️

var func = function(){
...
}

2⃣️

function func(){
...
}
两种定义方式在表现形式上的不同:

第一种var方式定义的函数,不能先调用,后声明;只能先声明,后调用。
第二种function方式定义函数可以先调用,后声明。

(function($) {

    var a = 1;
    var b = 2;


    func1();
    var func1 = function () {
        alert("func1");
    }


    func2();
    function func2() {
        alert("func2");
    }

    $(document).ready(function () {
        alert("ready");
    });

}($));

运行结果:
这里写图片描述


(function($) {

    var a = 1;
    var b = 2;


    var func1 = function () {
        alert("func1");
    }
    func1();


    func2();
    function func2() {
        alert("func2");
    }

    $(document).ready(function () {
        alert("ready");
    });

}($));
运行结果:

这里写图片描述


两种定义方式效果不同的本质原因:声明“被提前”
变量声明“被提前”
(function() {
  // Output: undefined
  console.log(declaredLater);

  var declaredLater = "Now it's defined!";

  // Output: "Now it's defined!"
  console.log(declaredLater);
})();

这其实是 JavaScript 解析器搞的鬼,解析器将当前作用域内声明的所有变量和函数都会放到作用域的开始处,但是,只有变量的声明被提前到作用域的开始处了,而赋值操作被保留在原处。上述代码对于解析器来说其实是如下这个样子滴:

(function() {
  var declaredLater; //声明被提前到作用域开始处了!

  // Output: undefined
  console.log(declaredLater);

  declaredLater = "Now it's defined!"; //赋值操作还在原地!

  // Output: "Now it's defined!"
  console.log(declaredLater);
})();

变量和函数经过“被提前”之后,declaredLater 变量其实就被放在了调用函数的前面。根据 JavaScript 语法的定义,已声明而未被赋值的变量会被自动赋值为 undefined 。所以,第一次打印 declaredLater 变量的值就是 undefined,后面我们对 declaredLater 变量进行了赋值操作,所以,第二次再打印变量就会输出Now it’s defined!

再来看一个更有意思的栗子:

var name = "haha";

(function () {
    // Output: "Original name was undefined"
    console.log("Original name was " + name);

    var name = "test";

    // Outputs: "New name is test"
    console.log("New name is " + name);
})();

我们本来希望在第一次打印 name 变量时能够输出全局范围内定义的 name 变量,然后再在函数中定义一个局部 name 变量覆盖全局变量,最后输出局部变量的值。可是第一次输出的结果和我们的预期完全不一致,原因就是我们定义的局部变量在其作用域内被“提前”了,也就是变成了如下形式:

var name = "haha";

(function () {
    var name;  //注意:name 变量被提前了!

    // Outputs: "Original name was undefined"
    console.log("Original name was " + name);

    name = "test";

    // Outputs: "New name is test"
    console.log("New name is " + name);
})();

函数声明“被提前”

上面讲述的是变量声明,接下来我们回归正题,说说函数。
函数的“被提前”还要分两种情况:
第一种:函数声明;
第二种:函数作为值赋值给变量;

函数声明:

 func2();//输出“func2”

 function func2() {
        alert("func2");
    }

如上所示,JavaScript 解释器允许你在函数声明之前使用,也就是说,函数声明并不仅仅是函数名“被提前”了,整个函数的定义也“被提前”了!所以上述代码能够正确执行。

函数作为值赋值给变量:

func1();//浏览器报错,提示“func1 is not a function”

var func1 = function () {
        alert("func1");
    }

func1变量“被提前”了,但是他的赋值(也就是函数主体实现)并没有被提前,从这一点上来说,和前面我们所讲的变量“被提前”是完全一致的;并且,由于“被提前”的变量的默认值是 undefined ,所以报的错误属于“类型不匹配”,因为 undefined 不是函数,当然不能被调用,如下图:

(function($) {

    var a = 1;
    var b = 2;

    alert(func1);

    var func1 = function () {
        alert("func1");
    }
    func1();


    func2();
    function func2() {
        alert("func2");
    }

    $(document).ready(function () {
        alert("ready");
    });

}($));

这里写图片描述

总结:
1⃣️变量的声明被提前到作用域顶部,赋值保留在原地;
2⃣️函数声明整个“被提前”;
3⃣️函数作为值赋给变量时只有变量“被提前”了,函数没有“被提前”;

### JavaScript 数据类型的定义与分类 JavaScript 是一种动态弱类型的语言,其数据类型分为 **基本数据类型** 和 **引用数据类型** 两大类。 #### 基本数据类型 基本数据类型是按值访问的数据类型,即可以直接操作存储在变量中的实际值。以下是常见的基本数据类型及其定义: 1. **String(字符串)** 字符串是由零个或多个字符组成的序列,用于表示文本信息[^3]。 2. **Number(数值)** 数值类型可以表示整数和浮点数,在 JavaScript 中统一为 `Number` 类型。可以通过拼接空字符串或将数值转换为字符串的方式实现类型转换[^1]。 3. **Boolean(布尔值)** 布尔值仅有两个可能的值:`true` 或 `false`,通常用于逻辑判断。 4. **Null(空值)** 表示一个空对象指针,尽管它的 `typeof` 返回的是 `'object'`,但实际上它是一个独立的基本数据类型[^2]。 5. **Undefined(未定义)** 当声明了一个变量但尚未赋值时,默认值为 `undefined`;或者当函数无返回值时也会默认返回 `undefined`。 6. **Symbol(符号)** Symbol 是 ES6 新增的一种原始数据类型,主要用于创建唯一的标识符[^1]。 7. **BigInt(大整数)** BigInt 是另一种新增的数据类型,允许表示大于 `Number.MAX_SAFE_INTEGER` 的安全范围之外的大整数[^1]。 #### 引用数据类型 引用数据类型是指向内存中某个位置的对象引用,而不是直接保存值本身。主要包含以下几种: 1. **Object(对象)** 对象是一种复杂的数据结构,由键值对组成,能够封装多种属性和方法。例如普通的对象字面量 `{key: value}`、数组 `[item1, item2]` 等都属于对象范畴[^3]。 2. **Array(数组)** 虽然语法上看起来像列表,但在底层实际上也是对象的一种特殊形式,具有可变长度以及内置的方法集合[^4]。 3. **Function函数)** 函数本质上也是一种特殊的对象,它可以被调用并执行特定的操作[^3]。 4. **Date(日期)** Date 对象用于处理时间戳及相关运算,提供了丰富的 API 来获取和设置年月日及时分秒等信息[^3]。 5. **RegExp(正则表达式)** 正则表达式对象用于匹配字符串模式,支持复杂的查找替换功能[^3]。 --- ### 示例代码展示不同类型的应用场景 ```javascript // 基本数据类型 let str = "Hello"; // String console.log(typeof str); // 输出 string let num = 42; // Number console.log(typeof num); // 输出 number let bool = true; // Boolean console.log(typeof bool); // 输出 boolean let nul = null; // Null console.log(typeof nul); // 输出 object (历史遗留问题) let undf; // Undefined console.log(typeof undf); // 输出 undefined let sym = Symbol("unique"); // Symbol console.log(typeof sym); // 输出 symbol let bigInt = 123n; // BigInt console.log(typeof bigInt); // 输出 bigint // 引用数据类型 let obj = { name: "Alice", age: 20 }; // Object console.log(typeof obj); // 输出 object let arr = [1, 2, 3]; // Array console.log(typeof arr); // 输出 object (注意:Array 实际上继承自 Object) function greet() { console.log("Hi!"); } // Function console.log(typeof greet); // 输出 function let date = new Date(); // Date console.log(typeof date); // 输出 object let regex = /abc/; // RegExp console.log(typeof regex); // 输出 object ``` --- ### 总结 JavaScript 提供了七种基本数据类型(String、Number、Boolean、Null、Undefined、Symbol、BigInt),以及若干种引用数据类型(Object 及其派生)。每种数据类型都有独特的用途和行为特性,理解它们的区别对于编写高效可靠的程序至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值