70个Java面试问题

朋友们,美好的一天!

我希望本文对新手开发人员和有经验的开发人员都有用。

在我看来比其他人更困难的问题中,引用了其他文献。

谢谢您的详细评论。编辑文章时将考虑所有评论。

所以走吧

70个Java面试问题


问题:
1. null和undefined之间有什么区别?
2. &&运算符的作用是什么?
3.“ ||”运算符用于什么?
4.使用一元加号(+运算符)是将字符串转换为数字的最快方法吗?
5.什么是DOM?
6.什么是事件传播?
7.什么是事件冒泡?
8.什么是事件捕获?
9. event.preventDefault()和event.stopPropagation()方法之间有什么区别?
10.如何学习使用event.preventDefault()方法?
11.为什么obj.someprop.x导致错误?
12.什么是事件目标或目标元素(event.target)?
13. (event.currentTarget)?
14. "==" "==="?
15. false?
16. "!!"?
17. ?
18. (Hoisting)?
19. (Scope)?
20. (Closures)?
21. JS ?
22. , ?
23. «use strict»?
24. this?
25. ?
26. IIFE?
27. Function.prototype.apply?
28. Function.prototype.call?
29. call apply?
30. Function.prototype.bind?
31. JS ?
32. (Higher Order Functions)?
33. JS (First-class Objects)?
34. Array.prototype.map?
35. Array.prototype.filter?
36. Array.prototype.reduce?
37. arguments?
38. , ?
39. b ?
40. ECMAScript?
41. JS ES6 ECMAScript2015?
42. «var», «let» «const»?
43. (Arrow Functions)?
44. (Classes)?
45. (Template Literals)?
46. (Object Destructuring)?
47. (Modules)?
48. Set?
49. (Callback Function)?
50. (Promises)?
51. async/await?
52. spread- rest-?
53. (Default Parameters)?
54. (Wrapper Objects)?
55. (Implicit and Explicit Coercion)?
56. NaN? , NaN?
57. , ?
58. , , ( "%")?
59. ?
60. AJAX?
61. JS ?
62. Object.freeze Object.seal?
63. «in» hasOwnProperty?
64.您知道在JS中使用异步代码的哪些技术?
65.正常函数和函数表达式有什么区别?
66.如何在JS中调用函数?
67.什么是记忆或记忆?
68.您将如何实现记忆的辅助功能?
69.为什么typeof null返回对象?如何检查一个值是否为空?
70.“ new”关键字的作用是什么?

1. null和undefined有什么区别?


首先,让我们谈谈它们的共同点。

首先,它们属于7个JS“基本体”(基本体类型):

let primitiveTypes = ['string', 'number', 'null', 'undefined', 'boolean', 'symbol', 'bigint']

其次,它们是错误的值,即 使用Boolean()或运算符“ !!”将它们转换为布尔值的结果 是错误的:

console.log(!!null) // false
console.log(!!undefined) // false

console.log(Boolean(null)) // false
console.log(Boolean(undefined)) // false

好的,现在介绍差异。

undefined是默认值:
  • 未赋值的变量,即 声明但未初始化的变量;
  • 一个不显式返回任何内容的函数,例如console.log(1);
  • 对象的不存在属性。

在这些情况下,JS引擎会将值设置为undefined。

let _thisIsUndefined
const doNothing = () => {}
const someObj = {
    a: 'ay',
    b: 'bee',
    c: 'si'
}
console.log(_thisIsUndefined) // undefined
console.log(doNothing()) // undefined
console.log(someObj['d']) // undefined

null是“无价值的价值”。null是显式分配给变量的值。在下面的示例中,当fs.readFile方法正常运行时,我们得到null:

fs.readFile('path/to/file', (e, data) => {
    console.log(e) //    null
if(e) {
    console.log(e)
}
    console.log(data)
})

比较null和undefined时,使用“ ==”运算符时为true,使用“ ===”运算符时为false。有关发生这种情况的原因,请参见下文。

console.log(null == undefined) // true
console.log(null === undefined) // false

2. &&运算符的作用是什么?


当所有值均为true时,&&运算符(逻辑和)查找并返回第一个false值或最后一个操作数。它使用短路来避免不必要的成本:

console.log(false && 1 && []) // false
console.log(' ' && true && 5) // 5

使用if语句:

const router: Router = Router()

router.get('/endpoint', (req: Request, res: Response) => {
    let conMobile: PoolConnection
    try {
        //    
    } catch (e) {
        if (conMobile) {
            conMobile.release()
        }
    }
})

与&&运算符相同:

const router: Router = Router()

router.get('/endpoint', (req: Request, res: Response) => {
    let conMobile: PoolConnection
    try {
        //    
    } catch (e) {
        conMobile && conMobile.release()
    }
})

3.“ ||”运算符用于什么?


运算符“ ||” (布尔值或)查找并返回第一个真值。它还使用短路。在ES6中标准化默认参数之前,使用该运算符在函数中分配默认参数。

console.log(null || 1 || undefined) // 1

function logName(name) {
    let n = name || Mark
    console.log(n)
}

logName() // Mark

4.使用一元加号(+运算符)是将字符串转换为数字的最快方法吗?


根据MDN的说法,+运算符确实是将字符串转换为数字的最快方法,因为它不会对数字值执行任何操作。

5.什么是DOM?


DOM或文档对象模型是用于处理HTML和XML文档的应用程序编程接口(API)。当浏览器第一次读取(“解析”)HTML文档时,它会形成一个大对象,即基于该文档的真正大对象-DOM。DOM是树结构(文档树)。DOM用于交互和更改DOM本身或其单个元素和节点的结构。

假设我们有以下HTML:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document Object Model</title>
</head>

<body>
    <div>
        <p>
            <span></span>
        </p>
        <label></label>
        <input>
    </div>
</body>

</html>

该HTML的DOM如下所示:



在JS中,该DOM由Document对象表示。Document对象具有用于处理元素,创建,修改,删除等的大量方法。

6.什么是事件传播?


当事件在DOM元素中发生时,它实际上不仅在事件中发生。事件从Window对象“传播”到调用它的元素(event.target)。而且,事件顺序地渗透(影响)目标元素的所有祖先。事件传播具有三个阶段或阶段:
  1. 浸入(捕获,拦截)阶段-事件在Window对象中发生,并通过其所有祖先下降到事件目标。
  2. 目标阶段是事件到达目标元素的时间。
  3. 升序阶段-事件从event.target上升,依次通过其所有祖先并到达Window对象。



在此处此处 阅读有关事件分布的更多信息

7.什么是事件弹出窗口?


当DOM元素中发生事件时,它不仅会影响此元素。事件“弹出”(如水中的气泡),从导致事件的元素(event.target)传递到其父元素,然后上升到该元素父元素的父元素,直到到达Window对象。

假设我们有这个标记:

<div class="grandparent">
    <div class="parent">
        <div class="child">1</div>
    </div>
</div>

这样的JS:

function addEvent(el, event, callback, isCapture = false) {
    if (!el || !event || !callback || typeof callback !== 'function') return

    if (typeof el === 'string') {
        el = document.querySelector(el)
    }
    el.addEventListener(event, callback, isCapture)
}

addEvent(document, 'DOMContentLoaded', () => {
    const child = document.querySelector('.child')
    const parent = document.querySelector('.parent')
    const grandparent = document.querySelector('.grandparent')

    addEvent(child, 'click', function(e) {
        console.log('child')
    })

    addEvent(parent, 'click', function(e) {
        console.log('parent')
    })

    addEvent(grandparent, 'click', function(e) {
        console.log('grandparent')
    })

    addEvent('html', 'click', function(e) {
        console.log('html')
    })

    addEvent(document, 'click', function(e) {
        console.log('document')
    })

    addEvent(window, 'click', function(e) {
        console.log('window')
    })
})

addEventListener方法具有第三个可选参数-useCapture。如果其值为false(默认值),则事件从上升阶段开始。当其值为true时,事件从沉浸阶段开始(对于附加到事件目标的事件的“侦听器”,该事件处于目标阶段,而不是沉浸或上升阶段。目标阶段的事件由元素上的所有侦听器触发,顺序为无论useCapture参数如何,都在其中注册了它们-大约 如果单击child元素,则控制台将显示:child,parent,祖父母,html,文档,窗口。这是事件弹出窗口。

8.什么是沉浸事件?


当事件发生在DOM元素中时,它不仅发生在事件中。在沉浸阶段,事件通过其所有祖先从Window对象下降到事件目标。

标记:

<div class="grandparent">
    <div class="parent">
        <div class="child">1</div>
    </div>
</div>

JS:

function addEvent(el, event, callback, isCapture = false) {
    if (!el || !event || !callback || typeof callback !== 'function') return

    if (typeof el === 'string') {
        el = document.querySelector(el);
    }
    el.addEventListener(event, callback, isCapture)
}

addEvent(document, 'DOMContentLoaded', () => {
    const child = document.querySelector('.child')
    const parent = document.querySelector('.parent')
    const grandparent = document.querySelector('.grandparent')

    addEvent(child, 'click', function(e) {
        console.log('child');
    }, true)

    addEvent(parent, 'click', function(e) {
        console.log('parent')
    }, true)

    addEvent(grandparent, 'click', function(e) {
        console.log('grandparent')
    }, true)

    addEvent('html', 'click', function(e) {
        console.log('html')
    }, true)

    addEvent(document, 'click', function(e) {
        console.log('document')
    }, true)

    addEvent(window, 'click', function(e) {
        console.log('window')
    }, true)
})

addEventListener方法具有第三个可选参数-useCapture。如果其值为false(默认值),则事件从上升阶段开始。当其值为true时,事件从潜水阶段开始。如果单击child元素,则会在控制台中看到以下内容:窗口,文档,html,祖父母,父项,子项。这就是事件的沉浸感。

9. event.preventDefault()和event.stopPropagation()方法之间有什么区别?


event.preventDefault()方法禁用元素的默认行为。如果在form元素中使用此方法,它将阻止表单提交。如果您在contextmenu菜单中使用它,则上下文菜单将被禁用(例如,在创建音乐/视频播放器或文本编辑器时,此方法通常在keydown中用于重新定义键盘)。event.stopPropagation()方法禁用事件传播(上升或沉浸)。

10.如何学习使用event.preventDefault()方法?


为此,我们可以使用event.defaulPrevented属性,该属性返回一个布尔值,该布尔值用作event.preventDefault方法的元素的应用程序指示。

11.为什么obj.someprop.x导致错误?



const obj = {}
console.log(obj.someprop.x)

答案很明显:我们正在尝试访问someprop属性的x属性,该属性未定义。obj .__ proto __.__ proto = null,因此返回undefined,并且undefined没有x属性。

12.什么是事件目标或目标元素(event.target)?


简而言之,event.target是发生事件的元素或引发事件的元素。

我们有以下标记:

<div onclick="clickFunc(event)" style="text-align: center; margin: 15px;
border: 1px solid red; border-radius: 3px;">
    <div style="margin: 25px; border: 1px solid royalblue; border-radius: 3px;">
        <div style="margin: 25px; border: 1px solid skyblue; border-radius: 3px;">
            <button style="margin: 10px">
                Button
            </button>
        </div>
    </div>
</div>

如此简单的JS:

function clickFunc(event) {
    console.log(event.target)
}

我们在外部div附加了“侦听器”。但是,如果单击按钮,则会在控制台中获得此按钮的布局。这使我们可以得出结论,导致事件的元素是按钮本身,而不是外部或内部div。

13.事件的当前目的是什么(event.currentTarget)?


Event.currentTarget是事件侦听器所附加的元素。

相似的标记:

<div onclick="clickFunc(event)" style="text-align: center;margin:15px;
border:1px solid red;border-radius:3px;">
    <div style="margin: 25px; border:1px solid royalblue;border-radius:3px;">
        <div style="margin:25px;border:1px solid skyblue;border-radius:3px;">
            <button style="margin:10px">
                Button
            </button>
        </div>
    </div>
</div>

和一个稍微修改的JS:

function clickFunc(event) {
    console.log(event.currentTarget)
}

我们将侦听器附加到外部div。无论在何处单击,无论是按钮还是内部div之一,在控制台中,我们始终会获得外部div的布局。这使我们可以得出结论,event.currentTarget是事件侦听器所附加的元素。

14.运算符“ ==”和“ ===”之间有什么区别?


运算符==(抽象或非严格相等)和运算符===(严格相等)之间的区别在于,第一个比较值在将其转换或转换为一种类型(强制转换)之后,第二个比较值-不进行这种转换。

让我们深入研究。首先,让我们谈谈转换。

转换是将值转换为另一种类型的过程,或者更确切地说是将比较值转换为一种类型的过程。比较时,运算符“ ==”产生所谓的隐式比较。运算符“ ==”在比较两个值之前执行一些操作。

假设我们比较x和y。

算法如下:

  1. 如果x和y是同一类型,则使用“ ===”运算符进行比较。
  2. x = null y = undefined true.
  3. x = undefined y = null true.
  4. x = , y = , x == toNumber(y) ( y ).
  5. x = , y = , toNumber(x) == y ( x ).
  6. x = , toNumber(x) == y.
  7. y = , x == toNumber(y).
  8. x = , , y = , x == toPrimitive(y) ( y ).
  9. x = , y = , , toPrimitive(x) == y.
  10. false.

请记住:要将对象强制转换为“原始”对象,toPrimitive方法首先使用valueOf方法,然后使用toString方法。

示例:



所有示例均返回true。

第一个例子是算法的第一个条件。
第二个例子是第四个条件。
第三是第二。
第四是第七。
第五-第八。
最后是第十。



如果我们使用“ ===”运算符,则除第一个示例外的所有示例都将返回false,因为这些示例中的值属于不同的类型。

15.比较两个相似对象的结果为什么是假的?


let a = {
    a: 1
}
let b = {
    a: 1
}
let c = a

console.log(a === b) // false
console.log(a === c) // true ...

在JS中,对象和基元的比较有所不同。比较基元的价值。对象-通过引用或存储变量的内存中的地址。这就是为什么第一个console.log返回false,第二个返回true的原因。变量“ a”和“ c”指的是同一对象,而变量“ a”和“ b”指的是具有相同属性和值的不同对象。

16.运算符“ !!”的作用是什么?


运营商 ”!!” (双重否定)将值向右引导到逻辑值。

console.log(!!null) // false
console.log(!!undefined) // false
console.log(!!'') // false
console.log(!!0) // false
console.log(!!NaN) // false
console.log(!!' ') // true
console.log(!!{}) // true
console.log(!![]) // true
console.log(!!1) // true
console.log(!![].length) // false

17.如何在一行上写多个表达式?


为此,我们可以使用运算符“,”(逗号)。该运算符从左向右“移动”并返回最后一个表达式或操作数的值。

let x = 5

x = (x++, x = addFive(x), x *= 2, x -= 5, x += 10)

function addFive(num) {
    return num + 5
}

如果将x的值打印到控制台,则得到27。首先,将x的值增加1(x = 6)。然后,我们使用参数6调用addFive()函数,并在其中添加5(x = 11)。之后,我们将x的值乘以2(x = 22)。然后减去5(x = 17)。最后,加10(x = 27)。

18.什么是吊装?


提升是描述全局或功能范围内变量或功能的增加的术语。

为了了解什么是提升,您需要了解什么是执行上下文。

执行上下文是在其中执行代码的环境。执行上下文有两个阶段-编译和执行本身。

汇编。在此阶段中,使用“ var”关键字声明且值为undefined的函数表达式和变量上升到全局(或函数)作用域的顶部(就像移到代码的开头一样)。这解释了为什么可以在调用函数之前先调用它们公告-大约

性能。在此阶段,为变量分配值,并调用或执行函数(或对象的方法)。

请记住:仅引发使用“ var”关键字声明的函数表达式和变量。不会引发普通函数和箭头函数,以及使用关键字“ let”和“ const”声明的变量。

假设我们有如下代码:

console.log(y)
y = 1
console.log(y)
console.log(greet('Mark'))

function greet(name) {
    return 'Hello ' + name + '!'
}

var y

我们得到未定义的1和'Hello Mark!'。

编译阶段如下所示:

function greet(name) {
    return 'Hello ' + name + '!'
}

var y //  undefined

//    

//    
/*
console.log(y)
y = 1
console.log(y)
console.log(greet('Mark'))
*/

编译阶段完成后,执行阶段从为变量分配值并调用函数开始。

有关起重的更多信息,请参见此处

19.范围是什么?


范围是我们可以(或从那里)访问变量或函数的地方。JS我们有三种类型的作用域:全局,功能和块(ES6)。

全局范围-全局名称空间中声明的变量和函数具有全局范围,可以从代码中的任何位置访问。

//   
var g = 'global'

function globalFunc() {
    function innerFunc() {
        console.log(g) //     g,    
    }
    innerFunc()
}

功能范围(功能范围)-在功能内部声明的变量,功能和参数仅在该功能内部可用。

function myFavouriteFunc(a) {
    if (true) {
        var b = 'Hello ' + a
    }
    return b
}
myFavouriteFunc('World')

console.log(a) // Uncaught ReferenceError: a is not defined
console.log(b) //  

块作用域-块({})中的变量(使用关键字“ let”和“ const”声明)仅在其内部可用。

function testBlock() {
    if (true) {
        let z = 5
    }
    return z
}

testBlock() // Uncaught ReferenceError: z is not defined

范围也是搜索变量的一组规则。如果变量在当前作用域中不存在,则在当前作用域的外部可见性中进行更高级别的搜索。如果外部作用域中没有变量,则其搜索将继续到全局作用域。如果在全局范围内找到变量,则搜索将停止;否则,将引发异常。搜索是通过最接近当前可见性区域的内容进行的,并在找到变量时停止。这称为范围链。

//   
//    ->    ->   

//   
var variable1 = 'Comrades'
var variable2 = 'Sayonara'

function outer() {
    //   
    var variable1 = 'World'

    function inner() {
        //   
        var variable2 = 'Hello'
        console.log(variable2 + ' ' + variable1)
    }
    inner()
}
outer()
//    'Hello World',
//   variable2 = 'Hello'  variable1 = 'World'  
//     



20.什么是封闭(封闭)?


这可能是列表中最困难的问题。我将尝试解释我如何理解闭包。

实际上,闭包是函数在创建时能够存储对当前作用域,父作用域的作用域,父作用域的作用域,父作用域的作用域以及位于作用域链范围内的全局作用域的变量和参数的引用的创建能力。通常,范围是在创建函数时确定的。

示例是解释闭包的好方法:

//   
var globalVar = 'abc'

function a() {
    //   
    console.log(globalVar)
}

a() // 'abc'
//   
//    a ->   

在此示例中,当我们声明一个函数时,全局作用域是闭包的一部分。



图片中的“ globalVar”变量无关紧要,因为其值可能会随调用函数的位置和时间而变化。但是在上面的示例中,globalVar将具有值“ abc”。

现在这个例子更加复杂:

var globalVar = 'global'
var outerVar = 'outer'

function outerFunc(outerParam) {
    function innerFunc(innerParam) {
        console.log(globalVar, outerParam, innerParam)
    }
    return innerFunc
}

const x = outerFunc(outerVar)
outerVar = 'outer-2'
globalVar = 'guess'
x('inner')



结果是“猜测外部内部”。解释是这样的:当我们调用externalFunc函数并将变量“ x”设置为innerFunc函数返回的值时,参数“ outerParam”等于“ outer”。尽管我们将变量“ outerVar”分配给“ outer-2”,但这是在调用outerFunc函数之后发生的,该函数“托管”以在范围链中找到变量“ outerVar”的值,但该值为“ outer”。当我们调用表示内部函数的“ x”时,“ innerParam”的值为“ inner”,因为在调用“ x”时我们将此值作为参数传递。 globalVar的值为“猜测”,因为我们在调用“ x”之前为其分配了该值。

电路误解的一个例子。

const arrFunc = []
for (var i = 0; i < 5; i++) {
    arrFunc.push(function() {
        return i
    })
}
console.log(i) // 5

for (let i = 0; i < arrFunc.length; i++) {
    console.log(arrFunc[i]()) //  5
}

此代码无法正常工作。使用var关键字声明变量将使该变量成为全局变量。在向arrFunc数组添加函数之后,全局变量“ i”的值变为“ 5”。因此,当我们调用该函数时,它将返回全局变量“ i”的值。闭包存储对变量的引用,而不是在创建时存储其值。通过使用IIFE或使用“ let”关键字声明变量可以解决此问题。

这里这里阅读有关封闭的更多信息

21. JS中哪些值是错误的?


const falsyValues = ['', 0, null, undefined, NaN, false]

将值转换为布尔值的值是False的是False。

22.如何检查值是否为假?


使用布尔函数或运算符“ !!” (两次)。

23. strict指令的用途是什么?


“严格使用”是ES5指令,它强制我们的所有代码或单个功能的代码以严格模式执行。严格模式在编写代码方面引入了一些限制,从而避免了早期的错误。

这是严格模式的限制。

您不能分配值或访问未声明的变量:

function returnY() {
    'use strict'
    y = 123
    return y
}
returnY() // Uncaught ReferenceError: y is not defined

禁止将全局值分配给只读或只写变量:

'use strict'
var NaN = NaN // Uncaught TypeError: Cannot assign to read only property 'NaN' of object '#<Window>'
var undefined = undefined
var Infinity = 'and beyond'

您不能删除对象的“不可删除”属性:

'use strict'
const obj = {}

Object.defineProperties(obj, 'x', {
    value: 1
})

delete obj.x // Uncaught TypeError: Property description must be an object: x

禁止重复参数:

'use strict'

function someFunc(a, b, b, c) {} // Uncaught SyntaxError: Duplicate parameter name not allowed in this context

您不能使用eval函数创建函数:

'use strict'

eval('var x = 1')

console.log(x) // Uncaught ReferenceError: x is not defined

缺省值为undefined:

'use strict'

function showMeThis() {
    return this
}

showMeThis() // undefined

…等等

24.这是什么意思?


这通常是指当前正在执行或调用该函数的对象的值。“立即”表示此值取决于执行上下文(我们在何处使用)。

const carDetails = {
    name: 'Ford Mustang',
    yearBought: 2005,
    getName() {
        return this.name
    }
    isRegistered: true
}

console.log(carDetails.getName()) // Ford Mustang

在这种情况下,getName方法返回this.name,它引用carDetails,运行getName的对象,即其“所有者”。

在console.log之后添加三行:

var name = 'Ford Ranger'
var getCarName = carDetails.getName

console.log(getCarName()) // Ford Ranger

第二个console.log生成一个Ford Ranger,这很奇怪。此行为的原因是getCarName的“所有者”是窗口对象。在全局范围内用var关键字声明的变量将写入窗口对象的属性。全局范围中的this指的是window对象(除非它是严格模式)。

console.log(getCarName === window.getCarName) // true
console.log(getCarName === this.getCarName) // true

在此示例中,this和window引用同一对象。

解决此问题的一种方法是使用call或apply方法:

console.log(getCarName.apply(carDetails)) // Ford Mustang
console.log(getCarName.call(carDetails)) // Ford Mustang

调用并应用将对象作为第一个参数,该对象将成为函数内部此对象的值。

在IIFE中,是在对象的方法的全局范围内创建的函数,匿名函数和内部函数,其默认值为window对象。

(function() {
    console.log(this)
})() // window

function iHateThis() {
    console.log(this)
}
iHateThis() // window

const myFavouriteObj = {
    guessThis() {
        function getName() {
            console.log(this.name)
        }
        getName()
    },
    name: 'Marko Polo',
    thisIsAnnoying(callback) {
        callback()
    }
}

myFavouriteObj.guessThis() // window
myFavouriteObj.thisIsAnnoying(function() {
    console.log(this) // window
})

有两种方法可以获取Marko Polo。

首先,我们可以将其值存储在变量中:

const myFavoriteObj = {
    guessThis() {
        const self = this //   this   self
        function getName() {
            console.log(self.name)
        }
        getName()
    },
    name: 'Marko Polo',
    thisIsAnnoying(callback) {
        callback()
    }
}

其次,我们可以使用箭头功能:

const myFavoriteObj = {
    guessThis() {
        const getName = () => {
            //   this   
            console.log(this.name)
        }
        getName()
    },
    name: 'Marko Polo',
    thisIsAnnoying(callback) {
        callback()
    }
}

箭头函数没有此特征值。他们从外部词汇环境中复制了这一含义。

25.什么是物体的原型?


简而言之,原型就是对象的计划(图或项目)。它用作该对象中现有属性和方法的后备。这也是在对象之间交换属性和功能的方法之一。这是JS中原型继承的基本概念。

const o = {}
console.log(o.toString()) // [object Object]

尽管“ o”对象没有toString属性,但访问此属性不会导致错误。如果对象中没有特定的属性,则首先在对象的原型中进行搜索,然后在对象的原型中进行搜索,依此类推,直到找到该属性。这称为原型链。原型链的顶部是Object.prototype。

console.log(o.toString === Object.prototype.toString) // true

在这里这里 阅读有关原型和继承的更多信息

26.什么是IIFE?


IIFE或立即调用的函数表达式是在创建或声明后立即调用或执行的函数。要创建IIFE,您需要将函数包装在括号中(分组运算符),将其转换为表达式,然后使用另一个括号进行调用。看起来像这样:(function(){})()。

(function( ) { }( ))

(function( ) { })( )

(function named(params) { })( )

(( ) => { })

(function(global) { })(window)

const utility = (function( ) {
    return {
        // 
    }
})

所有这些示例都是有效的。倒数第二个例子表明,我们可以将参数传递给IIFE。最后一个示例显示我们可以将IIFE的结果存储在变量中。

IIFE的最佳用途是执行初始化配置功能,并防止名称与全局范围内的其他变量发生冲突(全局命名空间污染)。我们举一个例子。

<script src="https://cdnurl.com/somelibrary.js"></script>

我们有一个指向somelibrary.js库的链接,该库提供了一些可以在代码中使用的全局函数,但是该库中有两个方法,createGraph和drawGraph,我们不使用它们,因为它们包含错误。我们希望自己实现这些功能。

解决此问题的一种方法是更改​​脚本的结构:

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
    function createGraph() {
        // 
    }

    function drawGraph() {
        // 
    }
</script>

因此,我们重新定义了库提供的方法。

第二种方法是更改​​函数的名称:

<script src="https://cdnurl.com/somelibrary.js"></script>
<script>
    function myCreateGraph() {
        // 
    }

    function myDrawGraph() {
        // 
    }
</script>

第三种方法是使用IIFE:

<script>
    const graphUtility = (function() {
        function createGraph() {
            // 
        }

        function drawGraph() {
            // 
        }
        return {
            createGraph,
            drawGraph
        }
    })
</script>

在此示例中,我们创建了一个包含IIFE结果的实用程序变量,该变量返回一个包含createGraph和drawGraph方法的对象。

这是使用IIFE可以解决的另一个问题:

val li = document.querySelectorAll('.list-group > li')
for (var i - 0, len = li.length; i < len; i++) {
    li[i].addEventListener('click', function(e) {
        console.log(i)
    })
}

假设我们有一个ul元素,其中的list-group类包含5个子元素li。我们希望在单击单独的“ li”时在控制台中显示值“ i”。但是,控制台始终显示5。故障全是故障。

一种解决方案是IIFE:

var li = document.querySelectorAll('.list-group > li')
for (var i = 0, len = li.length; i < len; i++) {
    (function(currentIndex) {
        li[currentIndex].addEventListener('click', function(e) {
            console.log(currentIndex)
        })
    })(i)
}

该代码按预期工作的原因是因为IIFE在每次迭代时都会创建一个新范围,并且我们在currentIndex中写入值“ i”。

27. Function.prototype.apply方法用于什么?


Apply用于将特定对象绑定到被调用函数的this值。

const details = {
    message: 'Hello World!'
}

function getMessage() {
    return this.message
}

getMessage.apply(details) // Hello World!

此方法类似于Function.prototype.call。唯一的区别是在apply中,参数作为数组传递。

const person = {
    name: 'Marko Polo'
}

function greeting(greetingMessage) {
    return `${greetingMessage} ${this.name}`
}

greeting.apply(person, ['Hello']) // Hello Marko Polo

28. function.prototype.call方法用于什么?


调用用于将特定对象绑定到该函数的值。

const details = {
    message: 'Hello World!'
};

function getMessage() {
    return this.message;
}

getMessage.call(details); // Hello World!

此方法类似于Function.prototype.apply。区别在于在调用参数中传递的参数之间用逗号分隔。

const person = {
    name: 'Marko Polo'
};

function greeting(greetingMessage) {
    return `${greetingMessage} ${this.name}`;
}

greeting.call(person, 'Hello'); // Hello Marko Polo

29. call和apply方法之间有什么区别?


调用和应用之间的区别在于我们如何在被调用函数中传递参数。在apply中,参数在调用时作为数组传递,并以逗号分隔。

const obj1 = {
    result: 0
}

const obj2 = {
    result: 0
}

function reduceAdd() {
    let result = 0
    for (let i = 0, len = arguments.length; i < len; i++) {
        result += arguments[i]
    }
    this.result = result
}

reduceAdd.apply(obj1, [1, 2, 3, 4, 5]) // 15
reduceAdd.call(obj2, 1, 2, 3, 4, 5) // 15

30. function.prototype.bind方法用于什么?


Bind返回一个新函数,该函数的值是指定为第一个参数的对象。与绑定不同,调用并立即应用将调用该函数。

import React from 'react'

class MyComponent extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value: ''
        }
        this.handleChange = this.handleChange.bind(this)
        //   handleChange   MyComponent
    }

    handleChange(e) {
        // 
    }

    render() {
        return ( < >
            <
            input type = {
                this.props.type
            }
            value = {
                this.state.value
            }
            onChange = {
                this.handleChange
            }
            /> </ >
        )
    }
}

31.什么是函数式编程?JS的哪些功能使我们可以将其称为函数式编程语言?


函数式编程是一个声明式编程概念,或者是一个示例应用程序,如何构建应用程序,如何使用包含可在不更改传递给它们的参数的情况下计算值的表达式的函数的示例(模式)。

Array对象包含map,filter和reduce方法,它们是函数式编程领域中最著名的函数,这是因为它们的用处以及它们不修改数组,从而使这些函数“干净”。JS还具有闭包和高阶函数,它们是功能编程语言的特征。

map方法返回一个新数组,其中包含数组每个元素的回调结果:

const words = ['Functional', 'Procedural', 'Object-Oriented']

const wordsLength = words.map(word => word.length)

filter方法创建一个新数组,其中所有元素均满足回调中指定的条件:

const data = {
    {
        name: 'Mark',
        isRegistered: true
    } {
        name: 'Mary',
        isRegistered: false
    } {
        name: 'Mae',
        isRegistered: true
    }
}

const registeredUsers = data.filter(user => user.isRegistered)

reduce方法对数组的每个元素执行一次回调,除了void之外,均采用四个参数:初始值(或上一个回调中的值),当前元素的值,当前索引和迭代数组:

const strs = ['I', ' ', 'am', ' ', 'Iron', ' ', 'Man']

const result = strs.reduce((acc, currentStr) => acc + str, '')

32.什么是高阶函数?


高阶函数是返回另一个函数或接受另一个函数作为参数的函数。

function higherOrderFunction(param, callback) {
    return callback(param)
}

33.为什么JS中的函数称为一流对象?


函数被称为一流对象,因为它们的处理方式与JS中的任何其他值一样。可以将它们分配给变量,可以是对象(方法)的属性,数组的元素,另一个函数的参数以及该函数返回的值。函数与JS中任何其他值之间的唯一区别是可以执行或调用该函数。

34.您将如何实现Array.prototype.map方法?


function map(arr, mapCallback) {
    //   
    if (!Array.isArray(arr) || !arr.length || typeof mapCallback !== 'function') {
        return []
    } else {
        let result = []
        //         
        //       
        for (let i = 0, len = arr.length; i < len; i++) {
            result.push(mapCallback(arr[i], i, arr))
            //   mapCallback  result
        }
        return result
    }
}

map方法创建一个新数组,并为该数组的每个元素调用指定的函数。

35.您将如何实现Array.prototype.filter方法?


function filter(arr, filterCallback) {
    //   
    if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') {
        return []
    } else {
        let result = []
        // ...
        for (let i = 0, len = arr.length; i < len; i++) {
            //      
            if (filterCallback(arr[i], i, arr)) {
                //  ,  ,  result
                result.push(arr[i])
            }
        }
        return result
    }
}

filter方法将创建一个新数组,其中包含所有通过了在函数中指定的测试的元素。

36.您将如何实现Array.prototype.reduce方法?


function reduce(arr, reduceCallbak, initialValue) {
    // ..
    if (!Array.isArray(arr) || !arr.length || typeof filterCallback !== 'function') {
        return []
    } else {
        //        initialValue, 
        let hasInitialValue = initialValue !== undefined
        let value = hasInitialValue ? initialValue : arr[0]
        //      initialValue

        //    ,   1,       initialValue,   0,    
        for (let i = hasInitialValue ? 0 : 1, len = arr.length; i < len; i++) {
            //         reduceCallback 
            value = reduceCallback(value, arr[i], i, arr)
        }
        return value
    }
}

reduce方法将reducer函数应用于数组的每个元素(从左到右),并返回一个结果值。

37.什么是参数对象?


参数是传递给函数的参数的集合。这是一个类似数组的对象,它具有length属性,我们可以使用参数[i]访问特定的值,但是它没有forEach,reduce,filter和map方法。它允许您找出功能参数的数量。

您可以使用Array.prototype.slice将参数转换为数组:

Array.prototype.slice.call(arguments)

请记住:在箭头函数中,arguments对象不起作用。

function one() {
    return arguments
}
const two = function() {
    return arguments
}
const three = function three({
    return arguments
})
const four = () => arguments

four() // arguments is not defined

调用这四个函数会导致ReferenceError:参数未定义错误。可以使用rest语句解决此问题:

const four = (...args) => args

这将自动将所有参数放入数组中。

38.如何创建没有原型的对象?


可以使用Object.create完成:

const o1 = {}
console.log(o1.toString) // [object Object]

const o2 = Object.create(null) //      Object-create  -
//    -,   null
console.log(o2.toString) // o2.toString is not a function

39.为什么在给出的代码中调用函数时变量b变为全局变量?



function myFunc(){
    let a = b = 0
}
myFunc()

发生这种情况是因为赋值运算符(“ =”)具有右手关联性,即 从右到左分配值。因此,代码采用以下形式:

function myFunc(){
    let a = (b = 0)
}
myFunc()

首先,将值0分配给未声明的变量“ b”。JS引擎使其具有全局性。然后,将b = 0返回的值(0)分配给局部变量“ a”。

这个问题可以通过首先声明局部变量然后给它们赋值来解决:

function myFunc(){
    let a, b
    a = b = 0
}
myFunc()

40.什么是ECMAScript?


ECMAScript是一种规范,一种标准的脚本编程语言,它是JS的基础,因此对ECMAScript的任何更改都将反映在JS中。

可以在此处查看ECMA-262规范的最新版本

41. ES6或ECMAScript2015给JS带来了什么新东西?


  • 箭头功能
  • 班级
  • 模板字符串。
  • 增强对象文字
  • 解构(对象解构)。
  • 承诺(Promises)。
  • 发电机
  • 模组
  • 符号。
  • 代理人
  • 集。
  • 默认选项。
  • 休息和散布算子。
  • 块范围(关键字“ let”和“ const”)。

42.关键字“ var”,“ let”和“ const”有什么区别?


使用var关键字声明的变量是全局变量。这意味着可以从代码中的任何位置访问它们:

function giveMeX(showX){
    if(showX){
        var x = 5
    }
    return x
}

console.log(giveMeX(false))
console.log(giveMeX(true))

第一个console.log的结果将是不确定的,第二个将是不确定的-5。由于变量“ x”已出现在全局范围中,因此我们可以访问它。上面示例中的代码解释如下:

function giveMeX(showX){
    var x //   undefined
    if(showX){
        x = 5
    }
    return x
}

第一个console.log的结果是不确定的,因为默认情况下未定义未分配值的声明变量。

使用关键字“ let”和“ const”声明的变量具有块范围。这意味着它们仅在块({})中可用:

function giveMeX(showX){
    if(showX){
        let x = 5
    }
    return x
}

function giveMeY(showY){
    if(showY){
        let y = 5
    }
    return y
}

使用false参数调用这些函数将导致ReferenceError错误,因为在块外无法访问变量“ x”和“ y”并且不返回其值(不会弹出)。

“ let”和“ const”之间的区别在于,在第一种情况下,我们可以更改变量的值,而在第二种情况下,可以更改变量(无)。同时,我们可以更改使用const声明的对象的属性的值,但不能更改属性本身(变量)。

43.什么是箭头功能(箭头功能)?


箭头函数是在JS中创建函数的一种相对较新的方法。箭头函数比函数表达式更快,语法更易读。在箭头功能中,省略了“功能”一词:

// ES5
var getCurrentDate = function(){
    return new Date()
}

// ES6
const getCurrentDate = () => new Date()

在函数表达式中,我们使用return关键字返回一个值。在arrow函数中,我们不这样做,因为arrow函数隐式返回值,只要我们返回单个表达式或值即可:

// ES5
function greet(name){
    return 'Hello ' + name + '!' 
}

// ES6
const greet = (name) => `Hello ${name}`
const greet2 = name = > `Hello ${name}`

我们还可以将参数传递给箭头函数。如果传递一个参数,则不必将其包装在括号中:

const getArgs = () => arguments

const getArgs2 = (...rest) => rest

箭头函数无权访问arguments对象。因此,调用第一个函数将导致错误。为了获得传递给函数的参数,我们可以使用rest运算符。

const data = {
    result: 0
    nums: [1,2,3,4,5]
    computeResult(){
        // this    data
        const addAll = () => {
        //     this   
        return this.nums.reduce((total, cur) => total + cur, 0)
        }
    this.result = addAll()
    }
}

44.什么是课程?


类是在JS中编写构造函数的一种相对较新的方法。这是构造函数的语法糖。类基于相同的原型和原型继承:

// ES5
function Person(firstName, lastName, age, address){
    this.firstName = firstName
    this.lastName = lastName
    this.age = age
    this.address = address
}

Person.self = function(){
    return this
}

Person.prototype.toString = function(){
    return '[object Person]'
}

Person.prototype.getFullName = function(){
    return this.firstName + ' ' + this.lastName
}

// ES6
class Person{
    constructor(firstName, lastName, age, address){
        this.firstName = firstName
        this.lastName = lastName
        this.age = age
        this.address = address
    }

    static self(){
        return this
    }

    toString(){
        return '[object Person]'
    }

    getFullName(){
        return `${this.firstName} ${this.lastName}`
    }
}

方法重写并从另一个类继承:

// ES5
Employee.prototype = Object.create(Person.prototype)

function Employee(firstName, lastName, age, address, jobTitle, yearStarted){
    Person.call(this, firstName, lastName, age, address)
    this.jobTitle = jobTitle
    this.yearStarted = yearStarted
}

Employee.prototype.describe = function(){
    return `I am ${this.getFullName()} and I have a position of #{this.jobTitle} and I started at ${this.yearStarted}}`
}

Employee.prototype.toString = function(){
    return '[object Employee]'
}

// ES6
class Employee extends Person{ //   Person
    constructor(firstName, lastName, age, address, jobTitle, yearStarted){
        super(firstName, lastName, age, address)
        this.jobTitle = jobTitle
        this.yearStarted = yearStarted
    }

    describe(){
       return `I am ${this.getFullName()} and I have a position of #{this.jobTitle} and I started at ${this.yearStarted}}` 
    }

    toString(){ //   toString  Person
        return '[object Employee]'
    }
}

如何了解原型的使用?

class Something{ }

function AnotherSomething(){ }

const as = new AnotherSomething()
const s = new Something()

console.log(typeof Something) // function
console.log(typeof AnotherSomething) // function
console.log(as.toString()) // [object Object]
console.log(a.toString()) // [object Object]
console.log(as.toString === Object.prototype.toString)
console.log(a.toString === Object.prototype.toString)
//     true
// Object.prototype     
// Something  AnotherSomething   Object.prototype

45.什么是模板文字?


模板文字是在JS中创建字符串的一种相对较新的方法。模板文字使用双反引号(``)创建:

// ES5
var greet = 'Hi I\'m Mark'

// ES6
let greet = `Hi I'm Mark`

在模板文字中,我们不需要转义单引号。
// ES5
var lastWords = '\n'
    + ' I \n'
    + ' am \n'
    + 'Iron Man \n'

// ES6
let lastWords = `
    I
    am
    Iron Man
`

在ES6中,我们不需要使用转义序列“ \ n”来填充行。

// ES5
function greet(name){
    return 'Hello ' + name + '!'
}

// ES6
function greet(name){
    return `Hello ${name}!`
}

在ES6中,我们无需使用字符串串联即可将文本与变量结合在一起:我们可以使用表达式$ {expr}来获取变量的值。

46.什么是对象解构?


销毁是一种获取(检索)对象或数组的值的相对较新的方法。

假设我们有一个像这样的对象:

const employee = {
    firstName: 'Marko',
    lastName: 'Polo',
    position: 'Software Developer',
    yearHired: 2017
}

以前,要创建对象的属性,我们为每个属性创建变量。这很无聊又很烦人:

var firstName = employee.firstName
var lastName = employee.lastName
var position = employee.position
var yearHired = employee.yearHired

使用解构可以使代码更整洁并花费更少的时间。解构语法如下:我们将要接收的对象属性放在大括号({})中,如果要讨论数组,则在方括号([])中:

let { firstName, lastName, position, yearHired } = employee

要更改变量名称,请使用“ propertyName:newName”:

let { firstName: fName, lastName: lName, position, yearHired } = employee

要将默认值分配给变量,请使用“ propertyName ='defaultValue'”:

let { firstName = 'Mark', lastName: lName, position, yearHired } = employee

47.什么是模块?


模块使您可以组合(使用)来自不同文件的代码,并使我们不必将所有代码保存在一个大文件中。在JS中出现模块之前,有两种流行的模块系统可以支持代码:

  • CommonJS-Node.js
  • AMD(AsyncronousModuleDefinition)-浏览器

模块的语法非常简单:我们使用import从另一个文件导入功能或值,并导出到导出。

将功能导出到另一个文件(称为导出):

// ES5 CommonJS - helpers.js
exports.isNull = function(val){
    return val === null
}

exports.isUndefined = function(val){
    return val === undefined
}

exports.isNullOrUndefined = function(val){
    return exports.isNull(val) || exports.isUndefined(val)
}

// ES6 
export function isNull(val){
    return val === null;
}

export function isUndefined(val) {
    return val === undefined;
}

export function isNullOrUndefined(val) {
    return isNull(val) || isUndefined(val);
}

将功能导入另一个文件:

// ES5 CommonJS - index.js
const helpers = require('./helpers.js')
const isNull = helpers.isNull
const isUndefined = helpers.isUndefined
const isNullOrUndefined = helpers.isNullOrUndefined

//    
const { isNull, isUndefined, isNullOrUndefined } = require('./helpers.js')

// ES6 
import * as helpers from './helpers.js' // helpers -  

// 
import { isNull, isUndefined, isNullOrUndefined as isValid} from './helpers.js' //  "as"  

默认导出:

// ES5 CommonJS - index.js
class Helpers {
    static isNull(val){
        return val === null
    }

    static isUndefined(val){
        return val === undefined
    }

    static isNullOrUndefined(val){
        return this.isNull(val) || this.isUndefined(val)
    }
}

module.exports = Helpers

// ES6 
class Helpers {
    static isNull(val){
        return val === null
    }

    static isUndefined(val){
        return val === undefined
    }

    static isNullOrUndefined(val){
        return this.isNull(val) || this.isUndefined(val)
    }
}

export default Helpers

进口:

// ES5 CommonJS - index.js
const Helpers = require('./helpers.js')
console.log(Helpers.isNull(null))

// ES6 
import Helpers from './helpers.js'
console.log(Helpers.isNull(null))

这是模块的基本用法。我没有详细介绍,因为我的帖子已经太大了。

48.什么是Set对象?


Set对象允许您存储唯一的值,基元和对象引用。再次:只能将唯一值添加到Set中。它使用SameZeroValue算法检查存储在其中的值。

使用Set构造函数创建Set实例。我们还可以在创建时将一些值传递给它:

const set1 = new Set()
const set2 = new Set(['a','b','c','d','d','e']) //  "d"  

我们可以使用add方法将值添加到Set中。由于add方法是可返回的,因此我们可以使用一系列调用:

set2.add('f')
set2.add('g').add('h').add('i').add('j').add('k').add('k') //  "k"  

我们可以使用delete方法从Set中删除值:

set2.delete('k') // true
set2.delete('z') // false,    set2   

我们可以使用has方法检查Set中的属性:

set2.has('a') // true
set2.has('z') // false

要获取Set的长度,请使用size方法:

set2.size // 10

clear方法清除Set:

set2.clear() // 

我们可以使用Set删除数组中的重复值:

const nums = [1,2,3,4,5,6,6,7,8,8,5]
const uniqNums = [...new Set(nums)] // [1,2,3,4,5,6,7,8]

49.什么是回调函数?


回调函数是其调用被推迟到将来的函数(在某些条件下,例如,事件发生时发生)。

const btnAdd = document.getElementById('btnAdd')

btnAdd.addEventListener('click', function clickCallback(e)){
    //   
}

在示例中,我们正在等待标识符为“ btnAdd”的元素上的“ click”事件。通过单击,将调用clickCallback函数。回调函数为数据或事件添加了一些功能。将reduce,filter和map方法传递给回调函数作为第二个参数。与回叫的一个很好的比喻是以下情况:您呼叫某人,他不应答,给他留言,等待他回电。呼叫或消息是事件或数据,而回调是回叫的期望(预期)。

50.什么是承诺?


承诺是在JS中使用异步代码的一种方式。它们返回异步操作的结果。发明了承诺来解决所谓的回调函数地狱的问题。

fs.readFile('somefile.txt', function(e, data){
    if(e){
        console.log(e)
    }
    console.log(data)
})

当我们需要向第一个(在第一个内部)添加另一个异步操作,然后向另一个添加异步操作时,这种方法的问题就开始了。结果,我们得到了一个混乱且不可读的代码:

fs.readFile('somefile.txt', function(e,data){
    // 
    fs.readFile('directory', function(e, files){
        // 
        fs.mkdir('directory', function(e){
            // 
        })
    })
})

这就是诺言的样子:

promReadFile('file/path')
.then(data => {
    return promReaddir('directory')
})
.then(data => {
    return promMkdir('directory')
})
.catch(e => {
    console.error(e)
})

承诺有四个条件:

  • 等待是承诺的初始状态。由于操作尚未完成,因此诺言的结果是未知的。
  • 完成-异步操作完成,有结果。
  • 拒绝-异步操作失败,这是有原因的。
  • 已完成-已完成或被拒绝。

Promise构造函数接受resolve和拒绝作为参数。在解决过程中,记录操作结果,而拒绝记录操作失败的原因。可以使用.then方法处理结果,可以使用.catch方法处理错误。.then方法还返回一个promise,因此我们可以使用由多个.then组成的链。

const myPromiseAsync = (...args) => {
    return new Promise((resolve, reject) => {
        doSomeAsync(...args, (error, data) => {
            if(error){
                reject(error)
            } else{
                resolve(data)
            }
        })
    })
}

myPromiseAsync()
.then(result => {
    console.log(result)
})
.catch(reason => {
    console.error(reason)
})

我们可以创建一个辅助函数,以将异步操作从回调转换为Promise。它将像Node.js中的util一样工作(“承诺”):

const toPromise = (asyncFuncWithCallback) => {
    return (...args) => {
        return new Promise((res, rej) => {
            asyncFuncWithCallback(...args, (e, result) => {
                return e ? rej(e) : res(result)
            })
        })
    }
}

const promiseReadFile = toPromise(fs.readFile)

promiseReadFile('file/path')
.then((data) => {
    console.log(data)
})
.catch(e => console.error(e))

您可以在此处此处阅读有关诺言的更多信息

51.什么是异步/等待?


异步/等待是在JS中编写异步(非阻塞)代码的一种相对较新的方法。他们被包裹在一个诺言中。比起promise和回调函数,它使代码更具可读性和简洁性。但是,要使用async / await,您需要非常了解诺言。

// 
function callApi(){
    return fetch('url/to/api/endpoint')
    .then(resp => resp.json())
    .then(data => {
        //   
    }).catch(err => {
        //   
    })
}

// async/await
//     try/catch
async function callApi(){
    try{
        const resp = await fetch('url/to/api/endpoint')
        const data = await res.json()
        //   
    } catch(e){
        //   
    }
}

切记:在函数强制其返回promise之前使用async关键字:

const giveMeOne = async () = 1

giveMeOne()
.then((num) => {
    console.log(num) // 1
})

await关键字只能在异步函数内使用。在另一个函数中使用await会导致错误。Await等待表达式在右边完成以返回其值,然后再执行下一行代码。

const giveMeOne = async() => 1

function getOne(){
    try{
        const num = await giveMeOne()
        console.log(num)
    } catch(e){
        console.log(e)
    }
}
// Uncaught SyntaxError: await is only valid in an async function

async function getTwo(){
    try{
        const num1 = await giveMeOne()
        const nm2 = await giveMeOne()
        return num1 + num2
    } catch(e){
        console.log(e)
    }
}

await getTwo() // 2

此处此处阅读有关异步/等待的更多信息

52.点差算子和休息算子有什么区别?


spread和rest语句具有相同的语法(“ ...”)。区别在于这样一个事实,在传播的帮助下,我们将数组的数据传输或传播到其他数据,在其余的帮助下,我们获得了函数的所有参数并将其放入数组中(或者我们提取了一些参数)。

function add(a, b){
    return a + b
}

const nums = [5, 6]
const sum = add(...nums)
console.log(sum) // 11

在此示例中,当使用nums数组数据调用add函数时,我们使用spread。变量“ a”的值为5,b = 6,总和= 11。

function add(...rest){
    return rest.reduce((total, current) => total + current)
}

console.log(add(1, 2)) // 3
console.log(add(1, 2, 3, 4, 5)) // 15

在这里,我们使用任意数量的参数调用add函数。Add返回这些参数的总和。

const [first, ...others] = [1, 2, 3, 4, 5]
console.log(first) // 1
console.log(others) // [2, 3, 4, 5]

在此示例中,我们使用rest将除第一个参数以外的任意数量的参数放入other数组中。

53.什么是默认参数?


这是定义默认变量值的一种相对较新的方法。

// ES5
function add(a,b){
    a = a || 0
    b = b || 0
    return a + b
}

// ES6
function add(a = 0, b = 0){
    return a + b
}
//      "a"  "b" - ,    0
add(1) // 1

您可以使用解构:

function getFirst([first, ...rest] = [0, 1]){
    return first
}

getFirst() // 0
getFirst([10,20,30]) // 10

function getArr({ nums } = { nums: [1,2,3,4] }){
    return nums
}

getArr // [1,2,3,4]
getArr({nums:[5,4,3,2,1]}) // [5,4,3,2,1]

我们甚至可以使用在同一位置声明的默认参数:

function doSomethingWithValue(value = 'Hello World', callback = () => { console.log(value) }){
    callback()
}
doSomethingWithValue() // Hello World

54.什么是对象包装器(Wrapper Objects)?


尽管字符串,数字和布尔值不是对象,但它们具有属性和方法:

let name = 'marko'

console.log(typeof name) // string
console.log(name.toUpperCase()) // MARKO

Name是没有属性和方法的字符串(原始类型),但是当我们调用toUpperCase()方法时,这不会导致错误,而会导致“ MARKO”。

此行为的原因是名称被临时转换为对象。除null和undefined之外,每个基元都有一个包装对象。这些对象是字符串,数字,布尔值,符号和BigInt。在我们的例子中,代码采用以下形式:

console.log(new String(name).toUpperCase()) // MARKO

使用属性或方法完成工作后,将丢弃临时对象。

55.隐性强制和显性强制之间有什么区别?


隐式转换是一种在我们不了解(参与)的情况下将值转换为另一种类型的方法。

假设我们有以下内容:

console.log(1 + '6')
console.log(false + true)
console.log(6 * '2')

第一个console.log的结果将为16。在其他语言中,这将导致错误,但是在JS 1中,它将转换为字符串并从6连接(附加)。我们什么也不做,转换自动发生。

第二个console.log的结果将为1。将False转换为0,将true转换为1。0+ 1 =1

。第三个console.log的结果将为12。第2行在未乘以6之前已转换为数字

。将值转换为另一种类型:

console.log(1 + parseInt('6'))

在此示例中,我们使用parseInt将字符串6转换为数字,然后将两个数字相加得到7。

56.什么是NaN?如何检查该值是否为NaN?


NaN或非数字(非数字)是对非数字值执行数字运算而获得的值:

let a

console.log(parseInt('abc'))
console.log(parseInt(null))
console.log(parseInt(undefined))
console.log(parseInt(++a))
console.log(parseInt({} * 10))
console.log(parseInt('abc' - 2))
console.log(parseInt(0 / 0))
console.log(parseInt('10a' * 10))

JS具有内置的isNaN方法,该方法可让您检查值是否为NaN,但其行为却很奇怪:

console.log(isNaN()) // true
console.log(isNaN(undefined)) // true
console.log(isNaN({})) // true
console.log(isNaN(String('a'))) // true
console.log(isNaN(() => { })) // true

尽管没有一个值是NaN,所有console.log的结果都是真实的。

ES6建议使用Number.isNaN方法检查该值是否为NaN。我们还可以编写一个辅助函数来解决“ NaN不等式本身”的问题:

function checkIsNan(value){
    return value !== value
}

57.如何检查值是否为数组?


为此,请使用Array.isArray方法:

console.log(Array.isArray(5)) // false
console.log(Array.isArray('')) // false
console.log(Array.isArray()) // false
console.log(Array.isArray(null)) // false
console.log(Array.isArray( {length: 5 })) // false
console.log(Array.isArray([])) // true

如果您工作的环境不支持此方法,则可以使用以下多文件:

function isArray(value){
    return Object.prototype.toString.call(value) === '[object Array]'
}

58.如何在不使用模除或除以余数(运算符“%”)的情况下检查数字是否为偶数?


要解决此问题,可以使用运算符“&”(二进制和)。&运算符将操作数比较为二进制值。

function isEven(num){
    if(num & 1){
        return false
    } else{
        return true
    }
}

以二进制符号0 000
1 -这是001
2 - 010
3 - 011
4 - 100
5 - 101
6 - 110
7 - 111




Console.log(5&1)将返回1。首先,&运算符将两个数字都转换为二进制值,5变成101,1变成001。然后进行按位比较:



比较1和0,得到0。
比较0和0 ,我们得到0。
比较1和1,我们得到1。
将二进制值转换为整数,我们得到1。

如果这个信息对您来说太复杂了,我们可以使用递归函数解决问题:

function isEven(num){
    if(num < 0 || num === 1) return false
    if(num == 0) return true
    return isEven(num - 2)
}

59.如何确定对象中是否存在属性?


有三种方法可以做到这一点。

第一种方法是使用in运算符:

const o = {
    'prop': 'bwahahah',
    'prop2': 'hweasa'
}

console.log('prop' in o) // true
console.log('prop1' in o) // false

第二种是使用hasOwnProperty方法:

console.log(o.hasOwnProperty('prop2')) // true
console.log(o.hasOwnProperty('prop1')) // false

第三是数组的索引符号:

console.log(o['prop']) // bwahahah
console.log(o['prop1']) // undefined

60.什么是AJAX?


AJAX或异步JavaScript和XML是一组互连技术,可让您以异步模式使用数据。这意味着我们可以在不重新加载网页的情况下将数据发送到服务器并从服务器接收数据。

AJAX使用以下技术:
HTML-网页结构。
CSS-网页样式。
JavaScript-页面行为并与DOM一起使用。
XMLHttpRequest API-从服务器发送和接收数据。
PHP,Python,Nodejs-某种服务器语言。

61.如何在JS中创建对象?


对象文字:

const o = {
    name: 'Mark',
    greeting(){
        return `Hi, I'm ${this.name}`
    }
}

o.greeting // Hi, I'm Mark

构造函数:

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

Person.prototype.greeting = function(){
    return `Hi, I'm ${this.name}`
}

const mark = new Person('Mark')

mark.greeting() // Hi, I'm Mark

Object.create方法:

const n = {
    greeting(){
        return `Hi, I'm ${this.name}`
    }
}

const o = Object.create(n)

o.name = 'Mark'

console.log(o.greeting) // Hi, I'm Mark

62. Object.freeze和Object.seal方法有什么区别?


区别在于,使用Object.freeze方法时,我们无法更改或编辑对象的属性,而使用Object.seal时,则有这样的机会。

63. in运算符和hasOwnProperty方法有什么区别?


区别在于,“ in”运算符不仅检查对象本身是否存在属性,还检查其原型中是否存在属性,并且仅在对象中检查hasOwnProperty方法。

console.log('prop' in o) // true
console.log('toString' in o) // true

console.log(o.hasOwnProperty('prop')) // true
console.log(o.hasOwnProperty('toString')) // false

64.您知道在JS中使用异步代码的哪些技术?


  • 回呼
  • 承诺(Promises)。
  • 异步/等待。
  • 像async.js,blueprint,q,co之类的库。

65.正常函数和函数表达式有什么区别?


假设我们有以下内容:

hoistedFunc()
notHoistedFunc()

function hoistedFunc(){
    console.log('I am hoisted')
}

var notHoistedFunc = function(){
    console.log('I will not be hoisted!')
}

对notHoistedFunc的调用将导致错误,但对hoistedFunc的调用则不会,因为hoistedFunc“弹出”会上升到全局范围,而notHoistedFunc不会。

66.如何在JS中调用函数?


在JS中,有4种方法来调用函数。该调用定义了此值或函数的“所有者”。

调用为函数。如果将函数称为方法,构造函数,或者使用apply或call方法,则将其称为函数。此类函数的所有者是window对象:

function add(a,b){
    console.log(this)
    return a + b
}

add(1,5) // window, 6

const o = {
    method(callback){
        callback()
    }
}

o.method(function(){
    console.log(this) // window
})

作为方法调用。当函数是对象的属性时,我们称其为方法。调用方法时,此对象将成为此方法的对象:

const details = {
    name: 'Marko',
    getName(){
        return this.name
    }
}

details.getName() // Marko,  this   details

调用为构造函数。当使用“ new”关键字调用函数时,我们将该函数称为构造函数。这将创建一个空对象,其值是:

function Employee(name, position, yearHired){
    //   ,   this
    // this = {}
    this.name = name
    this.position = position
    this.yearHired = yearHired
    //   Employee.prototype   this,    
}

const emp = new Employee('Marko Polo', 'Software Development', 2017)

使用apply或call方法的呼叫。当我们要显式确定this或函数所有者的值时,可以使用以下方法:

const obj1 = {
    result: 0
}

const obj2 = {
    result: 0
}

function reduceAdd(){
    let result = 0
    for(let i = 0, len = arguments.length; i < len; i++){
        result += arguments[i]
    }
    this.result = result
}

reduceAdd.apply(obj1, [1,2,3,4,5]) //  this  obj1
reduceAdd.call(obj2, 1,2,3,4,5) //  this  obj2

67.什么是记忆或记忆?


记忆化是一种创建可以记住先前计算的结果或值的函数的技术。记住的优点是我们避免使用相同的参数重新执行一个函数。缺点是我们被迫分配额外的内存来保存结果。

68.您将如何实现记忆的辅助功能?


function memoize(fn){
    const cache = {}
    return function(param){
        if(cache[param]){
            console.log('cached')
            return cache[param]
        } else{
            let result = fn(param)
            cache[param] = result
            console.log('not cached')
            return result
        }
    }
}

const toUpper = (str = '') => str.toUpperCase()

const toUpperMemoized = memoize(toUpper)

toUpperMemoized('abcdef')
toUpperMemoized('abcdef') //  

我们用一个参数实现了记忆功能。让我们将其设为“多参数”:

const slice = Array.prototype.slice
function memoize(fn){
    const cache = {}
    return (...args) => {
        const params = slice.call(args)
        console.log(params)
        if(cache[params]){
            console.log('cached')
            return cache[params]
        } else{
            let result = fn(...args)
            cache[params] = result
            console.log('not cached')
            return result
        }
    }
}
const makeFullName = (fName, lName) => `${fName} ${lName}`
const reduceAdd = (numbers, startValue = 0) => numbers.reduce((total, cur) => total + cur, startValue)

const memoizedFullName = memoize(makeFullName)
const memoizeReduceAdd = memoize(reduceAdd)

memoizedFullName('Marko', 'Polo')
memoizedFullName('Marko', 'Polo') //  

memoizeReduceAdd([1,2,3,4],5)
memoizeReduceAdd([1,2,3,4],5) //  

69.为什么typeof null返回对象?如何检查一个值是否为空?


typeof null =='object'总是出于历史原因返回true。有人提出了通过将typeof null ='object'更改为typeof null ='null'来解决此错误的建议,但为了保持向后兼容性而拒绝了该提议(这种更改会带来大量错误)。

要检查该值是否为空,可以使用严格相等运算符(===):

function isNull(value){
    return value === null
}

70.“ new”关键字的作用是什么?


在构造函数中使用关键字“ new”来创建新对象(类的新实例)。

假设我们有这样的代码:

function Employee(name, position, yearHired){
    this.name = name
    this.position = position
    this.yearHired = yearHired
}

const emp = new Employee('Marko Polo', 'Software Development', 2017)

关键字“ new”执行4件事:

  1. 创建一个空对象。
  2. 将此值绑定到它。
  3. 函数继承自functionName.prototype。
  4. 除非另有说明,否则返回此值。

Source: https://habr.com/ru/post/undefined/


All Articles