你的浏览器不支持canvas

Enjoy life!

我对前端面试的看法 (三)js

Date: Author: JM

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。

1、说说js的引入方式

  • 行内引入:让html结构变得混杂,不推荐使用
  • 内嵌:将代码写在script标签中,如果别的地方要用相同的代码,就要再copy一次,显得冗余
  • 外链:利用script标签的src属性,引入外部js文件

2、JSDoc注释规范

3、说一说有什么基本类型

  • String, Number, Boolean, Undefined, Null,Symbol

4、说一说Undfined 和 Null 异同

相同点:

  • 他们都是基本类型
  • 当遇到判断语句时:undefined 和 null 都会自动转化成false

不同点:

  • undefined表示缺少值,即声明了还没有初始化;null表示空对象,即此处不应有值
  • typeof 操作符去获取 undefinednull 的类型时,你会发现 undefinedundefined,而null 却是对象
  • undefined典型用法就是:
    • 变量只是被声明,却还没有赋值
    • 函数没有return语句,那么此函数默认返回undefined
    • 调用函数时,如果对应的参数没有赋值,那么这个参数也为undefined
  • null典型用法就是:
    • 作为对象原型链的终点(Object.getPrototypeOf(Object.prototype))

5、说一说数组常用的方法

  • 这四个方法都会影响原数组。
  • push:把想添加的项添加到数组的末尾,返回修改后数组的长度
  • pop:把数组的最后一项移除,返回移除的项
  • unshift:把想添加的项添加的数组前端,并返回新数组的长度
  • shift:移除数组第一项,返回移除的项,同时数组长度减1

  • 操作方法:
    • splice():基于原数组上,返回从原数组上删除的项组成的数组,一般用于数组元素的删除、插入、替换
    • slice():基于当前数组一或多项创建新的数组
    • concat():不传参,则创建数组的副本;传参则拼接数组

  • 重排序方法:
    • sort():正序,回调中参数为前一个值与后一个值
    • reverse():倒序

  • 迭代方法:
    • every():数组的每项调用回调都返回true则true
    • some():数组中有一项调用回调返回true则true
    • map():返回每次调用回调的结果组成的数组
    • filter():返回调用回调返回true的项组成的数组
    • forEach():迭代

  • 位置方法:
    • indexOf():正序查找,返回索引
    • lastIndexOf():倒序查找,返回索引
    • includes():是否包含指定的值

  • 其他:
    • join():返回使用指定分隔符构建的字符串

6、说一说队列和栈

  • 队列数据结构的访问规则是先进先出
    • 即:队列在列表的末端添加项,在列表的前端移除项
    • 用的数组方法是:push + shift
  • 栈的数据结构是后进先出
    • 即:最早添加的项最早被移除
    • 用的数组方法是:push + pop

7、说一说你对作用域链的理解

先结合例子阐述自己对作用域链的理解,最后回归专业术语,总结作用域链是什么,有什么用

  • 个人对作用域的理解就是:
    • 作用域就像一栋楼,作用域链就是连接每一层楼的阶梯,而每一层楼就是一个执行环境,里面有着当前环境的变量对象,而变量对象又保存着形参、变量、函数;
    • 最底层楼就是当前的执行环境,而最高那层楼就是全局执行环境。
    • 当我们要寻找标识符的时候,先在当前环境, 即:最底层寻找,就像我们要到5楼,那么我们肯定要先从1楼上去一样,
    • 如果在当前环境没有找到想要找到的标识符,那么我们就可以沿着作用域链,继续向上一层去寻找,直到找到我们想要找的标识符为止。
  • 这里也说一下与作用域链有关的一个十分重要的属性 [[Scope]] 属性,这个属性在函数创建的时候就已经存在了,它的特点是静态的,不可更改的,
  • 我们可以通过这个内部属性访问当前执行环境的上一层的变量对象
  • 其实一开始知道这个属性的时候,我还是有点懵的,但我在浏览器console.dir一个函数出来之后,就发现有这个[[Scopre]]属性,然后看了一下里面的内容, 果然是包含了其父级的作用域链
  • 最后总结一下:
    • 作用域链就是保存内部上下文所有变量对象的列表
    • 作用域链的作用就是:保证对执行环境有权访问的所有变量和函数的有序访问

8、说一说作用域

  • 作用域是一套规则,用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称寻找变量
  • 作用域又分为:全局作用域和函数作用域
  • 全局作用域定义的变量函数作用域可以访问,但是,函数作用域定义的变量全局作用域则不可以访问

9、说一说this

this 是什么,有什么作用,用途是什么

  • 我对this的理解就是:
    • this就是一个代名词,指示某个对象,方便我们通过this去调用这个对象上有的属性和方法,这个会比直接通过某对象.属性或方法要优雅得多。
  • 如果我们要用到this,必须要注意 this 的指向问题。
    • 一般来说,在全局作用域中定义的函数,在全局作用域中调用,this都是指向window
    • 一般来说,调用在某对象下定义的函数,那么this就会指向该对象
    • 如果遇到闭包,要注意:this可能会指向window,此时需要用一个变量保留this的引用,才能保证this的指向正确

  • 在新出的 es6 语法中的箭头函数的 this 又稍微与 es5 中函数的 this不一样:
  • 箭头函数中的this的指向取决于箭头函数定义时所处的环境
  • es5 中函数中的 this 中的指向取决于调用表达式的形式

  • 如果遇到混杂函数(就是既有箭头函数,又有普通函数),就要注意this可能会指向当前对象,也可能会指向window

10、如何理解闭包

先说说闭包是什么,有什么特点,然后再说一说优点,最后说一说缺点,最后说一说应用场景

  • 闭包:是指有权访问另一个函数作用域的函数
  • 特点:
    • 函数嵌套函数
    • 内部函数可以引用外层的参数和变量
    • 参数和变量不会被垃圾回收机制回收
  • 好处:建立私有变量和特权方法,避免全局变量的污染,如果希望一个变量长期驻扎在内存中
  • 缺点:由于大量的变量参数无法被回收,所以内存会容易过大,导致内存泄漏
  • 应用场景:
    • 模块化代码
    • 建立私有成员(有时候单例模式也会用到闭包)

垃圾回收机制:标记清除、引用计数

10、谈一谈面向对象

整体阐述面向对象是什么,再说一说js的面向对象,接着说一说面向对象的特点

  • 面向对象,就像我们的世界。
  • 比如说:人,它不都有一个鼻子,两只眼睛?这其实是很宽泛的去描述一个人。
  • 因此,人,其实是抽象的,它描述的是一类特定类别的东西。
  • 你我是人,人是动物
  • 但是你我除了有一个鼻子,两只眼睛外,也有许多地方不同的!
  • 比如,我的性别是女的,你的性别是男的,比如说,你的爱好是打篮球,我的爱好是学习等等
  • 所以,你我其实是基于抽象 的“人”的具体化的人。

  • 万物皆对象,每个对象都有其属性和方法。比如,人有姓名、年龄,这些就是人的属性;人跑步、学习,这些就是人的方法。
  • 面向对象有以下特点:
    • 抽象:就像刚刚说到的人一样,它只是一类特定类别的大类,具有这些特定类别都共有的特点,所以是宽泛的、抽象的
    • 封装:把功能写成方法,js里面就是函数
    • 继承:子类可以继承父类的属性和方法,避免重复写相同的代码
    • 多态:就是多种表现形态,比如学生对象有一个跑步的方法,那么可以在方法中根据不同学生的体质来决定学生跑多少米

11、说一说如何创建对象

  • 创建对象有4种方法
    • 工厂模式创建对象
      • 直接把函数和变量写在函数体内,将这些属性和方法存到一个对象里,最终返回这个对象
      • 缺点:无法判断对象的归属
    • 构造函数模式创建对象
      • 没有显式创建一个对象,将属性方法都赋值给this,没有return 语句;可传递参数
      • 缺点:功能相同的函数,重复声明浪费空间
    • 原型模式创建对象
      • 主要依靠prototype属性去创建对象
      • 缺点:共享问题,无法传递参数
    • 组合模式创建对象(构造函数和原型)
      • 属性写在构造函数里,方法写在prototype

i

12、说一说继承

  • 继承主要有4种方法
    • 原型链继承
      • 原理:利用原型,让一个引用类型继承另外一个引用类型的属性和方法
      • 缺点:construtor的指向,共享问题,无法传参
    • 借用构造函数继承
      • 原理:基于函数可以控制自己的执行环境去实现继承,关键代码:父类.call(this, 参数),这一行代码放在子类的构造函数中
      • 缺点:无法继承原型链上的属性和方法
      • 优点:解决了construtor的指向和传递参数的问题
    • 组合继承
      • 结合原型链继承以及构造函数继承
      • 缺点:两次调用父类的构造函数(第一次调用是在子类构造函数中借用构造函数,从而继承父类属性;第二次调用,是将父类实例引用给子类,让子类继承父类原型上的方法)
    • 寄生组合式继承
      • 主要解决两次调用父类的构造函数这个问题
      • 实现主要函数是 inheritPrototype,主要思路是
        • 传递两个参数,一个是subType,子类构造函数,superType,父类构造函数
        • const proto = Object.create(superType.prototype),copy一份父类的原型
        • proto.constructor = subType,设定construtor的指向,这里要指向子类
        • subType.prototype = proto,将子类原型指向proto

13、请描述一下 cookies,sessionStorage 和 localStorage 的异同?

这里先说说相同的地方,再说不同的地方

  • 相同点:它们都是用来存储数据的,而且都存储在客户端
  • 不同点:
    • 存储大小不一样:
      • cookie 不能超过4k
      • sessionStoragelocalStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M
    • 有效时间不同:
      • localStorage 存储持久数据,浏览器关闭后数据不丢失,除非主动删除数据
      • sessionStorage 存储的数据在当前浏览器窗口关闭后自动删除
      • cookie 在所设置的过期时间之前一直有效,即使浏览器被关闭,除非手动删除cookie
    • 数据与服务器之间的交互方式不同
      • cookie 的数据会自动传递到服务器,服务器端也可以发送cookie到客户端(通过那个头字段Set-Cookie
      • sessionStoragelocalStorage 不会自动将数据发送给服务器,仅在本地保存

14、深拷贝与浅拷贝

先说一说分别是什么,区别在哪里,如何实现

  • 浅拷贝:指不会开辟新的栈,浅拷贝后,两个对象的地址指向是相同的,一方的属性改变了,另外一方也会跟着变化
  • 深拷贝:深拷贝会开辟新的栈,两个对象的地址都是不一样的,一方的属性改变不会影响另外一方
  • 浅拷贝最简单的实践方法:直接赋值,即:创建一个对象,然后将这个对象赋值给一个新变量;还有另外一个方法:Object.assign()
  • 深拷贝:递归实现或者用 JSON.stringify() 以及 JSON.parse() 实现

15、创建一个有十行十列的表格(不准 innerHTML),并给每个单元格绑定事件。

考点在于关于表格的DOM操作以及事件委托

  const table = createTable()

  // 事件委托
  table.onclick = function (e) {
    const target = e.target

    if (target.tagName.toLocaleLowerCase() === 'td') {
      alert(target.innerHTML)
    }
  }
  
  /**
   * 创建表格
   * @returns {Element|*}
   */
  function createTable() {
    const table = document.createElement('table')
    table.border = 1
    table.width = 600
    let tBody = document.createElement('tBody')
    table.appendChild(tBody)
    createRowAndCol(tBody)
    document.body.appendChild(table)
    return table
  }

  /**
   * 创建行列
   * @param tbody
   * @returns {*}
   */
  function createRowAndCol(tbody) {
    for (let i = 0; i < 10; i++) {
      // 插入行
      tbody.insertRow(i)
      for (let j = 0; j < 10; j++) {
        // 插入列
        tbody.rows[i].insertCell(j)
        tbody.rows[i].cells[j].appendChild(document.createTextNode(`${i}${j}`))
      }
    }

    return tbody
  }

16、说一说原型、原型链

  • 对于js来说,继承的实现主要依靠原型链来实现。
  • 当我们访问一个属性的时候,如果在实例里找不到这个属性,就会到原型链去寻找
  • 原型链就像一座桥,是由原型去搭建这座桥。
  • 每一个函数在创建之后,都会有 prototype 这个属性,这个属性指向函数的原型对象,我们称这个为显式原型
  • js的每一个对象都有一个内置的 [[prototype]] ,大多数浏览器会支持通过 __proto__ 去访问,我们称这个为隐式原型
  • 隐式原型与显式原型的关系是:隐式原型会指向创建这个对象的构造函数的 prototype
  • 对于显式原型来说:显式原型可以用来实现基于原型的继承和属性的共享
    • 比如在人这个类的prototype中添加跑步这个方法,学生也是人,学生继承了人这个类后,那么学生类实例化出来的对象中也会有跑步这个方法
  • 对于隐式原型来说:隐式原型主要用来构造原型链

17、说一说new操作符

  • new操作符一般用来新建一个对象
  • new操作符会做以下事情
    • 创建一个新对象
    • 新对象的隐形原型会指向创建这个对象的构造函数的prototype
    • 调用call强行转换作用环境

18、说一说Object.create()的原理

  • 新建一个空函数,就是这个函数什么都不做
  • 将传入的对象赋值给此函数的prototype
  • 返回 一个新对象

19、将给定的格式转化为千分位的格式

  • 10000转化为10,000
  • 匹配规则:
    • 主要找位置,从后开始向前找,3个数字为一组,3个数字前添加一个逗号,如果是100000这种状态,最前面1那里则不需要添加逗号,即 100,000
const reg = /(?=((?!\b)\d{3})+$)/g
'1000000'.replace(reg, ',')

20、过滤年月日

  • 年,四位数字,比如 2017
  • 月,一位或者两位数字,第一位只能是 0 或者 1 ,比如 01 合法、1合法、10 合法
  • 日,一位或者两位数字,第一位可能是 0、1、2、3,比如 01 合法、1合法、10 合法、21 合法
const reg = /(\d{4})([01]?\d)([0123]?\d)/g
reg.exec('20130213')  //结果: ['20130213', '2013', '02', '13']

21、说一说事件

  • JavaScript 与 HTML 之间的交互是通过事件实现的。
  • 说到事件,那么事件流肯定是重点,事件流描述的是从页面接收事件的顺序
  • 事件流会包含三个阶段:事件捕获、处于目标、事件冒泡,这也是标准的DOM事件流,也称之为事件模型
    • 事件捕获就是指:不太具体的元素应该更早接收到事件,而具体的节点最后接收到事件
    • 事件冒泡是指:与事件捕获相反,而我们可以利用事件冒泡实现事件代理来提高性能

  • 说完事件模型,就要说说事件处理程序了。
  • 事件处理程序也叫做事件监听器,是响应某个事件的函数
  • 事件处理程序主要有两种:
    • 第一种是:DOM0级事件处理程序,比如 btn.onclick这种形式的就是DOM0级事件处理程序
    • 第二种是:DOM2级事件处理程序,这个比较特别,有兼容性的要求
      • ie的话:就是 element.attachEvent,其接受两个参数,事件处理程序的名称和事件处理程序的函数
      • 非ie的话:就是 element.addEventListener,接受三个参数,事件处理程序的名称和事件处理程序的函数以及是否捕获
  • 上面除了addEventListener以外,其事件处理程序名称都要加on

  • 接下来说一说事件对象
  • 事件对象就是我们常常在事件处理程序的函数里传的参数event
  • 它也是有兼容性的:ie中,事件对象作为window的属性,而非ie中,就是事件处理程序函数里的参数event
  • 事件对象里有许多属性和方法,下面说一下我常用到的属性和方法吧
  • 获取事件目标:event.target   event.srcElement(ie)
  • 阻止默认事件:event.preventDefault() , event.returnValue = true (ie)
  • 阻止冒泡:event.stropPropagation(), event.cancelBubble = true(ie)
  • 获取事件处理程序的类型:event.type

  • 接下来说说事件的类型
  • 有鼠标事件
  • 键盘事件
  • 文本事件
  • UI事件
  • 滚轮事件
  • 焦点事件

22、说说DOM的操作都有哪些

  • 前者包括文本节点,后者不包括文本节点
    • childNodes、children
    • firstChild、firstElementChild
    • lastChild、lastElementChild
    • nextSibling、nextElementSibling
    • previousSibling、previousElementSibling

  • 添加节点:
    • 父节点.appendChild(新节点)
    • 父节点.insertBefore(新节点, 参考节点)
  • 删除节点:父节点.removeChild(想要删除的节点)
  • 替换节点:父节点.replaceChild(新节点,要替换的节点)
  • 拷贝节点:要拷贝的节点.cloneNode()
    • 要拷贝的节点.cloneNode(true):表面深复制,复制节点及整个子节点树
    • 要拷贝的节点.cloneNode(false):表面浅复制,复制当前节点

  • 说说如何在已有的元素插入节点
    • 判断父元素是否有子节点,如果没有子节点,就有parent.appendChild,添加第一个子节点
    • 之后,就通过insertBefore插入节点即可

23、说说childNodes 与 Children 的异同

  • 相同点:都是表示子节点,都保存这一个类数组对象NodeList
  • 不同点:childNodes 包含文本节点,而children不会包含文本节点

24、javascript的typeof返回哪些数据类型

  • Boolean, String, Number, Object, Function, Undefined

25、说说location

  • location主要用来提供文档信息,我们也可以用其来导航
  • 有这些属性:location.href location.protocol location.pathname location.search location.hash
  • 方法 location.reload(),如果传进参数 true ,则表示不用缓存,重新从服务器拉取数据

26、说一说navigator

  • navigator:主要保留客户端信息
  • 尝使用的:navigator.userAgent,表示浏览器的用户代理

26、说一说history

  • history主要保存用户上网记录,其从用户打开浏览器那一刻算起
  • history.go(-1)/history.back():后退
  • history.go(1)/ history.forward():前进

27、说说有什么对话框

  • alert:警告框
  • confirm:确认框
  • prompt:提示框

28、说说ajax

发送ajax请求有多少个步骤?如何判定发送成功?(readyState和onreadystatechange)

  • 新建连接:创建 xhr 对象
  • 打开连接: xhr.open
  • 发送数据:xhr.send
  • 接收数据:xhr.onreadystatechange + xhr.readyState + xhr.status
    • 由于 Ajax 是异步的,所以 onreadystatechange 事件是放在 xhr.open() 之前的

29、说说事件委托

举例 + 原理说明

  • 我们最常用的事件,不用说,肯定是 点击事件。给一个按钮添加点击事件,超简单,但是,如果要给1000个按钮绑定点击事件,就没那么简单了!
  • 如果直接来个for循环,为每个按钮都绑定事件,这是可行的,但是这就会加大 DOM 与页面之间的联系,占用内存会增大,性能也就变得低下了。
  • 除此之外,如果之后我还想要添加几个新的按钮,那么又要重新为这些按钮添加点击事件,这就变得太麻烦了,感觉又在做重复的事情!
  • 因此,我们可以使用事件委托替代for循环。
  • 简单理解:委托就是把事情委托给别人去做,而事件委托就是利用事件冒泡机制,只指定一个事件处理程序,以达到管理某一类型的所有事件的目的。

30、设计模式

  • 设计模式都是固定的套路,我们只要根据不同的情况去使用不同的模式即可
  • 现在个人接触到的模式有:单例模式、适配器模式、观察者模式、mvc模式、装饰器模式、策略模式
  • 单例模式:
    • 核心思想就是一个类只能有一个实例,并提供全局访问的接口
    • 用途:登录框
  • 适配器模式:
    • 核心思想就是在不影响原有接口的情况下,兼容调用旧接口的代码
    • 比如,在香港买的iphone,它给的插头是港式插头,在内陆是不适用的,但是,我们可以添加一个转换器,就可以在内陆这里使用港式插头了
  • 装饰器模式:
    • 核心思想是动态为某个对象添加一些职责
    • 比如说,人人早餐都喜欢喝杯豆浆,但是有些人喜欢加糖,有些人不喜欢加糖,有些人还喜欢加蜂蜜,因此,应对不同的喜好,我们要做出相应的豆浆才行
    • 此时,我们就可以运用装饰器模式的思想,给对的人做出对的豆浆即可。
  • 观察者模式:
    • 核心思想是:当一个对象的状态发生改变的时候,所有依赖于它的对象都会得到通知
    • 观察者模式我曾在分页插件中使用过,在研究nodejs的formidable插件,也看到它使用过
  • 策略模式:
    • 将定义的一组算法封装起来,让其相互之间可替换你
    • 我在写表单验证的时候使用过,就是将各种表单验证的方法抽取出来,放到一个对象里,等要使用某个验证方法的时候,就将其取出来使用即可。
  • mvc模式:
    • 随着应用程序变得越来越复杂,我们需要通过关注点分离来简化问题;那么一个应用程序我们可以分为界面和数据,而他们之间则通过逻辑来联系。
    • 在mvc中,m代表model,是数据层,负责处理数据;v代表view,是视图层,负责处理界面;c代表controller,是控制层,负责逻辑。
    • 当用户与界面进行交互的时候,就会促发View层的事件,事件就会被Controller监听到,然后会向Model层调用相应的接口, 从而使得Model层的数据发生改变;而View层会通过观察者模式订阅Model层数据的变化,当数据改变时,Model层就会通知 View层, View层就能以最新的数据更新页面。
  • MVC模式比较适用于后端。对于前端的View来说,浏览器已具备独立处理事件的能力,并不需要每个事件都要通过Controller去处理, 这样只会使得 Controller 变得越来越臃肿;除此之外,对于网页来说,其需要与用户进行不断地交互,以及修改数据; 然而对于每次交互,都需要开发人员编写大量的逻辑,去修改 View ,添加相应的事件,并将事件传到Controller去处理, 然后修改Model,修改完Model后,再去修改View
  • MVVM模式:
    • 将 Controller 改成 ViewModel,即视图模型。将View 和ViewModel进行数据绑定,当View发生变化时,数据会自动更新; 当数据更新时view也会自动更新,即可以达到数据双向绑定,从而使得视图与数据自动化同步,常见的框架有 angularJS,vue.js
    • mvvm模式也有相应的缺点,即不适合简单的界面

31、说说鼠标事件

  • 主要有9个鼠标事件
  • click、dblclick、mousedown、mouseup、mouseenter、mouseleave、mouseover、mouseout、mousemove
  • mousedown -> mouseup -> click -> mousedown -> mouseup -> click -> dblclick

32、说说键盘事件

  • 有3个键盘事件
    • keydown:当用户按下键盘上的任意键触发,如果按住不放的话,会重复触发此事件
    • keypress:当用户按下键盘上的字符键时触发,如果按住不放,会重复触发此事件
    • keyup: 当用户释放键盘上的键时触发
  • 触发顺序:keydown ==> keypress ==> keyup
    • keydown 和 keypress 都是在文本框发生变化前触发的
    • keyup 是文本框发生变化后触发的

  • 键码:event.keyCode   event.charCode
    • charCode只有在发生keypress时才包含值

33、你使用过react吗?说说你对react生命周期的理解

  • 我只在做简单版的口袋豆瓣中使用过react,还没有深入研究,我尝试去说一下自己对react生命周期的理解吧!
  • 人从婴儿到少年到成年到中年到老年,是一个生命周期,是单向的
  • 而对于react来说,它也是有自己的生命周期的,其分为三个阶段:挂载、更新、卸载,但却不是单向的
  • 挂载阶段:
    • 一开始是初始化(constructor),我们可以在这个方法里用this.setState设置一些初始的状态
    • 接着是插入DOM前(componentWillMount)
    • 接着是插入到DOM中(render)
    • 接着是插入DOM后(componentDidMount):所需要的DOM在这个方法内都已经准备好了
      • 举个例子:我要打开页面,文本框就聚焦,那么我就回在render方法里通过refs获取文本框,然后在componentDidMount方法里通过this.input.focus()就可以实现了
  • 更新阶段:
  • shouldComponentUpdate() ==》 组件更新前(componentWillUpdate)==》 render ==》组件更新后(componentDidUpdate)
  • 挂载阶段:
    • 从DOM移除前(componentWillUnmount)

34、易错点之鼠标事件

  • mouseover和mouseenter的效果一样,都是鼠标移入到某元素触发
  • mouseout 和 mouseleave的效果一样,都是鼠标移出某元素后触发
  • mousemove:就是鼠标在某元素内移动会不断触发
  • 但是他们也是有区别的:
    • mouseenter 和 mouseleave 是不冒泡的,如果对象是其后代元素也是不会触发的
    • mouseover 和 mouseout ,如果对象是其后代元素也是会触发的
  • 再说一说上述5个鼠标事件触发的顺序吧:
  • mouseover ==> mouseenter ==> mousemove ==> mouseout ===> mouseleave

35、易错点之坐标区别

  • BOM
    • 浏览器在屏幕中的位置:screenLeft/screenTop(除了FF),screenX/screenY(除了ie)
    • innerHeight与innerWidth:浏览器视图区宽高(包含滚动条)
    • outerHeight/outerWidth: 浏览器本身宽高
  • DOM
    • offsetParent: 包含元素的引用
    • offsetLeft/offsetTop: 元素的左/上外边框到包含元素的左/上内边框的像素距离
    • offsetHeight/offsetWidth: 元素在垂直/水平方向上占用的空间像素
    • clientHeight/clientWidth:内容+内边距
    • scrollHeight/scrollWidth: 元素的总宽高(若有滚动条也计算在内)
    • scrollTop/scrollLeft: 被隐藏在内容区域左侧的像素

36、讲讲canvas动画循环

  • 更新操作(数据)
  • 清除操作(画布)
  • 绘制操作(画布)

37、说说js运行机制

javascript - 彻底理解同步、异步和事件循环

  • 首先,js是单线程,即:一次只能处理一件事情,等当前事情处理完,才可以执行下一件事情
  • 说到js运行机制,不得不说一下异步过程:
    • 主线程会发起异步请求,相关的工作线程接收到请求后,就会告知主线程:“我收到你的请求了”(实际上就是返回异步函数)
    • 接下来,主线程可以继续执行后面的代码,同时,工作线程则执行异步任务
    • 工作线程完成后,通知主线程
    • 主线程接到通知后,执行一定的动作(调用回掉函数)
  • 而工作线程通知主线程的通知机制的原理就是消息队列和事件循环
  • 先说说消息队列:消息队列是一个先进先出的队列,存储着各种消息(这里的消息指的是注册异步任务时添加的回调函数)
  • 再说说事件循环:指主线程重复从消息队列中取出消息并执行的过程
  • 实际上,主线程只会做一件事情,就是从消息队列中取消息,执行消息,再取消息,执行消息。当消息队列为空的时候,就会等到消息队列不为空。 而且主线程只有在将当前的消息执行完后,才能去取下一个消息。这种机制叫事件循环机制。取一个消息并执行的过程叫一次循环。

38、instanceof原理

  • 主要用来判断实例对象的属性(__proto__)和构造函数属性(prototype)是不是同一个引用

i


对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。