2018 – 2019 年前端 JavaScript 面试题

JavaScript 基础问题

1.使如下代码正常运行:

JavaScript 代码:
    const a = [1, 2, 3, 4, 5];
     
    // Implement this
    a.multiply();
     
    console.log(a); // [1, 2, 3, 4, 5, 1, 4, 9, 16, 25]

2.如下代码在 JavaScript 中返回 false 。 解释一下为何会这样:

JavaScript 代码:
    // false
    0.2 + 0.1 === 0.3

3.JavaScript 中有哪些不一样的数据类型?

提示:只有两种类型 – 主要数据类型和引用类型(对象)。 有 6 种主要类型。javascript

4.解决如下异步代码问题。

检索并计算属于同一教室中每一个学生的平均分数,其中一些ID为75。每一个学生能够在一年内参加一门或多门课程。 如下 API 可用于检索所需数据。php

JavaScript 代码:
    // GET LIST OF ALL THE STUDENTS
    GET /api/students
    Response:
    [{
    "id": 1,
    "name": "John",
    "classroomId": 75
    }]
    // GET COURSES FOR GIVEN A STUDENT
    GET /api/courses?filter=studentId eq 1
    Response:
    [{
    "id": "history",
    "studentId": 1
    }, {
    "id": "algebra",
    "studentId": 1
    },]
    // GET EVALUATION FOR EACH COURSE
    GET /api/evaluation/history?filter=studentId eq 1
    Response:
    {
    "id": 200,
    "score": 50,
    "totalScore": 100
    }

 

编写一个接受教室 ID 的函数,您将根据该函数计算该教室中每一个学生的平均值。该函数的最终输出应该是具备平均分数的学生列表:css

JavaScript 代码:
    [
    { "id": 1, "name": "John", "average": 70.5 },
    { "id": 3, "name": "Lois", "average": 67 },
    ]

使用普通的 callbacks ,promises ,observablesgenerator 或 async-wait 编写所需的函数。尝试使用至少 3 种不一样的技术解决这个问题。html

5.使用 JavaScript Proxy 实现简单的数据绑定

提示:ES Proxy 容许您拦截对任何对象属性或方法的调用。首先,每当更改底层绑定对象时,都应更新 DOM 。前端

6.解释 JavaScript 并发模型

您是否熟悉 Elixir,Clojure,Java 等其余编程语言中使用的任何其余并发模型?vue

提示:查找事件循环,任务队列,调用栈,堆等。html5

7.new 关键字在 JavaScript 中有什么做用?

提示:在 JavaScript 中,new 是用于实例化对象的运算符。 这里的目的是了解知识广度和记忆状况。java

另外,请注意 [[Construct]] 和 [[Call]]web

8.JavaScript 中有哪些不一样的函数调用模式? 详细解释。

提示:有四种模式,函数调用,方法调用,.call() 和 .apply()ajax

9.解释任一即将发布新的 ECMAScript 提案。

提示:好比 2018 的 BigInt,partial 函数,pipeline 操做符 等。

10.JavaScript 中的迭代器(iterators)和迭代(iterables)是什么? 你知道什么是内置迭代器吗?

11.为何 JavaScript classes(类)被认为是坏的或反模式?

这是一个神话吗?它是否遭受了误传?是否有一些有用的用例?

12.如何在 JSON 中序列化如下对象?

若是咱们将如下对象转换为 JSON 字符串,会发生什么?

JavaScript 代码:
    const a = {
    key1: Symbol(),
    key2: 10
    }
    // What will happen?
    console.log(JSON.stringify(a));

13.你熟悉 Typed Arrays 吗? 若是熟悉,请解释他们与 JavaScript 中的传统数组相比的异同?

Arrays

ES6对数组添加了一些新的方法,另外还添加了TypedArray类型,这种类型支持对内存的操做,ArrayBuffer和C语言内存分配同样,分配一块内存块。下面从如下几个方面来看看ES6数组的变化:

  1. 2个静态方法 Array.of(), Array.from();
  2. 数组原型上新添加的方法 find(), findIndex(), fill(), copyWithin();
  3. 新类型 ArrayBuffer;
  4. Typed Arrays, 以及和Array的相同性,差别性

一.Array.of() & Array.from()

1.Array.of()

ES6新添加了这个静态方法,用于数组构造器实例化数组。咱们知道数组实例化通常能够经过构造器或者数组字面量的形式来声明。ES5经过构造器声明数组会出现一个比较模糊的地方,好比:

var arr = new Array(1, 2, 3);
// 表示声明一个数组元素为[1, 2, 3]的数组
arr.length; // 3
arr[0]; // 1
arr[1]; // 2
arr[2]; // 3

var arr = new Array(2); // 2表示长度
// 而这样则表示一个数组长度为2的数组,而数组元素未声明
arr.length; // 2
arr; // [undefined, undefined]

Array.of()消除了这种模糊,凡是向方法中添加数字,都表示数组元素,而不是长度

var arr = Array.of(1, 2);
arr; // [1, 2]

var arr = Array.of(2); // 2表示元素
arr; // [2]

2.Array.from()

ES6以前要将一个array-like对象转换成数组,咱们通常是利用slice方法,好比

function doSomething() {

    // 将arguments类数组对象转换成数组
    var args = Array.prototype.slice.call(arguments);
    // 或者  [].slice.call(arguments)
    // ...

}

ES6经过静态方法 Array.from() 能够将 类数组对象 或者 可迭代的对象 转换成一个数组,其语法为:

Array.from(arraylike[, callback] [, thisArg])

上面的例子能够写为:

function doSomething() {
    var args = Array.from(arguments);
    // ...
}

将可迭代的对象转变为数组:

var set = new Set([1, 2, 2, 4, 5]);
// Set {1, 2, 4, 5}
var arr = Array.from(set); // [1, 2, 4, 5]

后面添加回调函数, 若是回调函数属于一个对象, 则能够添加第3个可选参数,指出this上下文:

let helper = {
    diff: 1,
    add(value) {
        return value + this.diff;
    }
}
 
function translate() {
    // 第2个参数为callback, 第3个参数为上下文
    return Array.from(arguments, helper.add, helper);
}

translate(1, 2, 3); // [2, 3, 4]

二.新添加的方法

1.find(),findIndex()

之前咱们查看数组中是否存在某个值或者某个值的位置时,通常使用indexOf(), lastIndexOf(),ES6添加了find(), findIndex()来添加条件查找。这两个方法和map(),forEach()同样添加一个回调函数,有选择性的添加thisArg指定上下文。

find找到了就返回第一个知足条件的,未找到返回undefined, findIndex返回索引位置,未找到返回 -1:

var arr = [1, 2, 19, 16];
arr.find(v => v > 10 );  // 返回 19
arr.findIndex(v => v > 10); // 返回 2

find(), findIndex()用于查找一个数组元素知足某个条件而不是值,要根据值查找建议使用indexOf(), lastIndexOf().

2.fill(), copyWithin()

这两个方法其实为了操做Typed Array对象使用的,可是为了保持一致性,也添加给普通数组了。看下语法:

    fill(value[,startFillPostion = 0 ] [, endFillPostion = arr.length])
    copyWithin(StartToBeCopiedPos[,StartCopyPos = 0] [,EndCopyPos = arr.length])

先看fill:

var arr = [1, 2, 3, 4];

// 不指定开始和结束填充的位置
arr.fill(5); // arr: [5, 5, 5, 5]

// 指定开始
arr.fill(5, 2); // arr: [1, 2, 5, 5]

// 都指定,不包含结束位置
arr.fill(5, 0, 2)// arr: [5, 5, 3, 4]

// 固然起始和结尾也能够为负数,至关于加上数组长度
arr.fill(5, -3); // arr: [1, 5, 5, 5]
// 至关于 arr.fill(5, -3+4)

copyWith: 比较绕, 它是指复制自身内容到指定的位置:

var arr = [1, 10, 15, 29, 18];

// 只有一个参数表示被复制的索引,另外2个参数则默认从0开始到结束
arr.copyWithin(2); // arr [1, 10, 1, 10, 15]

// 2个参数,指定自身复制的位置
arr.copyWithin(3, 1); // arr [1, 10, 15, 10, 15]

// 3个参数都指定
arr.copyWithin(2, 0, 1); // arr [1, 10, 1, 29, 18]
// 0-1只有一个数 "1", 全部索引位置为2的 "15" 被替换成 "1"

上面例子咱们能够发现,是有这些方法都会改变数组自身


三.ArrayBuffer

AarryBuffer是指分配内存中的一块位置用于操做,至关于C语言中的malloc(),对内存块进行二进制操做,会极大的提高性能,知足一些特别的接口要求。
先了解一下内存分配的基本语法:

var buffer = new ArrayBuffer(bytes);

好比:分配10个字节(byte)

var buffer = new ArrayBuffer(10);

内存的大小肯定以后是不能修改的,能够改变内部内容

属性: byteLength , slice()
slice方法是从已经存在的内存块中复制一段,添加都新的内存块中

var buffer = new ArrayBuffer(10);
var buffer2 = buffer.slice(3, 5);
// 将buffer中的3, 4字节内容拷贝到新的内存块中
 console.log(buffer2.byteLength); // 2

四.TypedArray, Views视图

光有内存块,而不进行操做也是没有用的,javascript经过视图的方式对内存块进行读写,存在两种视图:

  1. TypedArray: 特定类型的数据类型,特定类型的一种视图,对特定类型操做性能更高;
  2. DataView: 各类数据类型均可以,还能够指定大端序(BIG_ENDIAN),小端序(LITTLE_ENDIAN),功能更强大的一种视图

1.共同属性

这两种视图拥有一些共同的属性:

  • buffer: 表示指向的内存块;

2.DataView

DataView构造器可以添加三个参数:new DataView(buffer[, byteOffset][, byteLength])

var buffer = new ArrayBuffer(10);
// 指向整个内存块
var dataView1 = new DataView(buffer);    
dataView1.buffer === buffer; // true
dataView1.byteOffset; // 0
dataView1.byteLength; // 10

// 表示 字节 5, 6上的视图
var dataView2 = new DataView(buffer, 5, 2);
dataView2.buffer === buffer; // true
dataView2.byteOffset; // 5
dataView2.byteLength; // 2

3.TypedArray

TypedArray本质上是一个抽象类,他表示9中特定类型: Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array,
Float64Array,还有一种只针对Canvas颜色值的 Uint8ClampedArray

14. 默认参数是如何工做?

若是咱们在调用 makeAPIRequest 函数时必须使用 timeout 的默认值,那么正确的语法是什么?

JavaScript 代码:
    function makeAPIRequest(url, timeout = 2000, headers) {
    // Some code to fetch data
    }

15.解释 TCO – 尾调用优化(Tail Call Optimization)。 有没有支持尾调用优化的 JavaScript 引擎?

  • 尾调用指“函数尾调用”。
  • 尾调用是函数式编程的一个重要概念。
  • 尾调用就是某个函数最后一步是调用另外一个函数

提示:截至 2018 年,没有。


JavaScript 前端应用设计问题

1.解释单向数据流和双向数据绑定。

Angular 1.x 基于双向数据绑定,而 React,Vue,Elm 等基于单向数据流架构。

单向数据绑定

单向数据绑定,带来单向数据流。。
指的是咱们先把模板写好,而后把模板和数据(数据可能来自后台)整合到一块儿造成HTML代码,而后把这段HTML代码插入到文档流里面。适用于总体项目,并于追溯。

 

 


 

优势:
1. 全部状态的改变可记录、可跟踪,源头易追溯;
2. 全部数据只有一份,组件数据只有惟一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性;
3. 一旦数据变化,就去更新页面(data-页面),可是没有(页面-data);
4. 若是用户在页面上作了变更,那么就手动收集起来(双向是自动),合并到原有的数据中。
方神:双向绑定 = 单向绑定 + UI事件监听,可了解vuex
缺点:
1. HTML代码渲染完成,没法改变,有新数据,就须把旧HTML代码去掉,整合新数据和模板从新渲染;
2. 代码量上升,数据流转过程变长,出现不少相似的样板代码;
3. 同时因为对应用状态独立管理的严格要求(单一的全局store),在处理局部状态较多的场景时(如用户输入交互较多的“富表单型”应用),会显得啰嗦及繁琐。

双向数据绑定

双向数据绑定,带来双向数据流。AngularJS2添加了单向数据绑定
数据模型(Module)和视图(View)之间的双向绑定。不管数据改变,或是用户操做,都能带来互相的变更,自动更新。适用于项目细节,如:UI控件中(一般是类表单操做)。

 


优势:
1. 用户在视图上的修改会自动同步到数据模型中去,数据模型中值的变化也会马上同步到视图中去;
2. 无需进行和单向数据绑定的那些CRUD(Create,Retrieve,Update,Delete)操做;
3. 在表单交互较多的场景下,会简化大量业务无关的代码。
缺点:
1. 没法追踪局部状态的变化;
2. “暗箱操做”,增长了出错时 debug 的难度;
3. 因为组件数据变化来源入口变得可能不止一个,数据流转方向易紊乱,若再缺少“管制”手段,血崩。
双向数据绑定,Angular使用 脏检查“digest” - “dirty checking”
(在angular中,他没有办法判断你的数据是否作了更改, 因此它设置了一些条件,当你触发了这些条件以后,它就执行一个检测来遍历全部的数据,对比你更改了地方,而后执行变化。这个检查很不科学。并且效率不高,有不少多余的地方,因此官方称为脏检查)

2.单向数据流架构在哪些方面适合 MVC?

MVC 拥有大约 50 年的悠久历史,并已演变为 MVP,MVVM 和 MV *。二者之间的相互关系是什么?若是 MVC 是架构模式,那么单向数据流是什么?这些竞争模式是否能解决一样的问题?

3.客户端 MVC 与服务器端或经典 MVC 有何不一样?

提示:经典 MVC 是适用于桌面应用程序的 Smalltalk MVC。在 Web 应用中,至少有两个不一样的数据 MVC 周期。

4.使函数式编程与面向对象或命令式编程不一样的关键因素是什么?

提示:Currying(柯里化),point-free 函数,partial 函数应用,高阶函数,纯函数,独立反作用,record 类型(联合,代数数据类型)等。

5.在 JavaScript 和前端的上下文中,函数式编程与响应式编程有什么关系?

提示:没有正确答案。但粗略地说,函数式编程是关于小型编码,编写纯函数和响应式编程是大型编码,即模块之间的数据流,链接以 FP 风格编写的组件。 FRP – 功能响应式编程( Functional Reactive Programming)是另外一个不一样但相关的概念。

6.不可变数据结构(immutable data structures)解决了哪些问题?

不可变结构是否有任何性能影响? JS 生态系统中哪些库提供了不可变的数据结构?这些库的优势和缺点是什么?

提示:线程安全(咱们真的须要在浏览器 JavaScript 中担忧吗?),无反作用的函数,更好的状态管理等。

7.大型应用程序是否应使用静态类型?

  1. 如何比较 TypeScript/Flow 与 Elm/ReasonML/PureScript 等 JS 转换语言?这些方法的优缺点是什么?
  2. 选择特定类型系统的主要标准应该是什么?
  3. 什么是类型推断(type inference)?
  4. 静态类型语言和强类型语言有什么区别?在这方面 JavaScript 的本质是什么?
  5. 有你知道的弱类型但静态类型的语言吗?有你知道的动态类型但强类型的语言吗?举例一二。

提示:Structural 与 Nominal 类型系统,类型稳健性,工具/生态系统支持,正确性超过方便。

8.JavaScript 中有哪些杰出的模块系统(module systems )?如何评价 ES 模块系统。

列出在实现不一样模块系统之间互操做所涉及的一些复杂性问题(主要对 ES 模块和 CommonJS 互操做感兴趣)

9.HTTP/2 将如何影响 JavaScript 应用程序打包?

列出 HTTP/2 与其上一个版本的基本区别。

10.Fetch API 相对于传统的 Ajax 有哪些改进?

  1. 使用 Fetch API 有那些缺点/难点吗?
  2. 哪些是Ajax 能够作的,而 fetch 不能作的?

11.如何评论 pull-based 和 push-based 的反应系统。

讨论概念,含义,用途等。

  1. 在这个讨论中加入惰性和及早求值。
  2. 而后在讨论中添加单数和复数值维度。
  3. 最后,讨论值解析的同步和异步性质。
  4. 为JavaScript中可用的每一个组合提供示例。

提示:Observable 是惰性的,基于推送的复数值构造,同时具备 async/sync 调度程序。

12.讨论与 Promise 相关的问题。

提示:及早求值(eager evaluation),尴尬的取消机制,用 then() 方法假装 map() 和 flatMap() 等。


前端基础和理论问题

1.HTML 中 Doctype 的用途是什么?

具体谈谈,如下每种状况下会发生什么:

    1. Doctype 不存在。
    2. 使用了 HTML4 Doctype,但 HTML 页面使用了 HTML5 的标签,如 <audio> 或 <video> 。它会致使任何错误吗?
    3. 使用了无效的 Doctype。

一、告诉浏览器使用什么样的html或xhtml规范来解析html文档

二、对浏览器的渲染模式产生影响;不一样的渲染模式会影响到浏览器对于 CSS 代码甚至 JavaScript 脚本的解析,因此Doctype是很是关键的,尤为是在 IE 系列浏览器中,由DOCTYPE 所决定的 HTML 页面的渲染模式相当重要。 

两种渲染模式:

     BackCompat:标准兼容模式未开启(或叫怪异模式[Quirks mode]、混杂模式)

     CSS1Compat:标准兼容模式已开启(或叫严格模式[Standards mode/Strict mode])

选择什么样的DOCTYPE

如上例所示,XHTML 1.0中有3种DTD(文档类型定义)声明能够选择:过渡的(Transitional)、严格的(Strict)和框架的(Frameset)。这里分别介绍以下。

1.过渡的

一种要求不很严格的DTD,容许在页面中使用HTML4.01的标识(符合xhtml语法标准)。过渡的DTD的写法以下:

代码以下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

2.严格的

一种要求严格的DTD,不容许使用任何表现层的标识和属性,例如<br/>等。严格的DTD的写法以下:

代码以下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

3.框架的

一种专门针对框架页面所使用的DTD,当页面中含有框架元素时,就要采用这种DTD。框架的DTD的写法以下:

代码以下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">

使用严格的DTD来制做页面,固然是最理想的方式。可是,对于没有深刻了解Web标准的网页设计者,比较合适的是使用过渡的DTD。由于这种DTD还容许使用表现层的标识、元素和属性,比较适合大多数网页制做人员。

HTML 4.01 中的 doctype 须要对 DTD 进行引用,由于 HTML 4.01 基于 SGML。
HTML 5 不基于 SGML,所以不须要对 DTD 进行引用,可是须要 doctype 来规范浏览器的行为(html 5简化了这种声明,意在告诉浏览器使用统一的标准便可)

2. DOM 和 BOM 的区别是什么?

提示:BOM,DOM,ECMAScript 和 JavaScript 都是不一样的东西。

BOM和浏览器关系密切,DOM和HTML文档有关。BOM是Browser Object Mode的缩写,及对浏览器对象模型,用来获取或设置浏览器的属性、行为。DOM是Document Object Model 的缩写,即文档对象模型,用来获取或设置文档中标签的属性。BOM没有相关标准。DOM是W3C的标准。BOM的最根本对象是window。DOM最根本对象是document(其实是window.document)。因为DOM的操做对象是文档(Document),因此dom和浏览器没有直接关系。 
HTML DOM 模型被构造为对象的树。
经过可编程的对象模型,JavaScript 得到了足够的能力来建立动态的 HTML。
JavaScript 可以改变页面中的全部 HTML 元素
JavaScript 可以改变页面中的全部 HTML 属性
JavaScript 可以改变页面中的全部 CSS 样式
JavaScript 可以对页面中的全部事件作出反应

3.JavaScript 中的事件处理如何运行?

以下图所示,咱们有三个 div 元素。每一个 div 都有一个与之关联的点击处理程序。处理程序执行如下任务:

  • Outer div click 处理程序将 hello outer 打印到控制台。
  • Inner div click 处理程序将 hello inner 打印到控制台。
  • Innermost div click 处理程序将 hello innermost 打印到控制台。

编写一段代码来分配这些任务,以便在单击 innermost div 时始终打印如下序列?

hello inner → hello innermost → hello outer

事件冒泡和捕获

提示:事件捕获和事件冒泡

探究JavaScript中的五种事件处理程序

  咱们知道JavaScript与HTML之间的交互是经过事件实现的。事件最先是在IE3和Netscape Navigator 2中出现的,当时是做为分担服务器运算负载的一种手段。  通俗地理解,事件就是用户或浏览器自身执行的某种操做。而事件处理程序即为响应某个事件的函数。抽出主干,即事件处理程序为函数。  咱们又把事件处理程序称为事件侦听器。  事件处理程序是以"on"开头的,所以对于事件on的时间处理程序即为onclick。时间处理程序在JavaScript中大体有五种,下面会根据这五种不一样的时间处理程序分为5部分来介绍。

  1. HTML事件处理程序
  2. DOM0级事件处理程序
  3. DOM2级事件处理程序
  4. IE事件处理程序
  5. 跨浏览器的事件处理程序

第一部分:HTML事件处理程序

  什么使HTML事件处理程序呢?显然,经过名字就能够猜到,它是卸载HTML中的函数(事件处理程序)。初学者大多用到的事件处理程序即为HTML事件处理程序。下面举例:

例1:

    <button onclick="alert('success')">点我</button>

这条代码即为事件处理程序,点击button后,会弹出弹框,显示success。

特色:HTML事件处理程序中Javascript代码做为了onclick特性的值,所以,咱们不能在JavaScript代码中使用未经转义的HTML语法字符,如&(和号)、""(双引号)、<(小于号)、>(大于号)等等。因此这个例子中字符串我使用了单引号而没有使用双引号。看下面在JavaScript代码中使用了未经转义的HTML语法字符。

例2:

    <button onclick="alert("success")">点我</button>

这时,我在success外使用了HTML语法字符""(双引号),这时不会弹出窗口,而是报错语法错误。可是我若是仍是但愿使用双引号呢? 这时就要用&quot;实体来代替HTML中的语法字符。以下例所示:

例3:

<button onclick="alert(&quot;success&quot;)">点我</button>
    <!--  正常弹出窗口-->

这个例子中咱们在JavaScript代码中使用了HTML实体而没有使用HTML语法字符,这时就不会报错了。

例4:

1
2
3
4
5
6
7
8
<button onclick="show()">点我</button>
<!--  正常弹出窗口-->
 
<script>
    function show(){
        alert("success");
    }
</script>

这个例子中咱们调用函数,而把函数定义放在了script中,这样也是能够的。由于:事件处理程序中的代码在执行时,有权访问到全局做用域中的任何代码。这句话怎么理解呢?  实际上,咱们能够在chrome中观察button标签的做用域链。以下所示:

  接下来咱们再看看script所在的做用域,以下图所示:

能够看到script标签就在全局做用域。

  也就是说目前button中的HTML事件处理函数在做用域链的最前端,而Script在全局做用域,因此“事件处理程序中的代码在执行时,有权访问到全局做用域中的任何代码。”这句话就不难理解了。

例5:

    <button onclick="alert(event.type)">点我</button>

这时浏览器弹出窗口显示:click。这个例子是什么意思呢?注意到我并无在event.type外加单引号,说明这并非字符串。实际上,event是局部对象--在触发DOM上的某个事件时,会产生一个事件对象event,这个对象包含着全部与事件有关的信息。而这里是弹出了对象了类型,即为click。

HTML事件处理程序的三个缺点(重点):

1. 时差问题。 由于用户可能在HTML元素一出现就开始触发相应事件,可是有可能该事件的脚本(如例4中show()函数的函数定义在script中)尚未加载完成,此时不具有执行条件,故报错。

  解决方法:将HTML事件处理程序封装在一个try-catch块中,以便错误不会浮出水面。

<input type="button" value="click me" onclick="try{show();}catch(ex){}">

2.这样扩展事件实例程序的做用域链在不一样的浏览器中会致使不一样的结果(例4中我是在chrome中查看的做用域链,其余浏览器不必定是这样的,请注意)。不一样JavaScript引擎遵循的标识符解析规则略有差别,颇有可能会在访问非限定对象成员时出错。

3.HTML和JavaScript代码紧密耦合。 结果是:若是要更换事件处理程序,就必须改动两个地方--HTML代码和JavaScript代码。

那么怎么解决上面的问题呢? DOM0级事件处理程序是一个不错的选择!

第二部分:DOM0级事件处理程序

  DOM0级事件处理程序用的也很是广泛。之因此成为DOM0级,我认为是当时尚未出DOM标准,而IE和Netscape Navigator二者使用的时间处理程序(不知是否合理,望批评指正)。 总之,咱们先看看下面的例子吧。

例6:

复制代码
    <button id="button">点我</button>

<script>
    var button=document.getElementById("button");
    button.onclick=function(){
        alert("clicked");
    }
</script>
复制代码

即咱们先在script中取得元素的引用,而后再将一个函数赋值给onclick事件处理程序。 以前介绍过,事件处理程序即为函数,而button.onclick这种形式即函数做为了对象的方法。那么对象的方法即事件处理程序是在元素(对象)的做用域中运行而非在全局做用域中运行的,由于方法是属于对象的。(注意:例4中事件处理程序是在全局做用域中运行的)。 若是这个函数中存在this关键字,那么this就会指向这个对象。下面咱们在浏览器中证实事件处理程序是在元素的做用域中运行。

咱们看到alert("clicked");确实是在button中运行的。

 

咱们还能够经过下面的方式删除经过DOM0级方法指定的事件处理程序。

button.onclick=null;

经过上面的分析咱们能够知道DOM0级事件处理程序是很是不错的,它解决了HTML事件处理程序的三个缺点:时差问题、做用域链致使的不一样浏览器表现不一致问题和HTML和JavaScript紧密耦合问题。

  可是,DOM0级事件处理程序并非完美的,它一样有两个缺点:

  1. 咱们不能给一个元素同时添加两个事件。
  2. 咱们不能控制元素的事件流(捕获or冒泡)。

  对于第二个问题后面会讲到,第一个问题举例以下:

复制代码
    <button id="button">点我</button>

<script>
    var button=document.getElementById("button");
    button.onclick=function(){
        alert("clicked");
    }
    button.onclick=function(){
        alert("again");
    }
复制代码

虽然我对同一个元素设置了两个事件处理程序,可是最终的结果是:只有第二个事件有效(覆盖了第一个事件)。固然,人类是聪明的动物,DOM2级事件很好的解决了这个问题!

第三部分:DOM2级事件处理程序

  DOM2级事件处理程序定义了两个方法:

  • addEventListener()   ---添加事件侦听器
  • removeEventListener()   ---删除事件侦听器

 在博文的开头我就提到了事件处理程序即事件侦听器。这两个方法都接收三个参数:

  1. 要处理的事件名(注意:是时间名,因此没有on!),如click、mouseover等。
  2. 做为事件处理程序的函数,如function(){alert("clicked");}
  3.  表示事件流方式的布尔值。false为冒泡阶段调用事件处理程序;true为捕获阶段调用事件处理程序。对于冒泡和捕获这两种时间流能够看《JavaScript中的两种事件流


下面经过两个例子加深理解:

例7:

复制代码
<button id="button">点我</button>

<script>
    var button=document.getElementById("button");
    button.addEventListener("click",function(){
        alert(this.id);
    },false);
    button.addEventListener("click",function(){
        alert("another event");
    },false);
</script>
复制代码

结果:第一次弹出窗口:button。

   第二次弹出窗口:another event。

结论经过DOM2级事件处理程序,咱们能够为同一个元素添加两个或更多的事件。事件根据顺序依次触发。且this一样指向当前元素,故函数在元素的做用域中执行。

this分析:和前面的DOM0级事件处理程序同样,这里的addEventListener一样也能够看做对象的方法,不一样之初在于,DOM0级的方法须要另一个函数来赋值,而这里的方法是DOM2级规范预约义的。

 

  removeEventListener()这个删除事件处理程序的方法值得注意的是:使用addEventListener()来添加的事件处理程序只能经过它来移除,且须要传入相同的参数。

例8:

复制代码
    <button id="button">点我</button>

<script>
    var button=document.getElementById("button");
    button.addEventListener("click",function(){
        alert(this.id);
    },false);
    button.removeEventListener("click",function(){
        alert("another event");
    },false);
复制代码

上述代码貌似能够移除click的事件处理程序,可是经过实验证实是不能够的,缘由是:事件处理程序为匿名函数时没法移除。看下面的成功移除的例子:

例9:

复制代码
    <button id="button">点我</button>

<script>
    var button=document.getElementById("button");
    function handler(){
        alert(this.id);
    }
    button.addEventListener("click",handler,false);
    button.removeEventListener("click",handler,false);
</script>
复制代码

  成功移除!

注意:1.传入方法的handler没有(),是由于这里都只是定义函数,而不是调用,须要注意。

   2.这两个方法的第三个参数都是false,即事件处理程序添加到冒泡阶段。通常不使用true,由于低版本的IE不支持捕获阶段。

 

DOM2级事件处理程序成功地解决了前面全部事件处理程序的问题,堪称perfect!!!!  然而老是特立独行的IE浏览器又有新花样,它也有本身的一套事件处理程序,下面咱们就来看看吧。

第四部分:IE事件处理程序

  IE事件处理程序中有相似与DOM2级事件处理程序的两个方法:

  1. attachEvent()
  2. detachEvent()

  它们都接收两个参数:

  1. 事件处理程序名称。如onclick、onmouseover,注意:这里不是事件,而是事件处理程序的名称,因此有on。
  2. 事件处理程序函数。如function(){alert("clicked");}

   之因此没有和DOM2级事件处理程序中相似的第三个参数,是由于IE8及更早版本只支持冒泡事件流。

  注意:

  1.IE事件处理程序中attachEvent()的事件处理程序的做用域和DOM0与DOM2不一样,她的做用域是在全局做用域中。所以,不一样于DOM0和DOM2中this指向元素,IE中的this指向window。 

  2.一样,咱们能够使用attachEvent()来给同一个元素添加多个事件处理程序。可是与DOM2不一样,事件触发的顺序不是添加的顺序而是添加顺序的相反顺序。

  3.一样地,经过attachEvent()添加的事件处理程序必须经过detachEvent()方法移除,一样的,不能使用匿名函数。

  4.支持IE事件处理程序的浏览器不仅有IE浏览器,还有Opera浏览器。

第五部分:跨浏览器的事件处理程序

  实际上,这一部分视为了跨浏览器使用,将前面的几部分结合起来就能够了。

  这一部分须要建立两个方法:

  • addHandler()  --这个方法职责是视状况来使用DOM0级、DOM2级、IE事件处理程序来添加事件。
  • removeHandler()--这个方法就是移除使用addHandler添加的事件。

  这两个方法接收相同的三个参数:

  1. 要操做的元素--经过dom方法获取
  2. 事件名称--注意:没有on,如click、mouseover
  3. 事件处理程序函数--即handler函数

  这两个方法的构造状况以下:

复制代码
var EventUtil={
    addHandler:function(element,type,handler){
        if(element.addEventListener){
            element.addEventListener(type,handler,false);//注意:这里默认使用了false(冒泡)
        }else if(element.attachEvent){
            element.attachEvent("on"+type,handler);
        }else{
            element["on"+type]=handler;
        }
    },
    removeHandler:function(element,type,handler){
        if(element.removeEventListener){
            element.removeEventListener(type,handler,false);//注意:这里默认使用了false(冒泡)
        }else if(element.detachEvent){
            element.detachEvent("on"+type,handler);
        }else{
            element["on"+type]=null;
        }
    }
};
复制代码

即先判断DOM2级事件处理程序,再判断IE事件处理程序,最后使用DOM0级事件处理程序。

例10:经过这个例子来使用上面构造的方法。

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>跨浏览器事件处理程序</title>
</head>
<body>
    

    <button id="button">点我</button>

<script>
var EventUtil={
    addHandler:function(element,type,handler){
        if(element.addEventListener){
            element.addEventListener(type,handler,false);//注意:这里默认使用了false(冒泡)
        }else if(element.attachEvent){
            element.attachEvent("on"+type,handler);
        }else{
            element["on"+type]=handler;
        }
    },
    removeHandler:function(element,type,handler){
        if(element.removeEventListener){
            element.removeEventListener(type,handler,false);//注意:这里默认使用了false(冒泡)
        }else if(element.detachEvent){
            element.detachEvent("on"+type,handler);
        }else{
            element["on"+type]=null;
        }
    }
};

    function handler(){
        alert("clicked");
    }

    var button=document.getElementById("button");
    EventUtil.addHandler(button,"click",handler);
</script>
</body>
</html>
复制代码

最后浏览器成功弹出“clicked”。

4.使用单页应用将文件上传到服务器的有哪些方法?

提示:XMLHttpRequest2(streaming),fetch(non-streaming),File API

1、XMLHttpRequest2(streaming)

 

XMLHttpRequest是一个浏览器接口,使得Javascript能够进行HTTP(S)通讯。

 

最先,微软在IE 5引进了这个接口。由于它太有用,其余浏览器也模仿部署了,ajax操做所以得以诞生。

 

可是,这个接口一直没有标准化,每家浏览器的实现或多或少有点不一样。HTML 5的概念造成后,W3C开始考虑标准化这个接口。2008年2月,就提出了XMLHttpRequest Level 2 草案。

 

这个XMLHttpRequest的新版本,提出了不少有用的新功能,将大大推进互联网革新。本文就对这个新版本进行详细介绍。

 

 

1、老版本的XMLHttpRequest对象

 

在介绍新版本以前,咱们先回顾一下老版本的用法。

 

首先,新建一个XMLHttpRequest的实例。

 

  var xhr = new XMLHttpRequest();

 

而后,向远程主机发出一个HTTP请求。

 

  xhr.open('GET', 'example.php');

  xhr.send();

 

接着,就等待远程主机作出回应。这时须要监控XMLHttpRequest对象的状态变化,指定回调函数。

 

  xhr.onreadystatechange = function(){

    if ( xhr.readyState == 4 && xhr.status == 200 ) {

      alert( xhr.responseText );

    } else {

      alert( xhr.statusText );

    }

  };

 

上面的代码包含了老版本XMLHttpRequest对象的主要属性:

 

  * xhr.readyState:XMLHttpRequest对象的状态,等于4表示数据已经接收完毕。

  * xhr.status:服务器返回的状态码,等于200表示一切正常。

  * xhr.responseText:服务器返回的文本数据

  * xhr.responseXML:服务器返回的XML格式的数据

  * xhr.statusText:服务器返回的状态文本。

 

2、老版本的缺点

 

老版本的XMLHttpRequest对象有如下几个缺点:

 

  * 只支持文本数据的传送,没法用来读取和上传二进制文件。

  * 传送和接收数据时,没有进度信息,只能提示有没有完成。

  * 受到"同域限制"(Same Origin Policy),只能向同一域名的服务器请求数据。

 

3、新版本的功能

 

新版本的XMLHttpRequest对象,针对老版本的缺点,作出了大幅改进。

 

  * 能够设置HTTP请求的时限。

  * 能够使用FormData对象管理表单数据。

  * 能够上传文件。

  * 能够请求不一样域名下的数据(跨域请求)。

  * 能够获取服务器端的二进制数据。

  * 能够得到数据传输的进度信息。

 

下面,我就一一介绍这些新功能。

 

4、HTTP请求的时限

 

有时,ajax操做很耗时,并且没法预知要花多少时间。若是网速很慢,用户可能要等好久。

 

新版本的XMLHttpRequest对象,增长了timeout属性,能够设置HTTP请求的时限。

 

  xhr.timeout = 3000;

 

上面的语句,将最长等待时间设为3000毫秒。过了这个时限,就自动中止HTTP请求。与之配套的还有一个timeout事件,用来指定回调函数。

 

  xhr.ontimeout = function(event){

    alert('请求超时!');

  }

 

目前,Opera、Firefox和IE 10支持该属性,IE 8和IE 9的这个属性属于XDomainRequest对象,而Chrome和Safari还不支持。

 

5、FormData对象

 

ajax操做每每用来传递表单数据。为了方便表单处理,HTML 5新增了一个FormData对象,能够模拟表单。

 

首先,新建一个FormData对象。

 

  var formData = new FormData();

 

而后,为它添加表单项。

 

  formData.append('username', '张三');

  formData.append('id', 123456);

 

最后,直接传送这个FormData对象。这与提交网页表单的效果,彻底同样。

 

  xhr.send(formData);

 

FormData对象也能够用来获取网页表单的值。

 

  var form = document.getElementById('myform');

  var formData = new FormData(form);

  formData.append('secret', '123456'); // 添加一个表单项

  xhr.open('POST', form.action);

  xhr.send(formData);

 

6、上传文件

 

新版XMLHttpRequest对象,不只能够发送文本信息,还能够上传文件。

 

假定files是一个"选择文件"的表单元素(input[type="file"]),咱们将它装入FormData对象。

 

  var formData = new FormData();

  for (var i = 0; i < files.length;i++) {

    formData.append('files[]', files[i]);

  }

 

而后,发送这个FormData对象。

 

  xhr.send(formData);

 

7、跨域资源共享(CORS)

 

新版本的XMLHttpRequest对象,能够向不一样域名的服务器发出HTTP请求。这叫作"跨域资源共享"(Cross-origin resource sharing,简称CORS)。

 

使用"跨域资源共享"的前提,是浏览器必须支持这个功能,并且服务器端必须赞成这种"跨域"。若是可以知足上面的条件,则代码的写法与不跨域的请求彻底同样。

 

  xhr.open('GET', 'http://other.server/and/path/to/script');

 

目前,除了IE 8和IE 9,主流浏览器都支持CORS,IE 10也将支持这个功能。服务器端的设置,请参考《Server-Side Access Control》

 

8、接收二进制数据(方法A:改写MIMEType)

 

老版本的XMLHttpRequest对象,只能从服务器取回文本数据(不然它的名字就不用XML起首了),新版则能够取回二进制数据。

 

这里又分红两种作法。较老的作法是改写数据的MIMEType,将服务器返回的二进制数据假装成文本数据,而且告诉浏览器这是用户自定义的字符集。

 

  xhr.overrideMimeType("text/plain; charset=x-user-defined");

 

而后,用responseText属性接收服务器返回的二进制数据。

 

  var binStr = xhr.responseText;

 

因为这时,浏览器把它当作文本数据,因此还必须再一个个字节地还原成二进制数据。

 

  for (var i = 0, len = binStr.length; i < len; ++i) {

    var c = binStr.charCodeAt(i);

    var byte = c & 0xff;

  }

 

最后一行的位运算"c & 0xff",表示在每一个字符的两个字节之中,只保留后一个字节,将前一个字节扔掉。缘由是浏览器解读字符的时候,会把字符自动解读成Unicode的0xF700-0xF7ff区段。

 

8、接收二进制数据(方法B:responseType属性)

 

从服务器取回二进制数据,较新的方法是使用新增的responseType属性。若是服务器返回文本数据,这个属性的值是"TEXT",这是默认值。较新的浏览器还支持其余值,也就是说,能够接收其余格式的数据。

 

你能够把responseType设为blob,表示服务器传回的是二进制对象。

 

  var xhr = new XMLHttpRequest();

  xhr.open('GET', '/path/to/image.png');

  xhr.responseType = 'blob';

 

接收数据的时候,用浏览器自带的Blob对象便可。

 

  var blob = new Blob([xhr.response], {type: 'image/png'});

 

注意,是读取xhr.response,而不是xhr.responseText。

 

你还能够将responseType设为arraybuffer,把二进制数据装在一个数组里。

 

  var xhr = new XMLHttpRequest();

  xhr.open('GET', '/path/to/image.png');

  xhr.responseType = "arraybuffer";

 

接收数据的时候,须要遍历这个数组。

 

  var arrayBuffer = xhr.response;

  if (arrayBuffer) {

    var byteArray = new Uint8Array(arrayBuffer);

    for (var i = 0; i < byteArray.byteLength; i++) {

      // do something

    }
  }

 

更详细的讨论,请看Sending and Receiving Binary Data

 

9、进度信息

 

新版本的XMLHttpRequest对象,传送数据的时候,有一个progress事件,用来返回进度信息。

 

它分红上传和下载两种状况。下载的progress事件属于XMLHttpRequest对象,上传的progress事件属于XMLHttpRequest.upload对象。

 

咱们先定义progress事件的回调函数。

 

  xhr.onprogress = updateProgress;

  xhr.upload.onprogress = updateProgress;

 

而后,在回调函数里面,使用这个事件的一些属性。

 

  function updateProgress(event) {

    if (event.lengthComputable) {

      var percentComplete = event.loaded / event.total;

    }

  }

 

上面的代码中,event.total是须要传输的总字节,event.loaded是已经传输的字节。若是event.lengthComputable不为真,则event.total等于0。

 

与progress事件相关的,还有其余五个事件,能够分别指定回调函数:

 

  * load事件:传输成功完成。

  * abort事件:传输被用户取消。

  * error事件:传输中出现错误。

  * loadstart事件:传输开始。

  * loadEnd事件:传输结束,可是不知道成功仍是失败。

 

2、fetch(non-streaming)

与XMLHttpRequest(XHR)相似,fetch()方法容许你发出AJAX请求。区别在于Fetch API使用Promise,所以是一种简洁明了的API,比XMLHttpRequest更加简单易用。

从Chrome 40开始,Fetch API能够被利用在Service Worker全局做用范围中,自Chrome 42开始,能够被利用在页面中。

若是你还不了解Promise,须要首先补充这方面知识。

基本的Fetch请求

让咱们首先来比较一个XMLHttpRequest使用示例与fetch方法的使用示例。该示例向服务器端发出请求,获得响应并使用JSON将其解析。

XMLHttpRequest

一个XMLHttpRequest须要设置两个事件回调函数,一个用于获取数据成功时调用,另外一个用于获取数据失败时调用,以及一个open()方法调用及一个send()方法调用。

function reqListener(){
    var data=JSON.parse(this.responseText);
    console.log(data);
}
function reqError(err){
    console.log("Fetch错误:"+err);
}
var oReq=new XMLHttpRequest();
oReq.οnlοad=reqListener;
oReq.οnerrοr=reqError;
oReq.open("get","/students.json",true);
oReq.send();

Fetch

一个fetch()方法的使用代码示例以下所示:

fetch("/students.json")
.then(
    function(response){
        if(response.status!==200){
            console.log("存在一个问题,状态码为:"+response.status);
            return;
        }
        //检查响应文本
        response.json().then(function(data){
            console.log(data);
        });
    }
)
.catch(function(err){
    console.log("Fetch错误:"+err);
});

 

在上面这个示例中,咱们在使用JSON解析响应前首先检查响应状态码是否为200。

一个fetch()请求的响应为一个Stream对象,这表示当咱们调用json()方法,将返回一个Promise对象,由于流的读取将为一个异步过程。

响应元数据

在上一个示例中咱们检查了Response对象的状态码,同时展现了如何使用JSON解析响应数据。咱们可能想要访问响应头等元数据,代码以下所示:

fetch("/students.json")
.then(
    function(response){
        console.log(response.headers.get('Content-Type'));
        console.log(response.headers.get('Date'));
        console.log(response.status);
        console.log(response.statusText);
        console.log(response.type);
        console.log(response.url);
    }
)

响应类型

当咱们发出一个fetch请求时,响应类型将会为如下几种之一:“basic”、“cors”或“opaque”。这些类型标识资源来源,提示你应该怎样对待响应流。

当请求的资源在相同域中时,响应类型为“basic”,不严格限定你如何处理这些资源。

若是请求的资源在其余域中,将返回一个CORS响应头。响应类型为“cors”。“cors”响应限定了你只能在响应头中看见“Cache-Control”、“Content-Language”、“Content-Type”、“Expires”、“Last-Modified”以及“Progma”。

一个“opaque”响应针对的是访问的资源位于不一样域中,但没有返回CORS响应头的场合。若是响应类型为“opaque”,咱们将不能查看数据,也不能查看响应状态,也就是说咱们不能检查请求成功与否。目前为止不能在页面脚本中请求其余域中的资源。

你能够为fetch请求定义一个模式以确保请求有效。能够定义的模式以下所示:

  • "same-origin":只在请求同域中资源时成功,其余请求将被拒绝。
  • "cors":容许请求同域及返回CORS响应头的域中的资源。
  • "cors-with-forced-preflight":在发出实际请求前执行preflight检查。
  • "no-cors"针对的是向其余不返回CORS响应头的域中的资源发出的请求(响应类型为“opaque”),但如前所述,目前在页面脚本代码中不起做用。

为了定义模式,在fetch方法的第二个参数中添加选项对象并在该对象中定义模式:

fetch("http://www.html5online.com.cn/cors-enabled/students.json",{mode:"cors"})
.then(
    function(response){
        console.log(response.headers.get('Content-Type'));
        console.log(response.headers.get('Date'));
        console.log(response.status);
        console.log(response.statusText);
        console.log(response.type);
        console.log(response.url);
    }
)
.catch(function(err){
    console.log("Fetch错误:"+err);
});

Promise方法链

Promise API的一个重大特性是能够连接方法。对于fetch来讲,这容许你共享fetch请求逻辑。

若是使用JSON API,你须要检查状态而且使用JSON对每一个响应进行解析。你能够经过在不一样的返回Promise对象的函数中定义状态及使用JSON进行解析来简化代码,你将只须要关注于处理数据及错误:

function status(response){
    if(response.status>=200 && response.status<300){
        return Promise.resolve(response);
    }
    else{
        return Promise.reject(new Error(response.statusText));
    }
}
function json(response){
    return response.json();
}
fetch("/students.json")
.then(status)
.then(json)
.then(function(data){
    console.log("请求成功,JSON解析后的响应数据为:",data);
})
.catch(function(err){
    console.log("Fetch错误:"+err);
});

在上述代码中,咱们定义了status函数,该函数检查响应的状态码并返回Promise.resolve()方法或Promise.reject()方法的返回结果(分别为具备确定结果的Promise及具备否认结果的Promise)。这是fetch()方法链中的第一个方法。若是返回确定结果,咱们调用json()函数,该函数返回来自于response.json()方法的Promise对象。在此以后咱们获得了一个被解析过的JSON对象,若是解析失败Promise将返回否认结果,致使catch段代码被执行。

这样书写的好处在于你能够共享fetch请求的逻辑,代码容易阅读、维护及测试。

POST请求

在Web应用程序中常常须要使用POST方法提交页面中的一些数据。

为了执行POST提交,咱们能够将method属性值设置为post,而且在body属性值中设置须要提交的数据。

fetch(url,{
    method:"post",
    headers:{
        "Content-type":"application:/x-www-form-urlencoded:charset=UTF-8"
    },
    body:"name=lulingniu&age=40"
})
.then(status)
.then(json)
.then(function(data){
    console.log("请求成功,JSON解析后的响应数据为:",data);
})
.catch(function(err){
    console.log("Fetch错误:"+err);
});

使用Fetch请求发送凭证

你可能想要使用Fetch发送带有诸如cookie之类的凭证的请求。你能够在选项对象中将credentials属性值设置为“include”:

fetch(url,{
credentials:"include"
})

3、File API

file对象是对文件对象的一种表现

表明input上传时的文件独享对象

IE9中没有这个对象,因此没法操做文件

5.CSS 重排和重绘之间有什么区别?

哪些 CSS 属性会致使重排及重绘?

重绘是一个元素的外观变化所引起的浏览器行为;

重排是引发DOM树从新计算的行为;

一、回流/重排
渲染树的一部分必需要更新且节点的尺寸发生了变化,会触发重排操做。每一个页面至少在初始化的时候会有一次重排操做。
二、重绘
部分节点须要更新,但没有改变其形状,会触发重绘操做。
会触发重绘或回流/重排的操做
一、添加、删除元素(回流+重绘)
二、隐藏元素,display:none(回流+重绘),visibility:hidden(只重绘,不回流)
三、移动元素,如改变top、left或移动元素到另外1个父元素中(重绘+回流)
四、改变浏览器大小(回流+重绘)
五、改变浏览器的字体大小(回流+重绘)
六、改变元素的padding、border、margin(回流+重绘)
七、改变浏览器的字体颜色(只重绘,不回流)
八、改变元素的背景颜色(只重绘,不回流)

6. 什么是 CSS 选择器权重以及它如何工做?

说说计算 CSS 选择器权重的算法。

4个等级的定义以下: 第一等:表明内联样式,如: style=””,权值为1000。 第二等:表明ID选择器,如:#content,权值为100。 第三等:表明类,伪类和属性选择器,如.content,权值为10。 第四等:表明类型选择器和伪元素选择器,如div p,权值为1。 例如上图为例,其中#NAV为二等选择器,.ACTIVE为三等选择器,UL、LI和A为四等选择器。则整个选择器表达式的特殊性的值为1*100+1*10+3*1=113
注意:通用选择器(*),子选择器(>)和相邻同胞选择器(+)并不在这四个等级中,因此他们的权值都为0。

7.CSS 中的 pixel 与硬件/物理中的 pixel 有何不一样?

提示:像素不是像素不是像素 – ppk。

css里全部的px都是css pixel,其显示大小是相对的而不是绝对的,是设备相关的。
而css pixel * devicePixelRatio = 实际屏幕像素。
(平时电脑屏幕的devicePixelRatio都是1因此感受不到,会让你误觉得css里的px就是实际屏幕像素)
同时media query使用的px显然也是css pixel。

8.什么是 sectioning 算法?

提示:它也被称为 HTML5 大纲算法。特别是在构建具备语义结构的网站时很是重要。

在html5中有一个很重要的概念,叫作html5大纲算法(HTML5 Outliner),它的用途为用户提供一份页面的信息结构目录。合理的使用HTML5元素标签,能够生成一个很是清晰的文档大纲。

HTML5大纲算法

咱们能够经过各类工具去查看当前页面,这里推荐使用一个测试工具:HTML5 Outliner,网址以下:https://gsnedders.html5.org/outliner/

1. 了解一个 section 和 div 的区别

①div元素在html5以前是最经常使用的最流行的标签,但他自己是没有任何语义的,它只不过是用来布局页面和css样式以及js样式。

②在html5中 section 标签并非用来取代 div 的。他是具备语义的文档标签,在大纲规范中规定 session 至少要包含一个标题。也就是 section 标签内至少包含一个h1~h6。

③若是是页面布局,且不是 header、footer之类的专属区域都应该使用div。

2. body\nav\section 都是须要有标题才规范,header和div则是不须要标题的。

3. section 和 nav 元素大纲要求有标题h1~h6,暗示 section 必须有才规范,而 nav 若是没有标题,也是合理的。给他添加了标题会让大纲更好看,因此咱们能够添加完了再隐藏,就不会破坏布局了。(经过display:none;将其隐藏)

9.若是你用过 CSS Flex / CSS Grid(网格)布局,请说明你为何要使用它?它为你解决了什么问题?

  • 使用 CSS Grid,百分比%和 fr 单位有何不一样?
  • fr是一个相对尺寸单位,表示剩余空间作等分,此项分配到的百分比(若是只有一个项使用此单位,那就占剩余空间的100%,因此多个项联合使用更有意义)
  • 使用 CSS flexbox,有时 flex-items/children 会不考虑 flex 容器设置的宽度/高度?为何会这样?
  • 能够使用 CSS Grid 建立 Masonry layout(瀑布流布局)吗?若是能够,怎么作?
  • 解释 CSS Grid 和 CSS flexbox 术语?
  • CSS Grid布局 (又名"网格"),是一个基于二维网格布局的系统,主要目的是改变咱们基于网格设计的用户接口方式
  • 浮动元素(float: left | right;)如何在 CSS Grid 和 flexbox 中渲染?

提示:等高的列,垂直居中,复杂网格等。

10.何时应该使用 CSS animations 而不是 CSS transitions ?你作出这个决定标准是什么?

11.若是你正在 Review CSS 代码,那么你在代码中常常遇到的问题是什么?

示例:使用魔性数字,如 width: 67px; 或使用 em 代替 rem 单位,在通用代码以前编写 media queries(媒体查询),滥用 ID 和类等。

12.如何在 JavaScript 中检测触摸事件?

有三种状况,1为触摸鼠标均可以使用 2为触摸 3为鼠标

if((document.hasOwnProperty("ontouchstart")) && (document.hasOwnProperty("onmousedown"))){
}else if(document.body.ontouchstart !== undefined){
 
}else{
 
}
// document.body.ontouchstart !== undefined也是特性检测的一种和那位兄弟的代码差很少

mouse 事件是全部浏览器都支持的,一款普通的触屏手机也能够经过 USB OTG 外接鼠标。

因此你只能判断浏览器是否支持触屏,这里的关键就是

【ontouchstart,ontouchmove,ontouchend,ontouchcancel】

支持触屏的浏览器都会有这四个 touch 事件

window.onload = function() {
    if(document.hasOwnProperty("ontouchstart")) {
        alert("浏览器支持触屏");
    }
    else {
        alert("浏览器不支持触屏");
    }
};
  1. 你是否不看好检测设备对触摸事件的支持?若是是,为何?
  2. 主流作法通常是经过 UserAgent 判断。
  3. 比较触摸事件和点击事件。
  4. 在触摸设备上,touch事件从手指刚碰到屏幕就会触发,而click事件则要晚一下子才被触发。触发顺序以下:

    touchstart
    mouseover
    mousemove(一次)
    mousedown
    mouseup
    click
    touchend
    因此,若是想提高web在触摸设备下的用户体验,让用户以为响应很是迅速,应该对页面的触摸事件进行事件处理程序的注册,而不该再关注click事件。

  5. 当设备同时支持触摸和鼠标事件时,你认为这些事件的正确事件顺序是什么或应该是什么?
  • touchstart
  • Zero or more touchmove events, depending on movement of the finger(s)
  • touchend
  • mousemove
  • mousedown
  • mouseup
  • click

13.为 script 标签订义的 async 和 defer 属性有什么用?

  • 如今咱们有 HTTP/2 和 ES 模块,它们真的颇有用吗?
  • 向html页面中插入javascript代码的主要方法就是经过script标签。其中包括两种形式,第一种直接在script标签之间插入js代码,第二种便是经过src属性引入外部js文件。因为解释器在解析执行js代码期间会阻塞页面其他部分的渲染,对于存在大量js代码的页面来讲会致使浏览器出现长时间的空白和延迟,为了不这个问题,建议把所有的js引用放在</body>标签以前。

      script标签存在两个属性,defer和async,所以script标签的使用分为三种状况:

      1.<script src="example.js"></script>

       没有defer或async属性,浏览器会当即加载并执行相应的脚本。也就是说在渲染script标签以后的文档以前,不等待后续加载的文档元素,读到就开始加载和执行,此举会阻塞后续文档的加载;

      2.<script async src="example.js"></script>

         有了async属性,表示后续文档的加载和渲染与js脚本的加载和执行是并行进行的,即异步执行;

      3.<script defer src="example.js"></script>

             有了defer属性,加载后续文档的过程和js脚本的加载(此时仅加载不执行)是并行进行的(异步),js脚本的执行须要等到文档全部元素解析完成以后,DOMContentLoaded事件触发执行以前。

      下图能够直观的看出三者之间的区别:

     

      

     

      其中蓝色表明js脚本网络加载时间,红色表明js脚本执行时间,绿色表明html解析。

      从图中咱们能够明确一下几点:

      1.defer和async在网络加载过程是一致的,都是异步执行的;

      2.二者的区别在于脚本加载完成以后什么时候执行,能够看出defer更符合大多数场景对应用脚本加载和执行的要求;

      3.若是存在多个有defer属性的脚本,那么它们是按照加载顺序执行脚本的;而对于async,它的加载和执行是牢牢挨着的,不管声明顺序如何,只要加载完成就马上执行,它对于应用脚本用处不大,由于它彻底不考虑依赖。

相关文章
相关标签/搜索