JavaScript/JQuery自执行函数

本文深入探讨了jQuery中自执行函数的两种写法,并详细解释了函数声明与函数表达式的区别,以及如何通过添加括号立即调用匿名函数。

自执行函数

JavaScript中任何库与框架设计的第一个要点就是解决命名空间与变量污染的问题。jQuery就是利用了JavaScript函数作用域的特性,采用自执行函数包裹了自身的方法来解决这个问题。从jQuery不同的版本中可以看出它的自执行函数有如下两种写法:

    // 写法一
    (function(window, factory) {
        factory(window)
    }(this, function() {
        return function() {
           //jQuery的调用
        }
    }));

    // 写法二
    (function(window, undefined) {
        var jQuery = function() {}
        // ...
        window.jQuery = window.$ = jQuery;
    })(window);

为了更好地理解它的做法,需要先强化一些函数的基本概念。


函数声明、函数表达式与匿名函数

  • 函数声明:使用function关键字声明一个函数,再指定一个函数名,叫函数声明。
    function funcName() {
        ...
    }
  • 函数表达式与匿名函数:未给函数命名,但最后将匿名函数赋予一个变量,叫函数表达式,否则就是匿名函数。因此匿名函数也属于函数表达式
    var funcName = function() {
        ...
    }
  • 区别:关于函数声明,它的一个重要特征就是函数声明提升,意思是在执行代码之前会先读取函数声明。这就意味着可以把函数声明放在调用它的语句后面;而函数表达式在调用之前需要赋值。
    sayHi(); // It's OK
    function sayHi() {
        alert("Hi!");
    }

    sayHi(); // undefined!
    var sayHi = function() {
        alert("Hi!");
    }

函数表达式 + ()

很重要的一点是:函数表达式后面通过加括号,能够立即调用该函数,而函数声明不可以!

    function sayHi() {
        alert("Hi!");
    }() // illegal

    var sayHi = function() {
        alert("Hi!");
    }() // legal

需要注意的是,匿名函数虽然属于函数表达式,但是在匿名函数后面加括号来直接调用是行不通的,因为JavaScript解释器将开头的function关键字当做函数声明,会报错:需要一个函数名

    function() {
        alert("Hi!");
    }() // illegal : function name expected

如何正确告诉JavaScript解释器这是一个匿名函数,而不是一个函数声明

由于匿名函数这个会被误解的存在,因此需要想办法正确告诉JavaScript解释器这是一个匿名函数,即函数表达式,而不是一个函数声明。而这样的方法就是:在function前面加上+-=()等运算符,而这之中最安全的做法便是加()

    !function() {
        alert("Hi!");
    }(); // legal

    +function() {
        alert("Hi!");
    }(); // legal

    -function() {
        alert("Hi!");
    }(); // legal

    var sayHi = function() {
        alert("Hi!");
    }(); // legal

    (function() {
        alert("Hi!");
    })(); // legal, notice

    (function() {
        alert("Hi!");
    }()); // legal, notice

加别的运算符也许会引起不必要的麻烦,而加括号是很好的做法。注意上面最后的2个做法,代表了(function() {})();(function() {}());
回头来看jQuery的两种写法:

    // 写法一
    (function(window, factory) {
        factory(window)
    }(this, function() {
        return function() {
           //jQuery的调用
        }
    })); // 其实写法一可以看成是(function() {}());

    // 写法二
    (function(window, undefined) {
        var jQuery = function() {}
        // ...
        window.jQuery = window.$ = jQuery;
    })(window); // 而写法而可以看成是(function() {})();

所以这两种写法在自执行上本质是一样的。


采用这种写法的意义

JavaScript中没有私有作用域的概念,如果在多人开发的项目上,你在全局或局部作用域中声明了一些变量,可能会被其他人不小心用同名的变量给覆盖掉。根据JavaScript函数作用域链的特性,可以使用这种技术模仿一个私有作用域,把匿名函数作为一个“容器”,内部可以访问外部的变量,而外部环境不能访问内部。从而达到保护jQuery内部变量的作用。

### JavaScript 中 function 函数的封装与调用最佳实践 在 JavaScript 中,函数的封装与调用是开发中非常重要的部分。通过合理的封装,可以提高代码的可读性、复用性和维护性。以下是关于 JavaScript 函数封装与调用的最佳实践: #### 1. **函数声明与定义** 函数可以通过多种方式定义,最常见的方式包括函数声明和函数表达式。函数声明使用 `function` 关键字直接定义函数[^2],而函数表达式则是将函数赋值给一个变量。例如: ```javascript // 函数声明 function add(a, b) { return a + b; } // 函数表达式 const multiply = function(a, b) { return a * b; }; ``` #### 2. **模块化封装** 为了减少全局变量污染并增强代码的模块化,推荐使用立即执行函数表达式(IIFE)或 ES6 模块进行封装。IIFE 可以创建一个独立的作用域,避免变量泄露[^4]。 ```javascript // 使用 IIFE 封装 (function() { function privateFunction() { console.log("This is a private function"); } window.publicFunction = function() { privateFunction(); console.log("This is a public function"); }; })(); publicFunction(); // 调用公共函数 ``` #### 3. **参数处理** 在封装函数时,应考虑参数的默认值和灵活性。ES6 引入了默认参数功能,简化了参数处理逻辑。 ```javascript function greet(name = "Guest") { console.log(`Hello, ${name}!`); } greet(); // 输出: Hello, Guest! greet("Alice"); // 输出: Hello, Alice! ``` #### 4. **返回值设计** 函数应明确其职责,并合理设计返回值。如果需要异步操作,建议使用 Promise 或 async/await 结构[^4]。 ```javascript // 使用 Promise 的封装示例 function fetchData(url) { return new Promise((resolve, reject) => { setTimeout(() => { if (url === "/success") { resolve("Data fetched successfully"); } else { reject("Failed to fetch data"); } }, 1000); }); } fetchData("/success") .then(data => console.log(data)) .catch(error => console.error(error)); ``` #### 5. **链式调用** 通过返回对象本身,可以实现链式调用,提升代码的简洁性和可读性[^1]。 ```javascript class Calculator { constructor() { this.result = 0; } add(value) { this.result += value; return this; // 返回自身以支持链式调用 } subtract(value) { this.result -= value; return this; } getResult() { return this.result; } } const calc = new Calculator(); console.log(calc.add(5).subtract(2).getResult()); // 输出: 3 ``` #### 6. **动画函数封装** 对于复杂的动画效果,可以通过封装函数来简化操作逻辑[^3]。 ```javascript function animate(obj, target, duration = 30) { let start = obj.offsetLeft; let change = target - start; let startTime = null; function step(timestamp) { if (!startTime) startTime = timestamp; let progress = timestamp - startTime; obj.style.left = Math.min(start + (change * (progress / duration)), target) + "px"; if (progress < duration) { requestAnimationFrame(step); } } requestAnimationFrame(step); } animate(document.querySelector("#box"), 500); // 动画移动到 500px ``` #### 7. **错误处理** 在封装函数时,应考虑异常情况并提供适当的错误处理机制。 ```javascript function safeDivide(a, b) { if (b === 0) { throw new Error("Division by zero is not allowed"); } return a / b; } try { console.log(safeDivide(10, 0)); } catch (error) { console.error(error.message); } ``` ### 总结 JavaScript 函数的封装与调用需要结合实际需求选择合适的方式。通过模块化、参数处理、返回值设计等手段,可以显著提升代码的质量和可维护性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值