一、问答题(22分)

1.值类型和对象类型的数据有什么不同?(4分)
原始值类型:存在栈内存中,是按值操作
对象类型:存在堆内存中,是按地址操作
2.let与var有什么区别。(4分)
let、const:

  • 不存在变量提升。
  • 只能声明一次,不能重复声明
  • 变量声明出来存在VO(变量对象)或AO(活动对象)中,不会给window增加属性
  • 会产生块级上下文
  • 会产生暂时性死区

var:

  • 有变量提升
  • 在全局中声明的变量,会给window增加一个属性。

3.简要描述一下变量提升机制。(4分)
当栈内存(作用域)形成,js代码执行之前,浏览器会把所有带有var/function关键词的进行提前‘声明’或者‘定义’,这种预先处理机制称之为‘变量提升’。 var 声明 function 声明 +定义 【块级作用上下文除外】
4.函数的上级作用域由什么决定?(2分)
由函数创建(定义)的位置决定
5.什么是闭包?(2分)

  • 一个不销毁的作用域
  • 变量的一种保护/保存机制,保护私有变量不受外界影响

6.函数和箭头函数的区别(2分)
普通函数有arguments,有this
箭头函数没有arguments;没有自己的this,this由创建所在的执行上下文决定;没有prototype,不能被new执行
7.下面代码是否可以,每隔1000MS依次输出 0 1 2 3 4 5 ?
如果不可以,说明为啥?
以及如何解决?(4分)

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

不能。var i是全局的i,当setTimeout执行的时候i已经是5了。
var改成let,或者写闭包
for (var i = 0; i < 5; i++) {
setTimeout(function (i) {
console.log(i);
}, (i + 1) 1000, i);
}

var fn = function fn(i) {
return function () {
console.log(i);
};
};
for (var i = 0; i < 5; i++) {
setTimeout(fn(i), (i + 1)
1000);
}

二、基础编程(38分)

1.下面代码输出什么(6分,每小题2分)

//EC(G)
//    VO/GO
//        x=>10
//        fn------xx001[[scope]]:EC(G)
// 变量提升
//     var x,fn
var x = 10;
function fn() {
console.log(x);//undefined
var x = 20;
}
fn();
//EC(FN)
//    AO
//        X =20
console.log(x);//10
//EC(G)
//    VO(G)/GO
//        x=>10=>20
//        fn==>0x001 [[scope]]:EC(G)
//变量提升 x fn
var x = 10;
function fn() {
console.log(x);//10
    x = 20;
}
fn();
//EC(FN)
//    <EC(G),EC(G)>
console.log(x);//20
//报错 let声明的变量不允许在之前使用 词法分析的时候 
let x = 10;
function fn() {
console.log(x);
let x = 20;
}
fn();
console.log(x);

2、下面代码的输出结果(3分)

//EC(G)
//    VO(G)/GO
//        x=>1
//        y=>2=>6
//         z=>3=>6=>7
//        fn====>XX001 scope<EC(G)>
var x = 1,
    y = 2,
    z = 3;
function fn(x) {
  console.log(x, y, z);//2 undefined 3
  x = 4;
  var y = 5;
  z = 6;
  console.log(x, y, z);//4 5 6
  return z++;
}
y = fn(y);
//EC(fn)
//    AO(fn)
//        x=2=>4
//        y=5
//    作用域链 <EC(fn),EC(G)>
console.log(x, y, z);// 1 6 7

3、下面代码的输出结果(3分)

//EC(G)
//     VO(G)/GO
//            x===001{10,20,30,40}=>{10,20,30,40,100}
//            fn=>xxx001 <scope>:EC(G)
//变量提升
//    var x
var x = [10, 20, 30, 40];
function fn(x) {
    console.log(x.join('|'));//"10|20|30|40"
  x.push(100);
  x = x.slice(2);
  x.unshift(200);
    console.log(x.join('&'));//"200&30&40&100"
}
fn(x);
//EC(fn)
//    x=====xxx001=>xxx002{200,30,40,100}

console.log(x.toString());//"10,20,30,40,100"

4、下面代码的输出结果(3分)

//EC(G)
//    VO(G)/GO
//        ary====001{12,23}{100,23}
//        fn====xxx001 scope<EC(G)>
var ary = [12, 23];
function fn(ary) {
  console.log(ary);//[12,23]
  ary[0] = 100;
  ary = [100];
  ary[0] = 0;
  console.log(ary);//[0]
}
fn(ary);
//EC(fn)
//    ary===001=>002{0}

console.log(ary);//[100,23]

5、下面代码的输出结果(3分)

var a=1;
function fn(x) {
  for(var i=0;i<=x;i++) {
      if(x%3) {
       fn = function(y){
         console.log(y - (++x));
       };
      break;
    }
  }
}
fn(9);
fn(11);//更改了fn的引用地址
fn(12);//=> fn = function(y){
                 console.log(y - (++x));
                   };
//0

6、下面代码的输出结果(2分)

//EC(G)
//    VO(G)/GO
//        A=>0x001 scope<EC(G)>0x002
//        a=>0=>1
//        b=>0
let a=0,
    b=0;
function A(a){
    A=function(b){
        alert(a+b++);
    };
    alert(a++);
}
A(1);//1
//EC(A)
//    a==1
A(2);//4

7、下面代码的输出结果(4分)

var x = 10,//9=>8=>7
    y = 20,//21=>22
    z = 30;
function fn() {
    y++;
        return function (z) {
        x--;
                console.log(z + x + y);
    }
}
var f = fn();
f(40);//70
fn()(50);//80
f(60);//89
console.log(x,y,z);//7 22 30

1.png
8、下面代码的输出结果(4分)

{
    function foo() {}
    foo = 1;
    function foo() {}
}
console.log(foo);//1

1-3.png

var test = (function (i) {
    return function () {
        alert(i *= 2);
    }
})(2);
test(5);//'4'

1-2.png
9、下面代码的输出结果(4分)

let a=1;
let obj={
    a:2,
  fn:function () {
        console.log(this.a)
    }
};
let fn = obj.fn;
obj.fn();//2
fn();//undefined

10、下面代码的输出结果(6分)==>这个

var x=2;
var y={
    x:3,
    z:(function(x){
      this.x*=x;
      x+=2;
      return function(n){
        this.x*=n;
        x+=3;
        console.log(x);
      }
         })(x)
};
var m=y.z;
m(4);//7
y.z(5);//10
console.log(x, y.x);//16 15

1-2.png

三、编程实战(40分)

1、将下面字母大写转小写,小写转大写(10分)

function caseChange() {
}
caseChange('aBc')//=>'AbC'
caseChange('aHtReI')//=>'AhTrEi'

依次拿到字符串中的每一个字符 charAt 判断一下当前的字符是大写还是小写[如果是大写,我们把其转换为小写,反之转为大写] +把某个字符先转成小写和之前的自己进行比较 如果相等则是小写,反之大写 +例如:‘A’ @1 把其变为小写’a’ @2 ‘a’!=’A’ 之前是大写 把处理后的字符重新拼接起来即可

函数式编程=What? 关注结果

  • 把某个功能的具体实现,封装到函数内部(例如本案例中,内置的forEach方法,就是把循环每一向的操作,封装到了函数内部)
    • @1 优势 后期别人直接使用这个函数,即可实现对应的效果,无需关注内部底层是如何处理的,有利于开 发效率的提高和学习使用
    • @2 弊端 操作起来不够灵活 内部核心的处理我们是无法修改的

命令式编程========How? 关注过程 例如本案例中的for

  • @1优势:灵活
  • @2弊端:代码量多,实现相对复杂..

    方案1 循环

    function caseChange(str) {
             let result="";
             //循环字符串中的每个字符,
             for(let i=0;i<str.length;i++){
                 //获取当前迭代的这个字符
                 let char=str.charAt(i);
                 //验证其 大写还是小写
                 let isLoower=char.toLowerCase()===char;
                 // 大写->小写 小写->大写 最后拼接到result中即可
                 result+=isLoower?char.toUpperCase():char.toLowerCase();
             }
             return result;
    }
    

    方案2 forEach

    function caseChange(str) {
             let result="";
             //把字符串拆成数组,每一个字符就是数组的每一项,这样我们就可以使用数组的forEach来循环了
             str.split('').forEach((char)=>{
                 //char :循环的每个字符
                 let isLoower=char.toLowerCase()===char;
                 // 大写->小写 小写->大写 最后拼接到result中即可
                 result+=isLoower?char.toUpperCase():char.toLowerCase();
             })
             return result;
    }
    

    方案3 map

    function caseChange(str) {
              let result = '';
            return str.split('').map((char)=>{
                 //char :循环的每个字符
                 let isLoower=char.toLowerCase()===char;
                 return result+=isLoower?char.toUpperCase():char.toLowerCase();
             }).join('');
    }
    

    方案4 charAtCode

    function caseChange(str) {
     let result = '';
     str.split('').forEach(char => {
         // charCodeAt:获取某个字符ASCII码表中对应的十进制的值
         // 65~90 A-Z   97~122 a-z   48~57 零到九的数字  ...
         let dec = char.charCodeAt();
         result += (dec >= 97 && dec <= 122) ? char.toUpperCase() : char.toLowerCase();
     });
     return result;
    }
    console.log(caseChange('aBc'));; //=>'AbC'
    console.log(caseChange('aHtReI'));; //=>'AhTrEi' */
    

    2-1.png

    2、实现函数fn,让其具有如下功能(10分)

    let res = fn(1,2)(3);
    console.log(res); //=>6  1+2+3
    

    1.forEach

    let total = 0;
         params.forEach(item => {
             total += item;
         });
         return total;
    

    2.用数组的reduce的方法 https://www.yuque.com/go/doc/43757330

    const fn = function fn(...params) {
     // params:[1,2]
     return function proxy(...args) {
         // args:[3]
         // 把两次传递进来的实参合并在一个数组中 [1,2,3]
         params = params.concat(args);
         return params.reduce((result, item) => {
             return result + item;
         });
     };
    };
    
    //下面是对fn函数的箭头函数方式
    const fn = (...params) =>
     (...args) =>
     params.concat(args).reduce((result, item) => result + item);
    

    3、完成简单的签到系统(10分)

    数组info存放了班级中学生的签到信息,每一项代表一个同学,name属性是姓名。将info里的数据渲染到页面上。
    然后在输入框里填写学生姓名,点击签到按钮,可改变相应同学的签到状态。
    如果没有该学生,则提示“请输入正确的姓名”
    第三周考试 - 图7
    5过
    

    想法: @第一步 实现一个方法 根据数组info,控制box盒子中的内容展示 具体:数据中有多少条数据,我们就在BOX中放少个P标签,每一个P标签中包含了每一个对象中的name/status

       @2 第二步 点击签到按钮,更改info的数据
    

    +获取文本框输入的内容,例如”张三” +到info中,找到name和输入的值”相同的这一项[对象] +把找到的对象中的status修改为”已签到” +如果没有找到名字匹配的项,则让提示信息显示,反之让隐藏 +最后按照最新的数据,重新渲染页面(把第一步的事情再干一遍)

2-2.png

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div>
        <input type="text" id="signInput">
        <button id="submit">签到</button>
        <div id="tip" style="color: red;display: none;">请输入正确的姓名</div>
    </div>
    <div id="box">
        <!-- <p>张三:未签到</p> -->
    </div>

    <!--IMPORT JS-->
    <script>
        let info = [
            {
                name: '张三',
                status: '未签到',
            },
            {
                name: '李四',
                status: '未签到',
            },
            {
                name: '王五',
                status: '未签到',
            },
            {
                name: '吴六',
                status: '未签到',
            },
        ];

        //想要操作谁 就先获取谁
        let signInput = document.querySelector("#signInput"),
            submit = document.querySelector("#submit"),
            tip = document.querySelector("#tip"),
            box = document.querySelector("#box");
        //@1根据数据渲染视图
        const render=function render(){
            //循环数据中的每一项,每循环一次,动态创建一个P,并且插入到BOX当中,P中的内容是每一项的namee/status组成的
            //使用`模板字符串拼接`,实现内容拼接

            //性能不好 
            // info.forEach(item=>{
            //  
            //     // let p=document.createElement('p');//动态创建p标签
            //     // p.innerHTML=`${item.name}:${item.status}`;//<p>张三:未签到</p>
            //     // box.appendChild(p);//追加到box里
            // });

            //推荐使用
            let str='';
            info.forEach(item=>{
                str+=`<p>${item.name}:${item.status}<p>`;
            });
            box.innerHTML=str;
        };

        //加载页面就执行一次方法,把现有数据渲染到页面中
        render();


        //@2点击按钮进行相关处理
        submit.onclick=function(){
            //文本框.value:获取文本框中的内容[返回值是string字符串类型]
            let val=signInput.value;
            //到info数组中查找出name和val相同的这一项
            let cur=info.find(item=>{
                //依次迭代数组中的每一项,查找出符合条件这一项,
                //  +如果查找到了,则cur是找到的这一项 [本案例中是一个对象]
                //  +如果循环完都没找到,则cur是undefined
                return item.name===val;//就是个条件
            });

            //如果没找到 则提示错误信息
            if(!cur){
                tip.style.display='block';
                return;
            }
            //找到了,则修改数据和重新渲染
            tip.style.display='none';
            cur.status='已签到'
            render();
        }; 
    </script>
</body>

</html>

4、看需求文档,实现相关的JS代码(10分)

在珠峰毕业后,你成功进入了一家上市公司,公司专门是为用户提供 “车牌号摇号” 服务的;现在公司的领导想让你实现一个随机摇号功能: 1、车牌号开始必须是:京A、京B、京C、京E、京F、京G、京H、京J、京K (没有D和I) 2、车牌号是五位,分别由26个大写字母0~9十个数字组成(可以重复) 3、当用户点击页面上的摇号按钮,你编写的程序给用户随机生成一个车牌号,用户感觉不满意,还可以重新摇号,但是最多只能摇三次

image.png

<div class='box'>
    <!--P:存放生成的车牌号-->
    <p id='licensePlate'></p>
    <input type='button' value='摇号' id='shakeBtn'/> 
</div>
<script>
    //=>完成你的JS代码,实现老板安排的需求
</script>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div class='box'>
        <!--P:存放生成的车牌号-->
        <p id='licensePlate'></p>
        <input type='button' value='摇号' id='shakeBtn' />
    </div>
    <script>
        (function () {
            let licensePlate = document.querySelector("#licensePlate"),
                shakeBtn = document.querySelector('#shakeBtn');

            //次数限制
            let n=0;
            shakeBtn.onclick = function () {
                n++;
                if(n>3){
                    alert('只能摇号三次');
                    return;
                } 
                //str:存放生成的车牌号 Area1:京?的取值范围   area2:车牌号的取值范围
                let str = `京`,
                    area1 = 'ABCDEFGHJK';//0~8
                    area2 = '0123456789QWERTYUIOPASDFGHJKLZXCVBNM';//0~35
                //area1里随机取出1个 area2随机取出5个
                let ran=Math.round(Math.random()*(8-0)+0);
                    str+=area1.charAt(ran)+" ";
                //循环五次
                new Array(5).fill(null).forEach(item=>{
                    ran=Math.round(Math.random()*(35-0)+0);
                    str+=area2.charAt(ran);
                })
                licensePlate.innerHTML = str;
            };
        })();
    </script>
</body>

</html>