关于面试题之js变量作用域与提升

简介

这段时间一直在准备秋招,在这个过程中学习了非常多知识,如果不整理下来的话未免太过零散,于是笔者决定做个简单的面试相关知识点整理。原理啥的就不细写,有非常多大佬的文章写到了,此处主要是对部分题目的解析。

涉及

变量提升与函数提升,变量作用域。

例题

1.关于变量的作用域

        // 会输出什么 ?
        let x = 5;
        function fn(x) {
            // var x;  //(1)
            // var x = 10;  //(2)
            // let x = 10;  //(3)
            return function (y) {
                console.log(y + (++x));
            }
        }
        let f = fn(6); 
        f(7); 
        console.log(x); 

首先是全局作用域中,用let声明了变量x。而后声明了函数fn。
调用fn,此处的x值应为 6 。原因是fn中的x已经是由参数传入的局部变量了。fn返回一个函数。
调用返回的该函数。传入参数y = 7。该匿名函数先遇到y变量,为本函数参数,得到y=7。
遇到x,发现x并不在本域中定义,转向函数定义处的上级寻找,在fn函数中找到了关于x的定义,得到x = 6。进行运算。
最后打印x,自然是全局变量的x。

结果为:14 5

那如果添加了语句(1)呢?(只添加语句1)

结果为:14 5

原因:函数内部局部变量是不能覆盖函数参数的。所以此处x不为undefined。

那如果添加了语句(2)呢?(只添加语句2)

结果为:18 5

不是说不能覆盖???
原因:没有覆盖但重新赋值了吖。var x的声明无效,但x = 10有效吖。

那如果添加了语句(3)呢?(只添加语句3)

结果为: 报错,重复定义的错。
// Uncaught SyntaxError: Identifier 'x' has already been declared

这和 let 的特性有关,不允许定义前使用(报引用错误,原因是它是块级作用域,有暂时性死区),不允许重复声明等等。

2.关于变量的作用域链条

	// 会输出什么 ?
	let v = 1;
    function fn(){
        alert(v);
    }

    function fac(){
        let v = 3;
        fn();
    }

    fac()

让我们回顾一下上一道题写的那句:

遇到x,发现x并不在本域中定义,转向函数定义处的上级寻找,在fn函数中找到了关于x的定义,得到x = 6。

此处就是:运行函数 fn 时遇到 v,发现v并不在本域中定义,转向函数定义处的上级寻找,在全局作用域中找到了关于 v 的定义,得到 v = 1。
所以找寻变量作用域链的时候应该从定义处往上找,找到全局了还没找着就报错。

结果是:1

3.关于变量/函数提升

永远记得当初只知道变量提升不知函数提升,被面试官完虐的痛 QAQ
那道题当初没记下来,这找了道不知是哪个大厂的面试题

        function Foo() {
            log = function () {
                console.log(1);
            };
            return this;
        }

        Foo.log = function () {
            console.log(2);
        }

        Foo.prototype.log = function () {
            console.log(3);
        }
        var log = function () {
            console.log(4);
        }
        function log() {
            console.log(5);
        }

        Foo.log();
        log();
        Foo().log();
        log();
        new Foo.log();
        new Foo().log();

一堆定义看的人头晕。先上结论吧。

结果是:2 4 1 1 2 3

还是结合着题目来讲吧

		// 首先,定义了函数 Foo。当然在js中,函数也是对象。
		// 函数定义被提升到了顶部
		// 注意,函数还没真正运行之前,里面不论写了啥、定义了啥,那都是黑盒子,在Foo运行前都可以忽略里边写的乱七八糟的玩意儿
        function Foo() {
       		//没有用var/let/const声明的变量都是全局变量,window.xx那种
       		//显然window.xx的等级是最高的,可以盖掉其他函数/变量的定义
       		//道理很好理解,如果权限不是最高的,window的属性不就能随便改随便写了嘛
			//举个例子:var history = { state: 1 }; console.log(history);打印的还是原来的history对象。
            log = function () {
               console.log(1);
            }; 
            return this;
        }
		
		// 给Foo这个函数对象增加了个属性log
        Foo.log = function () {
            console.log(2);
        }
	
		// 原型链。简单地说,凡是用new Foo()的方式构建出的对象,它都能继承此log方法 
        Foo.prototype.log = function () {
            console.log(3);
        }

		// 定义了一个log变量,用的是函数表达式。变量提升。
		// 相同的函数名与变量名,函数提升变量也提升,但函数提升的等级更高。
        var log = function () {
            console.log(4);
        }
        // 定义了一个全局的log函数
        function log() {
            console.log(5);
        }
		
		// 现在我们来看题目
        Foo.log();//调用Foo的属性的函数,2
        log();// 全局定义了log函数,后面log又被赋值,所以打印 4 
        Foo().log(); // Foo运行后,window.log覆盖了前边的定义,此处this就是window。打印 1
        log();//同上,调用的window.log。打印 1
        new Foo.log();//意思就是把 function () {console.log(2);} 当作构造函数使,打印 2
        new Foo().log();//new Foo()出来的对象继承自Foo.prototype。打印 3

这中间还有个之前困扰了我很久的问题,大家都说函数提升优先于变量提升,那为啥我的log函数(打印5的)会被log变量(打印4的)给覆盖掉?
此处把它拿出来独立看。

		log();

        var log;

        log()

        var log = function () {
            console.log(4);
        }
        function log() {
            console.log(5);
        }

        log();
结果是: 5 5 4

原因还是在于,提升的是定义,不是整个赋值表达式。
我们说的函数提升优先于变量提升体现在,声明了函数log,第二句语句var log是没有效果的,第三句log()照样打印得出 5 。
但是后面赋值了吖。
所以它提升后的顺序应该是这样的。

        function log() {
            console.log(5);
        }

        var log; // 写了但是没有用,log还是函数

        log();
        log();

        log = function () {
            console.log(4);// 赋值是有用的
        }

        log();

总结

坑还是挺多的,只是整理了一小部分放在这里。还是要多看书多理解。
有问题欢迎指出。感谢阅读。

参考资料

深入理解JavaScript作用域和作用域链

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值