JavaScript面试题

本文深入探讨JavaScript中的关键概念,包括原型链、闭包、不同对象创建方式的影响、函数声明与表达式的区别,以及严格模式的用途。通过实例解析,帮助读者理解这些概念的实际应用。

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

原文链接:https://mp.weixin.qq.com/s/IRugR0BJi6FkI67t7ndQfw

问题:描述JavaScript中的继承和原型链,并举例子

虽然JavaScript是一种面向对象的语言,但它是基于原型的,而且并没有提供传统的基于类的继承系统。

在JavaScript中,每个对象都会在内部引用一个叫作prototype的对象。这个原型对象也会引用自己的原型对象,并以此类推。原型链的末尾是一个以null为原型的对象。JavaScript就是通过原型链机制来实现继承的-----更确切地说是原型式的继承。当一个对象引用了不属于自己的属性时,将遍历原型链,直到找到引用的属性为止。

举个简单的栗子:

function Animal(){
    this.eatsVeggies = true;
    this.eatsMeat = false;
}

function Herbivore(){}
Herbiover.prototype = new Animal();

function Carnivore() {
   this.eatsMeat = true;
}
Carnivore.prototype = new Animal();

var rabbit = new Herbiover();
var bear = new Carnivore();

console.log(rabbit.eatsMeat);//logs "false"
console.log(bear.eatsMeat);// logs "true"

问题:在下面的代码片段中,alert将会显示什么?

var foo = new Object();
var bar = new Object();
var map = new Object();

map[foo] = "foo";
map[bar] = "bar";

alert(map[foo]);

很少有人能够给出正确答案------“bar” 。大多数人会错误地认为是 “foo” ,所以让我们来看看为什么“bar”是正确答案

JavaScript对象本质上是键值对哈希表,其中名称(即键)总是字符串。事实上,当字符串以外的对象被用作键时,并不会发生错误,JavaScript会隐式地将其转换为字符串,并将该值用作键。map对象不会将对象foo映射到字符串“foo”,也不会将对象bar映射到字符串“bar”。由于对象foo和bar 不是字符串,所以当它们用作map的键时,JavaScript会自动调用每个对象的toString()方法,那么就使用默认的实现。默认的toString()方法在被调用时生成字符串"[object object]"。

var foo = new Object();
var bar = new Object();
var map = new Object();

map[foo] = "foo";  //-->map["[Object object]"] = "foo";
map[bar] = "bar";  //-->map["[Object object]"] = "bar";
                   //NOTE: second mapping Replaces first mapping!

alert(map[foo]);   //-->alert(map["[Object object]"]);
                   //and since map["[Object object]"] = "bar",
                   //this will alert "bar",not "foo"!!

问题:请解释JavaScript中的闭包。什么是闭包?它们有什么独特的特性?如何以及为什么要使用它们?

闭包是一个函数,包含在创建闭包时处于作用域内的所有变量或其他函数。在JavaScript中,闭包通过“内部函数”的形式来实现,也就是在另一函数的主体内定义的函数。举个简单的栗子:

(function outerFunc(outerArg){
   var outerVar = 3;
   (function middleFunc(middleArg){
      var middleVar = 4;

      (function innerFunc(innerArg){
         var innerVar = 5;
         //example of scope in closure:
         //Variables from innerFunc, middleFunc, and outerFunc,
         //as well as the global namespace, are ALL in scope here
         
         console.log("outerArg="+outerArg+        //outerArg=123
                     " middleArg="+middleArg+     //middleAre=456
                     " innerArg="+innerArg+"\n"+  //innerArg=789
                     " outerVar="+outerVar+       //outerVar=3
                     " middleVar="+middleVar+     //middleVar=4
                     " innerVar="+innerVar);      //innerVar=5
      })(789);
   })(456);
})(123);

闭包的一个重要特性是,即使是在外部函数返回后,内部函数仍然可以访问外部函数的变量。这是因为,在JavaScript中,当函数被执行时,它们任然使用创建函数时有效的作用域。

然而,如果内部函数在被调用时(而不是在创建时)访问外部函数变量的值,就会导致混淆。动态创建五个按钮,当用户点击第三个时显示什么?

function addButtons(numButtons){
   for(var i=0; i<numButtons; i++){
      var button = document.createElement('input');
      button.type='button';
      button.value='Button '+(i+1);
      button.onclick=function(){
         alert('Button '+(i+1)+ ' clicked');
      };
      document.body.appendChild(button);
      document.body.appendChild(document.createElement('br'));
   }
}

window.onload = function(){ addButtons(5); };

很多人会错误地回答,当用户点击第三个按钮时,会显示“Button 3 clicked”。实际上,上面的代码包含了一个错误(基于对closure的误解),当用户点击五个按钮中的任何一个,都将显示“Button 6 clicked”。这是因为,在调用onclick方法时,for循环已经完成并且变量i的值已经是5

如何解决上述代码中的错误,以便产生预期的行为(即点击按钮n将显示 “Button n clicked”)?

function addButton(numButtons){
   for(var i=0; i< numButtons;i++){
      var button = document.createElement('input');
      button.type = 'button';
      button.value = 'Button '+(i+1);
      //Here's the fix:
      //Employ the Immediately-Invoked Function Expression (IIFE)
      //pattern to achieve the desired behavior:
      button.onclick = function(buttonIndex){
         return function(){
            alert('Button ' + (buttonIndex+1)+ ' clicked');
         }
      }(i);
      document.body.appendChild(button);
      document.body.appendChild(document.createElement('br'));
   }
}

window.onload=function() { addButtons(5);}

作为多范式语言,JavaScript支持面向对象、命令式和函数式编程风格。因此,JavaScript提供了异常广泛的编程技术和设计模式

问题:描述创建对象的不同方式及其各自的影响,并提供示例。

问题:将函数定义为函数表达式(例如 var foo = function(){})或定义为函数语句(例如 function foo(){})是否存在实际差异?

根据函数的赋值方式和时间,它们之间存在不同

在使用函数语句(例如 function foo(){})时,函数foo可以在定义之前被引用(通过“hoisting”技术)。hoisting技术带来的结果是,函数的最后一个定义将被使用,不管它什么时候被引用。

相比之下,在使用函数表达式(例如 var foo = function(){})时,函数foo在定义之前不能被引用,就像任何其他赋值语句一样。因此,函数的最新定义会被使用(因此定义必须位于引用之前,否则函数就是未定义的)。

function foo() { return 1; }

alert(foo());

function foo() { return 2; }

如上所述,这是由于hoisting导致的。由于使用了函数语句来定义函数,函数的最后一个定义是在调用它时被“提升”的那个,所以输出为‘2’

var foo = function() { return 1; }

alert (foo());   //  输出为   1 

foo = function() {return 2; }

问题:将JavaScript源文件的全部内容封装在一个函数中,这样做的重要性和原因是什么?

这是一种日益普遍的做法,被许多流行的 JavaScript 库(jQuery、Node.js 等)所采用。这将为文件的全部内容创建一个闭包,也就是创建了一个私有命名空间,有助于避免不同JavaScript模块和库之间潜在的命名冲突。

这种技术的另一个特点是为全局变量提供一个容易引用(可能更短)的别名。例如,jQuery插件通常会使用这种技术。jQuery允许通过jQuery.noConflict()来禁用对jQuery命名空间$的引用。如果是这样,代码任然可以通过$来使用闭包,如:

(function($) { /* jQuery plugin code referencing $ */})(jQuery)

问题:== 和 === 有什么区别? != 和 !== 又有什么区别?

JavaScript中的“三重”比较运算符(===和 !==)和双重比较运算符(==和 != )之间的区别在于,双重运算符在对比之前会对操作数执行隐式类型转换,而使用三重比较运算符时不进行类型转换

表达式  123==‘123’   输出true    而  123===‘123’  则输出false

问题:在JavaScript源文件的开头包含“use strict” 的意义是什么?

使用 use strict 是一种自动在运行时对 JavaScript 代码执行严格解析和错误处理的方法。原先被忽略的代码问题在这个时候会产生错误或抛出异常。总的来说,这是一个很好的做法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值