JavaScript 专项练习(三)

本文深入探讨了JavaScript中的变量作用域、函数表达式、循环、事件处理、原型链、作用域链、异步执行、内存管理以及类型转换等方面的知识,通过实例解析了常见编程陷阱和理解难点,帮助读者深化对JavaScript运行机制的理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、写出程序运行的结果()

var k = 0;
for(var i=0,j=0;i<10,j<6;i++,j++){
    k += i + j;
}
console.log(k)

答案:30,解析:

考察知识点:逗号表达式只有最后一项是有效的,即对于i<10,j<6; 来说,判断循环是否结束的是j < 6;而对于 j<6,i<10; 来说,判断循环是否结束的是 i < 10

k = 0+0=0 1+1+0=2 2+2+2=6 3+3+6=12 4+4+12=20 5+5+20=30 下一步j=6循环结束


2、在JavaScript中,调用对象属性的描述中,以下代码错误的是( )例如:调用对象obj的arr属性。

obj["arr"]
obj["a"+"r"+"r"]
obj{"arr"}
obj.arr

答案:C,解析:

AB本质是一致的,B多做了一步就是字符串拼接。
访问obj的方式有两种 :

  1. 点的方式obj.arr;
  2. 中括号的方式obj["arr"];

3、问:控制台打印的结果是?

for(let i=0;i<2;i++){
    setTimeout(function(){
        console.log(i)
    },100);
}
for(var i=0;i<2;i++){
    setTimeout(function(){
        console.log(i)
    },100);
}

答案:0122,解析:

①Js是单线程的,setTimeout是异步宏任务,所以代码执行遇到异步的,就放在事件队列中的,等线程中的任务执行完后才会执行事件队列中的任务。
let是 es6 中声明变量的方式,有自己的作用域块,可以放变量,所以let绑定for 循环时,每个i都有自己的值. 在这个for循环中就是满足一次条件向事件队列中添加一个打印i的事件,且每个事件中的i有自己的值
③ Var 没有作用域块,for循环 的变量就会后一个覆盖前一个,当循环完毕时i就只有一个值,又因为 for循环 的判断条件是不满足跳出,所以i最后是2而不是1
④这些完了后执行事件队列中的任务,就打印了 0122


4、运行以下程序,y和z的最终结果为:

<script> 
    var m= 1, j = k = 0; 
    function add(n) { 
        return n = n+1; 
  } 
    y = add(m); 
    function add(n) { 
        return n = n + 3; 
    } 
z = add(m); 
</script> 

答案:4,4 解析:

js里面没有函数重载的概念,在其他语言中(如java)java中,可以存在同名函数,只要传入的参数数量或者类型不同即可。
在js中,定义了两个同名函数后,后面的函数会覆盖前面定义的函数。

结合这道题来说,由于函数声明提升,所以函数声明会提前,由于存在同名函数,后面的add函数将覆盖第一个 add 函数,所以两次调用 add() 返回的值是相同的。也就是 y,z 都为4.


5、以下代码执行后,a.x 和 b.x 的结果分别是?

 function A(x){
this.x = x;
}
A.prototype.x = 1;

function B(x){
this.x = x;
}
B.prototype = new A();
var a = new A(2), b = new B(3);
delete b.x;

答案:2, undefined 解析:

知识点梳理

如果有x但是没有赋值,则是 undefined ,相当于 x=undefined. 就不会进入原型链

答案解析

function A(x){
   this.x = x;
}
A.prototype.x = 1; //将A原型上的x设置为1
function B(x){
   this.x = x;
}
B.prototype = new A(); //将B原型上的x设置为 A的一种object. 所以B实例出来object的prototype就是{x:undefined}
var a = new A(2),//a.x首先要在自己的构造函数中查找,没有采取原型上找,这里有this.x = x.所以a.x = 2;
    b = new B(3);//此时 b.x = 3 ;但是因为上面[B.prototype = new A()],所以形成原型链,其父级prototype={x:undefined}
delete b.x; //删除实例b上的[x]属性,但是delete只能删除自己的x不能删除父级的x.
console.log(a.x,b.x)

var a = new A(2) 很容易理解,就是 a.x==2
看上面代码可以得知, 在进行到b = new B(3)这步的时候 b 的原型 prototype={x:undefined}
delete b.x; 删除实例 b上的[x]属性, 但是 delete 只能删除自己的 x 不能删除父级的 x . 所以我们查看b.x时会走到 b 的原型上就是 {x:undefined}这里也是很容易踩到的陷阱,只要有这个属性,即便是undefined也不会在原型上再往上找了

陷阱:

B的prototype=new A(), 想错的可能都认为构造函数的 [ x ]没有传值进去,this.x赋值为undefined, 所以还会去A的prototype原型上找,找到 x = 1 -->错误
实际上是:如果构造函数 没有x才会去原型下找
如果有x但是没有赋值,则是undefined,相当于x=undefined. 就不会进入原型链了


6、假设 output 是一个函数,输出一行文本。下面的语句输出结果是什么?

output(typeof (function() {output(“Hello World!)})());
undefined Hello World!
Object
string
function
Hello World! undefined

答案:E 解析:

1.先立即执行匿名函数,输出 Hello World!
2.函数执行后无返回值,则输出未定义

任何函数执行完一次,如果没有 return 返回值和声明变量接受返回值,都会立即消失,永远找不到值!

7、执行下面的代码,将会输出什么?

(function() {

    var x=foo();

    var foo=function foo() {
        return "foobar"
    };
    return x;
})();
foo()
类型错误
undefined
foobar

答案:B 解析:

foo变量“被提前”了,但是他的赋值(也就是函数)并没有被提前,从这一点上来说,和前面我们所讲的变量“被提前”是完全一致的,并且,由于“被提前”的变量的默认值是 undefined
函数声明可以被提前,但函数表达式不能被提前

var x = foo();
var foo=function foo() {...}

语句中变量的声明会提升,但是定义不会提升。以上代码等同于:

var foo;
var x = foo();
foo = function foo() {...}

当执行到 x = foo() 时,由于foo未被定义为函数,所以会返回

TypeError: foo is not a function

8、下面的输出结果:

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

答案:5,Uncaught ReferenceError: a is not defined
解析:

第一个考点在于var a=b=5相当于拆解成var a=b; b=5; 然后,b=5 前面没有 var,相当于声明为全局变量(这种方式在严格模式下会报错,此题不考虑)。
所以就相当于:

 var b; 
 (fun…{ var a=b; b=5; })(); 
 console.log(b); //5 
 console.log(a); //报错 

此处报错也就是另一个考点,a 声明的是函数的局部变量,在函数结束是就销毁了,所以在全局下找不到 a ,于是报错。


9、以下哪个语句打印出来的结果时false?

alert(3==true)
alert(2=="2")
alert(null == undefined)
alert(isNaN("true"))

答案:A 解析:

// A:
1 == true   // 布尔值会转成number true即为1 所以结果是true
2 == true   // 布尔值会转成number true即为1 所以结果是false
3 == true   // 布尔值会转成number true即为1 所以结果是false
1 == false  // 布尔值会转成number false即为0 所以结果是false
0 == false  // 布尔值会转成number false即为0 所以结果是true

B、数字字符串 2 会转换成数字 2 在和数字 2 进行比较 。== js会优先选择将字符串转成数字==

C、Javascript规范中提到, 要比较相等性之前,不能将nullundefined转换成其他任何值,并且规定null == undefined是相等的。

D、isNaN()函数用于检查其参数是否是非数字值。
如果参数值为 NaN或字符串、对象、undefined等非数字值则返回 true, 否则返回 false


10、以下哪些事件支持冒泡?

mouseenter
scroll
focus
keypress

答案:BD 解析:

不能被冒泡的9个事件:
① load 和 unload
② mouseenter 和 mouseleave
③ blur 和 focus
④ error
⑤ resize 和 abort

从3个角度说可分为 ui事件、鼠标移入移出事件、聚焦和失焦件,


11、以下关于Histroy对象的属性或方法描述正确的是()

back回到浏览器载入历史URL地址列表的当前URL的前一个URL
go表示刷新当前页面
length保存历史URL地址列表的长度信息
forward转到浏览器载入历史URL地址列表的当前URL的下一个URL

答案:AD 解析:

考察的是浏览器的内置对象管理模型,简称BOM(Browser Object Model)中的Histroy属性和方法。

History 对象

History 对象包含用户(在浏览器窗口中)访问过的 URL。
History 对象是 window 对象的一部分,可通过 window.history属性对其进行访问。
注意: 没有应用于 History 对象的公开标准,不过所有浏览器都支持该对象。

History 对象属性

属性说明
length返回历史列表中的网址数

History 对象方法

方法说明
back()加载 history 列表中的前一个 URL
forward()加载 history 列表中的下一个 URL
go()加载 history 列表中的某个具体页面

12、下列代码存在几个变量没有被回收?( )

var i = 1;
var i = 2;
var add = function() {
    var i = 0;
    return function()
{
        i++;
        console.log(i);
    }
}();
add();

答案:3个 解析:

全局变量 i = 1,赋值被替换为 2。
全局变量 add 返回值为一个函数
add() 执行内部函数后,产生闭包,函数内部变量 i 无法被回收。

代码回收规则如下:

1.全局变量不会被回收。

2.局部变量会被回收,也就是函数一旦运行完以后,函数内部的东西都会被销毁。

3.只要被另外一个作用域所引用就不会被回收(闭包)


13、现有如下html结构,依次点击4个li标签,正确的运行结果是()?

<body>
    <ul>
        <li>click me</li>
        <li>click me</li>
        <li>click me</li>
        <li>click me</li>
    </ul>
    <script>
        var elements = document.getElementsByTagName('li');
        var length = elements.length;
        for (var i = 0; i < length; i++) {
            elements[i].onclick = function () {
                console.log(i);
            }
        }
    </script>
</body>

答案:4444 解析:

简单分析:

页面加载后会自动把脚本部分执行,执行完脚本,i=4。部分执行指的是,需要用户主动触发才能执行的点击事件没有被执行。当点击 click me 时 触发 onclick事件 此时的i早已是 4。

for循环 当中使用 var 定义的 i 变量作用域是在全局作用域。所以当脚本执行完毕全局环境中的 i = 4,每个li标签的 onclick 事件执行时,本身onclick绑定的function的作用域中没有变量 i,解析引擎会寻找父级作用域,发现父级作用域中有 i = 4。这是作用域的问题。

权威解答——来自javascript语言精粹

var elements=document.getElementsByTagName('li');
    var length=elements.length;
    for(var i=0;i<length;i++){
        elements[i].onclick=function(){
        alert(i);
    }
 }

解答:这里的事件,绑定的并非是i的值,而是i本身(alert里的 i ),所以当程序执行完,i的值变为 4,去执行onclick事件时,执行 alert(i) ,自动查找i,值为 4,所以依次弹出 4。

改正:

var elements=document.getElementsByTagName('li');
    var length=elements.length;
    var handler = function(i){
        return fucntion(){
            alert(i);
        }
    }
    for(var i=0;i<length;i++){
        elements[i].onclick= handler(i);
 }

避免在循环中创建函数,可以在循环之外创建一个辅助函数,让这个辅助函数返回一个绑定了当前i值得函数,避免混淆


一个相关的类似问题,有代码如下:

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

可以猜到代码执行1s后,依次打印10个"10".

几个有效的解决办法

1、利用块级作用域(最简单,推荐)

for(let i=0; i < 10; i++) {               
 setTimeout(function() {
 console.log(i);
 }, 1000);
}
// 依次打印0 - 9

2、利用自执行函数,将 i 作为参数传入

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

3、利用setTimeout的第三个参数,将i作为参数传入function

for(var i=0; i < 10; i++) {
 setTimeout(function(j) {
 console.log(j);
 }, 1000, i);
}

4、利用promise(使用promise的办法,是为了更好地理解promise)

for(var i=0; i < 10; i++) {
 new Promise((resolve, reject) => {
 var j = i;
 setTimeout(function() {
     console.log(j)
 }, 1000);
 })
}

5、利用async函数(为了理解async函数)

async function foo() {
 for(var i=0; i < 10; i++) {
 let result = await new Promise((resolve, reject) => {
     setTimeout(function() {
     resolve(i);
         }, 1000);
 });
 console.log(result);
 }
}
foo();
// 每隔1s打印数字 0 - 9

14、如下代码输出的结果是什么:

console.log(1+ "2"+"2");
console.log(1+ +"2"+"2");
console.log("A"- "B"+"2");
console.log("A"- "B"+2);

答案:122,32,NaN2,NaN
解析

console.log(1+ "2"+"2");   

A:对于加法来说,如果只有一个操作数是字符串,则将另一个操作数也转换为字符串,然后将两者拼接,为122

console.log(1+ +"2"+"2");   

B:(+“2”)应用了一元加操作符,一元加操作符相当于Number()函数,会将 (+“2”)转换为2,1+2+“2”=32

console.log("A"- "B"+"2");   

C:在减法中遇到字符串和加法相反,调用Number()函数将字符串转换为数字,不能转换则返回NaN,此时运用加法规则,NaN+“2”,"2"是字符串,则将两者拼接。

console.log("A"- "B"+2);   

D:这个与上面的不太相同,减法运算后依然为NaN,但是加号后面的为数字2,加法规则中,如果有一个操作数是NaN,则结果为NaN


15、与其他 IEEE 754 表示浮点数的编程语言一样,JavaScript 的 number 存在精度问题,比如 0.2 + 0.4 的结果是 0.6000000000000001。以下选项中,能得到 0.6 的是?

parseFloat(0.2 + 0.4)
parseFloat((0.2 + 0.4).toFixed(1))
Math.round(0.2 + 0.4)
parseFloat((0.2 + 0.6).toPrecision(1))

答案:B ,解析:

parseFloat 解析一个字符串,并返回一个浮点数
toFixed把数字转换为字符串,结果的小数点后有指定位数的数字
Math.round 把一个数字舍入为最接近的整数
toPrecision 把数字格式化为指定的长度


16、现有一组人员年龄的数据,要求将这些人员的年龄按照从小到大的顺序进行排列起来,要怎样来实现( )

// A
function numberSort(a, b) {
    return a - b;
}
var arr = new Array("23", "6", "12", "35", "76");
document.write(arr.push(numberSort));

// B
function numberSort(a, b) {
    return b - a;
}
var arr = new Array("23", "6", "12", "35", "76");
document.write(arr.push(numberSort));

// C
function numberSort(a, b) {
    return b - a;
}
var arr = new Array("23", "6", "12", "35", "76");
document.write(arr.sort(numberSort));

// D
function numberSort(a, b) {
    return a - b;
}
var arr = new Array("23", "6", "12", "35", "76");
document.write(arr.sort(numberSort));

答案:D 解析:

arr.sort([compareFunction])

一、sort() 方法参数为空(即没有指明 compareFunction) ,元素按照转换为的字符串的诸个字符的 Unicode 位点进行排序,
本题不涉及,不详说。

二、sort() 方法参数指明 compareFunction ,数组会按照调用函数的返回值排序(这句不懂没关系)。
如:

[3, 2, 1].sort(function (a, b) {
    return a - b;
});

它会将数字数组 [3, 2, 1] 按 小到大 排序后返回 更新后的数组 [1, 2, 3]。

执行的机制(两数比较,小的排在大的前面):

  • a - b < 0,那么 a 会被排列到 b 之前;
  • a - b = 0,a 和 b 的相对位置不变。备注: ECMAScript 标准并不保证这一行为,而且也不是所有浏览器都会遵守(例如 Mozilla 在 2003 年之前的版本);
  • a - b > 0,那么 b 排列到 a 之前。

三、如何快速掌握呢?

升序排序 return a - b; 降序排序 return b - a。

学习传送门:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

gxhlh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值