JavaScript面试的23个难题

是否想为JavaScript面试做好准备并正在寻找练习的问题?如果是这样-请考虑您的搜索已经结束。该材料的作者(我们今天将发表其翻译)说,他已经收集了关于JavaScript的二十多个问题,这些问题的对象是希望从初级变成高级的人,以及那些希望成功通过前端开发领域的采访并从中获得有趣邀请的人。雇主。


图片

1.解释JavaScript平等验证功能


难度:*


JavaScript有两个用于检查值是否相等的运算符。第一个是所谓的严格相等运算符。第二个是非严格相等运算符,可用于转换检查数量的类型。

  • 严格相等运算符(===)在不执行类型转换的情况下检查值是否相等。
  • 非严格相等运算符(==)检查值是否相等,将其转换为通用类型。

var a = "42";
var b = 42;

a == b;         // true
a === b;        // false

以下是在JavaScript中使用各种相等性检查器的一些准则:

  • 如果任何比较值可以是一个值,true或者false-避免使用运算符==使用运算符===
  • ===如果要使用以下值0请使用运算符«»[](空数组)。
  • 在所有其他情况下,您可以安全地使用运算符==而且,这不仅安全,而且还有助于简化代码并提高其可读性。

来源

2.举例说明强制转换为与该类型无关的逻辑类型的值


难度:***


这个问题的实质是找出在将它们转换为逻辑类型的情况下哪些值变成了false,以及哪个值变成了true

这是可以称为``虚假''的值列表。它们在转换为逻辑类型时会转换为值false

  • «» (空行)。
  • 0-0NaN(非数字)。
  • nullundefined

“假”是一种逻辑含义false

转换为逻辑类型时,此列表中未包含的任何值都会变成true(这样的值称为``真''-真)。例如:

  • «hello»
  • 42
  • [ ][ 1, «2», 3 ](数组)。
  • { }{ a: 42 }(对象)。
  • function foo() { .. } (功能)。

“正确”也是逻辑含义true

来源

3.什么是IIFE?


难度:***


IIFE(立即调用函数表达式)是立即调用的函数表达式。创建后立即执行该表达式。

(function IIFE(){
    console.log( "Hello!" );
})();
// "Hello!"

该模式通常用于防止污染全局名称空间。事实是,在IIFE中声明的变量(与在任何其他普通函数中一样)在该函数之外是不可见的。

来源

4.我什么时候应该使用ES6中出现的箭头功能?


难度:***


这是一些简单的规则,用于使用各种方式声明为支持ES6和更高标准的环境开发代码时遵循的功能:

  • function在全局范围和属性中使用关键字Object.prototype
  • 将关键字function用于对象构造函数。
  • 在其他情况下,请使用箭头功能。

如您所见,建议在几乎所有地方都使用箭头功能。这种情况有多种原因:

  • 结合上下文进行便捷的工作。箭头函数使用this周围上下文的值而没有它们自己的this如果顺序使用这些功能,而不在复杂结构中使用普通功能,则可以确保在上下文中安全工作。
  • 紧凑。箭头功能代码更易于输入和阅读。在您看来,箭头功能相对于普通功能的这种优势将引起争议,并且取决于每个特定开发人员的观点。
  • 代码清晰。如果几乎所有代码都由箭头函数表示,则通过创建其自身的上下文来区分此类代码中的任何普通函数。程序员使用箭头功能创建了更容易理解的代码,与没有箭头功能的代码相比,使用起来更容易this

来源

5. ES6类和函数构造函数有什么区别?


难度:***


让我们先来看一些例子。

构造函数:

function Person(name) {
  this.name = name;
}

ES6课程:

class Person {
  constructor(name) {
    this.name = name;
  }
}

在创建简单对象时,用于此目的的构造函数和类看起来非常相似。

使用继承时,构造函数和类之间的主要区别出现。如果我们需要创建一个作为该类Student的子类的类Person,并向该新类添加字段studentId,则这就是使用构造函数的代码和使用这些类的代码的样子。

构造函数:

function Student(name, studentId) {
  //      ,   .
  Person.call(this, name);

  //    .
  this.studentId = studentId;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

ES6课程:

class Student extends Person {
  constructor(name, studentId) {
    super(name);
    this.studentId = studentId;
  }
}

来源

6.向我们介绍Function.prototype.bind()方法。


难度:***


引用MDN:“该方法bind()创建一个新函数,该函数在调用时会将this提供的值设置为执行上下文参数集也将传递给方法,该方法将在调用参数时将其设置为传递给绑定函数。

我相信这种方法。bind()对于this在需要传递给其他函数的类方法中绑定值特别有用该技术通常在React组件中使用。

来源 

7.匿名函数通常用于什么?


难度:***


匿名函数用于创建IIFE构造,在其中声明的变量不会污染全局范围。

(function() {
  // - .
})();

匿名函数用作回调函数,仅在程序的一个位置使用。如果在使用它的地方立即声明了回调,则代码将看起来更加自给自足且可读性强。这消除了在搜索功能主体时查看代码的需要。

setTimeout(function() {
  console.log('Hello world!');
}, 1000);

匿名函数可方便地用于特定于函数式编程风格的构造中,或在使用Lodash之类的库时使用(此用例类似于其用作回调的用法)。

const arr = [1, 2, 3];
const double = arr.map(function(el) {
  return el * 2;
});
console.log(double); // [2, 4, 6]

来源

8. Object.freeze()方法和const关键字有什么区别?


难度:***


关键字const和方法Object.freeze()是完全不同的东西。

关键字const适用于绑定(适用于“变量”)。它创建了一个不可变的绑定,也就是说,const不可能将新内容绑定到使用keyword声明的变量(常量)无法为常数分配新值。

const person = {
    name: "Leonardo"
};
let animal = {
    species: "snake"
};
person = animal; // Uncaught TypeError: Assignment to constant variable.

该方法Object.freeze()适用于值。或更确切地说,具有对象值。它使对象不可变,从而防止该对象的属性值发生变化。

let person = {
    name: "Leonardo"
};
Object.freeze(person);
person.name = "Lima"; // Uncaught TypeError: Cannot assign to read only property 'name' of object
console.log(person);

请注意,错误消息以严格模式显示。在普通模式下,更改“冻结”对象的属性的操作根本不起作用。

来源

9.什么是“发电机”?


难度:***


生成器是一些函数,您可以从中“退出”这些函数,并且可以根据需要“进入”这些函数。它们的上下文(变量绑定)在“进入”会话之间保持。生成器使用关键字声明function*初次调用此函数时,它不会执行代码,而是返回一个特殊的对象,即生成器,使您可以控制其执行。要获取生成器发出的下一个值,您需要调用其method next()因此,功能代码将执行到遇到yield返回值的关键字为止

生成器函数可以根据需要多次调用。每次有新的发电机返回。但是每个发电机只能旁路一次。

function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
    let iterationCount = 0;
    for (let i = start; i < end; i += step) {
        iterationCount++;
        yield i;
    }
    return iterationCount;
}

来源

10.什么时候应该使用发电机?


难度:***


简而言之,如果描述发生器的主要有用功能,那么它们将具有以下特征:

  • 使用生成器的代码确定接收下一个值的时间。生成器仅负责返回值,它是从外部控制的。
  • 有异步发电机。它们使您可以处理异步数据流。

生成器中的主要内容是,只有在使用生成器的代码中需要生成器返回的下一个值时,才可以获取它。生成器不会一次返回所有内容。在某些情况下,此功能可能非常方便。

来源

11.什么是“增加变量”?


难度:****


“提高变量”概念的实质是广告“上升”到当前范围的顶部。因此,可以在声明变量之前使用该变量。仅引发变量声明,而不引发其初始化代码。请注意,使用关键字声明的变量的行为var不同于使用let声明的变量和常量的行为const

来源

12.以下代码将输出什么?


难度:****


var output = (function(x) {
  delete x;
  return x;
})(0);

console.log(output);

此代码将输出0该运算符delete用于删除对象的属性。而且x-这不是对象属性-它是局部变量。运算符delete不影响局部变量。

来源

13.以下代码将输出什么?


难度:****


var Employee = {
  company: 'xyz'
}
var emp1 = Object.create(Employee);
delete emp1.company
console.log(emp1.company);

此代码将输出xyz属性company不是对象emp1的属性,而是其原型的属性。操作员delete不会删除对象的原型属性。对象emp1没有自己的属性company您可以通过以下方式对此进行验证:

console.log(emp1.hasOwnProperty('company')); // false

如果我们仍然需要删除此属性,则可以通过直接联系对象Employeedelete Employee.company)或emp1使用对象的属性__proto__delete emp1.__proto__.company联系对象的原型来实现

来源

14.告诉我们有关原型设计模式的信息。


难度:****


原型是一种通用的设计模式。它用于创建对象。使用它创建的对象包含从其原型(从示例对象)复制的值。该模板也称为属性模板。

使用``原型''模式的一个示例是使用数据库中存储的标准值初始化某些对象。原型中记录的此类值将被复制到新对象而无需访问数据库。

应当指出,这种模式在古典语言中很少使用。JavaScript使用原型继承模型。此模式用于设计新对象及其原型。

来源

15.什么是ES6中的“临时死区”?


难度:****


该ES6进行的变量提升和常量使用关键字声明letconst(这样做是和实体的崛起,通过使用关键字声明varclassfunction)。但是,代码具有从输入范围到声明变量或常量的区域。在该区域中访问变量或常量时,将生成错误。这就是“临时死区”(TDZ)。

//console.log(aLet)  //  ReferenceError

let aLet;
console.log(aLet); // undefined
aLet = 10;
console.log(aLet); // 10

在此示例中,TDZ在声明之后结束aLet,但在赋值之后结束aLet

来源

16.您能否描述forEach()和map()数组方法之间的主要区别?在哪种情况下,您更喜欢这些方法中的一种?


难度:****


为了了解这些方法之间的区别,让我们谈谈它们各自的功能。

运作方式.forEach()如下:

  • 它遍历数组的元素。
  • 它为数组的每个元素执行传递给它的回调函数。
  • 它不返回任何东西。

const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
  //  -  num /  index.
});

// doubled = undefined

这是该方法的简要说明.map()

  • 它遍历数组的元素。
  • 它将原始数组的每个元素转换为新数组的元素,并为原始数组的每个元素调用传递给它的函数。

const a = [1, 2, 3];
const doubled = a.map(num => {
  return num * 2;
});

// doubled = [2, 4, 6]

结果,发现.forEach()之间的主要区别在于.map().map()返回了一个新数组。如果需要在不更改此数组的情况下转换原始数组的元素的结果,则应选择.map()如果只需要遍历数组的元素,则可以使用它.forEach()

来源

17.未声明的变量,包含空值的变量和未定义的变量有什么区别?如何检查变量未声明的事实,以及null和undefined?


难度:****


当值被分配给以前未使用声明的标识符,创建未声明的变量varletconst。未声明的变量在当前范围之外的全局范围内声明。在严格模式下,尝试为未声明的变量赋值时会引发异常ReferenceError。不建议使用未声明的变量-就像不建议使用全局变量一样。一定要避免它们。为了保护自己免受使用未声明变量的影响,请使用block try/catch

function foo() {
  x = 1; //     ReferenceError
}

foo();
console.log(x); // 1

包含undefined的变量是未分配值的声明变量。值undefined形成自己的数据类型。如果该函数不返回任何内容,并且其调用结果被写入变量,则它将落入该变量中undefined。为了组织检查undefined,可以使用严格相等运算符(===)或typeof返回字符串的运算符undefined。请注意,在进行检查时,undefined您不应使用非严格相等运算符(==),因为它会将undefinedand 值视为相等null

var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true

console.log(foo == null); // true.        undefined!

function bar() {}
var baz = bar();
console.log(baz); // undefined

包含值的变量null必须明确设置为此值。它象征着意义的缺失,它与undefined-variable的不同之处在于,其中的值已明确分配给它。为了检查on的值null,使用严格的相等运算符就足够了。要进行检查null,就像在进行检查的情况一样undefined,不应使用非严格相等运算符,该运算符会考虑of nullequal的值undefined

var foo = null;
console.log(foo === null); // true
console.log(typeof foo === 'object'); // true

console.log(foo == undefined); // true        null!

我尽量不要让变量处于未声明状态或声明状态,但不会为它们明确分配任何值。如果我不打算在变量声明后立即将值写入变量,则将其写入null如果使用lint,通常会报告使用未声明变量的情况。

来源

18.向我们介绍设计模块“打开模块”


难度:*****


“显示模块”模板是“模块”模板的变体。使用此模式的目的是支持封装并发现对象文字中返回的一些属性和方法。以下是此模板的直接实现:

var Exposer = (function() {
  var privateVariable = 10;

  var privateMethod = function() {
    console.log('Inside a private method!');
    privateVariable++;
  }

  var methodToExpose = function() {
    console.log('This is a method I want to expose!');
  }

  var otherMethodIWantToExpose = function() {
    privateMethod();
  }

  return {
      first: methodToExpose,
      second: otherMethodIWantToExpose
  };
})();

Exposer.first();        // : This is a method I want to expose!
Exposer.second();       // : Inside a private method!
Exposer.methodToExpose; // undefined

此模板的明显缺点是使用模板时不能使用私有方法。

来源

19. Map和WeakMap对象之间有什么区别?


难度:*****


如果包含对对象的引用(是键/值对之一)的变量的变量不可用,则这些对象的行为会有所不同。这是一个例子:

var map = new Map();
var weakmap = new WeakMap();

(function() {
    var a = {
        x: 12
    };
    var b = {
        y: 12
    };

    map.set(a, 1);
    weakmap.set(b, 2);
})()

IIFE的执行完成后,我们将无法再访问对象ab因此,垃圾收集器b从中删除密钥weakmap并清除内存。但是内容map保持不变。

结果,事实证明对象WeakMap允许垃圾收集器摆脱那些在外部变量中未引用的记录。对象map存储键/值对,而不管是否存在外部键引用。可以说Map使用普通数组实现数据结构也是如此使用WeakMap“弱”键引用。如果没有其他用作键的对象的引用,它们不会干扰垃圾收集器的操作。

来源

20.如何将参数传递给JavaScript函数:按引用还是按值?


难度:*****


参数始终按值传递,但对对象的引用将写入表示对象的变量。因此,当将对象转移到函数并更改该对象的属性时,即使函数退出,此更改也会保存在对象中。结果,感觉到函数中的参数是通过引用传递的。但是,如果更改表示对象的变量的值,则此更改将不会影响该函数外部的对象。

这是一个例子:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

以下是此代码将输出的内容:

10
changed
unchanged

来源

21.如何组织对象的“深度冻结”?


难度:*****


为了使用来“深度冻结”对象Object.freeze(),您需要创建一个递归函数来“冻结”对象的属性,这些属性也是对象。

这是一个普通的对象“冻结”的示例:

let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
Object.freeze(person); //   
person.profession.name = "doctor";
console.log(person); // { name: 'Leonardo', profession: { name: 'doctor' } }

这是“深度冻结”:

function deepFreeze(object) {
    let propNames = Object.getOwnPropertyNames(object);
    for (let name of propNames) {
        let value = object[name];
        object[name] = value && typeof value === "object" ?
            deepFreeze(value) : value;
    }
    return Object.freeze(object);
}
let person = {
    name: "Leonardo",
    profession: {
        name: "developer"
    }
};
deepFreeze(person);
person.profession.name = "doctor"; // TypeError: Cannot assign to read only property 'name' of object

仅在严格模式下显示错误消息。在正常模式下,该值不会更改而不会出现错误消息。

来源

22.为什么JavaScript程序员在使用this关键字时会遇到麻烦?


难度:*****


要了解的最重要的事情this是函数没有固定的值this该值取决于函数的调用方式。如果我们说用某个特定值调用函数this,则意味着此值不是在函数声明期间确定的,而是在函数调用期间确定的。以下是一些功能this

  • 如果以通常的形式调用该函数(即使用view结构someFunc()),则this它将引用全局对象(在浏览器中为this window)。如果代码以严格模式执行,this则将值写入undefined
  • 如果将该函数作为对象的方法调用,则关键字this将由该方法所属的对象表示。
  • call apply, this , call apply.
  • , this .
  • , new, this , prototype -.
  • 如果函数是使用bind方法创建的,那么thisfunction 关键字将严格绑定到bind作为第一个参数传递的值这是函数没有硬编码值的规则的唯一例外this使用创建的函数bind是不可变的this

来源

23.比较使用异步/等待构造和生成器来实现相同的功能


难度:*****


  • 使用方法迭代生成器时,.next()对此方法的每次调用都会使用关键字返回单个值yield使用async / await构造时,将按顺序执行await表达式。
  • 异步/等待设计简化了特定生成器用例的实现。
  • 生成器返回的值始终具有形式{value: X, done: Boolean},并且异步函数返回使用值解析的承诺X,否则将失败。
  • 可以使用promise将异步函数转换为生成器。以下是这种转换的示例。

这是异步函数:

//  
async function init() {
    const res1 = await doTask1();
    console.log(res1);

    const res2 = await doTask2(res1);
    console.log(res2);

    const res3 = await doTask3(res2);
    console.log(res3);

    return res3;
}

init();

这是一个类似的发电机。

//    
function runner(genFn) {
    const itr = genFn();

    function run(arg) {
        let result = itr.next(arg);

        if (result.done) {
            return result.value;
        } else {
            return Promise.resolve(result.value).then(run);
        }
    }

    return run;
}

//   runner    
runner(function* () {
    const res1 = await doTask1();
    console.log(res1);

    const res2 = await doTask2(res1);
    console.log(res2);

    const res3 = await doTask3(res2);
    console.log(res3);

    return res3;
});

资料来源

尊敬的读者!您在面试中提出了哪些JavaScript问题?


All Articles