【JavaScript】同步代码执行顺序笔试题

第一题

var n  = 100

function foo() {
    n = 200
}
foo()
console.log(n) // 200 

第二题

function foo() {
    console.log(n) // undefined
    var n = 200
    console.log(n); // 200
}
var n = 100
foo()

第三题

var n = 100

function foo1() {
    console.log(n); // 100 作用域链在定义时就已经确定(词法作用域)
}

function foo2() {
    var n = 200
    console.log(n); // 200
    foo1()
}
foo2()
console.log(n); // 100

第四题

var a = 100

function foo() {
    console.log(a) // undefined 作用域提升(定义阶段)
    return
    var a = 100
}
foo()

第五题

function foo() {
    var a = b = 100
    // 上述代码相当于
    // var a = 100
    // b = 100 // 这里的写法是历史遗留问题 会在定义时添加给全局作用域 b = 100;
}
foo()

// console.log(a) // 单纯打印 a 会报错,因为全局中没有定义
console.log(b) // 100 // b 会存在于全局作用域

第六题

var func = 1
function func() {}
console.log(func + func)  // 2
  1. 编译阶段:函数声明提升 + 变量声明提升

函数声明 会被整体提升,并且优先级比 var 变量声明高。

变量声明(var)也会提升,但 不会提升赋值。

等价于:

function func() {}  // 函数声明整体提升
var func;           // var 声明提升(但这里不会覆盖函数的定义)
func = 1;           // 运行时执行赋值
  1. 执行阶段

先加载了函数 func(所以最初 func 是一个函数)。

然后执行 var func = 1 的赋值,把 func 变成了数字 1,所以最终为两个 1 相加。

第七题

let i
for (i = 1; i <= 3; i++) {
  setTimeout(function () {
    console.log(i)
  }, 0)
}

//  4 4 4

i 是全局变量,用来控制循环。循环调用了 3 次 setTimeout 延迟执行。当 setTimeout 执行的时候,i 已经变成了 4。但是此时是异步行为,所以只能输出最终的结果 4 。

如果省略 delay 参数,则使用值 0,意味着“立即”执行(下一个事件循环执行),是异步行为。

for (var i = 1; i <= 3; i++) {
  setTimeout(function () {
    console.log(i)
  }, 0)
}

//  4 4 4
for (let i = 1; i <= 3; i++) {
    setTimeout(function () {
        console.log(i)
    }, 0)
}
// 1 2 3 

除了使用 let (块级作用域)解决全局变量的问题,还可以使用 闭包(函数作用域)解决:

for (var i = 1; i <= 3; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(i)
        }, 0)
    })(i)
}
// 1 2 3

第八题

let n = 10
function f1() {
  n++                  // (1)
  function f2() {
    function f3() {
      n++              // (2)
    }
    let n = 20         // (3)
    f3()               // (4)
    n++                // (5)
  }
  f2()
  n++                  // (6)
}
f1()
console.log('n', n)

let n = 10

全局变量 n 初始化为 10

调用 f1() → 执行第一行:

n++   // (1)

这里的 n 指向 全局 n,所以:

n = 10 → 11

f2 内部:

function f3() { n++ }  // 定义 f3(闭包)
let n = 20             // 局部变量 n(注意!屏蔽了外层的 n)

执行 f3

f3() // (4)

这里关键点:

  • f3 内部写的是 n++
  • 变量解析时,就近查找作用域链
  • 最近的 nf2 内的 let n = 20

因此执行:

f2.n = 20 → 21

回到 f2

继续执行:

n++  // (5)

依然是 f2n,所以:

f2.n = 21 → 22

f2 执行结束(局部变量 n=22 销毁)。


回到 f1

继续执行:

n++  // (6)

这里的 n 又是全局的 n(因为 f1 内没定义自己的 n)。
所以:

global n = 11 → 12

最终结果

console.log('n', n) // 12

输出

n 12

第九题

const n = 10
function print() {
  console.log(n)
}

function f1(fn) {
  const n = 20
  fn()
}
f1(print) // 10

JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。换句话说,说函数的作用域在函数定义的时候就决定了。

所以当调用 print 的时候,它会根据定义的位置向外查找变量,也就是 n = 10。

第十题

function fn() {
  let num = 10
  return {
    set: (n) => (num = n),
    get: () => num,
  }
}

let num = 20
const { get, set } = fn()
console.log('result1: ', get())
set(100)
console.log('result2: ', num)

// result1: 10
// result2: 20

执行 fn()

const { get, set } = fn()
  • 进入 fn,声明了一个局部变量 num = 10
  • fn 返回一个对象,里面两个方法 getset 都是闭包,捕获了 fn 作用域中的 num
  • 注意:这里的 num 不是外面的 let num = 20,而是 fn 内部的那个 num

所以:getset 操作的都是 fn 内部作用域的 num(初始值 10)。

执行 get()

console.log('result1: ', get())
  • get 返回 fn 内部 num,此时还是 10

输出:

result1: 10

执行 set(100)

set(100)
  • set 修改的是 闭包里的 num,所以 fn 内部的 num 变成了 100
  • 这个修改跟全局的 let num = 20 没有关系。

打印全局 num

console.log('result2: ', num)

这里访问的 num 是全局作用域的 num,它始终是 20,并没有被闭包修改。

✅ 最终输出

result1: 10
result2: 20

parseInt

;['1', '2', '3'].map(parseInt) // [1, NaN, NaN]
  1. Array.prototype.map

map 回调函数会传 三个参数:

callback(element, index, array)
  • element:当前元素

  • index:当前索引

  • array:原数组本身

所以 map(parseInt) 实际等价于:

['1', '2', '3'].map((val, idx) => parseInt(val, idx));
  1. parseInt 的两个参数
parseInt(string, radix)
  • string:要解析的字符串

  • radix:进制(2 ~ 36 之间的整数,如果为 0 或不写,会默认按 10 进制解析,或按字符串格式猜测)

  1. 每次迭代分析
第一次:val = '1',idx = 0
parseInt('1', 0) 
// radix = 0 ⇒ 采用默认规则 ⇒ 当作 10 进制
// 结果 = 1

第二次:val = '2',idx = 1
parseInt('2', 1) 
// radix = 1 (非法,进制范围只能 2 ~ 36)
// 返回 NaN

第三次:val = '3',idx = 2
parseInt('3', 2) 
// radix = 2(二进制),只允许 0/1
// '3' 不是有效的二进制数字 ⇒ NaN
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秀秀不只会前端

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值