面JS、DOM、BOM

147 阅读8分钟

1. JS的数据类型(七种数据类型+新1)

null undefined

object

number bool symbol string

bigint

2. null 和undefined的区别是什么

  1. undefined 是声明之后的默认值 但 null 不是

  2. undefined一般是来给基本类型做未赋值

    null 是给对象做未赋值 (程序员一般 一个对象如果为空,会给null,而不是undefined)

总结 (对象就给null, 非对象就给undefined)

  1. undefined 不是一个关键字

    而null 是一个关键字

    你可以声明一个变量是null,但是不能声明一个变量是undefined

  • undefined 表示一个变量自然的、最原始的状态值,而 null 则表示一个变量被人为的设置为空对象,而不是原始状态。

3. typeof null 的结果是什么

  • object
typeof 'str' // 'string'
typeof NaN // 'number'
typeof 1 // 'number'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol() // 'symbol'
typeof null // 'object'

4. 0.1+0.2的结果为什么不等于0.3

  • 浮点数的精度问题,在内存中,0.1并不等于0.1, 0.2并不等于0.2 ,所以它们的结果是约等于0.3 , 并不是等于3

  • 至于让它们相等,有个Number.EPSILON的属性

5. typeof NaN的结果是什么

  • number , 因为NaN只是一个不能表示的数字,但它还是一个数字

🈶6. let var const 的区别

letconst 都只是块级作用域,不会变量提升且必须在声明之后使用,没有全局变量,不能重复声明变量
而var是全局作用域,不管你在哪声明,它都会影响全局,可以重复声明变量

letvar 可以不需要设置初始值
但const 必须设置初始值

7. 箭头函数和普通函数的区别

  1. 箭头函数比普通函数更简洁
  2. 箭头函数没有自己的this
  3. 箭头函数没有arguments
  4. 箭头函数不能作为构造函数使用
  5. 箭头函数没有原型prototype

8. 箭头函数什么情况下可以省略它的return

  1. 只有一个语句
  2. 没有花括号

9. 箭头函数的this指向哪里

  1. 箭头函数本身是没有this的
  2. 指向外面的this

10. 你对JSON的理解

JavaScript Object Notation(JavaScript 对象表示法)

  1. JSON一种通用数据结构的语言
  2. 是用来代替XML 做数据交换
  3. JSON支持的数据类型 对象、数组、bool、string、number、null , 不支持函数
  4. 可以使用 JSON.parse() 方法将数据转换为 JavaScript 对象
  5. 可以使用 JSON.stringify() 方法将 JavaScript 对象转换为字符串
  • JSON 与 JS 的区别
  1. JSON只支持双引号
  2. JSON没有undefined
  3. JSON不支持函数
  4. JSON不支持变量

11. Unicode 和 UTF-8 的区别

  1. Unicode是一个字符集,把世界上所有的文字编了一个号
  2. UTF-8是一种编码

12. 说说你对原型、原型链的理解

github.com/lurenacm/ag…

🈶13. 说说你对闭包的理解

什么是闭包:一个函数用到了外部的变量那么这个总和就是闭包

闭包的用途:闭包常用来间接访问一个变量,也就是说隐藏一个变量

关键点:用途,需要回答【隐藏局部变量,暴露操作函数】,用代码举例

const createAdd = ()=>{
    let n = 0
    return ()=>{
        n += 1
        console.log(n)
    }
}

const add = createAdd()
add() // 1
add() // 2

闭包的缺点:闭包在IE有bug,增加内存使用量,故而会导致内存泄露

🈶14. 什么是立即执行函数,为什么要用

  • 立即执行函数就是:声明一个匿名函数,马上调用这个匿名函数
  • 作用:创建一个独立的作用域。这个作用域里面的变量,外面访问不到(即避免「变量污染」)

立即执行函数

15. 请说一下异步编程的实现方式

  • 为什么要异步编程:为了实现多线程
  1. 回调函数
  2. Promise
  3. async 和await
  4. generator

🈶16. Promise(then、 catch、 all、 race

  • 如何创建一个 new Promise:
return new Promise((resolve,reject)=>{})
  • 如何使用 Promise.prototype.then
const promise1 = new Promise((resolve, reject) => {
  resolve('Success!')
})
promise1.then((value) => {
  console.log(value)
})

Promise.catch的用法就是和then一样,先执行这个(then)再执行那个(catch)

  • 如何使用 Promise.all,都成功才会调用
let Promise1 = new Promise(function(resolve,reject)=>{})
let Promise2 = new Promise(function(resolve,reject)=>{})
let Promise3 = new Promise(function(resolve,reject)=>{})
let p = Promise.all([Promise1,Promise2,Promise3])
p.then(function(){

},function(){

})
  • 如何使用 Promise.race,谁第一个成功或失败,就认为是race的成功或失败
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one')
})
const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two')
})
Promise.race([promise1, promise2]).then((value) => {
  console.log(value)
})

🈶17. 请问Promise解决了什么问题

  • Promise 的用途: 是前端解决异步问题的方案,用于避免回调地域,让代码看起来更同步

18. 什么是同步,什么是异步,什么是异步函数

  • 同步:能直接拿到结果
  • 异步: 不能直接拿到结果
  • 异步函数: 如果一个函数的返回值处于setTimeoutAJAX(XMLHttpRequest)AddEventListener,这三个东西内部,那么这个函数就是异步函数

注:不要把AJAX设置为同步

19. 并发与并行的区别

  • 并发是宏观概念,表示A和B同时发生
  • 并行是微观概念,表示CPU有多个核心,同时执行两个不同的任务

🈶20. 如何实现深拷贝****

背代码,要点:

  1. 递归
  2. 判断类型
  3. 检查环(也叫循环引用)
  4. 需要忽略原型

image.png

21. JS中值和地址的区别

  • 像string bool number这些都是值
  • 像存一些函数的变量 这些都是地址
  • 通过typeof object判断是值还是地址

🈶22. 函数防抖和函数节流****


1.  背代码

    ```
     冷却时间
     // 节流(一段时间执行一次之后,就不执行第二次)
     function throttle(fn, delay){
         let canUse = true
         return function(){
             if(canUse){
                 fn.apply(this, arguments)
                 canUse = false
                 setTimeout(()=>canUse = true, delay)
             }
         }
     }

     const throttled = throttle(()=>console.log('hi'))
     throttled()
     throttled()
    ```

    注意,有些地方认为节流函数不是立刻执行的,而是在冷却时间末尾执行的(相当于施法有吟唱时间),那样说也是对的。

1.  背代码

    ```
    带着一起做
     // 防抖(一段时间会等,然后带着一起做了)
     function debounce(fn, delay){
         let timerId = null
         return function(){
             const context = this
             if(timerId){window.clearTimeout(timerId)}
             timerId = setTimeout(()=>{
                 fn.apply(context, arguments)
                 timerId = null
             },delay)
         }
     }
     const debounced = debounce(()=>console.log('hi'))
     debounced()
     debounced()
    ```

🈶23. 如何实现数组去重***

1. 使用hash
unique = (array) => {
    const hash = []
    for(let i=0;i<array.length; i++){
        hash[array[i]] = true
    }
    const result = []
    for(let k in hash){
        result.push(k)
    }
    return result
}
缺点:只支持数字或者字符串数组,如果数组里面有对象,比如 array = [{number:1}, 2],就会出错。

2. 使用set
[...new set(array)]

unique = (array) => {
    return [...new Set(array)] 
    // 或者 return Array.from(new Set(array))
}

缺点:API 太新,旧浏览器不支持。

3. WeakMap  支持所有类型的去重

🈶24. 手写AJAX

  • 什么是AJAX
  • Ajax是一种异步请求数据的web开发技术
  • 简单地说,在不需要重新刷新页面的情况下,Ajax 通过异步请求加载后台数据,并在网页上呈现出来。
  • Ajax的目的是提高用户体验,较少网络数据的传输量
1.  背代码,完整版

    ```
     var request = new XMLHttpRequest()
     request.open('GET', '/a/b/c?name=ff', true);
     request.onreadystatechange = function () {
       if(request.readyState === 4 && request.status === 200) {
         console.log(request.responseText);
       }};
     request.send();
    ```

1.  背代码,简化版

    ```
     var request = new XMLHttpRequest()
     request.open('GET', '/a/b/c?name=ff', true)
     request.onload = ()=> console.log(request.responseText)
     request.send()
    ```

🈶26. 这段代码里的 this 是什么?

  1. 背代码

    1. fn()
      this => window/global
    2. obj.fn()
      this => obj
    3. fn.call(xx)
      this => xx
    4. fn.apply(xx)
      this => xx
    5. fn.bind(xx)
      this => xx
    6. new Fn()
      this => 新的对象
    7. fn = ()=> {}
      this => 外面的 this
  2. 看调用
    《this 的值到底是什么?一次说清楚》

🈶27. 什么是 JSONP,什么是 CORS,什么是跨域?

点击链接有解释什么是同源策略以及跨域解决方案

  1. JSONP 
  • 优点:兼容IE、跨域
  • 缺点:由于它是script标签,所以它拿不到状态码、拿不到header 、它不支持post , 只支持GET zhuanlan.zhihu.com/p/22600501
  1. CORS方法 跨域资源共享

在被分享的server.js响应头里添加这个网站(要一模一样)

response.setHeader("Access-Control-Allow-Origin", "http://frank.com:9990")

developer.mozilla.org/zh-CN/docs/…

🈶28. async/await 怎么用,如何捕获异常?

  • 为什么要await 为了让异步形式写成同步形式,让代码更像是同步的
  1. 阮一峰的书讲了
  2. 方方的视频课讲了 最后一节。

🈶29. 如何用正则实现trim() ?

背代码

String.prototype.trim = function(){
    return this.replace(/^\s+|\s+$/g, '')
}
//或者 
function trim(string){
    return string.replace(/^\s+|\s+$/g, '')
}

🈶30. 不用 class 如何实现继承?用 class 又如何实现?

1.  背代码,不用 class 这样实现

    ```
     function Animal(color){
         this.color = color
     }
     Animal.prototype.move = function(){} // 动物可以动
     function Dog(color, name){
         Animal.call(this, color) // 或者 Animal.apply(this, arguments)
         this.name = name
     }
     // 下面三行实现 Dog.prototype.__proto__ = Animal.prototype
     function temp(){}
     temp.prototype = Animal.prototype
     Dog.prototype = new temp()

     Dog.prototype.constuctor = Dog // 这行看不懂就算了,面试官也不问
     Dog.prototype.say = function(){ console.log('汪')}

     var dog = new Dog('黄色','阿黄')
    ```

1.  背代码,用 class 就简单了

    ```
     class Animal{
         constructor(color){
             this.color = color
         }
         move(){}
     }
     class Dog extends Animal{
         constructor(color, name){
             super(color)
             this.name = name
         }
         say(){}
     }
    ```

🈶31. 用mouse事件写一个可拖拽的div

参考代码:jsbin.com/munuzureya/…

32. 什么是DOM,什么是BOM

  1. DOM(Document Object Model)是文档对象模型,

    用途:就是把网页变成JS中的对象来进行增删改查操作

    平时我们要操作DOM的话可以用document.getElementById()来对它的各种属性进行修改

  2. BOM(Browser Object Model)是浏览器对象模型 用途: 用JS前进后退操作地址栏

🈶33. 事件委托

  1. 错误版(但是可能能过)

     ul.addEventListener('click', function(e){
         if(e.target.tagName.toLowerCase() === 'li'){
             fn() // 执行某个函数
         }
     })
    

    bug 在于,如果用户点击的是 li 里面的 span,就没法触发 fn,这显然不对。

  2. 高级版

     function delegate(element, eventType, selector, fn) {
         element.addEventListener(eventType, e => {
           let el = e.target
           while (!el.matches(selector)) {
             if (element === el) {
               el = null
               break
             }
             el = el.parentNode
           }
           el && fn.call(el, e, el)
         })
         return element
       }
    

    思路是点击 span 后,递归遍历 span 的祖先元素看其中有没有 ul 里面的 li。