面向C++程序员的JavaScript教程学习
目录
- [定义函数(Function 的几种写法)](#定义函数(Function 的几种写法))
- [调用函数(调用方式与 this)](#调用函数(调用方式与 this))
- [函数作用域(var / let / const / 块作用域)](#函数作用域(var / let / const / 块作用域))
- [作用域与函数栈(执行上下文与 call stack)](#作用域与函数栈(执行上下文与 call stack))
- 闭包(Closures)
- [使用
arguments对象](#使用 arguments 对象) - 函数参数(默认参数、剩余参数、参数解构)
- 箭头函数(与普通函数的区别)
- [预定义 / 标准内建函数(Global objects & functions)](#预定义 / 标准内建函数(Global objects & functions))
定义函数
JS中,定义函数非常简单,因为缺少类型,或者说,类型是自己约定的,模板这样套就行:
-
函数声明(Function Declaration)
jsfunction add(a, b) { return a + b; }函数声明会被提升(hoisting),整个函数体在运行前就可被调用。所以只要在文件中编写了,这个文件内到处都可以调用。
-
函数表达式(Function Expression)
jsconst mul = function(a, b) { return a * b; };函数表达式在运行到该语句时才会被赋值,不同于声明的提升行为。
-
箭头函数(=>)(详见后文):
jsconst square = x => x * x;
调用函数
函数是一个具备子功能的一个方法集合,调用函数本质上可以理解为调用一个函数,函数可以出现在全局的普通的函数,可以是对象方法,也可以是构造函数。
-
直接调用(普通函数调用)
jsfunction foo() { console.log(this); } foo(); // 严格模式下 this === undefined,非严格模式下指向全局对象(浏览器为 window) -
作为对象方法调用
jsconst obj = { method() { console.log(this === obj); } }; obj.method(); // this 指向 obj -
构造函数调用(new)
jsfunction Person(name) { this.name = name; } const p = new Person('Alice'); // 新对象作为 this -
call / apply / bind (显式设置
this)jsfunction greet(greeting) { console.log(greeting + ', ' + this.name); } greet.call({name:'Bob'}, 'Hi'); greet.apply({name:'Bob'}, ['Hello']); const bound = greet.bind({name:'Carol'}); bound('Hey');
函数作用域
要点
- JavaScript 使用词法(静态)作用域:函数定义的位置决定了它访问哪些外部变量。
var创建函数作用域(或全局),let/const创建块级作用域(block scope)。
示例:var 与 let
js
if (true) {
var x = 1; // 在整个外层函数或全局可见
let y = 2; // 仅在该块内可见
}
console.log(x); // 1
console.log(y); // ReferenceError
常见坑
- 使用
var在循环里创建闭包时会遇到"共享同一变量"的问题,通常用let或 IIFE(立即执行函数表达式)解决。
作用域和函数栈(执行上下文 / call stack)
我们知道,C++的函数调用后,咱们的CPU会压入栈的参数保存,栈向下生长,这里的JS也是类似的,对了,还记得使用let定义吗? let在这里就像自动栈上变量一样,比起来,var就会泄漏到全局,还是不太友善的。
要点
- 每次函数执行会创建一个 执行上下文(execution context) ,包含变量环境、作用域链、
this等信息。多个执行上下文按 LIFO(后进先出)方式放在 调用栈(call stack) 中。
简单演示
text
global() // push global context
-> foo() // push foo context
-> bar() // push bar context
<- bar returns // pop bar
<- foo returns // pop foo
调试建议
在浏览器 DevTools 调试时,可在 call stack 窗口查看当前执行路径(尤其在异常或递归时)。
闭包(Closures)
闭包看起来就像lambda表达式,这里说的就是一个封闭的执行环境。闭包是函数与其词法环境的组合 ------ 内部函数可以"记住"并访问外部函数作用域的变量,即使外部函数已经返回。MDN Closures 解释。
经典示例:计数器
js
function makeCounter() {
let count = 0;
return function() {
count += 1;
return count;
};
}
const c = makeCounter();
c(); // 1
c(); // 2
内部函数保持对 count 的引用 ------ count 不会被垃圾回收,直到没有引用该闭包为止。
用途
- 数据封装(模拟私有变量)
- 部分应用(partial application)
- 函数工厂
使用 arguments 对象
要点
arguments是非箭头函数内部可用的类数组(array-like)对象 ,包含被调用时传入的所有参数。现代代码更推荐使用 剩余参数(rest parameters) (...args)。MDN:arguments 对象。
js
function sum() {
// 把 arguments 转为真正数组
const args = Array.from(arguments);
return args.reduce((s, v) => s + v, 0);
}
sum(1,2,3); // 6
// 推荐写法(rest)
function sum2(...nums) {
return nums.reduce((s, v) => s + v, 0);
}
函数参数
要点与模式
-
位置参数(Positional):按顺序传递。
-
默认参数(Default parameters):
jsfunction greet(name = 'Guest') { console.log('Hello', name); }MDN:Default parameters。 (MDN Web 文档)
-
剩余参数(Rest parameters):
jsfunction f(a, b, ...rest) { /* rest 是数组 */ } -
参数解构(Destructuring):
jsfunction draw({x = 0, y = 0} = {}) { /* ... */ }
传参语义
- 原始类型按值传递(copy 值);
- 对象类型传递的是引用的副本:函数内修改对象的属性会影响外部对象(因为引用指向同一个对象),但把参数重新赋值不会影响外部引用。
箭头函数(Arrow functions)
要点
- 语法更简洁:
(a, b) => a + b。 - 没有自己的
this、arguments、super或new.target,箭头函数的this来自其词法环境(定义时所在的this)。MDN:Arrow function expressions。 - 不能作为构造函数(不能用
new)。
js
const obj = {
value: 42,
regular() { console.log(this.value); }, // this 指向 obj
arrow: () => { console.log(this.value); } // this 来自外层(通常不是 obj)
};
obj.regular(); // 42
obj.arrow(); // undefined(取决于外层 this)
何时使用
- 适合短的回调函数(比如 map/filter),或想要"继承"外层 this 的场景(例如在类方法内部的回调)。
- 不适合需要动态 this 或作为构造器的情形。
推荐 MDN 参考(快速链接)
- 函数(总览 / Guide): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions 。(MDN Web 文档)
function(声明)参考: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function 。(MDN Web 文档)- Scope(作用域,Glossary): https://developer.mozilla.org/en-US/docs/Glossary/Scope 。(MDN Web 文档)
- Closures(闭包 指南): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures 。(MDN Web 文档)
- Arrow functions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions 。(MDN Web 文档)
arguments对象: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments 。(MDN Web 文档)- 标准内建对象(Global Objects): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects 。(MDN Web 文档)