第二章练习答案:找清变量和调用边界
定义、参数与返回
第一题:
function area(width, height = width) {
return width * height;
}
console.log(area(3), area(3, 0)); // 9 0默认值可以读取前面的 width,0 不是 undefined,因此不会被覆盖。这个最小函数假定输入为合法尺寸;需要生产入口时还应定义负数和非数值策略。
第二题省略与 undefined 都返回 1,null 返回 null。对象参数重新赋值只替换局部参数的值;它不是调用者变量的别名。修改共享对象属性可以被外界观察,替换局部参数则不能。
第三题先执行double得到6,再以6和4调用total得到10。默认表达式在需要时执行:value = {}每次创建新对象;value = shared读取同一个外部对象会共享身份。
const shared = {};
function fresh(value = {}) { return value; }
function reused(value = shared) { return value; }
console.log(fresh() === fresh(), reused() === reused()); // false true作用域与闭包
第一题:
function makePrefix(prefix) {
return function (text) {
return prefix + text;
};
}
const label = makePrefix('JS:');
console.log(label('闭包')); // JS:闭包返回函数仍可访问该次外层调用的 prefix 绑定,外层调用结束不等于其被闭包使用的环境立刻失效。
第二题块内存在自己的 n,从块开始就遮蔽外层 n;执行到内层初始化之前处于 TDZ,读取抛 ReferenceError。查找找到内层绑定后不会因它还没初始化而跳过它去读外层。
第三题:read和advance访问同一个可变page绑定;initialText是创建时计算好的字符串,之后没有重新赋值。var绑定提前初始化为undefined,调用失败因为这个值不可调用,得到TypeError;let/const尚未初始化,连读取都不允许,得到ReferenceError。
接收者与箭头
第一题:
const reader = {
name: '小林',
introduce() { return this.name; },
};
const bound = reader.introduce.bind(reader);
const wrapper = () => reader.introduce();
console.log(bound(), wrapper()); // 小林 小林bind 保存当前函数与接收者;包装函数每次执行重新从 reader 读取方法。若以后替换 reader.introduce,二者可能不同,这个差异解释了为何不能无条件说两种方案完全等价。
第二题对象字面量不是 this 作用域,直接写进去的箭头沿更外层寻找 this;普通方法经对象调用时拥有接收者,在它执行期间创建的箭头读取该接收者。
第三题:目标函数的返回值不会自动穿透包装层;包装器不写return就默认返回undefined。new使用新对象作为初始化接收者,忽略bind保存的this,但仍把bind的前置参数传给原构造函数;普通call包装不能据此宣称实现了完整bind。
高阶与递归
第一题:
function applyTwice(fn, value) {
return fn(fn(value));
}
console.log(applyTwice(n => n + 3, 4)); // 10内层先得到 7,外层再得到 10。函数的输出必须仍满足自身的输入契约。
第二题:
function digitSum(n) {
if (!Number.isSafeInteger(n) || n < 0) throw new RangeError('需要非负安全整数');
let total = 0;
do {
total += n % 10;
n = Math.trunc(n / 10);
} while (n > 0);
return total;
}
console.log(digitSum(407), digitSum(0)); // 11 0循环复用当前函数的一组局部变量,不会为每位数字建立新的递归调用。递归和循环都需要处理 0 和非法输入;改成循环不会自动替你补上契约。
第三题:nest(2)等待nest(1),nest(1)等待nest(0),零层返回x,第一层包成(x),第二层包成((x))。每一层必须返回其结果。只打印的函数通常返回undefined,组合器只是传递这个返回值,不会从日志里取回数据。