Skip to main content

CSS3 相比于 CSS2 多了哪些属性?

详情请见:CSS Backgrounds and Borders Module Level 3 (csswg.org)

下面只列入部分常见以及常用属性:

  • 布局(flex 弹性布局、Grid 网格布局)
  • 背景(backgroundbackground-imagebackground-repeatbackground-originbackground-clip等)
  • 边框(border-imageborder-radius 等)
  • 颜色(可以用 RGBAHSL 两种颜色指定方法,也可也使用渐变指定)
  • 渐变(渐变 linear-gradient、径向渐变 radial-gradient
  • 过渡(transition
  • 形变、变换(transform
  • 动画(animation
  • 媒体查询(@media@import,这个属性是响应式设计的关键部分)
  • 文字(个性化字体 @font-face、文字装饰 text-stroke-color 、文字溢出 text-overflow

GET 和 POST 到底有什么区别

getpost 本质上就是 TCP 连接,并无差别,但由于 HTTP 的规定和浏览器、服务器的限制,导致它

们在应用过程中有一些不同:

  • get 参数通过 URL 传递;post 放在 request body

  • get 请求在 URL 中传递的参数有长度限制;post 没有(HTTP 协议未规定,是因为浏览器和服务器的限制)

  • get 请求只能进行 URL 编码;post 请求有多种编码方式

  • get 请求参数会被完整保留在浏览历史记录里;post 中的参数不会被保留

  • get 产生一个 TCP 数据包;post 产生两个 TCP 数据包

  • 对于 get 请求,浏览器将 http headerdata 一并发送,服务器响应 200 OK;对于 post 请求,

浏览器先发送 header,服务器响应 100 Continue,浏览器再发送 data,服务器响应 200 OK

  • 缓存方面:get 请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以

可以使用缓存;post 请求一般做的是修改和删除工作,必须与数据库交互,所以不能使用缓存

前言#

在 MDN 的 JavaScript 系列中我们已经学习了 callbackpromisegeneratorasync/await。而在这一篇文章中,作者将以实际样例阐述异步发展历史,介绍每种实现方式的优势与不足,以期帮助读者熟悉历史进程并把握异步发展的脉络。 异步发展历史

异步#

几十年前的导航网站,清爽又简单,没有什么特别的功能,只是单纯的展示,现成的网页在服务器上静静躺着,高效毫无压力,让人很喜欢。

几十年后的今天,静态页面远不能满足用户的需求,网站变得复杂起来,用户交互越来越频繁,从而产生大量复杂的内部交互,为了解决这种复杂,出现了各种系统“模式”,从而很容易的在外部获取数据,并实时展示给用户。

获取外部数据实际上就是“网络调用”,这个时候“异步”这个词汇出现了。

异步指两个或两个以上的对象或事件不同时存在或发生(或多个相关事物的发生无需等待其前一事物的完成)

WechatIMG891.png

异步 callbacks#

异步(callbacks)其实就是函数,只不过是作为参数传递给那些在后台执行的其他函数。当那些后台运行的代码结束,就调用 callbacks 函数,通知你工作已经完成,或者其他有趣的事情发生了。

场景

let readFile = (path, callBack) => {
setTimeout(function () {
callBack(path)
}, 1000)
}
readFile('first', function () {
console.log('first readFile success')
readFile('second', function () {
console.log('second readFile success')
readFile('third', function () {
console.log('third readFile success')
readFile('fourth', function () {
console.log('fourth readFile success')
readFile('fifth', function () {
console.log('fifth readFile success')
})
})
})
})
})

优点:

  • 解决了同步问题(即解决了一个任务时间长时,后面的任务排队,耗时太久,使程序的执行变慢问题)

缺点:

  • 回调地狱(多层级嵌套),会导致逻辑混乱,耦合性高,改动一处就会导致全部变动,嵌套多时,错误处理复杂
  • 不能使用 try...catch 来抓取错误
  • 不能 return
  • 可读性差

Promise#

一个Promise对象代表一个在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise,以便在未来某个时候把值交给使用者。

场景

let readFile = (path) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (!path) {
reject('error!!!')
} else {
console.log(path + ' readFile success')
resolve()
}
}, 1000)
})
}
readFile('first')
.then(() => readFile('second'))
.then(() => readFile('third'))
.then(() => readFile('fourth'))
.then(() => readFile('fifth'))

优点:

  • 状态改变后,就不会再变,任何时候都可以得到这个结果
  • 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
  • 一定程度上解决了回调地狱的可读性问题

缺点:

  • 无法取消 promise
  • 当处于 pending 状态时,无法得知目前进展到哪一个阶段
  • 代码冗余,有一堆任务时也会使语义不清晰

Generator#

Generator函数是 ES6 中提供的一种异步编程解决方案。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态,需要使用next()函数来继续执行下面的代码。

特征

  • function 与函数名之间带有(*)
  • 函数体内部使用 yield 表达式,函数执行遇到 yield 就返回

场景

var readFile = function (name, ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name + '读完了')
resolve()
}, ms)
})
}
var gen = function* () {
console.log('指定generator')
yield readFile('first', 1000)
yield readFile('second', 2000)
yield readFile('third', 3000)
yield readFile('forth', 4000)
yield readFile('fifth', 5000)
return '完成了'
}
var g = gen()
var result = g.next()
result.value
.then(() => {
g.next()
})
.then(() => {
g.next()
})
.then(() => {
g.next()
})
.then(() => {
g.next()
})

优点:

  • 可以控制函数的执行,可以配合 co 函数库使用

缺点:

  • 流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)

async 和 await#

async functionsawait 关键字是最近添加到 JavaScript 语言里面的。它们是 ECMAScript 2017 JavaScript 版的一部分(参见ECMAScript Next support in Mozilla)。简单来说,它们是基于 promises 的语法糖,使异步代码更易于编写和阅读。通过使用它们,异步代码看起来更像是老式同步代码,因此它们非常值得学习。

WechatIMG892.png

场景 1

var readFile = function (name, ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(name + '读完了')
resolve()
}, ms)
})
}
async function useAsyncAwait() {
await readFile('first', 1000)
await readFile('second', 2000)
await readFile('third', 3000)
await readFile('forth', 4000)
await readFile('fifth', 5000)
console.log('async文件阅读完毕')
}
useAsyncAwait()

优点

  • 内置执行器。意味着不需要像 generator 一样调用 next 函数或 co 模块
  • 更广的适用性。asyncawait 后面跟的都是 promise 函数,原始数据类型会被转为 promise
  • 语义更清晰、简洁

缺点

  • 大量的 await 代码会阻塞(程序并不会等在原地,而是继续事件循环,等到响应后继续往下走)程序运行,每个 await 都会等待前一个完成

场景 2

场景 1 中的代码,其实 secondthird 的伪请求其实并不依赖于 firstsecond 的结果,但它们必须等待前一个的完成才能继续,而我们想要的是它们同时进行,所以正确的操作应该是这样的。

async function useAsyncAwait() {
const first = readFile('first', 1000)
const second = readFile('second', 2000)
const third = readFile('third', 3000)
const forth = readFile('forth', 4000)
const fifth = readFile('fifth', 5000)
console.log('async文件阅读完毕')
await first
await second
await third
await forth
await fifth
}
useAsyncAwait()

在这里,我们将三个 promise 对象存储在变量中,这样可以同时启动它们关联的进程。

总结#

在这篇文章中,我们已经介绍了 JavaScript 异步发展史中 --- callbackpromisegeneratorasync/await 的使用方式、优点与缺点。

发展史优点缺点
callback解决了同步问题回调地狱、可读性差、无法 try / catch 、无法 return
promise一定程度上解决了回调地狱的可读性无法取消、任务多时,同样存在语义不清晰
generator可以控制函数的执行,可以配合 co 函数库使用流程管理却不方便(即何时执行第一阶段、何时执行第二阶段
async/await语义更清晰、简洁,内置执行器认知不清晰可能会造成大量 await 阻塞(程序并不会等在原地,而是继续事件循环,等到响应后继续往下走)情况

而在现有的异步解决方案中,async/await 是使用人数最多的,它带给我们最大的好处即同步代码的风格,语义简洁、清晰。

相关文章#

看到隐式类型转换这个词时,很多开发者就已经头大了。而其转换时的各种猫腻,相信各位开发者们更是饱受其折磨。

接下来,本文会通过对 ECMAScript 官方文档和各大网站高赞文章进行系统性的总结,务必帮助大家在读完后彻底掌握此知识点。

划重点#

你只需要搞清楚两个问题就好了!

  1. 类型会转换为哪几类?
  2. 什么时候该转化为哪类?

数学运算符中的类型转换#

众所周知,在 JS 中并没有类型声明,所以任意两个变量或字面量,都可以做加减乘除。

1.减、乘、除#

我们在对各种非Number类型进行数学运算时(- * / )时,会先将非Number类型转换为Number类型。

1 - fasle // 1, 首先将 false 转换为数字 0 , 然后再执行 1 - 0
null / 1 // 0, 首先将 null 转换为数字 0 , 然后再执行 0 / 1
1 * undefined // NaN, undefined 转换为数字是NaN

2.加法的特殊性#

谨记三条规则(优先级从高到低):

  • 当一侧为String类型,被识别为字符串拼接,并会优先将另一侧转换为字符串类型。
  • 当一侧为Number类型,另一侧为原始类型,则将原始类型转换为Number类型。
  • 当一侧为Number类型,另一侧为引用类型,将引用类型和Number类型转换成字符串后拼接。
1 + '1' // ’11’ (规则1)
1 + null // 1 (规则2)
1 + true // 2 (规则2)
1 + {} // '1[object object]'(规则3)

注: 原始类型( undefinednullstringnumberbooleansymbolBigInt

引用类型(Object Array DateRegExp Function统称为Object类型

逻辑语句中的类型转换#

当我们使用 if while for 语句时,我们期望表达式是一个 Boolean ,所以一定伴随着隐式类型转换。

1.单个变量#

这里有个小 tips

只有 null undefined '' NaN 0 false 这几个是 false,其他的情况都是 true,比如 {} , []

2.使用 == 比较中的 5 条规则#

  • 规则 1:NaN 和其他任何类型比较永远返回 fasle (包括和它自己)
NaN == NaN // false
  • 规则 2:Boolean 和其他任何类型比较,Boolean 首先会被转换为 Nmuber 类型
true == 1 // true
true == '1' // false, 先把 true 变成 1,而不是先把 ‘1’ 变成true
  • 规则 3:StringNumber比较,先将String转换为Number类型
1 == '1' // true, '1' 会先变成 1
'' == 0 // true, '' 会首先变成 0
  • 规则 4:null == undefined比较结果是true,除此之外,nullundefined和其他任何结果的比较值都为false
null == undefined // true
null == '' // false
null == 1 // false
null == false // false
undefined == '' // false
undefined == 1 // false
undefined == false // false
  • 规则 5:原始类型引用类型做比较时,引用类型会依照ToPrimitive(可在官方文档第七章节第一小节中找到)规则转换为原始类型

ToPrimitive规则,是引用类型向原始类型转变的规则,遵循先valueOftoString的模式,期望得到一个原始类型

'[object Object]' == {} // 引用类型通过 toString 得到一个类型
'1,2,3,4' == [1, 2, 3, 4] // 同上

练练手吧#

  1. [ ] == ! [ ]
第一步: ![] 会变为 false
这时,变为 [] == false
第二步:根据规则2,这时Boolean类型会先转换为 0
这时,变为 [] == 0
第三步:根据规则5,这时会调用ToPrimitive规则,[]调用valueOf 返回 [] , 不是原始类型 , 故接着调用toString , 返回 '' ;
这时,变为 '' == 0
第四步:根据规则3,string转换为number,''转换为0
故为true
  1. [ ] + { }
/*
第一步: [] 调用 toString 返回 ''
第二步: {} 调用 toString 返回 '[object object]'
故最后答案为 'object object'
*/

总结#

现在回头看看我们之前的两个问题

1.最后只会转换为 Boolean NumberString

2.两种场景,数据类型和逻辑类型,对应相关规则进行转换

补充:ToPrimitive 规则发生在引用类型转换,通常是先调用 valueOf 方法、后调用 toString 方法、当调用 valueOf 后,若返回不是原始类型,继续调用 toString 方法,若返回还不是原始类型,则报 TypeError

引用一张图片给大家做一个完美的总结

类型to Booleanto Numberto String
Booleantruetrue1"true"
Booleanfalsefalse0"false"
Number123true123"123"
NumberInfinitytrueInfinity"Infinity"
Number0false0"0"
NumberNaNfalseNaN"NaN"
String""false0""
String"123"true123"123"
String"123abc"trueNaN"123abc"
String"abc"trueNaN"abc"
Nullnullfalse0"null"
UndefinedundefinedfalseNaN"undefined"
Functionfunction(){}trueNaN"function(){}"
Object{}trueNaN"[object Object]"
Array[]true0""
Array["abc"]trueNaN"abc"
Array["123"]true123"123"
Array["123","abc"]trueNaN"123,abc"

参考资料#

1.js 隐式装箱-ToPrimitive | {XFE} (sinaad.github.io)

2.JavaScript 隐式类型转换,一篇就够了!

3.ECMAScript 2015 Language Specification – ECMA-262 6th Edition (ecma-international.org)

OSI/TCP 模型有哪几个部分

OSI 七层模型

  • 物理层 —— 比特流的传输
  • 数据链路层 —— 控制网络层和物理层间的通信
  • 网络层 —— IP 寻址和路由选择
  • 传输层 —— 创建、管理、维护端到端的连接
  • 会话层 —— 创建、管理、维护会话连接
  • 表示层 —— 加解密、数据格式化
  • 应用层 —— 为应用程序提供网络服务

TCP 四层模型则是将会话层、表示层、应用层统称为应用层,将物理层和数据链路层统称为数据链路层

详情请见:OSI 7 层模型和 TCP/IP 4 层模型

Promise

务必掌握 Promise 常用方法,如 thenallraceresolvereject

Promise 的关键点在于

  • 三个状态
  • 链式调用

更多详情:[图解 Promise](

本文主要针对 Redux 中常用的中间件作出分析,罗列出其优缺点,旨在帮助读者们了解其发展史,在实际开发中做出最合适的技术方案选型。

Redux 中间件(Middleware)#

Redux uses a special kind of addon called middleware to let us customize the dispatch function

简单来说,中间件是在 dispatching an actionreaches the reducer 中提供了一个第三方扩展点,你可以在这里 logging(打印) 、crash reporting(崩溃报告) 、talking to an asynchronous API (异步)、 routing(路由)等。

借用一张官方图帮助大家更好的去理解

Redux async data flow diagram

Redux - thunk — 异步方案的萌芽#

redux - thunk 是由 redux 作者 Dan 出品的,那他为什么会写出这样一个中间件呢?thunk 的出现帮助我们解决了什么问题呢?

让我们来看看作者是怎么说的!

With a plain basic Redux store, you can only do simple synchronous updates by dispatching an action. Middleware extends the store's abilities, and lets you write async logic that interacts with the store.

Thunks are the recommended middleware for basic Redux side effects logic, including complex synchronous logic that needs access to the store, and simple async logic like AJAX requests.

前半段很好的解释了他为什么会写出这样一个中间件( lets you write async logic that interacts with the store ),而后半段则很好的解释了 thunk 的出现解决了什么问题( basic Redux side effects logic )。

再深挖一下,我们在 Redux github 源码 issue1 号问题中可以看到这是激起作者写此中间件的源头

image.png

大概就是说一个开发者提出了 我该怎么在一个 action creatordispatch many actions 。 然后作者回答说 我可能会让 action creator 返回一个函数,这个函数将 dispatchstate 作为参数传递。

那它是怎么操作的呢?

//action types
const POST_DATA = 'POST_DATA',
POST_DATA_SUCCESS = 'POST_DATA_SUCCESS',
POST_DATA_FAILED = 'POST_DATA_FAILED'
//action creator
const postDataAction = function (parms) {
return function (dispatch, getState) {
dispatch({
type: POST_DATA,
payload: parms,
})
api
.postData(parms) //注:本文所有示例的api.getData都返回promise对象
.then((response) => {
dispatch({
type: POST_DATA_SUCCESS,
payload: response,
})
})
.catch((error) => {
dispatch({
type: POST_DATA_FAILED,
payload: error,
})
})
}
}
//reducer
const reducer = function (state, action) {
switch (action.type) {
case POST_DATA:
return state
case POST_DATA_SUCCESS:
return successState
case POST_DATA_FAILED:
return errorState
}
}

这是最简单的场景之一,试想一下,如果有 n 个网络请求,你是不是得写 n*2action.type (每个都得有成功和失败状态)呢?这些重复且枯燥的工作真是让人头大呢!

优缺点:#

优点#
  • 为异步操作提出了解决方案
  • 组件无需知道 action creator 是如何实现的、是否关心 Redux 状态、是同步还是异步以及是否有其他调度者
  • 使用方便,能满足大部分场景
缺点#
  • 模板代码多,重复且枯燥

redux - promise — thunk 的瘦身#

也许是看到 redux - thunk 重复代码太多了,写起来也忒麻烦了。所以社区的大佬们又推出了 redux - promise

它干了一件什么事呢?

//action creator
const postDataAction = function (parms) {
return function (dispatch, getState) {
dispatch({
type: POST_DATA,
payload: api.postData(parms),
})
}
}
//reducer
const reducer = function (state, action) {
switch (action.type) {
case POST_DATA:
if (action.status === 'success') {
return successState
} else {
return errorState
}
}
}

看到这样的代码,是不是突然觉得这就像瘦下来的 thunkpromise 中间件已经帮我们做好了成功,失败的处理,我们只需要在 reducer 中处理成功和失败就好啦。我们再也不用写大量重复代码了!

优缺点:#

优点#
  • 代码工作量减少
缺点#
  • 没有 pending 状态

redux - promise - middleware — 拨乱反正#

它来了,它来了! 它带着正义走来了!

什么?你没有 pending 状态,那可不行!

//action types
const POST_DATA_PENDING = 'POST_DATA_PENDING',
POST_DATA_SUCCESS = 'POST_DATA_SUCCESS',
POST_DATA_FAILED = 'POST_DATA_FAILED'
//action creator
const postDataAction = function (parms) {
return function (dispatch, getState) {
dispatch({
type: POST_DATA,
payload: api.postData(parms),
})
}
}
//reducer
const reducer = function (state, action) {
switch (action.type) {
case POST_DATA_PENDING:
return {
...state,
loading: true,
}
case POST_DATA_SUCCESS:
return {
...state,
loading: false,
successState,
}
case POST_DATA_FAILED:
return {
...state,
loading: false,
errorState,
}
}
}

redux - promise - middleware 很好的解决了没有 pending 状态的问题,当你 dispatch 一个 actionpromise )时, redux - promise - middleware 会自动向你的 type 后面加上一个 PENDING ,然后 dispatchreducer 中,当处理成功时,则 type 会变成 success 状态,dispatchreducer 中,处理失败,则变成 failed 状态。

优缺点:#

优点#
  • 处理了 pending 状态
缺点#
  • 只对 action creator 做出了优化,并没有优化 reducer

redux - saga — 优雅的它#

完美的它 ,redux - saga 出现了。

让我们看看官方的定义:

redux-saga is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, easy to test, and better at handling failures.

大致意思就是管理应用程序的 side effects

什么是 side effects 呢?

在计算机科学中,函数副作用指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响

如果你还是不太理解 side effects 是什么?可以联想一下之前的学习中关于纯函数的概念,纯函数是没有任何副作用的,只负责计算,任凭你无数次输入相同数据,它总是精准的返回给你相同的结果。

//action types
const POST_DATA = 'POST_DATA',
POST_DATA_SUCCESS = 'POST_DATA_SUCCESS',
POST_DATA_FAILED = 'POST_DATA_FAILED';
//action creator
function *postDataAction(parms) {
try {
const user = yield call(api.postData action.payload.userId);
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED", message: e.message});
}
}
//reducer
const reducer = function(state, action) {
switch(action.type) {
case POST_DATA :
return state;
case POST_DATA_SUCCESS :
return successState;
case POST_DATA_FAILED :
return errorState;
}
}
function *grabSaga () {
yield takeEvery("POST_DATA", postDataAction);
}

优缺点:#

优点#
  • 强大的异步控制流程
缺点#

URL 解析为对象

将以下输入的 URL 转化为对象:

http://www.baidu.com/s?wd=春节&name=justin
{
wd: '春节',
name: 'justin'
}

这道题重点在于分割,将 ? 后的键值对取出来,并放置于对象中,不同的键值对通过 & 符号分割。具体代码如下(这里也只给出一种解法):

let urlToJson = (url = window.location.href) => {
// 箭头函数默认传值为当前页面url
url = url.encodeURIComponent()
let obj = {},
index = url.indexOf('?'),
params = url.substr(index + 1)
if (index != -1) {
let parr = params.split('&')
for (let i of parr) {
let arr = i.split('=')
obj[arr[0]] = arr[1]
}
}
return obj
}