js部分习题解答

本文主要解析了JavaScript中的逻辑题目,涉及变量存储位置、局部与全局变量、作用域链以及闭包的概念。通过实例解析了函数执行上下文、变量对象、作用域查找以及函数内部变量的赋值和引用,帮助理解JS中变量的生命周期和作用范围。
摘要由CSDN通过智能技术生成

JS部分逻辑题个人理解

前面的一些小内容

存储位置

在看题之前我们要知道,基本数据是存储在栈中,函数存储在堆中。例子如下:

var a = 1;
var b = "hello";
function f(){
    console.log("f......")}

在上面的代码中,a,b在栈中,f在堆中。

局部变量与全局变量

根据名字我们不难看出,局部变量应用于局部,全局变量应用于全局,如下:

var a = 1;
function f(b){
    b = 2;
}

在上面的代码中,a是全局变量,b是局部变量

全局代码在执行时,会创建全局变量/函数,这些数据会存到VO,局部变量/函数会存到AO。

那么,当我们获取一个数据是,首先要看的是它是全局变量还是局部变量,如果是全局变量,那么看看它是否加var,如果加了,那就去VO中找它,如果没有加,那就去GO中找它,要是都找不到,那么我们可以说这个数据没有定义。

在代码运行时,我们首先会生成一个ECStack,每个函数运行时,也会产生自己的EC,当我们找数据是,首先从该函数的EC中找,如果自己的EC中没有,那么就去自己的父EC中找.

在函数运行完毕时,要出栈并销毁,改变的数也会没有影响,这是下面的题的一个重点。

习题

var arr = [11,22];
function f(ary){
    console.log(ary);
    ary[0] = 100;
    ary = [666];
    ary[0] = 0;
    console.log(ary);
}
f(arr);
console.log(arr);

解析:首先,我们会产生一个全局的ECStack,在这里,我们会有一个EC(G),在EC(G)中,有一个VO负责数据,从上面的代码我们可以看出,在VO中。我们首先声明arr与f,在没有赋值的前提下,arr是undefind,而f对应的是一个地址,我们假设为0x000fff

赋值之后,arr由于是一个数组,所以对应一个地址,假设为0x111fff,而该地址对应的,就是堆0x111fff,里面的数据就是[11,22]

在第一行赋值之后,代码来到了f(arr),这代表我们要去看看函数f。函数在运行时,会产生一个EC(f),先进入ECStack,并产生AO用于数据存储。和AO中一样,我们首先声明ary,由于赋值时我们把arr赋予ary,那么ary同样有一个堆0x111fff用于找到[11,22]。

那么,接下来输出的ary,就是[11,22]

然后我们将ary中的第一个数改为100,由于ary对应的地址是0x111fff,那么我们就将[11,22]改为[100,22]

接下来代码将666赋予ary,那么我们只能给ary一个新的地址用于存放666,则之前的地址0x111fff就消失了,新地址我们假设为0x222fff

然后将ary中第一个元素改为0,如上所述,我们将666改为0

则下面的输出ary,就是0

函数f结束之后,我们将f出栈,然后销毁。输出arr时,我们应记得arr中的数据,已经被ary改为了100,22.那么最后一行的输出,也是[100,22]

综上,最后的输出结果,是[11,22] [0] [100,22]

var a = 1;
var b = a;
function f(){
    console.log(a,b);
    var a = b = 2;
    console.log(a,b);
}
f();
console.log(a,b);

解析:
写在前面:var a = b =2;等价于var a = 2;
b = 2;

首先在VO中,我们有a,b,f。其中ab为undefined,而f对应地址0x000fff,赋值之后,a为1,b为1

接下来,由于代码f();我们进入函数f。依旧是产生EC(f),并在AO中,有a,为undefined

那么函数f的第一行输出,我们可以在该函数中找到a,为und,而我们找不到b,只能去该函数的父EC,也就是EC(G)中,找到b,为1,即输出为und,1

然后是赋值,a为2,而b,是全局变量,则EC(G)中的b改为2。同时,GO中的b也改为2,

则函数f的第二次输出,就是2,2

逃出函数f,销毁EC(f),则最后一行输出,就是1,2

var a = {m:666};
var b = a;
b = {m:888};
console.log(a.m);

解析:
先声明a,b,然后赋值,a对应堆0x000fff,并且根据第二行代码,将该地址给予b,则此时a与b都指向{m:666}

然后有一个新的堆{m:888}给予b,那么按照上面的题的解析,我们可以知道,b将会切开0x000fff,并连接新的数据堆。

那么,最后的输出,就是666

那么我们将上题改一下,将b = {m:888},改为b.n = b = {m:888},解析就不一样了

不同之处处在新的堆赋予b上,在这里,b不会立即切断0x000fff,或者说切断0x000fff与在0x000fff建立新地址0x111fff(数据位m:888)同步进行。
则最后的输出结果,b为{m:666},a为{m:666,n:{m:666}};

var i = 5;
function fn(i){
    return function (n){
        console.log(n+(++i));
    }
}
var f = fn(1);
f(2);
fn(3)(4);
fn(5)(6);
f(7);
console.log(i);

解析:老样子,先提升i,f,预编译fn,然后赋值,则i=5,fn指向地址0x000fff,然后f也指向fn的地址0x000fff,于是进入函数fn,给f返回一个新地址0x111fff指向新函数f。

然后f(2),进入函数f,输出n+(++i)。n可以在自己身上找到,是2,i找不到,要去父EC上找,找到了上一行给f赋值时留下的fn(1),于是i=1,则输出的是4

要注意的是此时f依然指向f函数的地址,所有依然有作用,这种情况叫闭包,直到我们关闭页面才会无效。

接着说代码,我们进行到fn(3)(4),没什么好说的,就是给i赋予3,n赋予4,得出8。fn(5)(6)也是一个道理,得出12

然后f(7),方法和f(2),一样,依旧是给n赋予7,从父EC中找到i,需要注意的是此时的i经过f(2)的运算之后,变成了++i即为2,那么最后输出的应该为7+2+1=10

最后,输出i,依旧是从EC(G)中找到,得5

注意:本题我们涉及到了闭包,这在接下来还会大量运用,闭包的原理我们可以去看百科,在此不加赘述

var i = 20;
function fn(){
    i -= 2;
    var i = 10;
    return function (n){
        console.log((++i)-n);
    }
}
var f = fn();
f(1);
f(2);
fn()(3);

解析:如同上题一般,这道题的关键仍然是闭包。首先我们提升i,f,预编译fn,得到两个und和一个地址。赋值之后,i=20,f指向一个地址,目的地是fn。

那么我们运行fn,依旧是先提升i,然后发现i -= 2;这行代码的意思是i = i-2;

然后给i赋值,为10,给f返回一个新地址,指向函数f,在此时并没有销毁,因为指向了变量f,所以还是产生了闭包

接下来就是走流程,f(1)进入,就是((++10)-1),输出10
f(2)进入,就是((++11)-2)输出10
fn()(3)进入,这时没有用到f产生的闭包,因为该行代码是从fn进入,所以最后输出(((++10)-3),输出8

需要注意的是,直到此时,f产生的闭包依旧没有结束,他会在你关闭页面之后才会被销毁。

let a = 0;
b = 0;
function A(a){
    A = function (b) {
        alert(a+b++);
    }
    alert (a++);
}
A(1);
A(2);

写在前面:在这里出现了新的定义方式let,由于篇幅原因,在此不加赘述,依旧可以看百科。需要注意的是,在以后的工作中,我们会经常使用let和const来分别定义变量和常量,而不是用var,他们的优点我也不加赘述,可以看看百科,这里毕竟是写题的解释。

首先提升a b A,得到a,b两个und,A一个地址。

b的定义是直接的,所以应该放到GO里,a和A放到VO里。赋值之后得到a,b两个0.
然后进入A(1),此时我们进入函数A,并将1赋予a,跳过函数function(b),直接输出a++。注意的是a++是先输出在运算,所以我们输出的是1,当然,此时A(a)中的a变成了2.

同样,产生闭包,因为依旧有指向。
然后走到了A(2),注意的是此时的A已经不再指向函数A,而是指向函数function(b),所以我们应该将2赋予b,然后输出a+b++,在自己身上找不到b,那就去父EC也就是EC(A)里找,得到了a=2,经过运算后输出4

var t = (function (i) {
        return function () {
            alert(i *= 2); 
        }
    })(2);
    t(5);

解析:
写在前面:在函数后+(),意思是直接运行该函数,这种写法叫IIFE,意思是立即调用函数表达式
首先提升t,然后赋予t地址,指向函数f(i),由于代码,我们立即运行函数f(i),并将2赋予i。得到一个返回地址给t。
老生常谈,此时还是产生闭包,并且t指向的地址发生改变,这使得t(5)在运行时,走的时候函数f而不是函数f(i)。
这里有一个陷阱,就是t(5)中的5,其实没有什么用,我们在运行时没有变量让5赋值。
运行函数f,输出i =2,这行代码的意思就是i = i2;,我们在函数f中无法找到i,就去他的父EC中找,得到i=2
所以最后输出4

var n = 0;
function a() {
    var n = 10;
    function b() {
        n++;
        console.log(n);
    }
    b();
    return b;
}
var c = a();
c();
console.log(n);

解析:首先依旧是提升,n,c;然后预定义函数a。结束后根据代码赋值,则n=0,c为一个地址,指向函数a。
那么我们运行函数a,依旧是先提升,然后赋值,得n=10,函数b有一个地址,接下来运行函数b,n++中的n在函数b中没有声明,所以我们需要去函数a中找,所以a中的n变成了11,我们输出n,则第一个输出值为11
可以很明显看出来,又又又产生了闭包,因为函数a在运行完之后,给c返回了函数b的地址。那么接下来的c(),运行的就是函数b。
依旧是老样子,b中没有n,就找a中的n,找到了之后n++,a中的n变成了12,输出12
最后在EC(G)中输出n,当然是先找自己的,找到了n=0,那么输出0

var a = 1;
function fn(a) {
    console.log(a);
    var a = 2;
    function a() { console.log("ok") }
    console.log(a);  
}
fn(a);
console.log(a);

解析:先提升,a,fn(a),得到一个und和一个地址,赋值1给a。然后我们看到fn(a),进入函数fn(a)。
在函数中,我们依旧是老办法,提升a,f(a)。需要注意的是,函数在提升中已经通过一遍了,所以在fn(a)中,a已经有一个地址,指向函数f(a)。所以,在第一个输出console.log(a);中,我们输出的是console.log(“ok”)。
然后给a赋值2,切断a的指向函数的地址,所以第二个输出为2
最后函数运行完毕,我们看看外面,第三次输出a,当然是先从自身找起,找到了一个a,输出,则最后输出为1

var a = 1;
function b() {
    if(!a){
        var a = 2;
    }
    console.log(a); 
}
b();

解析:先提升a,b,然后赋值a=1,根据b()运行函数b
进入函数b之后,我们可以在if中看到一个var a。所以我们先提升a,此时a为und,那么在if(!a)中,a为false,那么!a就是true,我们就可以运行if
运行if的结果就是给a赋值2,然后我们输出a,也就是输出结果,是2

var a=10, b = 11, c = 12;
function f(a) {
    a = 1;
    var b = 2;
    c = 3;
}
f(10);
console.log(a); 
console.log(b); 
console.log(c);

解析:首先先提升a,b,c,f(a)。接下来是赋值,a=10,b=11,c=12。
根据f(10),我们将10赋予函数中的a,并进入函数f(a)。函数中,将1赋予a,我们可以在函数中找到a,那么函数中的a=1。
接下来,函数声明了b,并赋值2,那么函数中的b=2.
函数同样赋值3给c,但是函数中没有c,所以只能从父EC中找到c并赋值。于是,EC(G)中的c位3
所以最终输出的abc,位10,11,3

后记

这一次只是给js的部分例题做了解析,由于初学js,所以错误很多,我也会在今后的学习中不断完善自己写的拙作。
至于定义,名次,并不在这片文章中,同时,笔者认为,这些可以通过很简单方法得到的知识,不应该再重复写,这样是在浪费大家的时间。
函数中的a,并进入函数f(a)。函数中,将1赋予a,我们可以在函数中找到a,那么函数中的a=1。
接下来,函数声明了b,并赋值2,那么函数中的b=2.
函数同样赋值3给c,但是函数中没有c,所以只能从父EC中找到c并赋值。于是,EC(G)中的c位3
所以最终输出的abc,位10,11,3

后记

这一次只是给js的部分例题做了解析,由于初学js,所以错误很多,我也会在今后的学习中不断完善自己写的拙作。
至于定义,名次,并不在这片文章中,同时,笔者认为,这些可以通过很简单方法得到的知识,不应该再重复写,这样是在浪费大家的时间。
更何况,那些地方已经足够完善,也足够通俗易懂

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值