Giter Club home page Giter Club logo

blog's People

Contributors

polluxlee avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

防抖和节流

防抖(Debounce)

使用场景:对于连续事件响应,只需要执行一次回调

function debounce(func, wait, immediate) {
  var timeId = null;

  return function () {
    
    clearTimeout(timeId);
    // 立即执行
    if (immediate) {
      if (!timeId) func.apply(this, arguments);
      timeId = setTimeout(() => {
        timeId = null;
      }, wait);
    }
    // 停止后执行
    else {
      timeId = setTimeout(() => {
        func.apply(this, arguments);
      }, wait);
    }
  }
}

节流(Throttle)

使用场景:对于连续事件响应,需要间隔一定时间触发回调来控制函数调用频率

function throttle(func, wait) {
  var now, previous = 0;

  return function () {
    now = +new Date();
    if (now - previous > wait) {
      func.apply(this, arguments);
      previous = now;
    }
  }
}

Reference

mqyqingfeng/Blog#22
mqyqingfeng/Blog#26

CSS Selector

# 基本选择器(Basic)

选择器 含义
* 匹配所有元素
Tag 匹配所有标签为 Tag 的元素
.className 匹配所有包含 className 的类名的元素
#idName 匹配 id 为 idName 的元素

# 组合选择器(Combine)

注:E 和 F 可以是标签,类名也可以是 ID

选择器 含义
E,F 逗号隔开表示或的关系,匹配所有E 或 F 的元素
E F 空格分隔,匹配 E 的所有后代包含 F 的元素
E>F 大于号分隔,匹配 E 的所有包含 F 的直接子元素
E+F 匹配 E 后面紧挨着的一个同级元素
E~F 匹配 E 后面所有同级元素

# 属性选择器(Attribute)

注:下面的 E 元素都可省略

选择器 含义
E[attr] 匹配所有具有属性 attr 的 E 元素
E[attr=val] 匹配所有具有属性 attr=value 的 E 元素
E[attr~=val] 匹配所有att属性具有多个空格分隔的值、其中一个值等于"val"的E元素
E[attr \= val] 匹配所有att属性具有多个连字号分隔(hyphen-separated)的值、其中一个值以"val"开头的E元素,主要用于lang属性,比如"en"、"en-us"、"en-gb"等等
E[attr^="val"] 匹配所有属性 attr 值以 val 开头的 E 元素
E[attr$="val"] 匹配所有属性 attr 值以 val结尾的 E 元素
E[attr*="val"] 匹配所有属性 attr 值含有 val 的 E 元素

# 伪类(Pseudo Class)

选择器 含义
E:first-child 匹配父元素的第一个子元素
E:link 匹配所有未被点击的链接
E:visited 匹配所有已被点击的链接
E:active 匹配所有已被鼠标按下但未释放的 E 元素
E:hover 匹配所有鼠标悬停其上的 E 元素
E:focus 匹配所有获得焦点的 E 元素
E:lang(c) 匹配 lang 属性等于 c 的 E 元素
E:enabled 匹配表单中激活的元素
E:disabled 匹配表单中禁用的元素
E:checked 匹配表单中被选中的radio(单选框)或checkbox(复选框)元素
E::selection 匹配用户当前选中的元素
E:root 匹配文档的根元素,对于HTML文档,就是HTML元素
E:nth-child(n) 匹配其父元素的第n个子元素,第一个编号为1
E:nth-last-child(n) 匹配其父元素的倒数第n个子元素,第一个编号为1
E:nth-of-type(n) 与:nth-child()作用类似,但是仅匹配使用同种标签的元素
E:nth-last-of-type(n) 与:nth-last-child() 作用类似,但是仅匹配使用同种标签的元素
E:last-child 匹配父元素的最后一个子元素,等同于:nth-last-child(1)
E:first-of-type 匹配父元素下使用同种标签的第一个子元素,等同于:nth-of-type(1)
E:last-of-type 匹配父元素下使用同种标签的最后一个子元素,等同于:nth-last-of-type(1)
E:only-child 匹配父元素下仅有的一个子元素,等同于:first-child:last-child或 :nth-child(1):nth-last-child(1)
E:only-of-type 匹配父元素下使用同种标签的唯一一个子元素,等同于:first-of-type:last-of-type或 :nth-of-type(1):nth-last-of-type(1)
E:empty 匹配一个不包含任何子元素的元素,注意,文本节点也被看作子元素
E:target 匹配文档中特定"id"点击后的效果
E:not(s) 匹配不符合当前选择器的任何元素

# 伪元素(Pseudo Element)

选择器 含义
E::before 在E元素之前插入生成的内容
E::after 在E元素之前插入生成的内容
E::first-letter 匹配E元素的第一个字母
E::first-line 匹配E元素的第一行

# 选择器优先级

当同一个元素有多个声明的时候,优先级才会有意义。因为每一个直接作用于元素的CSS规则总是会接管/覆盖(take over)该元素从祖先元素继承而来的规则

内联样式 > ID选择器 > (类选择器,属性选择器,伪类选择器) > (标签,伪元素)

通配选择符(universal selector)(*), 关系选择符(combinators) (+, >, ~, ' ') 和 否定伪类(negation pseudo-class)(:not()) 对优先级没有影响。(但是,在 :not() 内部声明的选择器是会影响优先级)

同权重的采用 就近原则后声明覆盖 的原则

当在一个样式声明中使用一个 !important 规则时,此声明将覆盖任何其他声明

权重:

选择器 权值
内联样式 1000
ID 选择器 100
属性选择器、class 或者伪类 10
标签名、伪元素 1

测试:

 *                           ====> 0
 li                          ====> 1(一个元素)
 li:first-line               ====> 2(一个元素,一个伪元素)
 ul li                       ====> 2(两个元素)
 ul ol+li                    ====> 3(三个元素)
 h1+ *[rel=up]               ====> 11(一个属性选择器,一个元素)
 ul ol li.red                ====> 13(一个类,三个元素)
 li.red.level                ====> 21(两个类,一个元素)
 style=""                    ====> 1000(一个行内样式)
 p                           ====> 1(一个元素)
 div p                       ====> 2(两个元素)
 .sith                       ====> 10(一个类)
 div p.sith                  ====> 12(一个类,两个元素)
 #sith                       ====> 100(一个ID选择器)
 body #darkside .sith p      ====> 112(1+100+10+1,一个Id选择器,一个类,两个元素)

参考

https://www.w3cplus.com/css/css-specificity-things-you-should-know.html

https://developer.mozilla.org/zh-CN/docs/Web/CSS/Specificity

JavaScript 事件

# DOM 事件流

DOM2 级事件 规定事件流包括三个阶段
1. 事件捕获阶段
2. 处于目标阶段
3. 事件冒泡阶段

事件流

即时 “DOM2级事件” 规范明确要求捕获阶段不会涉及事件目标,但很多浏览器都会在捕获阶段触发事件对象上的事件。结果就是有两个机会在目标对象上面操作事件。

# DOM0 级事件处理程序

  1. 每个元素(包括 window 和 document)都有自己的事件处理程序属性,这些属性全部小写
  2. DOM0 级没有事件捕获阶段
var btn = document.getElementById("myBtn");
btn.onclick = function() {
  console.log(this.id);  //  myBtn
}

// 将属性设置为 null 可删除事件处理程序
btn.onclick = null;

以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理

# DOM2 级事件处理程序

DOM2 级事件定义了两个方法,addEventListener() 和 removeEventListener()。所有 DOM 节点都包含这两个方法,并且它们接收 3 个参数(事件名,事件处理函数,布尔值),若布尔值为 true ,表示在捕获阶段调用事件处理函数;false,表示在冒泡阶段调用事件处理程序

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function() {
  console.log(this.id);
}, false);

// removeEventListener 无法移除匿名函数
// 像下面这样才有效
var btn = document.getElementById("myBtn");
var handler = function() {
  console.log(this.id);
}
btn.addEventListener("click",handler , false);

btn.removeEventListener("click", handler, false);  //  有效

# IE 事件处理程序

当使用 attachEvent() 时,事件处理方法会在全局作用域运行,因此 this 等于 window

var btn = document.getElementById("myBtn");
var handler = function() {
  console.log(this === window); // true
  console.log("Hello World!");
}
btn.attachEvent("onclick",handler , false);

btn.detachEvent("onclick", handler, false);

# DOM 中的事件对象

event 对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。不过,所有事件都会有下列成员:

属性/方法 类型 说明
bubbles Boolean 表明事件是否冒泡
currentTarget Element 其事件处理程序当前正在处理事件的那个元素,和回调函数的 this 指向相同
target Element 事件的目标
stopPropagation() Function 取消事件的进一步捕获或冒泡。如果 bubbles 为 true 可使用此方法
stopImmediatePropagation() Function 取消事件的进一步捕获或冒泡。同时阻止任何事件处理程序被调用(DOM3 级事件中新增)
type String 被触发的事件的类型
view Abstractiview 与事件关联的抽象视图。等同于发生事件的 window 对象
cancelable Boolean 表示是否可取消事件的默认行为
preventDefault() Function 取消事件的默认行为。如果 cancelable 为 true 可使用此方法
defaultPrevented Boolean 为 true 表示已经调用了 preventDefault(),(DOM3 新增)
detail Integer 与事件相关的细节信息
eventPhase Integer 调用事件处理程序的阶段:1表示捕获阶段,2表示处于目标,3表示冒泡阶段
trusted Boolean 为 true 表示事件是浏览器生成,为 false 表示事件是由开发人员通过 JS 创建的(DOM3 事件中新增)

# IE 中的事件对象

属性/方法 类型 读/写 说明
cancelBubble Boolean 读/写 默认值为 false,为 true 可以取消事件冒泡
returnValue Boolean 读/写 默认值为 true,设置 false 可取消事件的默认行为
srcElement Element 只读 事件的目标
type String 只读 被触发的事件的类型

# 事件委托

事件委托就是利用了冒泡,集中处理同类型的事件

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(event){
  var targetId = event.target.id;
  switch(targetId) {
    /* 根据不同的 target 做不同的处理 */
  }
}, false);

跨域数据通信

如果两个源的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源

# CORS

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)
它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制

简单请求

同时满足下列条件,属于简单请求

1. 请求方法:
HEAD
GET
POST

2. HTTP 首部不超过以下字段:
Accept
Accept-Language
Content-Language
Content-Type (application/x-www-form-urlencoded、multipart/form-data、text/plain)

3. 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问

4. 请求中没有使用 ReadableStream 对象

请求头
浏览器发现这次跨源 AJAX 请求是简单请求,就自动在头信息之中,添加一个 Origin 字段

当,http://polluxlee.com 请求 http://aymaxli.com,`Origin` 字段被设为 http://polluxlee.com

响应头
Access-Control-Allow-Origin: <origin> | *

该字段为必须的,只能是 Origin 的值,或是通配符 *
表示允许访问服务器的源,通配符表示不做限制

需要注意的是,如果要发送 Cookie,Access-Control-Allow-Origin 就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie 依然遵循同源政策,只有用服务器域名设置的 Cookie 才会上传,其他域名的 Cookie 并不会上传,且(跨源)原网页代码中的 document.cookie 也无法读取服务器域名下的 Cookie

Access-Control-Allow-Credentials: true | false
设置为 true 时表示允许发送 cookie

// xhr 需要将 withCredentials 设置为 true ,浏览器才会发送 cookie
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

CORS 请求时,XMLHttpRequest 对象的 getResponseHeader() 方法只能拿到6个基本字段:
Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
如果想拿到其他字段,就必须在 Access-Control-Expose-Headers 里面指定

预检请求

当请求满足下述任一条件时,即应首先发送预检请求

1. 请求方法:
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH

2. 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段,集合为:
Accept
Accept-Language
Content-Language
Content-Type (application/x-www-form-urlencoded、multipart/form-data、text/plain)

3. 请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器

4. 请求中使用了ReadableStream对象

“需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响

OPTIONS 请求头

// 告知服务器,实际请求将使用什么方法
Access-Control-Request-Method: POST | ...

// 告知服务器,实际请求将携带哪些自定义请求首部字段,服务器据此决定,该实际请求是否被允许
Access-Control-Request-Headers: X-CUSTOM, Content-Type ...

OPTIONS 响应头

// 允许访问的源
Access-Control-Allow-Origin: http://foo.example

// 允许使用的方法
Access-Control-Allow-Methods: POST, GET, OPTIONS

// 允许使用的自定义头
Access-Control-Allow-Headers: X-CUSTOM, Content-Type

// 表明该响应的有效时间为多少秒。在有效时间内,浏览器无须为同一请求再次发起预检请求
Access-Control-Max-Age: 86400

预检请求完成之后,发送实际请求

# 图像 Ping

i. 加载图像不会产生跨域
ii. 只能发送 GET 请求
iii. 无法访问服务器的响应文本
用处:在线广告跟踪浏览量

var img = new Image();
img.onload = img.onerror = function() {
  alert("Done!");
};
img.src = "http://www.example.com/test?name=polluxlee"

# JSONP

script 标签不受浏览器跨域限制,通过 GET 请求,向后端发送请求,后端通过返回形如,handleResponse(data) 的 JavaScript 字符串给前端,前端通过 script 标签解析执行

i. 只能发送 GET 请求
ii. 通过 script 的 onerror 处理请求失败
iii. 需要自己实现计时器检测指定时间是否收到响应

function handleResponse(res) {
  console.log(res)
}

var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);

# Web Sockets

提供全双工、双向通信,但浏览器同源策略对 Web Sockets 不适用

# document.domain + iframe

只能用于主域相同,子域不同的两个页面之间的跨域请求
比如 foo.com 和 img.foo.com 之间

只要把两个页面的 document.domain 都指向主域就可以了,比如document.domain='foo.com'

<!-- foo.com/a.html -->
<iframe id="ifr" src="http://img.foo.com/b.html"></iframe>
<script>
document.domain = 'foo.com';
function aa(str) {
  console.log(str);
}
window.onload = function () {
  document.querySelector('#ifr').contentWindow.bb('aaa');
}
</script>
<!-- img.foo.com/b.html -->
<script>
document.domain = 'foo.com';
function bb(str) {
  console.log(str);
}

parent.aa('bbb');
</script>

# window.name + iframe

iframe: iframe 的 src 属性可加载不同域中的页面
window.name: 页面如果设置了 window.name,那么在不关闭页面的情况下,即使进行了页面跳转location.href=...,这个 window.name 还是会保留

准备 3 个页面,
a.com/index.html
a.com/empty.html
b.com/index.html

  • a.com/index.html 页面中嵌入一个 iframe,设置 srcb.com/index.html
  • b.com/index.html 载入后,设置 window.name,然后再使用location.href='a.com/empty.html' 跳转到与 iframe 外页面同域的页面中
  • a.com/index.html 页面中,就可以通过$('iframe').contentWindow.name 来获取 iframe 内页面a.com/empty.htmlwindow.name 值了,而这个值正是b.com/index.html 设置的

# location.hash + iframe

domain1.com/a.html

<iframe id="iframe" src="http://www.domain2.com/b.html" 
  style="display:none;"></iframe>
<script>
  iframe.src = iframe.src + '#user=admin';
</script>

domain2.com/b.html

<script>
    // 监听 a.html 传来的 hash 值
    window.onhashchange = function () {
        iframe.src = iframe.src + location.hash;
    };
</script>

# window.postMessage

HTML5 新增方法,现在浏览器及 IE8+ 支持

otherWindow.postMessage(message, targetOrigin, [transfer]);

message: 需要传递的信息
targetOrigin: 目标窗口,即允许接收该信息的窗口

参考

http://www.ruanyifeng.com/blog/2016/04/cors.html
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
https://www.w3.org/TR/cors/

JavaScript 数组去重

1)

var array = [1, 2, 1, 1, '1'];

function unique(array) {
    var obj = {};
    return array.filter(function(item, index, array){
        return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
    })
}

console.log(unique(array)); // [1, 2, "1"]

2)ES6

var array = [1, 2, 1, 1, '1'];

function unique(array) {
   return Array.from(new Set(array));
}

console.log(unique(array)); // [1, 2, "1"]

JavaScript 变量提升与函数提升

# 变量声明提升

  1. 变量提升会把变量声明提升到全局作用域或函数作用域顶端
  2. 变量提升只提升声明,而不提升初始化

1)

var x = 1;                 // 声明 + 初始化 x
console.log(x + " " + y);  // '1 undefined'
var y = 2;                 // 声明 + 初始化 y

//上面的代码和下面的代码是一样的 
var x = 1;                 // 声明 + 初始化 x
var y;                     //声明 y
console.log(x + " " + y);  //y 是未定义的
y = 2;                     // 初始化  y 

变量提升只提升声明,而不提升初始化。y 变量的初始化是在 log 之后,所以 log 会打印 undefined

2)

var foo = 3;
function bar() {
    var foo = foo || 5;
    console.log(foo); // 5
}
bar();

// 相当于
var foo = 3;
function bar() {
    var foo;
    foo = foo || 5;
    console.log(foo);  // 5
}
bar();

foo 被提升到函数作用域顶端,foo || 5 得到的结果就是 5,而屏蔽了 global 中的 foo

# 函数声明提升

  1. 函数提升仅适用于函数声明,而不适用于函数表达式,即,形如,function foo() {} 才可以,而 var foo = function() {} 不能被提升
  2. 函数提升是为了解决两个函数相互递归调用的问题

1)

console.log(square(5));  //  25
/* ... */
function square(n) { return n*n }

2)

function bar() {
    foo(); // 2
    var foo = function() {
        console.log(1);
    };
    foo(); // 1
    function foo() {
        console.log(2);
    }
    foo(); // 1
}
bar();

//  相当于
function bar() {
    var foo;
    function foo() {
        console.log(2);
    }
    foo(); // 2
    foo = function() {
        console.log(1);
    };
    foo(); // 1
    foo(); // 1
}
bar();

# let 和 const 关键字

在 ES6 之前,只有全局作用域与函数作用域,而没有块级作用域。在 ES6 中,let 和 const 关键字为 JavaScript 增加了块级作用域

1)

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

f1();

2)

function f1() {
  const MAX = 6;
  if (true) {
    const MAX = 10;
  }
  console.log(MAX); // 6
}

f1();

3)不会变量提升

function do_something() {
  console.log(bar); // undefined
  console.log(foo); // ReferenceError: foo is not defined
  var bar = 1;
  let foo = 2;
}

4)暂时性死区

由于词法作用域,表达式(foo + 55)内的标识符“foo”会解析为if块的foo,而不是覆盖值为33的foo。在这一行中,if块的“foo”已经在词法环境中创建,但尚未达到(并终止)其初始化(这是语句本身的一部分):它仍处于暂存死区

function test(){
   var foo = 33;
   if (true) {
      let foo = (foo + 55); // ReferenceError
   }
}
test();

cookie,sessionStorage 和 localStorage

cookie

HTTP Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上

生命周期: 一般由服务器生成,可设置失效时间
大小: 4KB 左右
与服务端通信: 携带在 HTTP 头中
易用性: 需要程序员自己封装,原生的 Cookie 接口不友好
Response header 中的 Set-Cookie

作用

  1. 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  2. 个性化设置(如用户自定义设置、主题等)
  3. 浏览器行为跟踪(如跟踪分析用户行为等)

服务端设置 cookie

cookie 中的键和值都要经过 URL 编码,服务端可设置以下字段:

<cookie-name>=<cookie-value>  //  cookie name
Expires=<date>   //  设置过期时间,若不设置,则浏览器关闭后就失效
Max-Age=<non-zero-digit>  //  优先级比 Expires,表示,在 cookie 失效之前需要经过的秒数
Domain=<domain-value> //  cookie 可以送达的主机名,若没指定,则表示当前文档访问地址中的主机部分(但是不包含子域名)
Path=<path-value>  //  指定一个 URL 路径,这个路径必须出现在要请求的资源的路径中才可以发送 Cookie 首部
Secure  //  只有在请求使用SSL和HTTPS协议的时候才会被发送到服务器
HttpOnly //  设置了 HttpOnly 属性的 cookie ,不能使用 JavaScript 经由  Document.cookie 属性、XMLHttpRequest 和  Request APIs 进行访问,以防范跨站脚本攻击(XSS)

客户端设置 cookie

客户端无法设置 HttpOnly,需要注意 document.cookie 只能设置一个 cookie

document.cookie="age=12; expires=Thu, 26 Feb 2116 11:50:25 GMT; domain=sankuai.com; path=/";

更新(增加或修改键值对)或删除(设置 Expires 为过去时间)必须 cookie 的 domain、path 要与原来保持一致

Web Storage API

sessionStorage

为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开状态,包括页面重新加载和恢复)

生命周期: 仅在当前会话下有效,关闭页面或浏览器后被清除
大小: 5MB 左右
与服务端通信: 仅在客户端(即浏览器)中保存,不参与和服务器的通信
易用性: 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持

localStorage

localStorage 同样的功能,但是在浏览器关闭,然后重新打开后数据仍然存在

生命周期: 除非被清除,否则永久保存
大小: 5MB 左右
与服务端通信: 仅在客户端(即浏览器)中保存,不参与和服务器的通信
易用性: 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持

StorageEvent

通过监听 StorageEvent 可实现不同页面间数据通信
在同一个页面内发生的改变不会起作用——在相同域名下的其他页面(如一个新标签或 iframe)发生的改变才会起作用。在其他域名下的页面不能访问相同的 Storage 对象

window.addEventListener('storage', function(e) {  
  document.querySelector('.my-key').textContent = e.key;
  document.querySelector('.my-old').textContent = e.oldValue;
  document.querySelector('.my-new').textContent = e.newValue;
  document.querySelector('.my-url').textContent = e.url;
  document.querySelector('.my-storage').textContent = e.storageArea;
});

引申

IndexedDB 可存储更大量的数据,例如文件
IndexedDB

References

详说 Cookie, LocalStorage 与 SessionStorage
使用 Web Storage API

从输入URL到页面加载发生了什么?

从输入URL到页面加载发生了什么?

  1. DNS 解析
  2. TCP 连接
  3. HTTP 请求
  4. 服务器处理请求并返回 HTTP 报文
  5. 浏览器解析渲染页面
  6. 连接结束

DNS 解析

以解析 www.google.com 为例

  1. 首先在本地域名服务器查询 IP 地址
  2. 如果没找到,本地域名服务器会向根域名服务器发送一个请求
  3. 如果没找到,. 根域名服务器会让本地服务器去 com 顶级域名服务器找,本地域名服务器会向 com 顶级域名服务器发送一个请求
  4. 如果没找到,com 域名服务器会让本地服务器去 google.com 域名服务器找,本地域名服务器会向 google.com 域名服务器发送一个请求
  5. 最后找到了,www.google.com 的 IP 地址返回给本地域名服务器
  6. 本地域名服务器将 www.google.com 的 IP 地址缓存到本地,供下次查询使用

最终解析过程如下

. -> .com -> google.com. -> www.google.com. -> IP 地址

DNS 缓存有

浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存

TCP 连接

TCP 三次握手建立连接 #61

HTTP 请求

HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成

服务器处理请求并返回 HTTP 报文

HTTP响应报文也是由三部分组成: 状态码, 响应报头和响应报文

浏览器解析渲染页面

  • 处理 HTML 标记并构建 DOM 树
  • 处理 CSS 标记并构建 CSSOM 树
  • 将 DOM 与 CSSOM 合并成一个渲染树
  • 根据渲染树来布局,以计算每个节点的几何信息
  • 将各个节点绘制到屏幕上

回流和重绘
https://juejin.im/post/5a9923e9518825558251c96a

Event Loop

连接结束

参考

https://segmentfault.com/a/1190000006879700#articleHeader0

Polyfill of 「new」

# new

形如,new Constructor(),new 然后紧接着一个构造函数可以创建一个新的对象,1)对象中包含构造函数中定义的实例属性,2)并且新对象的 __proto__ 指向构造函数的原型对象 prototype

// 构造函数
function func(name, age) {
  this.name = name;
  this.age = age;
}

func.prototype.showAge = function () {
  console.log(this.age);
}

// 测试一下
var obj = new func("April", 21);
console.log(obj.name);  //  April
obj.showAge();  //  21

# Polyfill

function objectFactory(func) {
  var obj = {};
  if (typeof func !== "function") return obj;
  var ret = func.apply(obj, [].slice.call(arguments, 1));

  // 考虑构造函数有返回值的情况
  // 当返回的是引用类型的时候,会覆盖 new 创建的对象
  if (typeof ret === "function" || typeof ret === "object") {
    return ret;
  }
  obj.__proto__ = func.prototype;
  return obj;
}

function func(name, age) {
  this.name = name;
  this.age = age;
}

func.prototype.showAge = function () {
  console.log(this.age);
}

# Test

// 测试一下
var obj = objectFactory(func, "April", 21);
console.log(obj.name);  //  April
obj.showAge();  //  21

# Reference

mqyqingfeng/Blog#13

.

  1. HTTP 全过程
  2. HTTP 缓存
  3. Angular vue jq 对比
  4. fis3、glup、webpack
  5. CSS 基本布局
  6. 项目亮点总结
  7. Angular 生命周期,MVVM,数据检查
  8. 排序算法

JavaScript This 用法

this

this 返回一个对象(属性或方法“当前”所在的对象),函数中的 this 指向调用这个函数的对象

var a = {
  name: 'a',
  say: function() {
    console.log(this.name)
  }
}
var name = 'global'

a.say();  // a
(a.say = a.say)();  // global
(false || a.say)();  // global
(a.say)(); // a

new 绑定

用 new 关键字调用构造函数可以创建一个新的对象(object),this 就指向这个新的对象

//将this绑定到一个对象上
function Cat() {
  this.name = 'kitty';
}

let cat = new Cat();
console.log(cat.name);  // kitty

若显式返回一个值,则 new 的返回值会被覆盖

function Cat() {
  this.name = 'kitty';
  return {
    name: 'kitten'
  }
}

let cat = new Cat();
console.log(cat.name);  // kitten

apply()、call() 和 bind() 方法

一个函数在其正文中使用 this 关键字,它的值可以调用 apply,call 和 bind 方法绑定到调用中的一个特定对象,所有这些函数都是继承自Function.prototype的所有函数

var name = "PolluxLee";
var a = {   
    name : "AymaxLi"
};
function foo(arg) {
    console.log(this.name + arg);
}
foo.apply(a, [" is coding."]);
foo.call(a, " is coding.");
var foo1 = foo.bind(a, " is coding.");
foo1();

apply 和 call 的功能相同,但是传递参数的方式不同,它们第一个参数都是指定调用的对象,apply 的第二个参数是数组,call 的第二个参数是不定参数,即可传递多个

箭头函数

箭头函数看上去是匿名函数的一种简写,但实际上,箭头函数和匿名函数有个明显的区别:箭头函数内部的this是词法作用域,由上下文确定。即,箭头函数声明时,this 是它所在的作用域的 this

var obj = {
    birth: 1996,
    getAge: function () {
        var b = this.birth; // 1996
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj对象
        return fn();
    }
};
obj.getAge(); // 21

ES6 Export & Import

ES6 Export & Import

Import 命令会被 JavaScript 引擎静态分析(必须在代码的顶层,比如在 if, for, 或 function 里会报错),先于模块内的其他语句执行(具有提升效果)

Export

1. 因为要规定对外的接口,所以要加花括号

export var m = 1
export function f() {}

/*--------------div--------------*/

// or
var m = 1
export {m}
function f() {}
export {f}

/*--------------div--------------*/

var a = 1
var b = 2
export {a, b}

2. 重命名

var m = 1
export {m as n}

3. export default命令,为模块指定默认输出
一个模块只能有一个默认输出

export default function foo() {
  console.log('foo');
}

// or
function foo() {
  console.log('foo');
}
export default foo;

/*--------------div--------------*/

// right
var a = 1;
export default a;

// wrong
export default var a = 1;

Import

1. 导入 default 变量不需要加花括号

// a.js
var str = 'hello'
export default str

// b.js
// import 命令可以为 default 变量指定任意名字
import hello from './a'
console.log(hello)

2. 除了 default 之外的导入都需要加花括号

// a.js
var m = 1
var n = 2
export {m, n}
function f() { console.log('hello') }
export {f}

// b.js
import {m, n as num, f} from './a'
console.log(m, num)
f()

ES6 模块与 CommonJS 模块的差异

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口

CommonJS

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
}

// main.js
var mod = require('./lib');

console.log(mod.counter);  // 3
mod.incCounter();
console.log(mod.counter); // 3

ES6

// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}

// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4

CSS loading

Loading

twinkle.html

<div class="loading">
  <div class="loading__content">
    <div class="loading__content--circle circle1"></div>
    <div class="loading__content--circle circle2"></div>
    <div class="loading__content--circle circle3"></div>
  </div>
</div>

twinkle.scss

$orange: #D35400;
$circle-width: 16px;
$circle-height: 16px;

.loading {
  background-color: $orange;
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  &__content {
    letter-spacing: 5px;
    &--circle {
      width: $circle-width;
      height: $circle-height;
      background-color: white;
      display: inline-block;
      border-radius: 50%;
      animation: twinkle 1.2s infinite ease-in-out;
      -webkit-animation: twinkle 1.2s infinite ease-in-out;
    }
  }
}

.circle1 { animation-delay: 0s; -webkit-animation-delay: 0s; }
.circle2 { animation-delay: 0.1s; -webkit-animation-delay: 0.1s; }
.circle3 { animation-delay: 0.2s; -webkit-animation-delay: 0.2s; }

@keyframes twinkle {
  0%,40%,100% {
    transform: scale(1); 
    -webkit-transform: scale(1);
  }
  20% {
    transform: scale(0); 
    -webkit-transform: scale(0);
  } 
}

Full Demo

Demo

JavaScript 面向对象

# 创建对象

0. 字面量或内置构造函数

优点:简单
缺点:无法复用

var obj = {
  name: "Kitty",
  age: 22
}

var obj1 = new Object()
obj1.name = "Kitty";
obj1.age = 22

1. 工厂模式

工厂模式抽象了创建具体对象的过程
优点:复用性高,能创建多个相似对象
缺点:因为都指向同一个原型,所以不能识别对象类型

function createCat(name, sex) {
  var o  = new Object();
  o.name = name;
  o.sex = sex;
  o.sayName = function() {
    console.log(this.name);
  }
  return o;
}

var cat1 = createCat("Tom", "b");
var cat2 = createCat("kate", "g");

2. 构造函数模式

优点:复用性高,可识别对象的类型
缺点:每个方法都要在每个实例上创建一遍,实例之间不能共享方法和属性

function Cat(name, sex) {
  this.name = name;
  this.sex = sex;
  this.sayName = function() {
    console.log(this.name);
  }
}

// new 关键字将 this 指针绑定到对象实例
var cat1 = new Cat("Tom", "b");
var cat2 = new Cat("kate", "g");

// 直接调用构造函数,this 就指向 global,一般情况是 window 对象
Cat("kitty", "g");

Polyfill of 「new」#50

3. 原型模式

无论何时,只要创建了一个新函数,就会为函数创建一个 prototype 属性,这个属性指向函数的原型对象,同时原型对象会获得一个 constructor 属性,指向 prototype 所在的函数
优点:对象实例可共享原型的属性和方法

function Cat() {}
Cat.prototype.name = "Tom";
Cat.prototype.sayName = function() {
  alert(this.name);
}
var cat1 = new Cat();
cat1.sayName();  //  "Tom"
var cat2 = new Cat();
cat2.sayName();  //  "Tom"
cat1.sayName === cat2.sayName  //  "true"

4. 组合构造函数模式和原型模式

优点:构造函数用于定义实例属性,而原型模式用于定义方法和共享的属性。每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存

function Cat(name) {
  this.name = name
}
Cat.prototype.sayName = function() {
  console.log("My name is " + this.name)
}

var cat1 = new Cat("Tom");
cat1.sayName();  //  "My name is Tom"
var cat2 = new Cat("Kate");
cat2.sayName();  //  "My name is Kate"

5. 动态原型模式

特点:通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型

function Person(name, age, job) {
  this.name = name;
  this.age = age;
  this.job = job;
  if (typeof this.sayName !== "function") {
    Person.prototype.sayName = function() {
      console.log(this.name);
    };
  }
}

var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();

6. 寄生构造函数模式

构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数末尾添加一个 return 语句,可以重写调用构造函数时返回的值

function Person(name, age, job) {
  var o = new Object();
  o.name = name;
  o.age = age;
  o.job = job;
  o.sayName = function() {
    console.log(this.name)
  }
  return o;
}

var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();

7. 稳妥构造函数模式

friend 对象中保存的是一个稳妥对象,除了调用 sayName() 方法外,没有别的方式可以访问其数据成员

function Person(name, age, job) {
  var o = new Object();
  o.sayName = function() {
    console.log(name);
  }
  return o;
}

var friend = Person("Nicholas", 29, "Software Engineer");
friend.sayName();  //  "Nicholas"

8. class 语法糖创建对象

  • typeof (class Cat{}) === "function"
  • 类的所有方法都定义在类的 prototype 属性上面
  • 实例属性需要显示地定义在 this 对象上
//定义类
class Point {

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }

}

var point = new Point(2, 3);

point.toString() // (2, 3)

point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true

# 继承

1. 原型链

function SuperType() {
  this.property = true;
}

SuperType.prototype.getSuperValue = function() {
  return this.property;
}

function SubType() {
  this.subproperty = false;
}

// 通过 new 关键字
//   1)继承了实例属性
//   2)使得 SubType.prototype.__proto__ === SuperType.prototype,即继承了原型属性和方法
// 即 SuperType 是 SubType.prototype 的构造函数,SuperType.prototype 是 SubType.prototype 的原型
// 而 SubType.prototype 就是 SuperType 的实例了
SubType.prototype = new SuperType();

SubType.prototype.getSubValue = function() {
  return this.subproperty;
}

// instance 的 constructor 指向 SuperType,因为 SubType 的 prototype 被重写了
var instance = new SubType();
console.log(instance.getSubValue())  //  false
console.log(instance.getSuperValue());  //  true

原型链问题
但要注意的是,若原型中包含引用类型值时,那么这种引用类型的属性会被所有实例共享

function SuperType() {
  this.colors = ["red", "blue", "green"];
}

function SubType() {
}

SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
console.log(instance1.colors);   // ["red", "blue", "green", "black"]

var instance2 = new SubType();
console.log(instance2.colors);  // ["red", "blue", "green", "black"];

2. 借用构造函数

  1. 借用构造函数可解决原型链中,原型包含引用类型值的问题
  2. 可这跟构造函数面对的问题一样,方法都在构造函数中定义,就无法复用了
function SuperType() {
  this.colors = ["red", "blue", "green"];
}

function SubType() {
  SuperType.call(this);
}

var ins1 = new SubType();
ins1.colors.push("black");
console.log(ins1.colors);  // ["red", "blue", "green", "black"]

var ins2 = new SubType();
console.log(ins2.colors);  // ["red", "blue", "green"]

3. 组合继承

  1. 将原型链和借用构造函数结合
  2. 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
    缺点:在原型中存在多余(被屏蔽的)属性
function SuperType(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function() {
  console.log(this.name)
}

function SubType(name, age) {
  // 继承属性
  SuperType.call(this, name);
  this.age = age;
}

// 继承方法
SubType.prototype = new SuperType();

SubType.prototype.sayAge = function() {
  console.log(this.age);
}

var ins1 = new SubType("Nicholas", 29); // ["red", "blue", "green", "black"]
ins1.colors.push("black"); // Nicholas
console.log(ins1.colors); // 29
ins1.sayName();
ins1.sayAge();

var ins2 = new SubType("Greg", 27); // ["red", "blue", "green"]
console.log(ins2.colors); // Greg
ins2.sayName(); // 27
ins2.sayAge();

4. 原型式继承

  1. 以一个对象为基础,创建对象
  2. 新对象的 [[prototype]] 指向基础对象,即,新对象的原型是基础对象

通过传入的基础对象创建新对象

function object(o) {
  function F() {};
  F.prototype = o;
  return new F();
}

要注意的是,引用类型值会被所有实例共享
Object.create() 与 object() 方法的行为相同

var person = {
  name: "Nicholas",
  friends: ["Shelby", "Court", "Van"];
}

var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

console.log(person.friends);  // ["Shelby", "Court", "Van", "Rob", "Barbie"]

5. 寄生式继承

寄生式继承是在原型式继承的基础上增强

function createAnother(original) {
  function F() {};
  F.prototype = o;
  var clone = new F();  //  创建一个新对象
  clone.sayHi = function() {  //  以某种方式增强这个对象
    console.log("hi");
  }
  return clone;  //  返回这个对象
}

6. 寄生组合式继承

组合继承在调用 SubType 里的借用构造函数和初始化 prototype 时都调用 SuperType 构造函数,一共调用了 2 次,从而使得 SubType.prototype 中存在不必要、多余的属性。寄生组合式继承利用原型式继承中创建超类原型副本的方法,避免了这个问题

function object(o) {
  function F() {};
  F.prototype = o;
  return new F();
}

function inheritPrototype(subType, superType) {
  var prototype = object(superType.prototype);  // 以 superType.prototype 为原型创建对象
  prototype.constructor = subType;  // 增强对象
  subType.prototype = prototype;  // 指定对象
}

function SuperType(name) {
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function() {
  console.log(this.name)
};

function SubType(name, age) {
  SuperType.call(this, name);
  this.age = age;
}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function() {
  console.log(this.age);
};

使用 Object.create 方法:

// 创建超类型原型的对象副本
function object(o) {
  function F() { };
  F.prototype = o;
  return new F();
}

const inheritPrototype = (function () {
  let createObject = Object.create || object;
  return function (Son, Father) {
    let prototype = createObject(Father.prototype);
    prototype.constructor = Son;
    Son.prototype = prototype;
  }
})()

function Father(name) {
  this.name = name;
}

Father.prototype.showName = function () {
  console.log(this.name);
}

function Son(name, age) {
  Father.call(this, name);
  this.age = age;
}

inheritPrototype(Son, Father);
Son.prototype.showAge = function () {
  console.log(this.age)
}

let ins = new Son("kitty", 21);
console.log("实例属性", ins.name, ins.age);
ins.showName();
ins.showAge();

7. class 的继承

ES5 的继承,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面(Parent.apply(this))

ES6 的继承机制完全不同,实质是,先将父类实例对象的属性和方法加到 this 上面(所以必须先调用 super方法),然后再用子类的构造函数修改 this

class A {}
class B extends A {
  constructor() {
    super();
  }
}

1) 两条继承链

类(即构造函数)同时有 prototype 属性和 __proto__ 属性

  • 子类的 __proto__ 属性,指向父类

  • 子类的 prototype 属性的 __proto__ 属性,指向父类的 prototype 属性

B 继承 A 的静态属性
作为一个对象,子类(B)的原型(__proto__属性)是父类(A)

B.__proto__ === A  //  true

B 的实例继承 A 的实例
作为一个构造函数,子类(B)的原型对象(prototype 属性)的原型是父类的原型对象(prototype属性)

B.prototype.__proto__ === A.prototype  //  true

2) super() 和 super 区别

i. super 作为函数调用时,代表父类的构造函数

super 内部的 this 指的是 B,因此 super() 在这里相当于 A.prototype.constructor.call(this)

class A {
  constructor() {
    console.log(new.target.name);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B

ii. super 作为对象时,在普通方法中,指向父类的原型对象

子类普通方法中通过 super 调用父类的方法时,方法内部的 this 指向当前的子类实例

class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();
  }
}

let b = new B();
b.m() // 2

iii. super 作为对象时,在静态方法中,指向父类

在子类的静态方法中通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例

class A {
  constructor() {
    this.x = 1;
  }
  static print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  static m() {
    super.print();
  }
}

B.x = 3;
B.m() // 3

# 对象相关

1. 属性类型

ES5 定义只有内部才用的特性,描述了属性(property)的各种特征

数据属性

  • [[Configurable]]: 表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性
  • [[Enumerable]]: 表示能否通过 for-in 循环返回属性
  • [[Writable]]: 表示能否修改属性的值
  • [[Value]]: 包含这个属性的数据值

访问器属性

  • [[Configurable]]: 表示能否通过 delete 删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性
  • [[Enumerable]]: 表示能否通过 for-in 循环返回属性
  • [[Get]]: 在读取属性时调用的函数
  • [[Set]]: 在写入属性时调用的函数

2. 相关方法

i) Object.defineProperty()

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。像上面提到的内部属性,只能通过这个函数修改

var person = {};
Object.defineProperty(person, "name", {
  writable: false,
  value: "Nicholas"
});
console.log(person.name);  //  Nicholas
person.name = "Greg";
console.log(person.name);  //  Nicholas

ii) Object.getPrototypeOf()

Object.getPrototypeOf() 方法返回指定对象的原型(内部 [[Prototype]] 属性的值),相当于对象的 __proto__ 属性

var proto = {};
var obj = Object.create(proto);
Object.getPrototypeOf(obj) === proto; // true

var reg = /a/;
Object.getPrototypeOf(reg) === RegExp.prototype; // true

iii) Object.getOwnPropertyNames() 实例 可枚举/不可枚举

Object.getOwnPropertyNames() 方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组

//不可枚举属性
var my_obj = Object.create({}, {
  getFoo: {
    value: function() { return this.foo; },
    enumerable: false
  }
});
my_obj.foo = 1;

console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"]

iv) Object.keys() 实例 可枚举
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组

// getFoo is a property which isn't enumerable
var myObj = Object.create({}, {
  getFoo: {
    value: function () { return this.foo; }
  } 
});
myObj.foo = 1;
console.log(Object.keys(myObj)); // console: ['foo']

v) in 或 for-in 实例/原型 可枚举

for...in语句以任意顺序遍历一个对象的可枚举属性

var triangle = {a: 1, b: 2, c: 3};

function ColoredTriangle() {
  this.color = 'red';
}

ColoredTriangle.prototype = triangle;

var obj = new ColoredTriangle();

for (var prop in obj) {
  console.log(`obj.${prop} = ${obj[prop]}`);
}

// 输出
// obj.color = red
// obj.a = 1
// obj.b = 2
// obj.c = 3

vi) Object.prototype.hasOwnProperty() 实例 可枚举/不可枚举

hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性

//不可枚举属性
var my_obj = Object.create({}, {
  getFoo: {
    value: function() { return this.foo; },
    enumerable: false
  }
});
my_obj.foo = 1;

console.log(my_obj.hasOwnProperty('getFoo')); // true

vii) Object.prototype.isPrototypeOf()

isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上

isPrototypeOf() 与 instanceof 运算符不同。在表达式 "object instanceof AFunction"中,object 的原型链是针对 AFunction.prototype 进行检查的,而不是针对 AFunction 本身

function Foo() {}
function Bar() {}
function Baz() {}

Bar.prototype = Object.create(Foo.prototype);
Baz.prototype = Object.create(Bar.prototype);

var baz = new Baz();

console.log(Baz.prototype.isPrototypeOf(baz)); // true
console.log(Bar.prototype.isPrototypeOf(baz)); // true
console.log(Foo.prototype.isPrototypeOf(baz)); // true
console.log(Object.prototype.isPrototypeOf(baz)); // true

viii) Object.setPrototypeOf()

**Object.setPrototypeOf() **方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或  null

警告: 由于现代 JavaScript 引擎优化属性访问所带来的特性的关系,更改对象的 [[Prototype]]各个浏览器和 JavaScript 引擎上都是一个很慢的操作。其在更改继承的性能上的影响是微妙而又广泛的,这不仅仅限于 obj.__proto__ = ...语句上的时间花费,而且可能会延伸到任何代码,那些可以访问任何[[Prototype]]已被更改的对象的代码。如果你关心性能,你应该避免设置一个对象的 [[Prototype]]。相反,你应该使用 Object.create()来创建带有你想要的[[Prototype]]的新对象

CSS 水平垂直居中

# flexbox + justify-content + align-items

  • 父元素和子元素都可不定宽高,也不用考虑子元素是块级元素或者行内元素,在兼容 flexbox 的情况下,我认为是最好的方案
  • 适用于:父子元素不定宽高、子元素是块级元素或行内元素
<style>
  .parent {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 200px;
    height: 200px;
    border: 2px solid black;
  }
  .child {
    width: 100px;
    height: 100px;
    background: green;
  }
</style>

<div class="parent">
  <div class="child"></div>
</div>

# absolute + transform

  • 如果不兼容 transform,就只能用 margin 去控制自身偏移,但子元素需要定宽高
  • 适用于:父子元素不定宽高、子元素是块级元素或行内元素
<style>
  .parent {
    position: relative;
    width: 200px;
    min-height: 200px;
    border: 2px solid black;
  }
  .child {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: green;
    color: white;
  }
</style>

<div class="parent">
  <span class="child">行内元素</span>
</div>

# 伪元素 + vertical-align + text-align

  • 设置 text-align: center 和 vertical-align: middle 对 inline-block 水平垂直居中
  • 因为伪元素设置了 height: 100% 所以需要父元素定高
  • 为了解决 inline-block 之间的空隙,设置了 word-spacing: -5px
  • 适用于:父元素定高、子元素为行内或行内块,可不定宽高
<style>
  .parent {
    width: 200px;
    height: 200px;
    border: 2px solid black;
    word-spacing: -5px;
    text-align: center;
  }
  .parent::before {
    content: "";
    display: inline-block;
    height: 100%;
    width: 0;
    vertical-align: middle;
    
  }
  .child {
    display: inline-block;
    vertical-align: middle;
    background: green;
    color: white;
  }
</style>

<div class="parent">
  <div class="child">你好</div>
</div>

# line-height + text-align

  • 父元素需要定高
  • 适用于:父元素定高、子元素为行内元素
<style>
  .parent {
    width: 200px;
    height: 200px;
    border: 2px solid black;
    text-align: center;
  }
  .child {
    background: green;
    color: white;
    line-height: 200px;
  }
</style>

<div class="parent">
  <span class="child">哈哈</span>
</div>

# table + vertical-align + text-align

  • 利用 table 对内容进行居中
  • 适用于:多行文本的水平垂直居中
<style>
  .parent {
    width: 200px;
    height: 200px;
    border: 2px solid black;
    display: table;
  }
  .child {
    display: table-cell;
    vertical-align: middle;
    text-align: center;
  }
</style>

<div class="parent">
  <div class="child">你好</div>
</div>

# grid + margin

  • grid 布局的一个小技巧
  • 适用于:兼容 grid 的浏览器
<style>
  .parent {
    width: 200px;
    height: 200px;
    border: 2px solid black;
    display: grid;
  }
  .child {
    background: green;
    color: white;
    margin: auto;
  }
</style>

<div class="parent">
  <span class="child">哈哈</span>
</div>

# 其他

1. 块级元素的水平居中 margin: 0 auto
适用于:子元素为定宽的块级元素

<style>
  .parent {
    width: 200px;
    height: 200px;
    border: 2px solid black;
  }
  .child {
    background: green;
    color: white;
    width: 100px;
    height: 100px;
    margin: 0 auto;
  }
</style>

<div class="parent">
  <div class="child"></div>
</div>

2. 行内元素的水平居中 text-align: center
适用于:子元素为行内元素

<style>
  .parent {
    width: 200px;
    height: 200px;
    border: 2px solid black;
    text-align: center;
  }
  .child {
    background: green;
    color: white;
  }
</style>

<div class="parent">
  <span class="child">哈哈</span>
</div>

# 参考

https://css-tricks.com/centering-css-complete-guide/

https://www.zhangxinxu.com/wordpress/2012/04/inline-block-space-remove-%E5%8E%BB%E9%99%A4%E9%97%B4%E8%B7%9D/

Promise

Promise

  • Promise 三个状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)
  • 状态只能从 pending 变为 fulfilledpending 变为 rejected
  • Promise 对象的状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果
  • 回调函数只接收一个参数
let promise = new Promise((resolve, reject) => {
  let ok = true
  if (ok) resolve('yes')
  else reject('no')
})
promise.then(data => console.log(data))
  .catch(err => console.log(data))

then( ) 函数返回值

  • 返回一个 promise 对象
  • 返回一个同步值(什么也不返回,那就是返回 undefined)

返回 Promise 对象,被下一个 then() 捕获

Promise.resolve()
  .then(_ => new Promise(resolve => resolve('Hi')))
  .then(data => console.log(data))

返回的同步值,被 Promise.resolve() 方法转换

Promise.resolve('Hello')
  .then(data => data)
  .then(data => console.log(data))

//等价于
Promise.resolve('Hello')
  .then(data => Promise.resolve(data))
  .then(data => console.log(data))


//使用 await 关键字直接获取到返回的同步值
let a = await Promise.resolve(1)
  .then(data => data)
console.log(a)

Catch

为什么不建议写 then 的第二个参数而使用 catch ?

  • catch 更接近同步的写法 (try/catch)
  • 可以捕获前面 then 方法抛出的错误
//bad
new Promise((resolve, reject) => { resolve() })
.then(data => { throw new Error('Something wrong!') }, err => { console.log(err) })
//Uncaught (in promise) Error: Something wrong!
//  at Promise.then.data

// good
new Promise((resolve, reject) => { resolve() })
.then(data => { throw new Error('Something wrong!') })
.catch(err => { console.log(err) });
//Error: Something wrong!
//  at Promise.then.data

// good
new Promise((resolve, reject) => { throw new Error('reject') })
.then(data => { throw new Error('Something wrong!') })
.catch(err => { console.log(err) });
//Error: reject
//  at Promise (<anonymous>:1:42)
//  at new Promise (<anonymous>)
//  at <anonymous>:1:1

上面一种写法捕获不到 then 方法的异常,下面的写法在最外层加 catch 能够捕获 promise 中冒泡的异常,也能捕获 then 的异常

Catch 无法捕获异步的异常

Promise.resolve()
  .then(_ => {
    setTimeout(() => {
      throw new Error('the err can not catch');
    }, 1000);
  })
  .catch(err => console.log(err))

// Uncaught Error: the err can not catch
//    at setTimeout (<anonymous>:4:13)

Promise 串行

let images = [
  'http://os5w1n6kc.bkt.clouddn.com/01.jpg',
  'http://os5w1n6kc.bkt.clouddn.com/11.jpg',
  'http://os5w1n6kc.bkt.clouddn.com/10.jpg',
]

let load = src => {
  return new Promise((resolve, reject) => {
    let image = new Image()
    image.onload = _ => {
      //为了测试看效果而加的同步阻塞
      let sum = 0
      for(let i=0; i<9999999; i++) { sum ++ }
      console.log('finish', src)
      return resolve({ src })
    }
    image.onerror = err => {
      console.log('error', src, err)
      return resolve({ src, err })
    }
    image.src = src
  })
}

let loadImages = images => {
  images.reduce(
    (promise, src) => {
      return promise.then(() => load(src))
    }, 
    Promise.resolve()
  ).then(() => {})
  .catch(err => console.log(err))
}

loadImages(images)

等价于

Promise.resolve()
  .then(() => load(images[0]))
  .then(() => load(images[1]))
  .then(() => load(images[2]))
  .then(() => {})
  .catch(err => console.log(err))

Promise 并行

const promises = [1, 2, 3].map(function(el) {
  return new Promise(resolve => {
    //为了测试看效果而加的同步阻塞
    let sum = 0
    for(let i=0; i<9999999; i++) { sum ++ }
    return resolve(el)
  })
});

Promise.all(promises)
  .then(data => {
    // data = [1,2,3]
    console.log(data)
  })
  .catch(err => console.log(err))

References

Event Loop

Event Loop

Event Loop.png

Tasks and Microtasks

  • Tasks: Tasks execute in order, and the browser may render between them
  • Microtasks: Microtasks execute in order, and are executed:
    • after every callback, as long as no other JavaScript is mid-execution
    • at the end of each task
1. 事件循环中有两种任务,一种是宏任务 `Macrotasks` 即 `Tasks`,另一种是微任务 `Microtasks`

2. Tasks 按顺序(队列)执行,浏览器在两个宏任务之间渲染视图

3. Microtasks 按顺序(队列)执行,且在每一次回调函数结束之后执行,或在 Task 结束之前执行

4. Tasks 包含了 setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering 等

5. Microtasks 包含了 process.nextTick, Promises, Object.observe, MutationObserver 等

Try these !

1)

console.log('script start');

setTimeout(_ => {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(_ => {
  console.log('promise1');
}).then(_ => {
  console.log('promise2');
});

console.log('script end');

执行过程:

  • 执行 Run script 任务,按顺序执行同步语句,将 被 Web API 处理好的 setTimeout 消息添加到 Tasks Queue, 将 Promise then 消息添加到 Microtasks Queue
Tasks : Run script、setTimeout callback
Microtasks : Promise then[1]
JS stack : script
Log : script start、script end
  • 在 Tasks 的最后,执行 Microtasks, 在执行 Microtasks 期间产生的 Microtasks 消息同样加入微任务队列,一直执行直到 Microtasks 队列为空
Tasks : Run script、setTimeout callback
Microtasks : Promise then[1]、Promise then[2]
JS stack : Promise Callback[1]
Log : script start、script end、promise1
Tasks : Run script、setTimeout callback
Microtasks : Promise then[2]
JS stack :
Log : script start、script end、promise1
Tasks : Run script、setTimeout callback
Microtasks : Promise then[2]
JS stack : Promise Callback[2]
Log : script start、script end、promise1、promise2
  • Run script 执行完成,执行下一个 Tasks 即 setTimeout callback
Tasks : setTimeout callback
Microtasks :
JS stack : setTimeout callback
Log : script start、script end、promise1、promise2、setTimeout
  • 执行完成
Tasks :
Microtasks :
JS stack :
Log : script start、script end、promise1、promise2、setTimeout

输出结果:

script start
script end
promise1
promise2
// undefined
setTimeout

ref shadeofgod.github.io

2)

console.log('start')

new Promise(resolve => {
  console.log('promise1')

  setTimeout(_ => {
    console.log('setTimeout')
    resolve('I promise u')
    console.log('promise2')
  }, 3000)

  console.log('promise3')
}).then(data => console.log(data))

console.log('end')

执行过程:

  • 执行第一个 Task,Run script,顺序输出 start, promise1, promise 3, end, 中途遇到的 setTimeout 在 3s 后加入 Tasks Queue 队列,此时 Run script 执行完成
  • 执行第二个 Task,setTimeout, 回调函数入栈,顺序输出 setTimeout, promise2, 中途遇到的 Promise resolve 加入 Microtasks Queue
  • setTimeout 任务结束时,执行 Microtasks,即 Promise then 的回调函数入栈,输出 I promise u

输出结果:

start
promise1
promise3
end
// undefined
--------------- 3s later --------------- 
setTimeout
promise2
I promise u

3)

(_ => {
  const f = async _ => {
    console.log('async')

    await new Promise(resolve => {
      console.log('await1')
      resolve('hello')
    }).then(data => console.log(data))

    console.log('await2')
    return 'haha'
  }

  console.log('start')

  setTimeout(_ => {
    console.log('setTimeout')
  }, 0)

  f().then(data => console.log(data))

  console.log('end')
})()

输出结果:

start
async
await1
end
hello
await2
haha
//undefined
setTimeout

4)

(_ => {
  const f = async _ => {
    console.log('async')

    await new Promise(resolve => {
      console.log('await1')
      setTimeout(_ => { 
        console.log('setTimeout1')
        resolve('hello') 
      }, 0)
    }).then(data => console.log(data))

    console.log('await2')
    return 'haha'
  }

  console.log('start')

  setTimeout(_ => {
    console.log('setTimeout2')
  }, 0)

  f().then(data => console.log(data))

  console.log('end')
})()

执行结果

start
async
await1
end
//undefined
setTimeout2
setTimeout1
hello
await2
haha

5)await 与 promise

async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')

setTimeout(function () {
  console.log('setTimeout')
}, 0)

async1();

new Promise(function (resolve) {
  console.log('promise1')
  resolve();
}).then(function () {
  console.log('promise2')
})

console.log('script end')

执行结果:

script start
async1 start
async2
// async2 then 入队
promise1
// promise then 入队
script end

// 取出 async2 then
// await then 入队
// 取出 promise then
promise2
// 取出 await then
async1 end

setTimeout

References

Sass Basics

Variables(变量)

SCSS

$font-stack:    Helvetica, sans-serif;
$primary-color: #333;

body {
  font: 100% $font-stack;
  color: $primary-color;
}

CSS

body {
  font: 100% Helvetica, sans-serif;
  color: #333;
}

Nesting(嵌套)

SCSS

nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li { display: inline-block; }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}

CSS

nav ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

nav li {
  display: inline-block;
}

nav a {
  display: block;
  padding: 6px 12px;
  text-decoration: none;
}

Import(引入)

_reset.scss

html,
body,
ul,
ol {
  margin:  0;
  padding: 0;
}

base.scss

@import 'reset';

body {
  font: 100% Helvetica, sans-serif;
  background-color: #efefef;
}
html, body, ul, ol {
  margin: 0;
  padding: 0;
}

body {
  font: 100% Helvetica, sans-serif;
  background-color: #efefef;
}

Mixins

SCSS

@mixin border-radius($radius) {
  -webkit-border-radius: $radius;
     -moz-border-radius: $radius;
      -ms-border-radius: $radius;
          border-radius: $radius;
}

.box { @include border-radius(10px); }

CSS

.box {
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  -ms-border-radius: 10px;
  border-radius: 10px;
}

Extend/Inheritance(扩展或继承)

SCSS

// This CSS won't print because %equal-heights is never extended.
%equal-heights {
  display: flex;
  flex-wrap: wrap;
}

// This CSS will print because %message-shared is extended.
%message-shared {
  border: 1px solid #ccc;
  padding: 10px;
  color: #333;
}

.message {
  @extend %message-shared;
}

.success {
  @extend %message-shared;
  border-color: green;
}

.error {
  @extend %message-shared;
  border-color: red;
}

.warning {
  @extend %message-shared;
  border-color: yellow;
}

CSS

.message, .success, .error, .warning {
  border: 1px solid #cccccc;
  padding: 10px;
  color: #333;
}

.success {
  border-color: green;
}

.error {
  border-color: red;
}

.warning {
  border-color: yellow;
}

Operators(操作符)

SCSS

.container { width: 100%; }


article[role="main"] {
  float: left;
  width: 600px / 960px * 100%;
}

aside[role="complementary"] {
  float: right;
  width: 300px / 960px * 100%;
}

CSS

.container {
  width: 100%;
}

article[role="main"] {
  float: left;
  width: 62.5%;
}

aside[role="complementary"] {
  float: right;
  width: 31.25%;
}

advice

修改建议

1. 遍历 ali 的范围要忽略最后一个 li 标签,因为,因为你最后的 li 中只有 divp 标签,访问 span strong 那些会报错

var alist = document.getElementById("list");
var ali = alist.getElementsByTagName("li");
for (var i = 0; i < ali.length-1; i++) {
  fnl(ali[i]);
}

2. 传进 function fn1 的形参名字,最好不要和你的 li 数组一样叫做 ali 的名字,虽然局部变量会屏蔽外面的,使用起来没有什么问题,但是也让人很困惑的(滑稽)

function fnl(aliItem) {
  var aBtn = aliItem.getElementsByTagName("input");
  var astrong = aliItem.getElementsByTagName("strong")[0];
  var aem = aliItem.getElementsByTagName("em")[0];     //em里面放的是单价
  var span = aliItem.getElementsByTagName("span")[0];  //span是总价
  var num = Number(astrong.innerHTML);
  var price = parseFloat(aem.innerHTML);           //将单价字符串变成浮点数
  /*......*/
}

3. 最后计算总价的地方,最好是写成一个函数,让每次点击增加数量或减少数量的那个时刻,都会去调用这个函数去计算总价,从而实现实时显示总价的效果

aBtn[0].onclick = function () {
  if (num > 0) {
    num--;
    astrong.innerHTML = num;
    span.innerHTML = (num * price).toFixed(2);
    //  把计算总价的函数调用写在这里,每次增加或减少数量都会去计算一次总价
    getTotalPrice();
  }
}

4. 原来佳琪写的计算总价的方法,不应该写在 fn1 函数外面,因为函数都没执行完,你就计算总价了,而且还只是计算一次,这可达不到你想要的效果。而且计算总价不应该用 parseInt 转换哦,这个会转换成整数的

5. 你可能一直搞不明白为什么计算不出总价哈哈,因为你设置单个商品总价的时候,在后面加了个 “元” 字,就像下面这样,加到 span 里面去了。那你计算总价的时候,转成数字就会出问题了,因为这个中文,转不了数字,所以这里我帮你暂时去掉了,你之后再想解决方案

span.innerHTML = (num * price).toFixed(2) + "元";

6. 这里有一个坑,就是 JavaScript 数字类型采用 IEEE 754,所以... 0.1 + 0.2 是不等于 0.3 的,0.3 + 0.6 也不等于 0.9,而是 0.899999999....,所以一个解决办法,总价计算完之后四舍五入,所以总价的最终计算方法如下

function getTotalPrice() {
  var spa = document.getElementsByTagName('span');
  var p = document.getElementsByTagName('p')[0];
  var total = 0;
  for (var i = 0; i < spa.length; i++) {
    total += Number(spa[i].innerHTML);
  }
  p.innerHTML = total.toFixed(2) + "元";
}

修改后代码

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <title></title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    body {
      overflow-y: auto;
    }

    ul {
      margin-top: 20px;
      border-top: 1px solid #666;
    }

    h1 {
      width: 500px;
      margin: 0 auto;
      color: deeppink;
      height: 100px;
      line-height: 100px;
    }

    li {
      list-style: none;
      padding: 15px 0px 15px 60px;
      border-bottom: 1px solid #ccc;
      font-size: 0px;
      line-height: 30px;
      /*height: 60px;*/
    }

    input[type=button],
    li strong,
    li em,
    li span,
    li div,
    li p {
      height: 30px;
      font-size: 20px;
      line-height: 30px;
      text-align: center;
    }

    input[type=button],
    li strong {
      width: 60px;
    }

    li span,
    li em,
    li p {
      width: 80px;
      background: pink;
    }

    li div {
      width: 100px;
    }

    li strong,
    li span,
    li em,
    li div,
    li p {
      display: inline-block;
    }
  </style>

  <script>
    window.onload = function () {
      var alist = document.getElementById("list");
      var ali = alist.getElementsByTagName("li");
      for (var i = 0; i < ali.length-1; i++) {
        fnl(ali[i]);
      }

      function fnl(aliItem) {
        var aBtn = aliItem.getElementsByTagName("input");
        var astrong = aliItem.getElementsByTagName("strong")[0];
        var aem = aliItem.getElementsByTagName("em")[0];     //em里面放的是单价
        var span = aliItem.getElementsByTagName("span")[0];  //span是总价
        var num = Number(astrong.innerHTML);
        var price = parseFloat(aem.innerHTML);           //将单价字符串变成浮点数

        aBtn[0].onclick = function () {
          if (num > 0) {
            num--;
            astrong.innerHTML = num;
            span.innerHTML = (num * price).toFixed(2);
            getTotalPrice()
          }
        }

        aBtn[1].onclick = function () {
          num++;
          astrong.innerHTML = num;
          span.innerHTML = (num * price).toFixed(2);
          getTotalPrice()
        }
      }

      function getTotalPrice() {
        var spa = document.getElementsByTagName('span');
        var p = document.getElementsByTagName('p')[0];
        var total = 0;
        for (var i = 0; i < spa.length; i++) {
          total += Number(spa[i].innerHTML);
        }
        p.innerHTML = total.toFixed(2) + "元";
      }
    }

  </script>
</head>

<body>
  <ul id="list">
    <li>
      <input type="button" value="-" />
      <strong>0</strong>
      <input type="button" value="+" />
      <div class="s1">单价:</div>
      <em>12.9元</em>
      <div class="s1">共计:</div>
      <span></span></li>
    <li>
      <input type="button" value="-" />
      <strong>0</strong>
      <input type="button" value="+" />
      <div class="s1">单价:</div>
      <em>7.53元</em>
      <div class="s1">共计:</div>
      <span></span></li>
    <li>
      <div class="s1"> 总价:</div>
      <p>0</p></li>
  </ul>

</body>

</html>

CSS grid

grid

#garden {
  display: grid;
  grid-template-columns: 20% 20% 20% 20% 20%;
  grid-template-rows: 20% 20% 20% 20% 20%;
}

grid-column-start

网格将要放置对象的起始位置

  • [integer]:第几个
  • span [integer]:跨越两格

grid-column-end

网格将要放置对象的结束位置的后一位

  • [integer]:第几个
  • span [integer]:跨越两格

比如要放置 {1,2,3} 这三个位置,则 左开右闭

#water {
  grid-column-start: 1;
  grid-column-end: 4;
  // grid-column-end: span 3
}

grid-column

grid-column-start 和 grid-column-end 的合体

grid-row

grid-row-start 和 grid-row-end 的合体

grid-area

四个值分别是
grid-row-start,grid-column-start,grid-row-end,grid-column-end

#water {
   grid-area: 1/2/4/-1;
}

order

网格项目的顺序,类似于 z-index,默认值为 0

  • [integer]

grid-template-rows + grid-template-columns = grid-template

// 可使用单位 px em % fr 等等,其中 fr 为比例
#garden {
  display: grid;
  grid-template-columns: repeat(8, 12.5%); // 8 个 12.5% 重复
  grid-template-rows: 20% 20% 20% 20% 20%;
}

HTTP 状态码

HTTP statusCode

1xx: 消息响应
2xx: 成功响应
3xx: 重定向
4xx: 客户端错误
5xx: 服务器错误

消息响应

code 原因短语 含义
100 Continue
(继续)
这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝,客户端应当继续发送请求的剩余部分
101 Switching Protocal
(切换协议)
服务器已经理解了客户端的请求,并将通过 Upgrade 消息头通知客户端采用不同的协议(Upgrade 头中定义的那些协议)来完成这个请求。

成功响应

code 原因短语 含义
200 OK
(成功)
请求成功
GET:资源已被提取,并作为响应体传回客户端
HEAD:实体头已作为响应头传回客户端
POST:经过服务器处理客户端传来的数据,适合的资源作为响应体传回客户端
TRACE: 服务器收到请求消息作为响应体传回客户端
PUT, DELETE, 和 OPTIONS 方法永远不会返回 200 状态码
201 Created
(已创建)
请求成功
而且有一个新的资源已经依据请求的需要而建立,通常这是 PUT 方法得到的响应码
202 Accepted
(已创建)
服务器已接受请求,但尚未处理。 不必让客户端一直保持与服务器的连接直到批处理操作全部完成
203 Non-Authoritative
Information
(未授权信息)
服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝
204 No Content
(无内容)
该响应没有响应内容,只有响应头,响应头也可能是有用的
205 Reset Content
(重置内容)
告诉用户代理去重置发送该请求的窗口的文档视图
206 Partial Content
(部分内容)
当客户端通过使用range头字段进行文件分段下载时使用该状态码

重定向

code 原因短语 含义
300 Multiple Choice
(多种选择)
该请求有多种可能的响应,用户代理或者用户必须选择它们其中的一个.服务器没有任何标准可以遵循去代替用户来进行选择
301 Moved Permanently
(永久移动)
该状态码表示所请求的URI资源路径已经改变,新的URL会在响应的Location:头字段里找到
302 Found
(临时移动)
该状态码表示所请求的URI资源路径临时改变,并且还可能继续改变.因此客户端在以后访问时还得继续使用该URI.新的URL会在响应的Location:头字段里找到
303 See Other
(查看其他位置)
服务器发送该响应用来引导客户端使用GET方法访问另外一个URI
304 Not Modified
(未修改)
所请求的内容距离上次访问并没有变化. 客户端可以直接从浏览器缓存里获取该资源
305 Use Proxy
(使用代理)
所请求的资源必须统过代理才能访问到.由于安全原因,该状态码并未受到广泛支持
306 unused
(未使用)
这个状态码已经不再被使用,当初它被用在HTTP 1.1规范的旧版本中
307 Temporary Redirect
(临时重定向)
服务器发送该响应用来引导客户端使用相同的方法访问另外一个URI来获取想要获取的资源.新的URL会在响应的Location:头字段里找到.与302状态码有相同的语义,且前后两次访问必须使用相同的方法(GET POST)
308 Permanent Redirect
(永久重定向)
所请求的资源将永久的位于另外一个URI上.新的URL会在响应的Location:头字段里找到.与301状态码有相同的语义,且前后两次访问必须使用相同的方法(GET POST)

客户端错误

code 原因短语 含义
400 Bad Request
(错误请求)
因发送的请求语法错误,服务器无法正常读取
401 Unauthorized
(未授权)
需要身份验证后才能获取所请求的内容,类似于403错误.不同点是.401错误后,只要正确输入帐号密码,验证即可通过
402 Payment Required
(需要付款)
该状态码被保留以供将来使用
403 Forbidden
(禁止访问)
客户端没有权利访问所请求内容,服务器拒绝本次请求
404 Not Found
(未找到)
服务器找不到所请求的资源
405 Method Not Allowed
(不允许使用该方法)
该请求使用的方法被服务器端禁止使用,RFC2616中规定, GETHEAD 方法不能被禁止
406 Not Acceptable
(无法接受)
在进行服务器驱动内容协商后,没有发现合适的内容传回给客户端
407 Proxy Authentication Required
(要求代理身份验证)
类似于状态码 401,不过需要通过代理才能进行验证
408 Request Timeout
(请求超时)
客户端没有在服务器预备等待的时间内完成一个请求的发送.这意味着服务器将会切断和客户端的连接
409 Conflict
(冲突)
该请求与服务器的当前状态所冲突
410 Gone
(已失效)
所请求的资源已经被删除
411 Length Required
(需要内容长度头)
因服务器在本次请求中需要 Content-Length 头字段,而客户端没有发送.所以,服务器拒绝了该请求
412 Precondition Failed
(预处理失败)
服务器没能满足客户端在获取资源时在请求头字段中设置的先决条件
413 Request Entity Too Large
(请求实体过长)
请求实体大小超过服务器的设置的最大限制,服务器可能会关闭HTTP链接并返回Retry-After 头字段
414 Request-URI Too Long
(请求网址过长)
客户端请求所包含的URI地址太长,以至于服务器无法处理
415 Unsupported Media Type
(媒体类型不支持)
服务器不支持客户端所请求的媒体类型,因此拒绝该请求
416 Requested Range Not Satisfiable
(请求范围不合要求)
请求中包含的Range头字段无法被满足,通常是因为Range中的数字范围超出所请求资源的大小
417 Expectation Failed
(预期结果失败)
在请求头 Expect 中指定的预期内容无法被服务器满足

服务端错误

code 原因短语 含义
500 Internal Server Error
(内部服务器错误)
服务器遇到未知的无法解决的问题
501 Implemented
(未实现)
服务器不支持该请求中使用的方法,比如POSTPUT.只有``GETHEAD 是RFC2616规范中规定服务器必须实现的方法
502 Bad Gateway
(网关错误)
服务器作为网关且从上游服务器获取到了一个无效的HTTP响应
503 Service Unavailable
(服务不可用)
由于临时的服务器维护或者过载,服务器当前无法处理请求.这个状况是临时的,并且将在一段时间以后恢复.如果能够预计延迟时间,那么响应中可以包含一个Retry-After:头用以标明这个延迟时间.如果没有给出这个Retry-After:信息,那么客户端应当以处理500响应的方式处理它.同时,这种情况下,一个友好的用于解释服务器出现问题的页面应当被返回,并且,缓存相关的HTTP头信息也应该包含,因为通常这种错误提示网页不应当被客户端缓存
504 Gateway Timeout
(网关超时)
服务器作为网关且不能从上游服务器及时的得到响应返回给客户端
505 HTTP Version Not Supported
(HTTP版本不受支持)
服务器不支持客户端发送的HTTP请求中所使用的HTTP协议版本

Reference

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/HTTP_response_codes

float 实现常用布局

两列定宽布局

左右两侧都固定宽度

<div class="content">
  <div class="left"></div>
  <div class="right"></div>
</div>
.content {
  width: 950px;
  height: 200px;
  margin: 0 auto;
}

.left {
  width: 230px;
  height: 200px;
  float: left;
  background: #FF0097;
}

.right {
  width: 710px;
  height: 200px;
  float: right;
  background: #4EB3B9;
}

两列右侧自适应布局

左侧定宽,右侧自适应

<div class="left"></div>
<div class="right">
  <div class="content"></div>
</div>
.left {
  width: 190px;
  height: 200px;
  float: left;
  background: #4EB3B9;
  margin-right: -190px;
}

.right {
  width: 100%;
  float: right;
}

.content {
  margin-left: 200px;
  height: 200px;
  background: #FF0097;
}

两列左侧自适应布局

左侧定宽,右侧自适应

<div class="left">
  <div class="content"></div>
</div>
<div class="right"></div>
<script src="./index.js"></script>
.left {
  width: 100%;
  float: left;
}

.right {
  width: 190px;
  height: 200px;
  float: right;
  background: #4EB3B9;
  margin-left: -190px;
}

.content {
  margin-right: 200px;
  height: 200px;
  background: #FF0097;
}

三列中间自适应

左右两侧定宽,中间自适应

<div class="left"></div>
<div class="middle">
  <div class="content"></div>
</div>
<div class="right"></div>
.middle {
  width: 100%;
  float: left;
}

.content {
  height: 200px;
  margin-left: 240px;
  margin-right: 200px;
  background: blueviolet;
}

.left {
  width: 230px;
  height: 200px;
  float: left;
  margin-right: -230px;
  background: #FF0097;
}

.right {
  width: 190px;
  height: 200px;
  margin-left: -190px;
  float: right;
  background: #4EB3B9;
}

三列右侧自适应布局

<div class="left1"></div>
<div class="left2"></div>
<div class="right">
  <div class="content"></div>
</div>
.left1 {
  width: 200px;
  height: 200px;
  float: left;
  margin-right: 10px;
  background: #FF0097;
}

.left2 {
  width: 200px;
  height: 200px;
  float: left;
  margin-right: 10px;
  background: #4EB3B9;
}

.right {
  width: 100%;
  float: right;
  margin-left: -420px;
}

.content {
  height: 200px;
  margin-left: 420px;
  background: blueviolet;
}

Git Command

git init                                                  # 初始化本地git仓库(创建新仓库)
git config --global user.name "xxx"                       # 配置用户名
git config --global user.email "[email protected]"              # 配置邮件
git config --global color.ui true                         # git status等命令自动着色
git config --global color.status auto
git config --global color.diff auto
git config --global color.branch auto
git config --global color.interactive auto
git config --global --unset http.proxy                    # remove  proxy configuration on git
git clone git+ssh://[email protected]/VT.git             # clone远程仓库
git status                                                # 查看当前版本状态(是否修改)
git add xyz                                               # 添加xyz文件至index
git add .                                                 # 增加当前子目录下所有更改过的文件至index
git commit -m 'xxx'                                       # 提交
git commit --amend -m 'xxx'                               # 合并上一次提交(用于反复修改)
git commit -am 'xxx'                                      # 将add和commit合为一步
git rm xxx                                                # 删除index中的文件
git rm -r *                                               # 递归删除
git log                                                   # 显示提交日志
git log -1                                                # 显示1行日志 -n为n行
git log -5
git log --stat                                            # 显示提交日志及相关变动文件
git log -p -m
git show dfb02e6e4f2f7b573337763e5c0013802e392818         # 显示某个提交的详细内容
git show dfb02                                            # 可只用commitid的前几位
git show HEAD                                             # 显示HEAD提交日志
git show HEAD^                                            # 显示HEAD的父(上一个版本)的提交日志 ^^为上两个版本 ^5为上5个版本
git tag                                                   # 显示已存在的tag
git tag -a v2.0 -m 'xxx'                                  # 增加v2.0的tag
git show v2.0                                             # 显示v2.0的日志及详细内容
git log v2.0                                              # 显示v2.0的日志
git diff                                                  # 显示所有未添加至index的变更
git diff --cached                                         # 显示所有已添加index但还未commit的变更
git diff HEAD^                                            # 比较与上一个版本的差异
git diff HEAD -- ./lib                                    # 比较与HEAD版本lib目录的差异
git diff origin/master..master                            # 比较远程分支master上有本地分支master上没有的
git diff origin/master..master --stat                     # 只显示差异的文件,不显示具体内容
git remote add origin git+ssh://[email protected]/VT.git # 增加远程定义(用于push/pull/fetch)
git branch                                                # 显示本地分支
git branch --contains 50089                               # 显示包含提交50089的分支
git branch -a                                             # 显示所有分支
git branch -r                                             # 显示所有原创分支
git branch --merged                                       # 显示所有已合并到当前分支的分支
git branch --no-merged                                    # 显示所有未合并到当前分支的分支
git branch -m master master_copy                          # 本地分支改名
git checkout -b master_copy                               # 从当前分支创建新分支master_copy并检出
git checkout -b master master_copy                        # 上面的完整版
git checkout features/performance                         # 检出已存在的features/performance分支
git checkout --track hotfixes/BJVEP933                    # 检出远程分支hotfixes/BJVEP933并创建本地跟踪分支
git checkout v2.0                                         # 检出版本v2.0
git checkout -b devel origin/develop                      # 从远程分支develop创建新本地分支devel并检出
git checkout -- README                                    # 检出head版本的README文件(可用于修改错误回退)
git merge origin/master                                   # 合并远程master分支至当前分支
git cherry-pick ff44785404a8e                             # 合并提交ff44785404a8e的修改
git push origin master                                    # 将当前分支push到远程master分支
git push origin :hotfixes/BJVEP933                        # 删除远程仓库的hotfixes/BJVEP933分支
git push --tags                                           # 把所有tag推送到远程仓库
git fetch                                                 # 获取所有远程分支(不更新本地分支,另需merge)
git fetch --prune                                         # 获取所有原创分支并清除服务器上已删掉的分支
git pull origin master                                    # 获取远程分支master并merge到当前分支
git mv README README2                                     # 重命名文件README为README2
git reset --hard HEAD                                     # 将当前版本重置为HEAD(通常用于merge失败回退)
git rebase
git branch -d hotfixes/BJVEP933                           # 删除分支hotfixes/BJVEP933(本分支修改已合并到其他分支)
git branch -D hotfixes/BJVEP933                           # 强制删除分支hotfixes/BJVEP933
git ls-files                                              # 列出git index包含的文件
git show-branch                                           # 图示当前分支历史
git show-branch --all                                     # 图示所有分支历史
git whatchanged                                           # 显示提交历史对应的文件修改
git revert dfb02e6e4f2f7b573337763e5c0013802e392818       # 撤销提交dfb02e6e4f2f7b573337763e5c0013802e392818
git ls-tree HEAD                                          # 内部命令:显示某个git对象
git rev-parse v2.0                                        # 内部命令:显示某个ref对于的SHA1 HASH
git reflog                                                # 显示所有提交,包括孤立节点
git show HEAD@{5}
git show master@{yesterday}                               # 显示master分支昨天的状态
git log --pretty=format:'%h %s' --graph                   # 图示提交日志
git show HEAD~3
git show -s --pretty=raw 2be7fcb476
git stash                                                 # 暂存当前修改,将所有至为HEAD状态
git stash list                                            # 查看所有暂存
git stash show -p stash@{0}                               # 参考第一次暂存
git stash apply stash@{0}                                 # 应用第一次暂存
git grep "delete from"                                    # 文件中搜索文本“delete from”
git grep -e '#define' --and -e SORT_DIRENT
git gc
git fsck

JavaScript 原型链

1. [[Prototype]] 属性

JavaScript 对象都有一个私有属性 [[Prototype]],它指向它的原型对象 prototype,这个 prototype 对象的私有属性 [[Prototype]] 也指向它的原型对象... 一直指向 Object 类型的 prototype 对象,Object.prototype 对象的 [[Prototype]] 指向原型链的末端 null

[[Prototype]] 可通过以下几种方式访问:

var a = { }

//访问器
Object.getPrototypeOf(a)
Object.setPrototypeOf(a)
//属性
a.__proto__
  • 所有对象的 [[Prototype]] 默认指向 Object.prototype
  • 当访问某对象的属性时,若不存在于当前对象,则会沿着原型链寻找原型对象中是否存在此属性,直到原型链的末端 null
var cat = { a: 1, b: 2 }
var dog = { b: 3, c: 4 }

//将 cat 的 [[Prototype]] 指向 dog 对象,即 dog 是 cat 的原型对象
//原型链:cat ===> dog ===> Object.prototype ===> null
// cat.__proto__ === dog
// dog.__proto__ === Object.prototype
// Object.prototype.__proto__ === null
cat.__proto__ = dog    
console.log(cat.a)    // 1
console.log(cat.b)    // 2 属性遮蔽 (property shadowing)
console.log(cat.c)    // 4 访问原型上的属性

2. 创建对象的几种方式

语法结构创建对象

// 原型链:o ===> Object.prototype ===> null
// o.__proto__ === Object.prototype
// Object.prototype.__proto__ === null
var o = { a: 1 }

// 原型链:arr ===> Array.prototype ===> Object.prototype ===> null
// arr.__proto__ === Array.prototype
// Array.prototype.__proto__ === Object.prototype
// Object.prototype.__proto__ === null
var arr = [1, 2, 3]

// 原型链:f ===> Function.prototype ===> Object.prototype ===> null
// f.__proto__ === Function.prototype
// Function.prototype.__proto__ === Object.prototype
// Object.prototype.__proto__ === null
function f() { }

构造器创建对象

function Graph() {
  this.vertices = [];
  this.edges = [];
}

Graph.prototype = {
  addVertex: function(v){
    this.vertices.push(v);
  }
};

var g = new Graph();

// g 是生成的对象,他的自身属性有'vertices'和'edges'.
// 在 g 被实例化时, g.[[Prototype]] 指向了 Graph.prototype.
// g.__proto__ === Graph.prototype
// Graph.prototype.__proto__ === Object.prototype
// Object.prototype.__proto__ === null

Object.create 创建对象

// 原型链:a ===> Object.prototype ===> null
// a.__proto__ === Object.prototype
var a = { a: 1 }; 

// 原型链:b ===> a ===> Object.prototype ===> null
// b.__proto__ === a
var b = Object.create(a);
console.log(b.a); // 1 (继承而来)

// 原型链:c ===> b ===> a ===> Object.prototype ===> null
// c.__proto__ === b
var c = Object.create(b);

var d = Object.create(null);
// 原型链:d ===> null
// d.__proto__ === null

console.log(d.hasOwnProperty); // undefined, 因为d没有继承Object.prototype

Class 关键字创建对象

// 原型链:a ===> cat.prototype ===> Object.prototype
// a.__proto__ === cat.prototype
// cat.prototype.__proto__ === Object.prototype
// Object.prototype.__proto__ === null
class cat {}
var a = new cat()

3. instanceof

instanceof 运算符的左边是实例对象,右边是构造函数。它会检查右边构建函数的原型对象(prototype),是否在左边对象的原型链上

function C() { }
var o = new C()
o instanceof C    // true, because: o.__proto__ === C.prototype

4. 补充例子

// Object.__proto__ === Function.prototype
// Function.prototype.__proto__ === Object.prototype
Object instanceof Object   // true

证明

首先要清楚存在这样两个构造函数
function Function(){}
function Object(){}
所以问题转化为 (function Object() {}) instanceof Object

因为,Object.__proto__.constructor === Function  true
所以,函数 function Object(){} 是由构造函数 function Function(){} 构造的
也就,Object.__proto__ === Function.prototype

又因为,Function.prototype.__proto__.constructor === Object
所以,对象 Function.prototype 是由构造函数 function Object(){} 构造的
也就,Function.prototype.__proto__ === Object.prototype

参考

继承与原型链
Javascript 继承机制的设计**
instanceof

CSS Box Model

# 标准盒模型

实际宽度不包括 paddingborder 的宽度

width = content
height = content

# 怪异盒子

IE6 及以下在怪异模式下,会把盒子解析为怪异盒子,实际宽度包括 paddingborder

width = content + padding + border
height= content + padding + border

# CSS3 box-sizing 属性

可以随意切换 box-sizing 来告诉浏览器如何解析盒子,该属性有三个值

  • content-box: width (或 height) = content 标准盒模型
  • padding-box: width (或 height) = content + padding
  • border-box: width (或 height) = content + padding + border 怪异盒模型

IE 7 及以下完全不支持 box-sizing 属性,需要 Polyfill

# 在 ResetCSS 中设置 border-box

  • 用继承根元素属性的方式设置 border-box 使得对于单独设置 content-boxpadding-box 变得无比方便,因为设置其他类型的盒模型之后,其子盒子都继承此属性
  • 当今主流的浏览器都支持 box-sizing 属性,但如果需要支持旧版本的浏览器, Safari (< 5.1), Chrome (< 10), and Firefox (< 29),就需要添加 -webkit-moz
html {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}
*, *:before, *:after {
  -webkit-box-sizing: inherit;
  -moz-box-sizing: inherit;
  box-sizing: inherit;
}

# 参考

https://css-tricks.com/box-sizing/

https://developer.mozilla.org/zh-CN/docs/Web/CSS/box-sizing

https://github.com/Schepp/box-sizing-polyfill

https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model

JavaScript 排序算法

算法复杂度

排序方式 平均 最坏 最好 空间 稳定性
插入排序 O(n^2) O(n^2) O(n) O(1) 稳定
希尔排序 O(n^1.3) O(1) 不稳定
冒泡排序 O(n^2) O(n^2) O(n) O(1) 稳定
快速排序 O(nlog2n) O(n^2) O(nlog2n) O(log2n) 不稳定
选择排序 O(n^2) O(n^2) O(n^2) O(1) 不稳定
堆排序 O(nlog2n) O(nlog2n) O(nlog2n) O(1) 不稳定
归并排序 O(nlog2n) O(nlog2n) O(nlog2n) O(n) 稳定
基数排序 O(d(n+r)) O(d(n+r)) O(d(n+r)) O(r) 稳定

Preparation(交换)

  function swap(arr, i, j) {
    [arr[i], arr[j]] = [arr[j], arr[i]]
  }

Bubble Sort(冒泡排序)

插入排序和冒泡排序在平均和最坏情况下的时间复杂度都是 O(n^2),最好情况下都是 O(n),空间复杂度是 O(1)

  function bubbleSort(arr) {
    let len = arr.length
    for (let i = len - 1; i > 0; i--) {
      for (let j = 0; j < i; j++) {
        if (arr[j] > arr[j+1]) {
          swap(arr, j, j+1)
        }
      }
    }
    return arr
  }

Selection Sort(选择排序)

  function selectionSort(arr) {
    let len = arr.length
    let minIdx
    for (let i = 0; i < len - 1; i++) {
      minIdx = i
      for (let j = i + 1; j < len; j++) {
        if (arr[j] < arr[minIdx]) {
          minIdx = j
        }
      }
      if (i !== minIdx) swap(arr, i, minIdx)
    }
    return arr;
  }

Insertion Sort(插入排序)

插入排序和冒泡排序在平均和最坏情况下的时间复杂度都是 O(n^2),最好情况下都是 O(n),空间复杂度是 O(1)

  function insertionSort(arr) {
    let len = arr.length
    for (let i = 1; i < len; i++) {
      let preIdx = i - 1
      let cur = arr[i]
      while (preIdx >= 0 && arr[preIdx] > cur) {
        arr[preIdx + 1] = arr[preIdx]
        preIdx--
      }
      arr[preIdx + 1] = cur
    }
    return arr
  }

Radix Sort(基数排序)

/*
[99,15,48,75,46,37,90,100]

// 第一步

0 90 100
1 
2
3
4
5 15 75
6 46
7 37
8 48
9 99

=> 90 100 15 75 46 37 48 99

// 第二步

0 100
1 15
2
3 37
4 46 48
5
6
7 75
8
9 90 99

=> 100 15 37 46 48 75 90 99

// 第三步

0 15 37 46 48 75 90 99
1 100 
2
3
4
5
6
7
8
9

=> 15 37 46 48 75 90 99 100

*/

  function radixSort(arr, maxDigit) {
    let counter = []
    let mod = 10
    let div = 1
    for (let i = 0; i < maxDigit; i++, div*=10, mod*=10) {
      for (let el of arr) {
        let pos = parseInt(el / div % mod)
        if (!counter[pos]) {
          counter[pos] = []
        }
        counter[pos].push(el)
      }
      arr = []
      for (let bucket of counter) {
        if (!bucket) continue
        let value
        while ((value = bucket.shift()) !== undefined) {
          arr.push(value)
        }
      }
    }
    return arr
  }

Quicksort(快速排序)

快速排序使用 分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)

步骤为:

  1. 从数列中挑出一个元素,称为"基准"(pivot)
  2. 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为 分区(partition) 操作
  3. 递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序

递归到最底部时,数列的大小是零或一,也就是已经排序好了。这个算法一定会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去

function partition(arr, low, high) {
  let pivot = arr[low];
  while (low < high) {
    while (low < high && arr[high] >= pivot) high--;
    arr[low] = arr[high];
    while (low < high && arr[low] <= pivot) low++;
    arr[high] = arr[low];
  }
  arr[low] = pivot;
  return low;
}

function partition2(arr, low, high) {
  let pivot = arr[high];
  let i = low - 1;
  for (j = low; j < high; j++) {
    if (arr[j] <= pivot) {
      i++;
      [arr[i], arr[j]] = [arr[j], arr[i]];
    }
  }
  [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
  return i + 1;
}

function quickSort(arr, low, high) {
  if (low < high) {
    let pivotLoc = partition2(arr, low, high);
    quickSort(arr, low, pivotLoc - 1);
    quickSort(arr, pivotLoc + 1, high);
  }
}

let arr = [23,1,6,14,54,33,76,98,21,10];
quickSort(arr, 0, 9);

console.log(arr);

Merge Sort(归并排序)

  function merge(left, right){
    let result = []
    while (left.length > 0 && right.length > 0) {
      if (left[0] <= right[0]) {
        result.push(left.shift())
      }
      else {
        result.push(right.shift())
      }
    }
    while (left.length > 0) {
      result.push(left.shift())
    }
    while (right.length > 0) {
      result.push(right.shift())
    }
    return result
  }

  function mergeSort(arr) {
    let len = arr.length
    if (len < 2) return arr
    let mid = Math.floor(len / 2)
    let left = arr.slice(0, mid)
    let right = arr.slice(mid)
    return merge(mergeSort(left), mergeSort(right))
  }

The Shapes of CSS

Shape

shape.html

<div class="shape">
  <div class="shape__circle"></div>
  <div class="shape__oval"></div>
  <div class="shape__triangle"></div>
  <div class="shape__triangle--topleft"></div>
  <div class="shape__trapezoid"></div>
  <div class="shape__parallelogram"></div>
  <div class="shape__bubble"></div>
  <div class="shape__moon"></div>
  <div class="shape__pointer"></div>
  <div class="shape__yinyang"></div>
</div>

shape.scss

$blue-grey: #607D8B;

.shape {
  display: flex;
  flex-wrap: wrap;

  div {
    box-sizing: border-box;
    margin: 20px;
  }

  &__circle {
    width: 100px;
    height: 100px;
    background: $blue-grey;
    border-radius: 50px;
  }

  &__oval {
    width: 200px;
    height: 100px;
    background: $blue-grey;
    border-radius: 90px/50px;
  }

  &__triangle {
    width: 0;
    height: 0;
    border-radius: 50%;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-bottom: 100px solid $blue-grey;
  }

  &__triangle--topleft {
    width: 0;
    height: 0;
    border-left: 100px solid $blue-grey;
    border-bottom: 100px solid transparent;
  }

  &__trapezoid {
    width: 200px;
    height: 0;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-bottom: 100px solid $blue-grey;
  }

  &__parallelogram {
    width: 150px;
    height: 100px;
    transform: skew(-15deg,0deg);
	  background: $blue-grey; 
  }

  &__bubble {
    width: 200px;
    height: 50px;
    background: $blue-grey;
    border-radius: 5px;
    position: relative;

    &::before {
      width: 0;
      height: 0;
      content: "";
      position: absolute;
      right: 100%;
      top: 50%;
      transform: translateY(-50%);
      border-top: 5px solid transparent;
      border-bottom: 5px solid transparent;
      border-right: 8px solid $blue-grey;
    }
  }

  &__moon {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    box-shadow: 15px 15px 0 0 $blue-grey;
  }

  &__pointer {
    width: 200px;
    height: 40px;
    background: $blue-grey;
    position: relative;

    &::after {
      content: "";
      position: absolute;
      top: 50%;
      left: 100%;
      transform: translateY(-50%);
      border-top: 20px solid transparent;
      border-bottom: 20px solid transparent;
      border-left: 30px solid $blue-grey;
    }

    &::before {
      content: "";
      position: absolute;
      top: 50%;
      right: 100%;
      transform: translate(100%,-50%);
      border-top: 20px solid transparent;
      border-bottom: 20px solid transparent;
      border-left: 30px solid white;
    }
  }

  &__yinyang {
    width: 96px;
    height: 96px;
    background: white;
    border-color: $blue-grey;
    border-style: solid;
    border-width: 2px 2px 50px 2px;
    border-radius: 100%;
    position: relative;

    &::before {
      content: "";
      position: absolute;
      top: 50%;
      left: 0;
      background: white;
      border: 18px solid $blue-grey;
      border-radius: 100%;
      width: 10px;
      height: 10px;
    }

    &::after {
      content: "";
      position: absolute;
      top: 50%;
      left: 50%;
      background: $blue-grey;
      border: 18px solid white;
      border-radius:100%;
      width: 10px;
      height: 10px;
    }
  }
}

Full Demo

Demo

References

The Shapes of CSS

JavaScript 作用域与闭包

# 作用域链

当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问

var color = "blue";
function changeColor() {
  var anotherColor = "red";
  function swapColors() {
    var tempColor = anotherColor;
    anotherColor = color;
    color = tempColor;
   //  这里可以访问 color、anotherColor 和 tempColor
  }
  //  这里可以访问 color 和 anotherColor,但不能访问 tempColor
  swapColors();
}
//  这里可以访问 color
changeColor();

以上代码有 3 个执行环境:

  1. 全局环境
  2. changeColor() 的局部环境
  3. swapColors() 的局部环境
window
  |__ color
  |__ changeColor()
       |__ anotherColor()
       |__ swapColors()
            |__ tempColor

局部环境会先在自己的变量对象中搜索变量和函数名,如果搜索不到则沿着作用域链往上搜索

# 闭包

闭包 = 函数 + 函数能够访问的自由变量(不是函数的形参或局部变量)

var lang = "JavaScript";
function makeFunc() {
    var name = "Mozilla";
    function displayStr() {
        console.log(lang, name);
    }
    return displayStr;
}

var myFunc = makeFunc();
myFunc();  //  JavaScript Mozilla

闭包特性

1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收

优缺点

优点:有权访问另一个函数作用域里的变量,可避免全局变量的污染,可封装私有数据
缺点:常驻内存,会增大内存使用量,使用不当很容易造成内存泄露

内存泄漏

由于 IE9 之前的版本对 JScript 对象和 COM 对象使用不同的垃圾收集例程,因此闭包在 IE 的这些版本中会导致一些特殊的问题。

如果闭包的作用域链中保存着一个 HTML 元素,那么就意味着该元素无法被销毁

function assignHandler () {
  var element = document.getElementById("someElement");
  element.onclick = function () {
    alert(element.id);
  };
}

由于匿名函数保存一个对 assignHandler() 的活动对象的引用,因此就导致无法减少 element 的引用数。只要匿名函数存在,element 的引用数至少也是 1,因此它所占用的内存就永远不会被回收。所以下面的修改,用将 element.id 用副本暂存,最后把 element 引用指向 null

function assignHandler () {
  var element = document.getElementById("someElement");
  var id = element.id;
  element.onclick = function () {
    alert(element.id);
  };
  element = null;
}

# 参考

JavaScript 高级程序设计

https://segmentfault.com/a/1190000000652891#articleHeader0

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory_Management

代码回滚:Reset、Checkout、Revert

Git 仓库组成

  • 工作区:我们编写代码的地方
  • 暂存区:执行 add 命令后将代码加入到 暂存区
  • 提交历史:执行 commit 命令后将暂存区的修改提交到 提交历史

reset、checkout、revert 的主要区别

reset 和 checkout 可对文件层面的更改操作,也可对提交层面的更改操作,而 revert 只能够对对提交层面的更改操作

命令 文件层面常用情景 提交层面常用情景
git reset 将文件从缓存区中移除 在私有分支上舍弃一些没有提交的更改
git checkout 舍弃工作工作区中的更改 切换分支或查看旧版本
git revert 在公共分支上回滚更改

文件层面

git reset

# 将缓存区同步到你指定的那个提交
git reset [HEAD~2] foo.js

# 最后加一点,撤销缓存区的所有更改
git reset .

git checkout

# 将工作区同步到你指定的那个提交
git checkout[HEAD~2] foo.js

# 最后加一点,撤销工作区的所有更改
git checkout .

提交层面

git reset

可通过传入几个标记来修改工作区或缓存区

  • --soft – 缓存区和工作目录都不会被改变
  • --mixed – 默认选项。缓存区和你指定的提交同步,但工作目录不受影响
  • --hard – 缓存区和工作目录都同步到你指定的提交

git checkout

git reset 不一样的是,git checkout 没有移动这些分支

  • 切换到其他分支,本质上是将 HEAD 指针指向新的分支
# 将 HEAD 指针指向 hotfix 分支
git checkout hotfix
  • 查看旧版本
# 将 HEAD 移动到前两个版本的提交
git checkout HEAD~2

但要特别注意 HEAD 分离,这是非常危险的

比如,当前提交版本为 2c55a13 ,此时如果执行

git checkout HEAD~1

那么 HEAD 指针指向了 b5fb8c4 ,而 master 指针还是指向最新的提交,HEAD 指针此时不指向 master,就出现了 HEAD 分离,如果此时在 HEAD 指向的结点提交了更改,那么 master 指针所指向的提交就无法被找到了,所做的工作都白费了。所以,此种情况提交更改必须要创建新的分支

解决办法参考:危险!分离头指针

git revert

git revert 不会重写提交历史,而是提交一个新的版本(这个版本的状态是 指定的版本撤销了提交 的状态)

git revert HEAD~2

最常用

# 撤销工作区的所有更改
git checkout .

# 撤销缓存区的所有更改
git reset .

# 对提交层面的版本回退
git revert [HEAD~x]

参考

geeeeeeeeek/git-recipes

JavaScript 数组乱序

1)

var values = [1, 2, 3, 4, 5];

values.sort(function(){
  return Math.random() - 0.5;
});

console.log(values);

2)

遍历数组元素,然后将当前元素与以后随机位置的元素进行交换

function shuffle(a) {
  for (let i = a.length; i; i--) {
    let j = Math.floor(Math.random() * i);
    [a[i - 1], a[j]] = [a[j], a[i - 1]];
  }
  return a;
}

References

mqyqingfeng/Blog#51

Angular 自定义双向绑定

Requirement

We often use two-way data binding like this:

<input class="form-control" [(ngModel)]="value" />

But sometimes we want to use like these:

<input class="form-control" [(value)]="value" />
<input class="form-control" [(content)]="value" />
<input class="form-control" [(hello)]="value" />

Via getter and setter

child.component.html

<p>{{master}}</p>

child.component.ts

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {

  _master: number = 0;
  @Output() masterChange = new EventEmitter();

  get master() { return this._master; }

  @Input()
  set master(m) {
    this.masterChange.emit(m);
    this._master = m;
  }

  ngOnInit() { }

  add() { this.master++; }
  sub() { this.master--; }
}

parent.component.html

<p>子组件的值 <app-child #child [(master)]="master"></app-child></p>

<p>变更子组件的值 <button (click)="child.sub()"></button>
<button (click)="child.add()"></button></p>

<p>父组件的值 {{master}}</p>

<p>变更父组件的值 <button (click)="sub()"></button>
<button (click)="add()"></button></p>

parent.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.css']
})
export class ParentComponent implements OnInit {

  master: number = 0;

  constructor() { }

  ngOnInit() { }

  add() { this.master++; }
  sub() { this.master--; }
}

使用 getter 和 setter 要通过一个中间变量 _master 操作。对于 child 组件来说,模板通过 getter 获取 _master 进行显示,master 的值发生变化时会调用 setter,用新的值更新 _master 的值;

对于 parent 组件来说,子组件中 @Input() 装饰器标记 master (可以标记在 getter 也可标记在 setter,只要是标记你要接收的数据即可),用于接收来自父组件的单向绑定,@Output() 装饰器标记 masterChange 向父组件发射数据(注:如果父组件要使用 [(master)] 双向绑定,则一定要命名为 masterChange);

所以当 child 中的 master 值发生变更时,会做两件事:1,更新旧的 _master 值,使得模板中的 master 从 getter 中读取的 _master 是最新的,2,向父组件发射数据,使得和子组件的值同步,达到双向绑定的效果

Demo

stackblitz.com

References

blog.thoughtram.io

你不知道的类型转换

Number() 或 一元操作符 +

  • 传入 Boolean,true 和 false 将分别被转换为 1 和 0
  • 传入 数字值,只做简单传入和返回
  • 传入 null,返回 0
  • 传入 undefined,返回 NaN
  • 传入字符串
    • 空串(包括空格),返回 0
    • 只包含数字,返回数字
    • 十六进制,返回十进制值
    • 浮点,返回浮点
    • 其余情况 NaN
  • 传入对象
    • 先调用 valueOf() 方法,按照前面方法转换
    • 若为 NaN,再调用 toString() 方法,按照前面方法转换
Number("   ") === 0  //  true
Number("123") === 123,Number("-0") === -0  //  true
Number("0xf") === 15  //  true
Number("1.1") === 1.1  //  true
Number("123abc")  //  NaN

parseInt(string, radix)

从第一个非空格字符开始解析,遇到数字就解析,遇到非数字字符就停止,如果没有数字字符,则返回 NaN

parseInt("123blue")  //  123
parseInt("")  //  NaN
parseInt("0xA")  //  10
parseInt("22.5")  //  22
parseInt("070")  //  70,ECMAScript 5
parseInt("070", 8)  //  56
parseInt("70")  //  70
parseInt("0xf")  //  15

parseFloat 类似,不过有不同点

  • 会识别字符串中第一个小数点
  • 只解析十进制,没有第二个参数
parseFloat("122blue")  //  123
parseFloat("0xA")  //  0
parseFloat("22.34.5")  //  22.34
parseFloat("3.125e7")  //  31250000

关系操作符

即小于、大于、小于等于、大于等于;他们在比较数值时可直接比较,当有一个操作数中有非数值时,则需要做相应的转换

  • 如果两个操作数都是数值,则执行数值比较
  • 如果两个操作数都是字符串,则比较 ASCII
  • 如果一个操作数是数值,则另一个转换为数值再比较
  • 如果一个操作数是对象,则先调用 valueOf(),如果没有 valueOf() ,则调用 toString() 方法
  • 如果一个操作数是布尔值,则转换为数值再比较

相等操作符

1. 相等或不相等 == 和 !=

比较前先判断类型,类型不同要做下面操作,再进行比较

  • true 和 false 转换为 1 和 0
  • 一个数值,一个字符串,则字符串转换为数值
  • 一个对象,一个其他,则调用对象的 valueOf() 方法,若无法判断,则调用 toString() 方法
  • null 和 undefined 相等
  • null 和 undefined 和谁比较都不转换成其他任何值
  • 只要有 NaN 都返回 false,因为 NaN 不等于任何值,包括自身
  • 若两个操作数都是对象,则判断是否指向同一对象
"0" == null;  //  false,null 不转换
"0" == undefined;  //  false,undefined 不转换
null == undefined;  //  true,他们相等
"0" == false;  //  true,字符串转成数值 0,false 转成数值 0
"0" == 0;  //  true,字符串转成数值 0
false == NaN;  //  false,NaN 和任何值都不等
false == "";  // true,false 转为数值 0,空串转为数值 0
false == [];  //  true,[] toString() 转换后变成 "",然后转为 0,false 转为 0
false == {};  //  false,{} toString() 变成 "[object Object]",所以是 NaN
2 == [2];  //  true,[2].toString() 后变成 "2"
"" == [null];  //  true,[null].toString() 后变成 ""

// 极端例子
[] == ![]  //  true
// ![] 布尔转换成 false,就等价于 [] == false

2. 全等或不全等 === 和 !==

只有当类型和值都相等时,才返回 true;类型不同时,不做类型转换

Angular 生命周期钩子

钩子 用途及时机
ngOnChanges() 当 Angular(重新)设置数据绑定输入属性时响应。该方法接收当前和上一属性值的 SimpleChanges 对象
当被绑定的输入属性的值发生变化时调用,首次调用一定会发生在 ngOnInit() 之前
ngOnInit() 在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。
在第一轮 ngOnChanges() 完成之后调用,只调用一次
ngDoCheck() 检测,并在发生 Angular 无法或不愿意自己检测的变化时做出反应。在每个 Angular 变更检测周期中调用,ngOnChanges()ngOnInit 之后
ngAfterContentInit() 当把内容投影进组件之后调用。第一次 onDoCheck() 之后调用,只调用一次
ngAfterContentChecked() 每次完成被投影组件内容的变更检测之后调用。
ngAfterContentInit() 和每次 ngDoCheck() 之后调用
ngAfterViewInit() 每次做完组件视图和子视图的变更检测之后调用。
第一次 ngAfterContentChecked() 之后调用,只调用一次
ngAfterViewChecked() 每次做完组件视图和子视图的变更检测之后调用。
ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用
ngOnDestroy() 当 Angular 每次销毁指令/组件之前调用并清扫。
在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。在 Angular 销毁指令/组件之前调用

CSS object-fit

Definition

The object-fit CSS property specifies how the contents of a replaced element, such as an img or video, should be resized to fit its container.
You can alter the alignment of the replaced element's content object within the element's box using the object-position property.

CSS 属性 object-fit 指定可替换元素的内容该如何适应其容器,比如,可替换元素 img 的内容是 srcobject-fit 就可以调整 src 以不同的方式适应 img。要明确一点是,object-fit 是调整替换元素的内容而非替换元素本身

Value

  • contain: 维持内容比例不变,拉伸内容直到内容的宽等于容器的宽,或内容的高等于容器的高
  • cover: 维持内容比例不变,拉伸内容直到内容和容器之间刚好没有空白
  • fill: 内容比例有可能改变,拉伸内容,使得内容的宽和高分别等于容器的宽和高
  • none: 维持内容比例不变,不做任何放缩,若有溢出部分则隐藏
  • scale-down: 选择 contain 和 none 较小的一个

Demo

Demo

References

object-fit
半深入理解CSS3 object-position/object-fit属性

Sass Nesting

Sass Ampersand

The Ampersand '&' represents the class which it is inside.

Basic Nesting

SCSS

.parent {
  .child {}
}

/* or */
.parent {
  & .child {}
}

CSS

.parent .child {}

Both of Two Classes

SCSS

.some-class {
  &.another-class {}
}

CSS

.some-class.another-class { }

Pseudo Classes

SCSS

.button {
  &:visited { }
  &:hover { }
  &:active { }
}

CSS

.button:visited { }
.button:hover { }
.button:active { }

Using the & with >, +, and ~

SCSS

.button {
  & > span { }
  & + span { }
  & ~ span { }
}

/* or */
.button {
  > span { }
  + span { }
  ~ span { }
}

CSS

.button > span { }
.button + span { }
.button ~ span { }

@at-root

SCSS

.grand-parent {
  .parent {
    @at-root .child {}
  }
}

CSS

.child {}

Attach

SCSS

.btn {
  &-primary {}
  &-secondary {}
}

CSS

.btn-primary {}
.btn-secondary {}

Swap

异或

a = a ^ b;
b = b ^ a;
a = a ^ b;

加减

a = a + b;
b = a - b;
a = a - b;

ES6 解构赋值

[a, b] = [b, a]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.