【 学 ES6 ?】 先看看这篇文章,还清ES5的帐吧!

9,601 阅读28分钟

1、前言

  • 虽然很多人入门ES6都是去阮一峰老师的ES6教程ECMAScript 6 入门,但是注意了,阮老师开篇就建议如果你的js基础不够扎实,还是先把基础打扎实再回来学es6。出来混,ES5的债,迟早要还滴!!(泛指ES6之前的基础知识)

知识点1:属性描述对象(详细资料链接

注:下方基础题如果对你来你说不能很好的回答,请点击上方详细资料链接,系统学习这个知识点

JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。这个内部数据结构称为属性描述对象(attributes object)

1 问题一:属性描述对象有哪些

  1. value
  2. writable
  3. enumerabele
  4. configurable
  5. get
  6. set

2 问题二:configurable的作用,以及设为false有哪些影响

configurable(可配置性)返回一个布尔值,决定了是否可以修改属性描述对象。也就是说,configurable为false时,value、writable、enumerable和configurable都不能被修改了。

var obj = Object.defineProperty({}, 'p', {
  value: 1,
  writable: false,
  enumerable: false,
  configurable: false
});

Object.defineProperty(obj, 'p', {value: 2})
// TypeError: Cannot redefine property: p

Object.defineProperty(obj, 'p', {writable: true})
// TypeError: Cannot redefine property: p

Object.defineProperty(obj, 'p', {enumerable: true})
// TypeError: Cannot redefine property: p

Object.defineProperty(obj, 'p', {configurable: true})
// TypeError: Cannot redefine property: p

上面代码中,obj.p的configurable为false。然后,改动value、writable、enumerable、configurable,结果都报错。

注意,writable只有在false改为true会报错,true改为false是允许的。

var obj = Object.defineProperty({}, 'p', {
  writable: true,
  configurable: false
});

Object.defineProperty(obj, 'p', {writable: false})
// 修改成功
至于value,只要writable和configurable有一个为true,就允许改动。

var o1 = Object.defineProperty({}, 'p', {
  value: 1,
  writable: true,
  configurable: false
});

Object.defineProperty(o1, 'p', {value: 2})
// 修改成功

var o2 = Object.defineProperty({}, 'p', {
  value: 1,
  writable: false,
  configurable: true
});

Object.defineProperty(o2, 'p', {value: 2})
// 修改成功

另外,writable为false时,直接目标属性赋值,不报错,但不会成功。

var obj = Object.defineProperty({}, 'p', {
  value: 1,
  writable: false,
  configurable: false
});

obj.p = 2;
obj.p // 1

上面代码中,obj.p的writable为false,对obj.p直接赋值不会生效。如果是严格模式,还会报错。

可配置性决定了目标属性是否可以被删除(delete)。

var obj = Object.defineProperties({}, {
  p1: { value: 1, configurable: true },
  p2: { value: 2, configurable: false }
});

delete obj.p1 // true
delete obj.p2 // false

obj.p1 // undefined
obj.p2 // 2

上面代码中,obj.p1的configurable是true,所以可以被删除,obj.p2就无法删除。

接下来就不写问题了,如果以上问题你没有很好的答上来,就可以点开链接开学了,嘿嘿

知识点2:RegExp 对象(详细资料链接

注:下方基础题如果对你来你说不能很好的回答,请点击上方详细资料链接,系统学习这个知识点

1 问题一:知道正则里\1, \2表示什么意思吗?

/(.)b(.)\1b\2/.test("abcabc") // true
  • 正则表达式内部,还可以用\n引用括号匹配的内容,n是从1开始的自然数,表示对应顺序的括号。
  • 上面的代码中,\1表示第一个括号匹配的内容(即a),\2表示第二个括号匹配的内容(即c)。

2 String.prototype.replace()中,第二个参数可以是字符串或者函数,当是字符串的时候$1,$2表示什么意思?

'hello world'.replace(/(\w+)\s(\w+)/, '$2 $1')
// "world hello"

replace方法的第二个参数可以使用美元符号$,用来指代所替换的内容。

  • $&:匹配的子字符串。
  • $`:匹配结果前面的文本。
  • $':匹配结果后面的文本。
  • $n:匹配成功的第n组内容,n是从1开始的自然数。
  • ?:指代美元符号$。

3 贪婪模式是什么?

var s = 'aaa';
s.match(/a+/) // ["aaa"]
s.match(/a+?/) // ["a"]

上面为什么不一样呢,因为两次+之后有个,这是开启非贪婪模式的标记。

除了非贪婪模式的加号,还有非贪婪模式的星号(*)和非贪婪模式的问号(?)。

  • +?:表示某个模式出现1次或多次,匹配时采用非贪婪模式。
  • *?:表示某个模式出现0次或多次,匹配时采用非贪婪模式。
  • ??:表格某个模式出现0次或1次,匹配时采用非贪婪模式。

4 请看以下案例, 为什么返回false?知道lastIndex属性吗?

var r = /x/g;
var s = '_x_x';

r.lastIndex = 4;
r.test(s) // false

5 字符串的match方法,第一个参数可以是正则,那正则里有g的模式有什么问题吗?

注意,使用组匹配时,不宜同时使用g修饰符,否则match方法不会捕获分组的内容。

var m = 'abcabc'.match(/(.)b(.)/g);
m // ['abc', 'abc']

可以看到,捕获组没有捕获分组内容。但是match不加g模式的时候,能捕获第一次碰见的捕获组内容

var m = 'abcabc'.match(/(.)b(.)/);
m
// ['abc', 'a', 'c']

6 知道非捕获组、先行断言、先行否定断言吗?

(?:x)称为非捕获组(Non-capturing group),表示不返回该组匹配的内容,即匹配的结果中不计入这个括号。

var m = 'abc'.match(/(?:.)b(.)/);
m // ["abc", "c"]

上面代码中的模式,一共使用了两个括号。其中第一个括号是非捕获组,所以最后返回的结果中没有第一个括号,只有第二个括号匹配的内容。

x(?=y)称为先行断言(Positive look-ahead),x只有在y前面才匹配,y不会被计入返回结果。比如,要匹配后面跟着百分号的数字,可以写成/\d+(?=%)/

“先行断言”中,括号里的部分是不会返回的。

var m = 'abc'.match(/b(?=c)/);
m // ["b"]

上面的代码使用了先行断言,b在c前面所以被匹配,但是括号对应的c不会被返回。

x(?!y)称为先行否定断言(Negative look-ahead),x只有不在y前面才匹配,y不会被计入返回结果。比如,要匹配后面跟的不是百分号的数字,就要写成/\d+(?!%)/

/\d+(?!\.)/.exec('3.14')
// ["14"]

上面代码中,正则表达式指定,只有不在小数点前面的数字才会被匹配,因此返回的结果就是14。

“先行否定断言”中,括号里的部分是不会返回的。

var m = 'abd'.match(/b(?!c)/);
m // ['b']

上面的代码使用了先行否定断言,b不在c前面所以被匹配,而且括号对应的d不会被返回。

知识点3:实例对象和new命令(详细资料链接

1 知道new 命令的原理吗?

使用new命令时,它后面的函数依次执行下面的步骤。

  • 创建一个空对象,作为将要返回的对象实例。
  • 将这个空对象的原型,指向构造函数的prototype属性。
  • 将这个空对象赋值给函数内部的this关键字。
  • 开始执行构造函数内部的代码。

2 知道new.target,Object.create()吗?

函数内部可以使用new.target属性。如果当前函数是new命令调用,new.target指向当前函数,否则为undefined。

function f() {
  console.log(new.target === f);
}

f() // false
new f() // true

构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()方法。

var person1 = {
  name: '张三',
  age: 38,
  greeting: function() {
    console.log('Hi! I\'m ' + this.name + '.');
  }
};

var person2 = Object.create(person1);
person2.name // 张三
person2.greeting() // Hi! I'm 张三.

上面代码中,对象person1是person2的模板,后者继承了前者的属性和方法。

知识点4:DOM事件模型

1 DOM里监听函数的this指向哪里?(详细资料链接)

// 例如:
// HTML 代码如下
// <button id="btn">点击</button>
var btn = document.getElementById('btn');

btn.addEventListener(
  'click',
  function (e) {
    console.log(this.id);
  },
  false
);

上面的代码里,this指向的是触发事件的那个元素节点

2 鼠标事件相关问题?(详细资料链接)

2.1 鼠标里的双击事件是什么,鼠标按下右键触发什么事件?

  • dblclick:在同一个元素上双击鼠标时触发。
  • contextmenu:按下鼠标右键时(上下文菜单出现前)触发,或者按下“上下文菜单键”时触发。

2.2 鼠标事件里clientX, screenX, offsetX有什么区别?

  • MouseEvent.clientX属性返回鼠标位置相对于浏览器窗口左上角的水平坐标(单位像素)
  • MouseEvent.screenX属性返回鼠标位置相对于屏幕左上角的水平坐标(单位像素)
  • MouseEvent.offsetX属性返回鼠标位置与目标节点左侧的padding边缘的水平距离(单位像素)

图示如下:

2.3 如何知道用户是否按下了大写键?

MouseEvent.getModifierState方法返回一个布尔值,表示有没有按下特定的功能键。它的参数是一个表示功能键的字符串。

document.addEventListener('click', function (e) {
  console.log(e.getModifierState('CapsLock'));
}, false);

知识点5:touch事件相关问题?(详细资料链接)

1 什么是Touchlist?

  • 由Touch对象构成的数组,通过event.touches取到。
  • 一个Touch对象代表一个触点,当有多个手指触摸屏幕时,TouchList就会存储多个Touch对象。

2 touch事件里TouchEvent.touches和TouchEvent.changedTouches的区别是什么?

  • changedTouches也是一个 TouchList 对象,对于touchstart 事件, 这个 TouchList 对象列出在此次事件中新增加的触点。
  • 对于touchmove事件,列出和上一次事件相比较,发生了变化的触点。对于touchend ,列出离开触摸平面的触点(这些触点对应已经不接触触摸平面的手指)。

touchend这里要特别注意,touches和targetTouches只存储接触屏幕的触点,要获取触点最后离开的状态要使用changedTouches。

知识点6 drag & drop事件相关问题?(详细资料链接)

1 绑定在拖拽目标的事件和绑定在放置目标的事件分别有哪些?

绑定在拖拽目标

事件名 描述
dragstart 当用户开始拖拽一个元素或者一个文本选取区块的时触发
drag 当用户正在拖拽一个元素或者一个文本选取区块的时触发
dragend 当用户结束拖拽一个元素或者一个文本选取区块的时触发。(如放开鼠标按键或按下键盘的 escap 键)

绑定在放置目标

事件名 描述
dragenter 当用户开始拖拽一个元素或者一个文本选取区块的时触发
dragover 当用户正在拖拽一个元素或者一个文本选取区块的时触发
dragleave 当用户结束拖拽一个元素或者一个文本选取区块的时触发。(如放开鼠标按键或按下键盘的 escap 键)
drop 当一个元素或文字选取区块被放置至一个有效的放置目标时触发。

2 将文件从操作系统拉进浏览器,会触发dragstart和dragend事件吗?

不会

3 为什么要在dragover事件上阻止默认事件(event.preventDefault())?

  • 由于网页的大部分区域不适合作为放下拖拉元素的目标节点,所以这个事件的默认设置为当前节点不允许接受被拖拉的元素。
  • 如果想要在目标节点上放下的数据,首先必须阻止这两个事件的默认行为。

4 在进行拖放时,每个event对象中都有DataTransfer对象来保存被拖动的数据,请问,DataTranfer中dropEffect属性和effectAllowed属性的区别?

  • DataTransfer.dropEffect属性用来设置放下(drop)被拖拉节点时的效果,会影响到拖拉经过相关区域时鼠标的形状。 例如:
target.addEventListener('dragover', function (e) {
  e.preventDefault();
  e.stopPropagation();
  e.dataTransfer.dropEffect = 'copy';
});
  • DataTransfer.effectAllowed属性设置本次拖拉中允许的效果。例如:
source.addEventListener('dragstart', function (e) {
  e.dataTransfer.effectAllowed = 'move';
});

target.addEventListener('dragover', function (e) {
  ev.dataTransfer.dropEffect = 'move';
});

5 如何利用dataTransfer对象传递数据?

主要是用到DataTransfer.setData()DataTransfer.getData()方法, 例如

document.addEventListener('dragstart', function (event) {
  // 被拖拉节点的背景色变透明
  event.dataTransfer.setData('data',this.nodeName);
}, false);

document.addEventListener('drop', function( event ) {
  // 防止事件默认行为(比如某些元素节点上可以打开链接),
  event.preventDefault();
  console.log(event.dataTransfer.getData('data'))
}, false);

知识点6 浏览器加载脚本和渲染相关问题(详细资料链接)

1 请简要说一下浏览器加载js脚本的原理?

正常的网页加载流程是这样的。

  • 浏览器一边下载 HTML网页,一边开始解析。也就是说,不等到下载完,就开始解析。
  • 解析过程中,浏览器发现<script>元素,就暂停解析,把网页渲染的控制权转交给 JavaScript 引擎。
  • 如果<script>元素引用了外部脚本,就下载该脚本再执行,否则就直接执行代码。
  • JavaScript 引擎执行完毕,控制权交还渲染引擎,恢复往下解析 HTML 网页。

2 为什么加载外部脚本时,浏览器会暂停页面渲染?

原因是 JavaScript 代码可以修改 DOM,所以必须把控制权让给它,否则会导致复杂的线程竞赛的问题

3 从以上js脚本加载原理来看,为什么js脚本最好放在页面底部?

  • 如果外部脚本加载时间很长(一直无法完成下载),那么浏览器就会一直等待脚本下载完成,造成网页长时间失去响应,浏览器就会呈现“假死”状态,这被称为“阻塞效应”。

  • 为了避免这种情况,较好的做法是<script>标签都放在页面底部

4 加载css会阻塞DOM的解析吗?加载css会阻塞DOM的渲染吗?

css的加载和解析不会阻塞html的解析,但会阻塞渲染。

5 不同的浏览器有哪些知名的渲染引擎?渲染引擎的渲染流程是什么?

不同的浏览器有不同的渲染引擎。

  • Firefox:Gecko 引擎
  • Safari:WebKit 引擎
  • Chrome:Blink 引擎
  • IE: Trident 引擎
  • Edge: EdgeHTML 引擎

渲染引擎处理网页,通常分成四个阶段。

  • 解析代码:HTML 代码解析为 DOM,CSS 代码解析为CSSOM(CSS Object Model)。
  • 对象合成:将 DOM 和 CSSOM 合成一棵渲染树(render tree)。
  • 布局:计算出渲染树的布局(layout)。
  • 绘制:将渲染树绘制到屏幕。

以上四步并非严格按顺序执行,往往第一步还没完成,第二步和第三步就已经开始了。所以,会看到这种情况:网页的 HTML 代码还没下载完,但浏览器已经显示出内容了。

5.5 浏览器渲染中发生的重回和重流是什么?

  • 渲染树转换为网页布局,称为布局流(flow);布局显示到页面的这个过程,称为绘制(paint)。它们都具有阻塞效应,并且会耗费很多时间和计算资源。

  • 页面生成以后,脚本操作和样式表操作,都会触发重流(reflow)重绘(repaint)。用户的互动也会触发重流和重绘,比如设置了鼠标悬停(a:hover)效果、页面滚动、在输入框中输入文本、改变窗口大小等等。

  • 重流重绘并不一定一起发生,重流必然导致重绘重绘不一定需要重流。比如改变元素颜色,只会导致重绘,而不会导致重流;改变元素的布局,则会导致重绘和重流。

5.6 浏览器解析js的大概过程?

早期,浏览器内部对 JavaScript 的处理过程如下:

  • 读取代码,进行词法分析(Lexical analysis),将代码分解成词元(token)。
  • 对词元进行语法分析(parsing),将代码整理成“语法树”(syntax tree)。
  • 使用“翻译器”(translator),将代码转为字节码(bytecode)。
  • 使用“字节码解释器”(bytecode interpreter),将字节码转为机器码。

逐行解释将字节码转为机器码,是很低效的。为了提高运行速度,现代浏览器改为采用“即时编译”(Just In Time compiler,缩写 JIT),即字节码只在运行时编译,用到哪一行就编译哪一行,并且把编译结果缓存(inline cache)。通常,一个程序被经常用到的,只是其中一小部分代码,有了缓存的编译结果,整个程序的运行速度就会显著提升。

知识点7 cookie(详细资料链接)

1 cookie的数量和大小限制是多少?

不同浏览器对 Cookie 数量和大小的限制,是不一样的。一般来说,单个域名设置的 Cookie 不应超过30个,每个 Cookie 的大小不能超过4KB。超过限制以后,Cookie 将被忽略,不会被设置 。

2 什么情况下,两个网址能共享cookie?

浏览器的同源政策规定,两个网址只要域名相同和端口相同,就可以共享 Cookie(参见《同源政策》一章)。注意,这里不要求协议相同。也就是说,example.com 设置的 Cookie,可以被https://example.com读取。

3 设置cookie里的Expires,Max-Age字段的区别是什么?

  • Expires属性指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie。它的值是 UTC 格式,可以使用Date.prototype.toUTCString()进行格式转换。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
  • Max-Age属性:指定从现在开始 Cookie 存在的秒数,比如60 * 60 * 24 * 365(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie。

  • 如果同时指定了ExpiresMax-Age,那么Max-Age的值将优先生效。

  • 如果Set-Cookie字段没有指定ExpiresMax-Age属性,那么这个 Cookie 就是 Session Cookie,即它只在本次对话存在,一旦用户关闭浏览器,浏览器就不会再保留这个 Cookie

4 设置cookie里的Secure,HttpOnly字段的区别是什么?

Secure属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。该属性只是一个开关,不需要指定值。如果通信是 HTTPS 协议,该开关自动打开。

HttpOnly属性指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是document.cookie属性、XMLHttpRequest对象和 Request API 都拿不到该属性。

知识点8 同源限制相关问题

1 同源指的是什么?

  • 协议相同
  • 域名相同
  • 端口相同

23 哪些行为受到同源策略的限制?

目前,如果非同源,共有三种行为受到限制

(1) 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。

(2) 无法接触非同源网页的 DOM。

(3) 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)

3 HTML5如何实现窗口间数据通信,请简要介绍一下?(详细资料

postMessage是html5引入的API,postMessage()方法允许来自不同源的脚本采用异步方式进行有效的通信,可以实现跨文本文档,多窗口,跨域消息传递.多用于窗口间数据通信,这也使它成为跨域通信的一种有效的解决方案。

举一个postMessage跨域通信的案例,并介绍其API

父窗体创建跨域iframe并发送信息

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	
<iframe src="http://127.0.0.1:9090/b.html" name="postIframe" onload="messageLoad()"></iframe>

<script>
	function messageLoad(){
		var url = "http://127.0.0.1:9090";
		window.postIframe.postMessage("给我tsort的信息",url); //发送数据
	}

	window.onmessage = function(e){
		e = e || event;
		console.log(e.data); //接收b返回的数据,在控制台有两次输出
	}
</script>
</body>
</html>

子窗体接收信息并处理

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
<script>
	window.onmessage = function(e){
		e = e || event;
		alert(e.data); //立即弹出a发送过来的数据
		e.source.postMessage("好的,请稍等三秒!",e.origin); //立即回复a

		var postData = {name:"tsrot",age:24};
		var strData = JSON.stringify(postData); //json对象转化为字符串
		setTimeout(function(){
			e.source.postMessage(strData,e.origin);
		},3000); //3秒后向a发送数据
	}
</script>
</body>
</html>

4 克服了 AJAX 只能同源使用的限制的CORS请介绍一下?(详细资料

4.1 CORS里的简单请求和非简单请求的区别是什么?

只要同时满足以下两大条件,就属于简单请求。

1)请求方法是以下三种方法之一。

HEAD
GET
POST

2)HTTP 的头信息不超出以下几种字段。

Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

4.2 浏览器发现这次跨域 AJAX 请求是简单请求,就自动在头信息之中添加哪个字段,表示什么意思?

添加一个Origin字段。说明本次请求来自哪个域(协议 + 域名 + 端口)

4.3 如果origin指定的地址,不在服务器的许可范围内,浏览器会怎样,那在服务器许可的范围,响应头会多出哪几个字段?

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。注意,这种错误无法通过状态码识别,因为 HTTP 回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

4.4 Access-Control-Allow-Credentials: true表示什么意思?

  • CORS 请求默认不包含 Cookie 信息(以及 HTTP 认证信息等),这是为了降低 CSRF 攻击的风险。
  • 但是某些场合,服务器可能需要拿到 Cookie,这时需要服务器显式指定Access-Control-Allow-Credentials字段,告诉浏览器可以发送 Cookie。

4.5 非简单请求中的预检请求是什么,预检请求有哪些不同于普通请求的请求头?

  • 非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为预检请求(preflight)
  • 浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 方法和头信息字段。

举例:

var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

上面代码中,HTTP 请求的方法是PUT,并且发送一个自定义头信息X-Custom-Header

浏览器发现,这是一个非简单请求,就自动发出一个“预检”请求,要求服务器确认可以这样请求。下面是这个“预检”请求的 HTTP 头信息。

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

“预检”请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。

除了Origin字段,“预检”请求的头信息包括两个特殊字段。

(1)Access-Control-Request-Method

该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些 HTTP 方法,上例是PUT。

(2)Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,指定浏览器 CORS 请求会额外发送的头信息字段,上例是X-Custom-Header。

4.6 预检请求的响应?

  • 非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为预检请求(preflight)
  • 浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 方法和头信息字段。

服务器收到“预检”请求以后,检查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

知识点9 history对象访问浏览器会话历史API相关问题?(详细资料链接)

1 history.pushState()是什么意思,它会跳转网页吗?

History.pushState()方法用于在历史中添加一条记录。

window.history.pushState(state, title, url)

该方法接受三个参数,依次为:

  • state:一个与添加的记录相关联的状态对象,主要用于popstate事件。该事件触发时,该对象会传入回调函数。也就是说,浏览器会将这个对象序列化以后保留在本地,重新载入这个页面的时候,可以拿到这个对象。如果不需要这个对象,此处可以填null。
  • title:新页面的标题。但是,现在所有浏览器都忽视这个参数,所以这里可以填空字符串。
  • url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。 假定当前网址是example.com/1.html,使用pushState()方法在浏览记录(History 对象)中添加一个新记录。
var stateObj = { foo: 'bar' };
history.pushState(stateObj, 'page 2', '2.html');

此API不会跳转网页。

2 history.pushState()之后,用户点击浏览器的倒退按钮,会触发什么事件?

会触发popstate 事件 使用方法如下

window.addEventListener('popstate', function(event) {
  console.log('location: ' + document.location);
  console.log('state: ' + JSON.stringify(event.state));
});

知识点10 URL对象相关问题?(详细资料链接)

1 URL 的编码和解码中,URL只能包含两类合法的字符,是那两类?

  • URL 元字符:分号(;)逗号(,)斜杠(/)问号(?)冒号(:)at(@)&等号(=)加号(+)美元符号($)井号(#)
  • 语义字符:a-zA-Z0-9连词号(-)下划线(_)点(.)感叹号(!)波浪线(~)星号(*)单引号(')圆括号(())

除了以上字符,其他字符出现在 URL 之中都必须转义,规则是根据操作系统的默认编码,将每个字节转为百分号(%)加上两个大写的十六进制字母。

2 URL提供了哪些URL的编码/解码方法?

JavaScript 提供四个 URL 的编码/解码方法。

  • encodeURI()
  • encodeURIComponent()
  • decodeURI()
  • decodeURIComponent()

1、 encodeURI()

encodeURI()方法用于转码整个 URL。它的参数是一个字符串,代表整个 URL。它会将元字符和语义字符之外的字符,都进行转义。

encodeURI('http://www.example.com/q=春节')
// "http://www.example.com/q=%E6%98%A5%E8%8A%82"

2、encodeURIComponent()

encodeURIComponent()方法用于转码 URL 的组成部分,会转码除了语义字符之外的所有字符,即元字符也会被转码。所以,它不能用于转码整个 URL。它接受一个参数,就是 URL 的片段。

encodeURIComponent('春节')
// "%E6%98%A5%E8%8A%82"
encodeURIComponent('http://www.example.com/q=春节')
// "http%3A%2F%2Fwww.example.com%2Fq%3D%E6%98%A5%E8%8A%82"

上面代码中,encodeURIComponent()会连 URL 元字符一起转义,所以如果转码整个 URL 就会出错。

3、decodeURI() 4、decodeURIComponent()

3和4,都是1,2的逆运算

3 URL对象的实例有哪些常用属性和方法?

var url = new URL('http://www.example.com:4097/path/a.html?x=111#part1');

url.href
// "http://user:passwd@www.example.com:4097/path/a.html?x=111#part1"
url.pathname
// "/path/a.html"
url.search
// "?x=111"

常用的静态方法有

  • URL.createObjectURL()

我自己经常会用这个API做本地图片预览。如下案例

<body>
	<div id="display" />
	<input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
	<script>
		var div = document.getElementById('display');

		function handleFiles(files) {
			for (var i = 0; i < files.length; i++) {
				var img = document.createElement('img');
				console.log(window.URL.createObjectURL(files[i]))
				img.src = window.URL.createObjectURL(files[i]);
				div.appendChild(img);
			}
		}
	</script>
</body>

注意,每次使用URL.createObjectURL方法,都会在内存里面生成一个 URL 实例。如果不再需要该方法生成的 URL 字符串,为了节省内存,可以使用URL.revokeObjectURL()方法释放这个实例。

  • URL.revokeObjectURL()

URL.revokeObjectURL方法用来释放URL.createObjectURL方法生成的 URL 实例。它的参数就是URL.createObjectURL方法返回的 URL 字符串。

下面为上一段的示例加上URL.revokeObjectURL()。

var div = document.getElementById('display');

function handleFiles(files) {
  for (var i = 0; i < files.length; i++) {
    var img = document.createElement('img');
    img.src = window.URL.createObjectURL(files[i]);
    div.appendChild(img);
    img.onload = function() {
      window.URL.revokeObjectURL(this.src);
    }
  }
}

知识点11 Blob 对象?(详细资料链接)

Blob构造函数接受两个参数。第一个参数是数组,成员是字符串或二进制对象,表示新生成的Blob实例对象的内容;第二个参数是可选的,是一个配置对象,目前只有一个属性type,它的值是一个字符串,表示数据的 MIME 类型,默认是空字符串。

var htmlFragment = ['<a id="a"><b id="b">hey!</b></a>'];
var myBlob = new Blob(htmlFragment, {type : 'text/html'});

1 Blob对象的实例如何保存JSON?

例子如下:

var obj = { hello: 'world' };
var blob = new Blob([ JSON.stringify(obj) ], {type : 'application/json'});

2 File 对象是什么?(详细资料

File对象代表一个文件,用来读写文件信息。它继承了 Blob对象,或者说是一种特殊的 Blob对象,所有可以使用 Blob 对象的场合都可以使用它。 下面代码中,file是用户选中的第一个文件,它是 File 的实例。

// HTML 代码如下
// <input id="fileItem" type="file">
var file = document.getElementById('fileItem').files[0];
file instanceof File // true

3 FileReader 对象是什么?

FileReader对象用于读取File对象或Blob对象所包含的文件内容。

浏览器原生提供一个FileReader构造函数,用来生成 FileReader 实例。

var reader = new FileReader();

常用的FileReader 有以下的实例属性

  • FileReader.result:读取完成后的文件内容,有可能是字符串,也可能是一个 ArrayBuffer 实例。
  • FileReader.onload:load事件(读取操作完成)的监听函数,通常在这个函数里面使用result属性,拿到文件内容。

下面是监听load事件的一个例子。

// HTML 代码如下
// <input type="file" onchange="onChange(event)">

function onChange(event) {
  var file = event.target.files[0];
  var reader = new FileReader();
  reader.onload = function (event) {
    console.log(event.target.result)
  };

  reader.readAsText(file);
}

FileReader 比较重要的实例方法。

  • FileReader.readAsDataURL():读取完成后,result属性将返回一个 Data URL 格式(Base64 编码)的字符串,代表文件内容。对于图片文件,这个字符串可以用于<img>元素的src属性。注意,这个字符串不能直接进行 Base64 解码,必须把前缀data:*/*;base64,从字符串里删除以后,再进行解码。
  • FileReader.readAsText():读取完成后,result属性将返回文件内容的文本字符串。该方法的第一个参数是代表文件的 Blob 实例,第二个参数是可选的,表示文本编码,默认为 UTF-8

下面是一个例子。

/* HTML 代码如下
  <input type="file" onchange="previewFile()">
  <img src="" height="200">
*/

function previewFile() {
  var preview = document.querySelector('img');
  var file    = document.querySelector('input[type=file]').files[0];
  var reader  = new FileReader();

  reader.addEventListener('load', function () {
    preview.src = reader.result;
  }, false);

  if (file) {
    reader.readAsDataURL(file);
  }

知识点12 FormData 对象?(详细资料链接)

Blob构造函数接受两个参数。第一个参数是数组,成员是字符串或二进制对象,表示新生成的Blob实例对象的内容;第二个参数是可选的,是一个配置对象,目前只有一个属性type,它的值是一个字符串,表示数据的MIME 类型,默认是空字符串。

1 FormData 对象的实例,有哪些常用的方法?

  • FormData.append(key, value):添加一个键值对。如果键名重复,则会生成两个相同键名的键值对。如果第二个参数是文件,还可以使用第三个参数,表示文件名。
  • FormData.set(key, value):设置指定键名的键值,参数为键名。如果键名不存在,会添加这个键值对,否则会更新指定键名的键值。如果第二个参数是文件,还可以使用第三个参数,表示文件名。
  • FormData.delete(key):删除一个键值对,参数为键名。
  • FormData.get(key):获取指定键名对应的键值,参数为键名。如果有多个同名的键值对,则返回第一个键值对的键值。
  • FormData.getAll(key):返回一个数组,表示指定键名对应的所有键值。如果有多个同名的键值对,数组会包含所有的键值。

案例如下:

var formData = new FormData();
// 设置某个控件的值
formData.set('username', '张三');
formData.get('username') // "张三"
formData.set('username', '张三');
formData.append('username', '李四');
formData.get('username') // "张三"
formData.getAll('username') // ["张三", "李四"]

formData.append('userpic[]', myFileInput.files[0], 'user1.jpg');
formData.append('userpic[]', myFileInput.files[1], 'user2.jpg');

2 enctype 属性是什么,有哪些值?(详细资料

表单能够用四种编码,向服务器发送数据。编码格式由表单的enctype属性决定。

(1)GET 方法

如果表单使用GET方法发送数据,enctype属性无效。

<form
  action="register.php"
  method="get"
  onsubmit="AJAXSubmit(this); return false;"
>
</form>

数据将以 URL 的查询字符串发出。

?foo=bar&baz=The%20first%20line.%0AThe%20second%20line.

(2)application/x-www-form-urlencoded

如果表单用POST方法发送数据,并省略enctype属性,那么数据以application/x-www-form-urlencoded格式发送(因为这是默认值)。

<form
  action="register.php"
  method="post"
  onsubmit="AJAXSubmit(this); return false;"
>
</form>

发送的 HTTP 请求如下。

Content-Type: application/x-www-form-urlencoded

foo=bar&baz=The+first+line.%0D%0AThe+second+line.%0D%0A

上面代码中,数据体里面的%0D%0A代表换行符(\r\n)。

(3)text/plain

如果表单使用POST方法发送数据,enctype属性为text/plain,那么数据将以纯文本格式发送。

<form
  action="register.php"
  method="post"
  enctype="text/plain"
  onsubmit="AJAXSubmit(this); return false;"
>
</form>

发送的 HTTP 请求如下。

Content-Type: text/plain

foo=bar
baz=The first line.
The second line.

(4)multipart/form-data

如果表单使用POST方法,enctype属性为multipart/form-data,那么数据将以混合的格式发送。

<form
  action="register.php"
  method="post"
  enctype="multipart/form-data"
  onsubmit="AJAXSubmit(this); return false;"
>
</form>

发送的 HTTP 请求如下。

Content-Type: multipart/form-data; boundary=---------------------------314911788813839

-----------------------------314911788813839
Content-Disposition: form-data; name="foo"

bar
-----------------------------314911788813839
Content-Disposition: form-data; name="baz"

The first line.
The second line.

-----------------------------314911788813839--

这种格式也是文件上传的格式。