一、问答题(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分)
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, (i + 1) * 1000);
}
不能。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
8、下面代码的输出结果(4分)
{
function foo() {}
foo = 1;
function foo() {}
}
console.log(foo);//1
var test = (function (i) {
return function () {
alert(i *= 2);
}
})(2);
test(5);//'4'
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
三、编程实战(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、实现函数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
//下面是对fn函数的箭头函数方式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; }); }; };
const fn = (...params) => (...args) => params.concat(args).reduce((result, item) => result + item);
3、完成简单的签到系统(10分)
数组info存放了班级中学生的签到信息,每一项代表一个同学,name属性是姓名。将info里的数据渲染到页面上。
然后在输入框里填写学生姓名,点击签到按钮,可改变相应同学的签到状态。
如果没有该学生,则提示“请输入正确的姓名”5过
想法: @第一步 实现一个方法 根据数组info,控制box盒子中的内容展示 具体:数据中有多少条数据,我们就在BOX中放少个P标签,每一个P标签中包含了每一个对象中的name/status
@2 第二步 点击签到按钮,更改info的数据
+获取文本框输入的内容,例如”张三” +到info中,找到name和输入的值”相同的这一项[对象] +把找到的对象中的status修改为”已签到” +如果没有找到名字匹配的项,则让提示信息显示,反之让隐藏 +最后按照最新的数据,重新渲染页面(把第一步的事情再干一遍)
<!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、当用户点击页面上的摇号按钮,你编写的程序给用户随机生成一个车牌号,用户感觉不满意,还可以重新摇号,但是最多只能摇三次
<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>