node.js常见问题

JavaScript 是一种有趣的语言,我们都喜欢它,因为它的性质。浏览器是JavaScript的主要运行的地方,两者在我们的服务中协同工作。JS有一些概念,人们往往会对它掉以轻心,有时可能会忽略不计。原型、闭包和事件循环等概念仍然是大多数JS开发人员绕道而行的晦涩领域之一。正如我们所知,无知是一件危险的事情,它可能会导致错误。

接下来,来看看几个问题,你也可以试试想想,然后作答。

问题1:浏览器控制台上会打印什么?

var a = 10;
function foo() {
    console.log(a); // ??
    var a = 20;
}
foo();

问题2:如果我们使用 let 或 const 代替 var,输出是否相同?

var a = 10;
function foo() {
    console.log(a); // ??
    let a = 20;
}
foo();     

问题3:“newArray”中有哪些元素?

var array = [];
for (var i = 0; i < 3; i++) {
    array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // ??  

问题4:如果我们在浏览器控制台中运行'foo'函数,是否会导致堆栈溢出错误?

function foo() {
    setTimeout(foo, 0); // 是否存在堆栈溢出错误?
}

问题5: 如果在控制台中运行以下函数,页面(选项卡)的 UI 是否仍然响应

function foo() {
    return Promise.resolve().then(foo);
}   

问题6: 我们能否以某种方式为下面的语句使用展开运算而不导致类型错误

var obj = { x: 1, y: 2, z: 3 };
[...obj]; // TypeError

问题7:运行以下代码片段时,控制台上会打印什么?

var obj = { a: 1, b: 2 };
Object.setPrototypeOf(obj, { c: 3 });
Object.defineProperty(obj, "d", { value: 4, enumerable: false });
​
// what properties will be printed when we run the for-in loop?
for (let prop in obj) {
    console.log(prop);
}

问题8:xGetter() 会打印什么值?

var x = 10;
var foo = {
    x: 90,
    getX: function() {
        return this.x;
    }
};
foo.getX(); // prints 90
var xGetter = foo.getX;
xGetter(); // prints ??

答案

现在,让我们从头到尾回答每个问题。我将给您一个简短的解释,同时试图揭开这些行为的神秘面纱,并提供一些参考资料。

问题1: undefined

使用var关键字声明的变量在JavaScript中会被提升,并在内存中分配值undefined。 但初始化恰发生在你给变量赋值的地方。 另外,var声明的变量是函数作用域的,而letconst是块作用域的。 所以,这就是这个过程的样子:

var a = 10; // 全局使用域
function foo() {
    // var a 的声明将被提升到到函数的顶部。
    // 比如:var a
​
    console.log(a); // 打印 undefined
​
    // 实际初始化值20只发生在这里
    var a = 20; // local scope
}

问题 2:ReferenceError:a undefined

letconst声明可以让变量在其作用域上受限于它所使用的块、语句或表达式。与var不同的是,这些变量没有被提升,并且有一个所谓的暂时死区(TDZ)。试图访问TDZ中的这些变量将引发ReferenceError,因为只有在执行到达声明时才能访问它们。

var a = 10; // 全局使用域
function foo() {
    // TDZ 开始
​
    // 创建了未初始化的'a'
    console.log(a); // ReferenceError
​
    // TDZ结束,'a'仅在此处初始化,值为20
    let a = 20;
}

下表概述了与JavaScript中使用的不同关键字声明的变量对应的提升行为和使用域:

问题 3: [3, 3, 3]

for循环的头部声明带有var关键字的变量会为该变量创建单个绑定(存储空间)。 阅读更多关于闭包的信息。 让我们再看一次for循环。

// 误解作用域:认为存在块级作用域
var array = [];
for (var i = 0; i < 3; i++) {
    // 三个箭头函数体中的每个`'i'`都指向相同的绑定,
    // 这就是为什么它们在循环结束时返回相同的值'3'。
    array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [3, 3, 3]

如果使用 let 声明一个具有块级作用域的变量,则为每个循环迭代创建一个新的绑定。

// 使用ES6块级作用域
var array = [];
for (let i = 0; i < 3; i++) {
    // 这一次,每个'i'指的是一个新的的绑定,并保留当前的值。
    // 因此,每个箭头函数返回一个不同的值。
    array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [0, 1, 2]

解决这个问题的另一种方法是使用闭包

let array = [];
for (var i = 0; i < 3; i++) {
    array[i] = (function(x) {
        return function() {
            return x;
        };
    })(i);
}
const newArray = array.map(el => el());
console.log(newArray); // [0, 1, 2]  

问题4 : 不会溢出

JavaScript并发模型基于“事件循环”。 当我们说“浏览器是 JS 的家”时我真正的意思是浏览器提供运行时环境来执行我们的JS代码。

浏览器的主要组件包括调用堆栈事件循环**,任务队列Web API**。 像setTimeoutsetIntervalPromise这样的全局函数不是JavaScript的一部分,而是 Web API 的一部分。 JavaScript 环境的可视化形式如下所示:

JS调用栈是后进先出(LIFO)的。引擎每次从堆栈中取出一个函数,然后从上到下依次运行代码。每当它遇到一些异步代码,如setTimeout,它就把它交给Web API(箭头1)。因此,每当事件被触发时,callback 都会被发送到任务队列(箭头2)。

事件循环(Event loop)不断地监视任务队列(Task Queue),并按它们排队的顺序一次处理一个回调。每当调用堆栈(call stack)为空时,Event loop获取回调并将其放入堆栈(stack )(箭头3)中进行处理。请记住,如果调用堆栈不是空的,则事件循环不会将任何回调推入堆栈

现在,有了这些知识,让我们来回答前面提到的问题:

步骤

  1. 调用 foo()会将foo函数放入调用堆栈(call stack)
  2. 在处理内部代码时,JS引擎遇到setTimeout
  3. 然后将foo回调函数传递给WebAPIs(箭头1)并从函数返回,调用堆栈再次为空
  4. 计时器被设置为0,因此foo将被发送到任务队列<Task Queue>(箭头2)。
  5. 由于调用堆栈是空的,事件循环将选择foo回调并将其推入调用堆栈进行处理。
  6. 进程再次重复,堆栈不会溢出。

运行示意图如下所示:

 

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

问题5 : 不会响应

大多数时候,开发人员假设在事件循环<event loop>图中只有一个任务队列。但事实并非如此,我们可以有多个任务队列。由浏览器选择其中的一个队列并在该队列中处理回调<callbacks>

在底层来看,JavaScript中有宏任务和微任务。setTimeout回调是宏任务,而Promise回调是微任务

主要的区别在于他们的执行方式。宏任务在单个循环周期中一次一个地推入堆栈,但是微任务队列总是在执行后返回到事件循环之前清空。因此,如果你以处理条目的速度向这个队列添加条目,那么你就永远在处理微任务。只有当微任务队列为空时,事件循环才会重新渲染页面、

现在,当你在控制台中运行以下代码段

function foo() {
    return Promise.resolve().then(foo);
}

每次调用'foo'都会继续在微任务队列上添加另一个'foo'回调,因此事件循环无法继续处理其他事件(滚动,单击等),直到该队列完全清空为止。 因此,它会阻止渲染。

问题6 : 会导致TypeError错误

展开语法for-of 语句遍历iterable对象定义要遍历的数据。ArrayMap 是具有默认迭代行为的内置迭代器。对象不是可迭代的,但是可以通过使用iterableiterator协议使它们可迭代。

Mozilla文档中,如果一个对象实现了@@iterator方法,那么它就是可迭代的,这意味着这个对象(或者它原型链上的一个对象)必须有一个带有@@iterator键的属性,这个键可以通过常量Symbol.iterator获得。

上述语句可能看起来有点冗长,但是下面的示例将更有意义:

var obj = { x: 1, y: 2, z: 3 };
obj[Symbol.iterator] = function() {
    // iterator 是一个具有 next 方法的对象,
    // 它的返回至少有一个对象
    // 两个属性:value&done。
​
    // 返回一个 iterator 对象
    return {
        next: function() {
            if (this._countDown === 3) {
                const lastValue = this._countDown;
                return { value: this._countDown, done: true };
            }
            this._countDown = this._countDown + 1;
            return { value: this._countDown, done: false };
        },
        _countDown: 0
    };
};
[...obj]; // 打印 [1, 2, 3]

还可以使用 generator 函数来定制对象的迭代行为:

var obj = { x: 1, y: 2, z: 3 };
obj[Symbol.iterator] = function*() {
    yield 1;
    yield 2;
    yield 3;
};
[...obj]; // 打印 [1, 2, 3]

问题7 : a, b, c

for-in循环遍历对象本身的可枚举属性以及对象从其原型继承的属性。 可枚举属性是可以在for-in循环期间包含和访问的属性。

var obj = { a: 1, b: 2 };
var descriptor = Object.getOwnPropertyDescriptor(obj, "a");
console.log(descriptor.enumerable); // true
console.log(descriptor);
// { value: 1, writable: true, enumerable: true, configurable: true }

现在你已经掌握了这些知识,应该很容易理解为什么我们的代码要打印这些特定的属性

var obj = { a: 1, b: 2 }; //a,b 都是 enumerables 属性
​
// 将{c:3}设置为'obj'的原型,并且我们知道
// for-in 循环也迭代 obj 继承的属性
// 从它的原型,'c'也可以被访问。
Object.setPrototypeOf(obj, { c: 3 });
​
// 我们在'obj'中定义了另外一个属性'd',但是
// 将'enumerable'设置为false。 这意味着'd'将被忽略。
Object.defineProperty(obj, "d", { value: 4, enumerable: false });
​
for (let prop in obj) {
    console.log(prop);
}
// 打印
// a
// b

问题8 : 10

在全局范围内初始化x时,它成为window对象的属性(不是严格的模式)。看看下面的代码:

var x = 10; // global scope
var foo = {
    x: 90,
    getX: function() {
        return this.x;
    }
};
foo.getX(); // prints 90
let xGetter = foo.getX;
xGetter(); // prints 10

咱们可以断言:

window.x === 10; // true

this 始终指向调用方法的对象。因此,在foo.getx()的例子中,它指向foo对象,返回90的值。而在xGetter()的情况下,this指向 window对象, 返回 window 中的x的值,即10

要获取 foo.x的值,可以通过使用Function.prototype.bindthis的值绑定到foo对象来创建新函数。

let getFooX = foo.getX.bind(foo);
getFooX(); // 90

就这样! 如果你的所有答案都正确,那么干漂亮。 咱们都是通过犯错来学习的。 这一切都是为了了解背后的“原因”。

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

另一份:
 

1.JS中letconst有什么用?

在现代js中,letconst是创建变量的不同方式。 在早期的js中,咱们使用var关键字来创建变量。 letconst关键字是在ES6版本中引入的,其目的是在js中创建两种不同类型的变量,一种是不可变的,另一种是可变的。

const:它用于创建一个不可变变量。不可变变量是指其值在程序的整个生命周期中永不改变的变量。

let: let用于创建一个可变变量,可变变量是像var这样的普通变量,可以任意次数地更改。

2. JS 中的主要有哪几类错误

JS有三类的错误:

加载时错误:加载web页面时出现的错误(如语法错误)称为加载时错误,它会动态生成错误。

运行时错误:由于滥用HTML语言中的命令而导致的错误。

逻辑错误:这些错误是由于对具有不同操作的函数执行了错误的逻辑而导致的

3. 如何通过类别名获取 dom 元素

在 JS 中使用document.getElementsByClassName() 方法来获取具有类名的元素。

 

 

4.JS的作用域链是什么及其作用

一般情况下,变量取值到创建这个变量的函数的作用域中取值。但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链

JS中的作用域链主要用于解析变量的值。 如果没有这个,在不同的作用域内定义了许多变量,JS很难为变量选择某个值。

5.解释JS中的MUL函数

MUL表示数的简单乘法。在这种技术中,将一个值作为参数传递给一个函数,而该函数将返回另一个函数,将第二个值传递给该函数,然后重复继续。例如:x*y*z可以表示为

function mul (x) {  
 return function (y) { 
   return function (z) {   
     return x * y * z;
   }
  }
}

6.用纯JS编写一个程序来反转字符串

使用内置函数:内置函数reverse()直接反转字符串。

str="jQuery";
str = str.split("")
str = str.reverse()
str = str.join("")
alert(str);

首先将字符串拆分为数组,然后反转数组,最近将字符连接起来形成字符串。

使用循环:首先,计算字符串中的字符数,然后对原始字符串应用递减循环,该循环从最后一个字符开始,打印每个字符,直到count变为零。

7.JS中如何将页面重定向到另一个页面?

  1. 使用 location.href:window.location.href =“https://www.onlineinterviewquestions.com/”
  2. 使用 location.replace: window.location.replace(" https://www.onlineinterviewquestions.com/;");

8. 列出JS中的一些设计模式:

设计模式是软件设计中常见问题的通用可重用解决方案,以下是一些设计模式是:

创建模式:该模式抽象了对象实例化过程。

结构型模式:这些模式处理不同的类和对象以提供新功能。

行为模式:也称发布-订阅模式,定义了一个被观察者和多个观察者的、一对多的对象关系。

并行设计模式:这些模式处理多线程编程范例。

架构设计模式:这些模式用于处理架构设计。

9. JS中的Array.splice()Array.slice()方法有什么区别

话不多说,来看第一个例子:

var arr=[0,1,2,3,4,5,6,7,8,9];//设置一个数组
console.log(arr.slice(2,7));//2,3,4,5,6
console.log(arr.splice(2,7));//2,3,4,5,6,7,8
//由此我们简单推测数量两个函数参数的意义,
slice(start,end)第一个参数表示开始位置,第二个表示截取到的位置(不包含该位置)
splice(start,length)第一个参数开始位置,第二个参数截取长度

接着看第二个:

var x=y=[0,1,2,3,4,5,6,7,8,9]
console.log(x.slice(2,5));//2,3,4
console.log(x);[0,1,2,3,4,5,6,7,8,9]原数组并未改变
//接下来用同样方式测试splice
console.log(y.splice(2,5));//2,3,4,5,6
console.log(y);//[0,1,7,8,9]显示原数组中的数值被剔除掉了

slicesplice虽然都是对于数组对象进行截取,但是二者还是存在明显区别,函数参数上slicesplice第一个参数都是截取开始位置,slice第二个参数是截取的结束位置(不包含),而splice第二个参数(表示这个从开始位置截取的长度),slice不会对原数组产生变化,而splice会直接剔除原数组中的截取数据!

10.如何在JS中动态添加/删除对象的属性?

咱们可以使用object.property_name = value向对象添加属性,delete object.property_name 用于删除属性。

例如:

let user = new Object();
// adding a property
user.name='Anil';
user.age  =25;
console.log(user);
delete user.age;
console.log(user);

11.解释一下什么是 promise ?

promise是js中的一个对象,用于生成可能在将来产生结果的值。 值可以是已解析的值,也可以是说明为什么未解析该值的原因。

promise 可以有三种状态:

  • pending:初始状态,既不是成功也不是失败
  • fulfilled:意味着操作完全成功
  • rejected:意味着操作失败

一个等待状态的promise对象能够成功后返回一个值,也能失败后带回一个错误 当这两种情况发生的时候,处理函数会排队执行通过then方法会被调用

12. 数组去重复的方法有哪些

1.使用 set function uniquearray(array) { let unique_array= Array.from(set(array)) return unique_array; }

2.使用 filter

function unque_array (arr) {
  let unique_array = arr.filter(function(elem, index, self) {
    return index == self.indexOf(elem);
  })
  return unique_array;
}

 console.log(unique_array(array_with_duplicates));

3.使用 for 循环

Array dups_names = ['Ron', 'Pal', 'Fred', 'Rongo', 'Ron'];
function dups_array(dups_names) {
 let unique = {};
 names.forEach(function(i) {
    If (!unique[i]) {
      unique[i] = true;    }
  });
return Object.keys(unique);}   // Ron, Pal, Fred, Rongo
Dups_array(names);

13. undefined,null 和 undeclared 有什么区别?

1.null表示"没有对象",即该处不应该有值,转为数值时为0。典型用法是:

(1) 作为函数的参数,表示该函数的参数不是对象。

(2) 作为对象原型链的终点。

2.undefined表示"缺少值",就是此处应该有一个值,但是还没有定义,转为数值时为NaN。典型用法是:

(1)变量被声明了,但没有赋值时,就等于undefined。

(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。

(3)对象没有赋值的属性,该属性的值为undefined。

(4)函数没有返回值时,默认返回undefined。

3.undeclared:js语法错误,没有申明直接使用,js无法找到对应的上下文。

14.列出JS基本和非基本数据类型之间的一些区别?

1.目前JS中有6种基本数据类型: UndefinedNullBooleanNumberSymbolString。还有1种复杂的数据类型————ObjectObject本质上是由一组无序的名值对组成的。ObjectArrayFunction则属于引用类型。

2.基本数据类型是不可变的,而非基本数据类型是可变的。

3.基本数据类型是不可变的,因为它们一旦创建就无法更改,但非基本数据类型刚可更改,意味着一旦创建了对象,就可以更改它。

4.将基本数据类型与其值进行比较,这意味着如果两个值具有相同的数据类型并具有相同的值,那么它们是严格相等的。

5.非基本数据类型不与值进行比较。例如,如果两个对象具有相同的属性和值,则它们严格不相等。

15. 如何在现有函数中添加新属性

只需给现有函数赋值,就可以很容易地在现有函数中添加新属性。例如,现有一个对象person,通过下面的代码来为 person 添加新的属性:

person.country= “India”;

16. JS中的深拷贝与浅拷贝的区别?

  • 深拷贝递归地复制新对象中的所有值或属性,而拷贝只复制引用。
  • 在深拷贝中,新对象中的更改不会影响原始对象,而在浅拷贝中,新对象中的更改,原始对象中也会跟着改。
  • 在深拷贝中,原始对象不与新对象共享相同的属性,而在浅拷贝中,它们具有相同的属性。

17. 如何在JavaScript中每x秒调用一个函数

在JS中,咱们使用函数 setInterval() 在每x秒内调用函数。如:

setInterval(function (){ alert("Hello"); }, 3000);

18. 解释一下JS的展开操作符?

展开运算符在需要多个参数/变量/元素的位置展开表达式,它用三个点(...)。如:

var mid = [3, 4];

var newarray = [1, 2, ...mid, 5, 6];

console.log(newarray);

// [1, 2, 3, 4, 5, 6]

19. JS中的宿主对象与原生对象有何不同?

宿主对象:这些是运行环境提供的对象。这意味着它们在不同的环境下是不同的。例如,浏览器包含像windows这样的对象,但是Node.js环境提供像Node List这样的对象。

原生对象:这些是JS中的内置对象。它们也被称为全局对象,因为如果使用JS,内置对象不受是运行环境影响。

20. 解释JS中的高阶函数?

高阶函数是JS函数式编程的最佳特性。它是以函数为参数并返回函数作为结果的函数。一些内置的高阶函数是mapfilterreduce 等等。

21. JS 中 == 和 === 区别是什么?

1、对于string,number等基础类型,=====有区别

1)不同类型间比较,==之比较“转化成同一类型后的值”看“值”是否相等,===如果类型不同,其结果就是不等。 2)同类型比较,直接进行“值”比较,两者结果一样。

2、对于Array,Object等高级类型,=====没有区别

进行“指针地址”比较。

3、基础类型与高级类型,=====有区别

1)对于==,将高级转化为基础类型,进行“值”比较。 2)因为类型不同,===结果为false

22. JS中的匿名函数是什么?

匿名函数:就是没有函数名的函数,如:

(function(x, y){
    alert(x + y);  
})(2, 3);

这里创建了一个匿名函数(在第一个括号内),第二个括号用于调用该匿名函数,并传入参数。

23. 是否可以在JS中执行301重定向?

JS完全运行在客户端上。301是服务器作为响应发送的响应代码。因此,在JS中不可能执行301重定向。

24. 解释JS中的事件冒泡和事件捕获

事件捕获和冒泡: 在HTML DOM API中,有两种事件传播方法,它们决定了接收事件的顺序。两种方法是事件冒泡和事件捕获。第一个方法事件冒泡将事件指向其预期的目标,第二个方法称为事件捕获,其中事件向下到达元素。

事件捕获

捕获过程很少被使用,但是当它被使用时,它被证明是非常有用的。这个过程也称为滴流模式。在这个过程中,事件首先由最外层的元素捕获,然后传播到最内部的元素。例如:

<div>
  <ul>
    <li></li>
  </ul>
</div>

从上面的示例中,假设单击事件发生在li元素中,在这种情况下,捕获事件将首先处理div,然后处理ul,最后命中目标元素li

事件冒泡

冒泡的工作原理与冒泡类似,事件由最内部的元素处理,然后传播到外部元素。

<div>
  <ul>
    <li></li>
  </ul>
</div>

从上面的例子中,假设click事件确实发生在冒泡模型中的li元素中,该事件将首先由li处理,然后由ul处理,最后由div元素处理。

24. 如何将文件的所有导出作为一个对象?

import * as objectname from ‘./file.js’用于将所有导出的成员导入为对象。 可以使用对象的点(.)运算符来访问导出的变量或方法,如:

objectname.member1;
objectname.member2;
objectname.memberfunc();

25. 解释一下什么是箭头函数?

箭头函数是在es6或更高版本中编写函数表达式的简明方法。箭头函数不能用作构造函数,也不支持thisargumentssupernew.target关键字,它最适合非方法函数。 通常,箭头函数看起来像 const function_name =()=> {}

const greet=()=>{console.log('hello');}
 greet();

25 解释 JS 中的函数提升

JS允许将声明移动到顶部的默认行为称为提升。JS中创建函数的两种方法是函数声明和函数表达式。

函数声明

具有特定参数的函数称为函数声明,在JS中创建变量称为声明。如:

hoisted(); // logs "foo"

function hoisted() {
  console.log('foo');
}

函数表达式

当使用表达式创建函数时,称为函数表达式。如:

notHoisted(); // TypeError: notHoisted is not a function

var notHoisted = function() {
   console.log('bar');
};

26. module.exports 和 exports 之间有什么区别?

moduleexportsNode.js给每个js文件内置的两个对象。可以通过console.log(module)console.log(exports)打印出来。如果你在main.js中写入下面两行,然后运行$ node main.js:

console.log(exports);//输出:{}
console.log(module);//输出:Module {..., exports: {}, ...} (注:...代表省略了其他一些属性)

从打印咱们可以看出,module.exportsexports一开始都是一个空对象{},实际上,这两个对象指向同一块内存。这也就是说module.exportsexports是等价的(有个前提:不去改变它们指向的内存地址)。

例如:exports.age = 18module.export.age = 18,这两种写法是一致的(都相当于给最初的空对象{}添加了一个属性,通过require得到的就是{age: 18})。

27. import 和 exports 是什么?

importexports 帮助咱们编写模块化的JS代码。使用importexports,咱们可以将代码分割成多个文件。import只允许获取文件的某些特定变量或方法。可以导入模块导出的方法或变量。

//index.js

 import name,age from './person';

 console.log(name);
 console.log(age);

 //person.js

 let name ='Sharad', occupation='developer', age =26;

 export { name, age};

28. 列出一些单元测试框架

下面是一些最流行的JS单元测试框架:

  • Unit.js
  • Jasmine
  • Karma
  • Chai
  • AVA
  • Mocha
  • JSUnit
  • QUnit
  • Jest

29. JS中有哪些不同类型的弹出框可用

在JS中有三种类型的弹出框可用,分别是:

  • Alert
  • Confirm
  • Prompt

30. 如何将 JS 日期转换为ISO标准

toISOString() 方法用于将js日期转换为ISO标准。 它使用ISO标准将js Date对象转换为字符串。如:

var date = new Date();
var n = date.toISOString();
console.log(n);
// YYYY-MM-DDTHH:mm:ss.sssZ

31. 如何在JS中克隆对象

Object.assign() 方法用于在JS中克隆对象。如:

var x = {myProp: "value"};
var y = Object.assign({}, x);

32. 如何在JS中编码和解码 URL

encodeURI() 函数用于在JS中对URL进行编码。它将url字符串作为参数并返回编码的字符串。

注意encodeURI()不会编码类似这样字符: / ? : @ & = + $ #,如果需要编码这些字符,请使用encodeURIComponent()。 用法:

var uri = "my profile.php?name=sammer&occupation=pāntiNG";
var encoded_uri = encodeURI(uri);

decodeURI() 函数用于解码js中的URL。它将编码的url字符串作为参数并返回已解码的字符串,用法:

var uri = "my profile.php?name=sammer&occupation=pāntiNG";
var encoded_uri = encodeURI(uri);
decodeURI(encoded_uri);

33. BOM 和 DOM 的关系

BOM全称Browser Object Model,即浏览器对象模型,主要处理浏览器窗口和框架。

DOM全称Document Object Model,即文档对象模型,是 HTML 和XML 的应用程序接口(API),遵循W3C 的标准,所有浏览器公共遵守的标准。

JS是通过访问BOM(Browser Object Model)对象来访问、控制、修改客户端(浏览器),由于BOMwindow包含了documentwindow对象的属性和方法是直接可以使用而且被感知的,因此可以直接使用window对象的document属性,通过document属性就可以访问、检索、修改XHTML文档内容与结构。因为document对象又是DOM的根节点。

可以说,BOM包含了DOM(对象),浏览器提供出来给予访问的是BOM对象,从BOM对象再访问到DOM对象,从而js可以操作浏览器以及浏览器读取到的文档。

34. JS中的substr()substring()函数有什么区别

substr() 函数的形式为substr(startIndex,length)。 它从startIndex返回子字符串并返回'length'个字符数。

var s = "hello";
( s.substr(1,4) == "ello" ) // true

substring() 函数的形式为substring(startIndex,endIndex)。 它返回从startIndexendIndex - 1的子字符串。

var s = "hello";
( s.substring(1,4) == "ell" ) // true

35. 解释一下 "use strict" ?

“use strict”是Es5中引入的js指令。 使用“use strict”指令的目的是强制执行严格模式下的代码。 在严格模式下,咱们不能在不声明变量的情况下使用变量。 早期版本的js忽略了“use strict”

36.解释 JS 事件委托模型?

在JS中,有一些很酷的东西。其中之一是委托模型。当捕获和冒泡时,允许函数在一个特定的时间实现一个处理程序到多个元素,这称为事件委托。事件委托允许将事件侦听器添加到父节点而不是指定的节点。这个特定的侦听器分析冒泡事件,以找到子元素上的匹配项。

 

什么是错误优先的回调函数?错误优先的回调函数用于传递错误和数据。第一个参数始终应该是一个错误对象, 用于检查程序是否发生了错误。其余的参数用于传递数据。例如:

fs.readFile(filePath, function(err, data) { 
    if (err) { 
        //handle the error 
    } 
    // use the data object 
});

解析:这个题目的主要作用在于检查被面试者对于Node中异步操作的一些基本知识的掌握。

如何避免回调地狱你可以有如下几个方法:

  • 模块化:将回调函数分割为独立的函数
  • 使用Promises
  • 使用yield来计算生成器或Promise

解析:这个问题有很多种答案,取决你使用的场景,例如ES6, ES7,或者一些控制流库。

如何用Node监听80端口这题有陷阱!在类Unix系统中你不应该尝试去监听80端口,因为这需要超级用户权限。 因此不推荐让你的应用直接监听这个端口。

目前,如果你一定要让你的应用监听80端口的话,你可以有通过在Node应用的前方再增加一层反向代理 (例如nginx)来实现,如下图所示。否则,建议你直接监听大于1024的端口。

 

Reverse Proxy

反向代理指的是以代理服务器来接收Internet上的连接请求,然后将请求转发给内部网络上的服务器, 并且将服务器返回的结果发送给客户端。

关于反向代理的更多内容,建议你阅读这篇文章。 关于如何利用nginx来为node配置方向代理的实践,可以参考这篇博文

解释:这个问题用于检查被面试者是否有实际运行Node应用的经验。

什么是事件循环Node采用的是单线程的处理机制(所有的I/O请求都采用非阻塞的工作方式),至少从Node.js开发者的角度是这样的。 而在底层,Node.js借助libuv来作为抽象封装层, 从而屏蔽不同操作系统的差异,Node可以借助livuv来来实现多线程。下图表示了Node和libuv的关系。

 

Libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个事件循环, 以异步的方式将任务的执行结果返回给V8引擎。可以简单用下面这张图来表示。

 

(图片来源于网络)

每一个I/O都需要一个回调函数——一旦执行完便推到事件循环上用于执行。 如果你需要更多详细的解释,可以参考这个视频。 你也可以参考这篇文章

解释:这用于检查Node.js的底层知识,例如什么是libuv,它的作用是什么。

哪些工具可以用来保证一致性的代码风格你可以选择如下的工具:

在团队开发中,这些工具对于编写代码非常的有帮助,能够帮助团队开发者强制执行规定的风格指南, 还能够通过静态分析捕获常见的错误。

解析:用于检查被面试者是否有大型项目开发经验。

运算错误与程序员错误的区别运算错误并不是bug,这是和系统相关的问题,例如请求超时或者硬件故障。而程序员错误就是所谓的bug。

解析:这个题目和Node关系并不大,用于考察面试者的基础知识。

使用NPM有哪些好处?通过NPM,你可以安装和管理项目的依赖,并且能够指明依赖项的具体版本号。 对于Node应用开发而言,你可以通过package.json文件来管理项目信息,配置脚本, 以及指明项目依赖的具体版本。

关于NPM的更多信息,你可以参考官方文档

解析:它能考察面试者使用npm命令的基础知识和Node.js开发的实际经验。

什么是Stub?举个使用场景Stub是用于模拟一个组件或模块的函数或程序。在测试用例中, 简单的说,你可以用Stub去模拟一个方法,从而避免调用真实的方法, 使用Stub你还可以返回虚构的结果。你可以配合断言使用Stub。

举个例子,在一个读取文件的场景中,当你不想读取一个真正的文件时:

var fs = require('fs'); 

var readFileStub = sinon.stub(fs, 'readFile', function (path, cb) { 
    return cb(null, 'filecontent'); 
});

expect(readFileStub).to.be.called;  
readFileStub.restore(); 
在单元测试中:Stub是完全模拟一个外部依赖,而Mock常用来判断测试通过还是失败。

有关Node.js的单元测试小结,你可以参考这个链接

解析:用于测试被面试者是否有测试的经验。如果被面试者知道什么是Stub, 那么可以继续问他是如何做单元测试的。

什么是测试金字塔?测试金字塔指的是: 当我们在编写测试用例时,底层的单元测试应该远比上层的端到端测试要多。

 

当我们谈到HTTP API时,我们可能会涉及到:

  • 有很多针对模型的底层单元测试
  • 但你需要测试模型间如何交互时,需要减少集成测试

解析:本文主要考察被面试者的在测试方面的经验。

你最喜欢的HTTP框架以及原因这题没有唯一的答案。本题主要考察被面试者对于他所使用的Node框架的理解程度, 考察他是否能够给出选择该框架的理由,优缺点等。常用的HTTP框架你可以参考这个网站。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值