polluxlee / blog Goto Github PK
View Code? Open in Web Editor NEWMy blog
My blog
使用场景:对于连续事件响应,只需要执行一次回调
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);
}
}
}
使用场景:对于连续事件响应,需要间隔一定时间触发回调来控制函数调用频率
function throttle(func, wait) {
var now, previous = 0;
return function () {
now = +new Date();
if (now - previous > wait) {
func.apply(this, arguments);
previous = now;
}
}
}
选择器 | 含义 |
---|---|
* | 匹配所有元素 |
Tag | 匹配所有标签为 Tag 的元素 |
.className | 匹配所有包含 className 的类名的元素 |
#idName | 匹配 id 为 idName 的元素 |
注:E 和 F 可以是标签,类名也可以是 ID
选择器 | 含义 |
---|---|
E,F | 逗号隔开表示或的关系,匹配所有E 或 F 的元素 |
E F | 空格分隔,匹配 E 的所有后代包含 F 的元素 |
E>F | 大于号分隔,匹配 E 的所有包含 F 的直接子元素 |
E+F | 匹配 E 后面紧挨着的一个同级元素 |
E~F | 匹配 E 后面所有同级元素 |
注:下面的 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 元素 |
选择器 | 含义 |
---|---|
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) | 匹配不符合当前选择器的任何元素 |
选择器 | 含义 |
---|---|
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
DOM2 级事件 规定事件流包括三个阶段
1. 事件捕获阶段
2. 处于目标阶段
3. 事件冒泡阶段
即时 “DOM2级事件” 规范明确要求捕获阶段不会涉及事件目标,但很多浏览器都会在捕获阶段触发事件对象上的事件。结果就是有两个机会在目标对象上面操作事件。
var btn = document.getElementById("myBtn");
btn.onclick = function() {
console.log(this.id); // myBtn
}
// 将属性设置为 null 可删除事件处理程序
btn.onclick = null;
以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理
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); // 有效
当使用 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);
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 事件中新增) |
属性/方法 | 类型 | 读/写 | 说明 |
---|---|---|---|
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是一个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
预检请求完成之后,发送实际请求
i. 加载图像不会产生跨域
ii. 只能发送 GET 请求
iii. 无法访问服务器的响应文本
用处:在线广告跟踪浏览量
var img = new Image();
img.onload = img.onerror = function() {
alert("Done!");
};
img.src = "http://www.example.com/test?name=polluxlee"
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 不适用
只能用于主域相同,子域不同的两个页面之间的跨域请求
比如 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>
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
,设置 src
为b.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.html
的 window.name
值了,而这个值正是b.com/index.html
设置的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>
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/
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"]
var array = [1, 2, 1, 1, '1'];
function unique(array) {
return Array.from(new Set(array));
}
console.log(unique(array)); // [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
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
- 函数提升仅适用于函数声明,而不适用于函数表达式,即,形如,function foo() {} 才可以,而 var foo = function() {} 不能被提升
- 函数提升是为了解决两个函数相互递归调用的问题
console.log(square(5)); // 25
/* ... */
function square(n) { return n*n }
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();
在 ES6 之前,只有全局作用域与函数作用域,而没有块级作用域。在 ES6 中,let 和 const 关键字为 JavaScript 增加了块级作用域
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
f1();
function f1() {
const MAX = 6;
if (true) {
const MAX = 10;
}
console.log(MAX); // 6
}
f1();
function do_something() {
console.log(bar); // undefined
console.log(foo); // ReferenceError: foo is not defined
var bar = 1;
let foo = 2;
}
由于词法作用域,表达式(foo + 55)内的标识符“foo”会解析为if块的foo,而不是覆盖值为33的foo。在这一行中,if块的“foo”已经在词法环境中创建,但尚未达到(并终止)其初始化(这是语句本身的一部分):它仍处于暂存死区
function test(){
var foo = 33;
if (true) {
let foo = (foo + 55); // ReferenceError
}
}
test();
HTTP Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上
生命周期: 一般由服务器生成,可设置失效时间
大小: 4KB 左右
与服务端通信: 携带在 HTTP 头中
易用性: 需要程序员自己封装,原生的 Cookie 接口不友好
Response header 中的 Set-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)
客户端无法设置 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 要与原来保持一致
为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开状态,包括页面重新加载和恢复)
生命周期: 仅在当前会话下有效,关闭页面或浏览器后被清除
大小: 5MB 左右
与服务端通信: 仅在客户端(即浏览器)中保存,不参与和服务器的通信
易用性: 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持
localStorage 同样的功能,但是在浏览器关闭,然后重新打开后数据仍然存在
生命周期: 除非被清除,否则永久保存
大小: 5MB 左右
与服务端通信: 仅在客户端(即浏览器)中保存,不参与和服务器的通信
易用性: 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持
通过监听 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
以解析 www.google.com
为例
com
顶级域名服务器找,本地域名服务器会向 com
顶级域名服务器发送一个请求com
域名服务器会让本地服务器去 google.com
域名服务器找,本地域名服务器会向 google.com
域名服务器发送一个请求www.google.com
的 IP 地址返回给本地域名服务器www.google.com
的 IP 地址缓存到本地,供下次查询使用最终解析过程如下
. -> .com -> google.com. -> www.google.com. -> IP 地址
DNS 缓存有
浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存
TCP 三次握手建立连接 #61
HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成
HTTP响应报文也是由三部分组成: 状态码, 响应报头和响应报文
回流和重绘
https://juejin.im/post/5a9923e9518825558251c96a
Event Loop
形如,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
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);
}
// 测试一下
var obj = objectFactory(func, "April", 21);
console.log(obj.name); // April
obj.showAge(); // 21
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 关键字调用构造函数可以创建一个新的对象(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
一个函数在其正文中使用 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
Import 命令会被 JavaScript 引擎静态分析(必须在代码的顶层,比如在 if, for, 或 function 里会报错),先于模块内的其他语句执行(具有提升效果)
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;
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()
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
<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>
$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);
}
}
优点:简单
缺点:无法复用
var obj = {
name: "Kitty",
age: 22
}
var obj1 = new Object()
obj1.name = "Kitty";
obj1.age = 22
工厂模式抽象了创建具体对象的过程
优点:复用性高,能创建多个相似对象
缺点:因为都指向同一个原型,所以不能识别对象类型
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");
优点:复用性高,可识别对象的类型
缺点:每个方法都要在每个实例上创建一遍,实例之间不能共享方法和属性
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");
无论何时,只要创建了一个新函数,就会为函数创建一个
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"
优点:构造函数用于定义实例属性,而原型模式用于定义方法和共享的属性。每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存
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"
特点:通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型
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();
构造函数在不返回值的情况下,默认会返回新对象实例。而通过在构造函数末尾添加一个 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();
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"
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
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"];
- 借用构造函数可解决原型链中,原型包含引用类型值的问题
- 可这跟构造函数面对的问题一样,方法都在构造函数中定义,就无法复用了
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"]
- 将原型链和借用构造函数结合
- 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承
缺点:在原型中存在多余(被屏蔽的)属性
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();
- 以一个对象为基础,创建对象
- 新对象的 [[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"]
寄生式继承是在原型式继承的基础上增强
function createAnother(original) {
function F() {};
F.prototype = o;
var clone = new F(); // 创建一个新对象
clone.sayHi = function() { // 以某种方式增强这个对象
console.log("hi");
}
return clone; // 返回这个对象
}
组合继承在调用 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();
ES5 的继承,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面(Parent.apply(this))
ES6 的继承机制完全不同,实质是,先将父类实例对象的属性和方法加到 this 上面(所以必须先调用 super方法),然后再用子类的构造函数修改 this
class A {}
class B extends A {
constructor() {
super();
}
}
类(即构造函数)同时有 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
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
ES5 定义只有内部才用的特性,描述了属性(property)的各种特征
数据属性
delete
删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性for-in
循环返回属性访问器属性
delete
删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性for-in
循环返回属性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]]
的新对象
justify-content
+ align-items
<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>
transform
<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
<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>
vertical-align
+ text-align
<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>
margin
<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>
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))
返回 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)
//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 的异常
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)
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))
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))
- 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 等
console.log('script start');
setTimeout(_ => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(_ => {
console.log('promise1');
}).then(_ => {
console.log('promise2');
});
console.log('script end');
执行过程:
Tasks : | Run script、setTimeout callback |
---|---|
Microtasks : | Promise then[1] |
JS stack : | script |
Log : | script start、script end |
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 |
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
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')
执行过程:
Run script
,顺序输出 start, promise1, promise 3, end, 中途遇到的 setTimeout
在 3s 后加入 Tasks Queue 队列,此时 Run script
执行完成setTimeout
, 回调函数入栈,顺序输出 setTimeout, promise2, 中途遇到的 Promise resolve 加入 Microtasks QueuesetTimeout
任务结束时,执行 Microtasks,即 Promise then 的回调函数入栈,输出 I promise u输出结果:
start
promise1
promise3
end
// undefined
--------------- 3s later ---------------
setTimeout
promise2
I promise u
(_ => {
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
(_ => {
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
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
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;
}
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;
}
_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;
}
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;
}
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;
}
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%;
}
1. 遍历 ali 的范围要忽略最后一个 li
标签,因为,因为你最后的 li
中只有 div
和 p
标签,访问 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>
#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}
网格将要放置对象的起始位置
网格将要放置对象的结束位置的后一位
比如要放置 {1,2,3} 这三个位置,则 左开右闭
#water {
grid-column-start: 1;
grid-column-end: 4;
// grid-column-end: span 3
}
grid-column-start 和 grid-column-end 的合体
grid-row-start 和 grid-row-end 的合体
四个值分别是
grid-row-start,grid-column-start,grid-row-end,grid-column-end
#water {
grid-area: 1/2/4/-1;
}
网格项目的顺序,类似于 z-index,默认值为 0
// 可使用单位 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%;
}
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中规定, GET 和 HEAD 方法不能被禁止 |
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 (未实现) |
服务器不支持该请求中使用的方法,比如POST 和 PUT.只有``GET 和 HEAD 是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协议版本 |
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/HTTP_response_codes
左右两侧都固定宽度
<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 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 对象都有一个私有属性 [[Prototype]]
,它指向它的原型对象 prototype
,这个 prototype
对象的私有属性 [[Prototype]]
也指向它的原型对象... 一直指向 Object 类型的 prototype
对象,Object.prototype
对象的 [[Prototype]]
指向原型链的末端 null
[[Prototype]] 可通过以下几种方式访问:
var a = { }
//访问器
Object.getPrototypeOf(a)
Object.setPrototypeOf(a)
//属性
a.__proto__
[[Prototype]]
默认指向 Object.prototype
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 访问原型上的属性
// 原型链: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
// 原型链: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
// 原型链:a ===> cat.prototype ===> Object.prototype
// a.__proto__ === cat.prototype
// cat.prototype.__proto__ === Object.prototype
// Object.prototype.__proto__ === null
class cat {}
var a = new cat()
instanceof 运算符的左边是实例对象,右边是构造函数。它会检查右边构建函数的原型对象(prototype),是否在左边对象的原型链上
function C() { }
var o = new C()
o instanceof C // true, because: o.__proto__ === C.prototype
// 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
实际宽度不包括 padding
和 border
的宽度
width = content
height = content
IE6 及以下在怪异模式下,会把盒子解析为怪异盒子,实际宽度包括 padding
和 border
width = content + padding + border
height= content + padding + border
可以随意切换 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
border-box
使得对于单独设置 content-box
或 padding-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
排序方式 | 平均 | 最坏 | 最好 | 空间 | 稳定性 |
---|---|---|---|---|---|
插入排序 | 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) | 稳定 |
function swap(arr, i, j) {
[arr[i], arr[j]] = [arr[j], arr[i]]
}
插入排序和冒泡排序在平均和最坏情况下的时间复杂度都是 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
}
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;
}
插入排序和冒泡排序在平均和最坏情况下的时间复杂度都是 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
}
/*
[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
}
快速排序使用 分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)
步骤为:
递归到最底部时,数列的大小是零或一,也就是已经排序好了。这个算法一定会结束,因为在每次的迭代(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);
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))
}
<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>
$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;
}
}
}
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问
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 个执行环境:
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
add
命令后将代码加入到 暂存区commit
命令后将暂存区的修改提交到 提交历史reset 和 checkout 可对文件层面的更改操作,也可对提交层面的更改操作,而 revert 只能够对对提交层面的更改操作
命令 | 文件层面常用情景 | 提交层面常用情景 |
---|---|---|
git reset | 将文件从缓存区中移除 | 在私有分支上舍弃一些没有提交的更改 |
git checkout | 舍弃工作工作区中的更改 | 切换分支或查看旧版本 |
git revert | 无 | 在公共分支上回滚更改 |
# 将缓存区同步到你指定的那个提交
git reset [HEAD~2] foo.js
# 最后加一点,撤销缓存区的所有更改
git reset .
# 将工作区同步到你指定的那个提交
git checkout[HEAD~2] foo.js
# 最后加一点,撤销工作区的所有更改
git checkout .
可通过传入几个标记来修改工作区或缓存区
和 git reset
不一样的是,git checkout
没有移动这些分支
# 将 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 HEAD~2
# 撤销工作区的所有更改
git checkout .
# 撤销缓存区的所有更改
git reset .
# 对提交层面的版本回退
git revert [HEAD~x]
var values = [1, 2, 3, 4, 5];
values.sort(function(){
return Math.random() - 0.5;
});
console.log(values);
遍历数组元素,然后将当前元素与以后随机位置的元素进行交换
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;
}
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" />
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,向父组件发射数据,使得和子组件的值同步,达到双向绑定的效果
Number(" ") === 0 // true
Number("123") === 123,Number("-0") === -0 // true
Number("0xf") === 15 // true
Number("1.1") === 1.1 // true
Number("123abc") // NaN
从第一个非空格字符开始解析,遇到数字就解析,遇到非数字字符就停止,如果没有数字字符,则返回 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
即小于、大于、小于等于、大于等于;他们在比较数值时可直接比较,当有一个操作数中有非数值时,则需要做相应的转换
比较前先判断类型,类型不同要做下面操作,再进行比较
"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
只有当类型和值都相等时,才返回 true;类型不同时,不做类型转换
钩子 | 用途及时机 |
---|---|
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 销毁指令/组件之前调用 |
The
object-fit
CSS property specifies how the contents of a replaced element, such as animg
orvideo
, 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 theobject-position
property.
CSS 属性 object-fit 指定可替换元素的内容该如何适应其容器,比如,可替换元素 img
的内容是 src
,object-fit
就可以调整 src
以不同的方式适应 img
。要明确一点是,object-fit
是调整替换元素的内容而非替换元素本身
contain
: 维持内容比例不变,拉伸内容直到内容的宽等于容器的宽,或内容的高等于容器的高cover
: 维持内容比例不变,拉伸内容直到内容和容器之间刚好没有空白fill
: 内容比例有可能改变,拉伸内容,使得内容的宽和高分别等于容器的宽和高none
: 维持内容比例不变,不做任何放缩,若有溢出部分则隐藏scale-down
: 选择 contain 和 none 较小的一个The Ampersand '&' represents the class which it is inside.
SCSS
.parent {
.child {}
}
/* or */
.parent {
& .child {}
}
CSS
.parent .child {}
SCSS
.some-class {
&.another-class {}
}
CSS
.some-class.another-class { }
SCSS
.button {
&:visited { }
&:hover { }
&:active { }
}
CSS
.button:visited { }
.button:hover { }
.button:active { }
SCSS
.button {
& > span { }
& + span { }
& ~ span { }
}
/* or */
.button {
> span { }
+ span { }
~ span { }
}
CSS
.button > span { }
.button + span { }
.button ~ span { }
SCSS
.grand-parent {
.parent {
@at-root .child {}
}
}
CSS
.child {}
SCSS
.btn {
&-primary {}
&-secondary {}
}
CSS
.btn-primary {}
.btn-secondary {}
a = a ^ b;
b = b ^ a;
a = a ^ b;
a = a + b;
b = a - b;
a = a - b;
[a, b] = [b, a]
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.