如果說 npm 的大新聞,莫過于之前的 left-pad 撤包事件,event-stream 投毒事件,Ant Design 彩蛋事件。使得很多前端開發者又開始重新思考 npm 生態時候真的存在問題?
今天我們文章的主角是 memwatch,一個用來幫助我們檢查 Node.js 是否存在內存泄漏的庫,和這個庫傳奇的一生。
2012 年 02 月 06 日,一位 Mozilla 的工程師 lloyd 創建了這個庫,并寫了一篇博文“Is My NodeJS Program Leaking?”(我的 Node.js 程序是否存在內存泄漏?)。這個包最初被命名為 gcstats,代碼上傳到了 github。
6 月 27 日,npm 包改名為 memwatch,發布 0.1.1 版。
7 月 4 日,lloyd 為這個庫添加了開源許可協議:WTFPL,發布 0.1.2 版。很多人對這個開源許可協議可能比較陌生,WTFPL 的全稱是 Do What The Fuck You Want To Public License,中文譯名:你他媽的想干嘛就干嘛公共許可證。也許就是這份協議開啟了 memwatch 庫不尋常的一生。
2013 年 3 月 14 日,作者 lloyd 提交了最后一次代碼,發布了 0.2.2 版本。支持的 Node.js 版本為 0.6.0。隨后這個庫再也沒有更新過。
從作者的博文和推文可以看到,作者在 2014 年離開了 Mozilla。而從作者的 github 動態更可以看出,作者應該是轉入了 golang 陣營。
2014 年 6 月 28 日,作者的一位前同事 deepak1556 fork 了這個庫,增加了對 Node.js 0.11 的支持,并發起了合并請求。但是作者并沒有回復,也沒有合并此次請求。此時距離原作者放棄這個庫也已經過去一年多了。
2015 年 2 月 7 日,marcominetti 又 fork 了 deepak1556 的庫,增加了對 Node.js 0.12 的支持,并向原庫發起了合并請求,同樣沒有得到作者的任何回復。于是 marcominetti 決定自立門戶,于是將 memwatch 改名為 memwatch-next 發布到了 npm。
2017 年 1 月 27 日,如同前兩位維護者一樣,marcominetti 也最終放棄了繼續更新這個庫。到此時,此庫支持的 Node.js 版本為 4、5、6。
2018 年 5 月 6 日,eduardbcom 又 fork 了 marcominetti 的庫,增加了 Node.js 9 的支持,并且放棄了對 Node.js 9 以下所有版本的支持。改名為 node-memwatch 并發布到了 npm。隨后再也沒有更新過代碼。
2018 年 7 月 17 日,一位開發者 dyatko 又 fork 了 eduardbcom 的庫,增加了對 Node.js 8 的支持,并向原庫發起了合并請求,同樣沒有得到作者的任何回復。
但在此次 pr 的評論中,另一位開發者說,airbnb 也 fork 了 marcominetti 的庫,并改名為 @airbnb/node-memwatch 發布到了 npm。
有了大廠接手,也算是這個庫最終的歸宿吧。
相關閱讀
開發者對 npm 公司不滿,unpublish 了自己的所有模塊
月下載量千萬的 npm 包被黑客篡改,Vue 開發者可能正在遭受攻擊
駁《我不是很懂 Node.js 社區的 DRY 文化》
機器人偽裝成人類在 GitHub 上為開源項目修復 bug
兩列布局的幾種方法
html結構
<div class="content">
<div class="content-left">
左側固定200px
</div>
<div class="content-right">
右側自適應
</div>
</div>
1.通過float和margin-left
/ 清除瀏覽器默認邊距 /
{
margin: 0;
padding: 0;
}
.content{
overflow: hidden;
}
/ 脫離文檔流 /
.content-left {
float: left;
width: 200px;
height: 200px;
background: red;
}
.content-right {
/ 通過margin-left將左邊位置空出 /
margin-left: 200px;
background: blue;
height: 200px;
}
2.通過 position: absolute;絕對定位
/ 清除瀏覽器默認邊距 /
{
margin: 0;
padding: 0;
}
.content {
overflow: hidden;
position: relative;
}
/ 脫離文檔流 /
.content-left {
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 200px;
background: red;
}
.content-right {
/ 通過margin-left將左邊位置空出 /
margin-left: 200px;
background: blue;
height: 200px;
}
3.通過flex彈性布局
/ 清除瀏覽器默認邊距 /
{
margin: 0;
padding: 0;
}
.content {
overflow: hidden;
display: flex;
}
.content-left {
/ 除了width: 200px;還可以flex-basis: 200px; /
width: 200px;
height: 200px;
background: red;
}
.content-right {
/ flex:1;將剩余空間分給它 /
flex: 1;
background: blue;
height: 200px;
}
4.通過 display: table;表格布局
/ 清除瀏覽器默認邊距 /
{
margin: 0;
padding: 0;
}
.content {
overflow: hidden;
display: table;
/ 必須給父級定寬不然自適應盒子沒定寬只會由內容撐開 /
width: 100%;
}
.content-left {
display: table-cell;
width: 200px;
height: 200px;
background: red;
}
.content-right {
display: table-cell;
background: blue;
height: 200px;
}
5.通過inline-block和calc()函數
/ 清除瀏覽器默認邊距 /
{
margin: 0;
padding: 0;
}
.content {
/ 必須加font-size=0;把inline-block默認間距去掉,
不過設置后里面文字不顯示了可以給里面塊設置font-size:20px;
或者把兩個塊之間的換行刪掉也能去掉間距/
font-size: 0;
overflow: hidden;
}
.content-left {
font-size: 20px;
display: inline-block;
width: 200px;
height: 200px;
background: red;
}
.content-right {
font-size: 20px;
display: inline-block;
background: blue;
height: 200px;
/ 注意calc里的運算符兩邊要有空格 /
width: calc(100% - 200px);
}
6.通過float和calc()函數,左右兩塊都要浮動
/ 清除瀏覽器默認邊距 /
{
margin: 0;
padding: 0;
}
.content {
overflow: hidden;
}
.content-left {
float: left;
width: 200px;
height: 200px;
background: red;
}
.content-right {
float: left;
background: blue;
height: 200px;
/ 注意calc里的運算符兩邊要有空格 /
width: calc(100% - 200px);
}
7.使用grid布局
/ 清除瀏覽器默認邊距 /
{
margin: 0;
padding: 0;
}
.content {
overflow: hidden;
display: grid;
grid-template-columns: 200px 1fr;
/ grid布局也有列等高的默認效果。需要設置: align-items: start;。 /
align-items: start;
}
.content-left {
height: 200px;
background: red;
/ grid布局還有一個值得注意的小地方和flex不同:在使用margin-left的時候,
grid布局默認是box-sizing設置的盒寬度之間的位置。
而flex則是使用兩個div的border或者padding外側之間的距離。 */
box-sizing: border-box;
grid-column: 1;
}
.content-right {
background: blue;
height: 200px;
box-sizing: border-box;
grid-column: 2;
}
我們經常需要引用(白嫖)一些網頁上的文字,但是豆丁網,百度文庫等等設置的有復制權限,我們無法直接復制,或者復制文字有上限,提示付費。
這里介紹幾種,都是些花里胡哨的白嫖方案:
1.手機掃描:
拿著手機,用手機QQ的文字掃描直接去識別問題,遇上好識別的文章短的直接就識別,但是這種方法遇到文章比較長的就十分麻煩。可以針對那些選中
2.魔鬼牽引:
原來在計蒜客就這樣搞,選中網站上的文字,然后用鼠標一直拖到別的頁面,或者一個記事本什么的,屢試不爽。
3.側邊翻譯:
側邊翻譯,火狐或者谷歌下載一些插件,比如說側邊翻譯,這個東西小巧玲瓏,選中文字側邊翻譯之后你就可以對文本進行復制了。
4.原始查看法:
在瀏覽器中直接F12,打開瀏覽器查看,就下面這個東西,瀏覽器你要復制的文本就在這里面,里面封裝的html語言,你可以搜索你需要找的文字,然后可以直接復制,如果想要復制多一點你也可以直接把里面的html語言拿出來解析到自己的網頁里面,然后再進行復制。
5.氪金法:
沒別的,充錢就完事了。
6.某巨巨提供:
下載一個ocr工具,類似qq掃描的功能。
詳細解讀—函數和變量的聲明提升
一 - 聲明提升常見面試題
?我們先以幾道面試題開頭來引入,
?大家可以先給自己做出一個答案,然后再看文章的思路捋一捋喲。
來一道基礎的吧~
var a="Hello";
function test(){
alert(a);
var a="World";
alert(a);
}
test();
難度+1
var a = 1;
function outer(){
a = 2;
function inner(){
alert(a);
a = 4;
}
inner();
}
outer();
alert(a);
繼續加油
(function(){
f1();
f2();
var f1 = function(){};
function f2(){
alert(1);
}
})();
最后一道
(function () {
console.log(a);
var a=1;
function a() {
console.log("biu~");
}
})()
二 - 究竟什么是聲明提升?
引擎在解釋JS代碼之前,首先要對JS代碼進行編譯,其中編譯的一部分工作就是找到所有的聲明,包括變量和函數的所有聲明都會在任何代碼被執行前首先被處理。
var a = 1這句話會被瀏覽器讀成 var a和a = 1兩句話執行,其中var a會在編譯階段就先執行了,而a = 1這段賦值代碼會在原地等待執行階段。
console.log(a);
var a = 2;
上邊這段代碼,如果代碼按照順序由上自下執行,那么執行到console.log(a);時,a還沒有聲明,所以會包一個找不到變量a的錯,但是事實上,這句話打印了一個undefined,說明a被聲明了,但是沒有被賦值,那么結合上一段的文字,我們可以得出代碼實際運行的是這樣的:
var a;
console.log(a);
a = 2;
三 - 函數的提升
大家可能在書寫代碼的時候發現,無論函數封裝寫在前或者后,我們的函數調用都可以順利執行。
fn1();//可以執行
function fn1() {
console.log("hello");
}
為什么呢?其實函數聲明,包括函數的代碼塊都i會被提升,所以調用函數的時候,函數聲明已經被執行過了。
但是有個案例大家了解一下:
fn2();//報錯,fn2不是一個函數
var fn2 = function () {
console.log("world");
}
我們可以看到 以給匿名函數賦值的形式定義函數,只會提升函數聲明,但是函數表達式卻不會被提升。因為變量fn2被提升,但是并沒有賦值,我們書寫的fn2()無法運行,而拋出了異常。
以下就是實際執行的順序:
var fn2;
fn2();
fn2 = function () {
console.log("world")
}
函數優先提升
我們都知道了,函數聲明和變量聲明都會被提升,那么遇到這樣的情況會怎么辦?
fn3();
var fn3=function () {
console.log("fn3-1");
}
fn3();
function fn3() {
console.log("fn3-2");
}
哎呦,嘛情況,突然迷了?。?!
? 這個時候你就要考慮,同樣的一個變量名稱,到底是把var fn3給先提聲上去,再提升 fn3函數體?還是先提升 fn3函數體,再提升var fn3???其實都不對?。?!
? 答案是:函數會被優先提升,但后才是變量提升,但是當函數提升后,然后發現還有一個變量聲明和函數聲明一樣的名稱,這個就是重復聲明,那么這個var fn3 是不生效直接忽略的。
所以實際代碼運行順序是:
function fn3() {
console.log("fn3-2");
}
fn3();//fn3-2
fn3=function () {//var fn3因為重復聲明被忽略
console.log("fn3-1");
}
fn3();//fn3-1
當然,我們還是建議再同一個作用域重復聲明是很爛的選擇
說在最后
再代碼作用域中的聲明,都會在代碼執行前被首先處理,所有的聲明都會被移動到各自作用域的最頂端,這個過程就叫做聲明提升。
四 - 答案:
問題1:
var a="Hello";
function test(){
alert(a);
var a="World";
alert(a);
}
test();
實際執行:
var a="Hello";
function test(){
//作用域有聲明a,聲明提升到這里
var a;
alert(a);//本作用域聲明a,所以不去使用父作用域的a,但是本作用域的a沒有賦值,所以彈出undefined
a="World";
alert(a);//賦值后 ,彈出world
}
test();
問題2:
var a = 1;
function outer(){
a = 2;
function inner(){
alert(a);
a = 4;
}
inner();
}
outer();
alert(a);
執行結果:
var a = 1;
function outer(){
a = 2;
function inner(){
//本作用域沒有聲明a,所以沒有任何提升,直接執行
alert(a); // 所以彈出 a為 2
a = 4;
}
inner();
}
outer();
alert(a);//只有全局聲明了a,所以所有作用域使用的都是全局的a,所以a最后被賦值為4 彈出4
問題3
(function(){
f1();
f2();
var f1 = function(){};
function f2(){
alert(1);
}
})();
實際執行結果:
(function(){
function f2(){
alert(1);
}
var fn1;
f1();//提升后先執行fn1(),但是fn1被提升的是變量不是函數,所以這里報錯,不是一個函數
f2();//上一句話報錯,這句話不再運行
f1 = function(){};
})();
問題4:
(function () {
console.log(a);
var a=1;
function a() {
console.log("biu~");
}
})()
實際執行結果:
(function () {
function a() {
console.log("biu~");
}
console.log(a);//打印了a這個函數的函數體
a=1;//因為函數有限聲明提升,所以這里的var a被提升時,發現重復聲明,故被忽略了var a;
})()
作者:陳大魚頭
github: KRISACHAN
<input /> 標簽是我們日常開發中非常常見的替換元素了,但是最近在刷 whattwg 跟 MDN 的時候發現 跟 <input /> 有很多相關的屬性,選擇器都沒怎么用過,所以就開篇文章來整理一下一些比較有趣或者實用的知識點。
本篇文章默認大家已經知道 <input /> 標簽的基本用法,不會做過多的基礎說明~
沒想到,這些選擇器居然跟 input …
到寫文章為止,根據的 drafts 指出,一共有3大類,16種跟 input 相關的選擇。其實都挺有用的,善用它們,會讓我們的用戶體驗更加美好。
下面我們來分享一下這3大類選擇器的作用:
第一類:控制系(Input Control States)
選擇器 作用
:enabled 選擇可使用狀態的 <input /> 元素
:disabled 選擇不可使用狀態的 <input /> 元素
:read-only 選擇不可編輯狀態的元素(不僅僅是 <input /> )
:read-write 選擇可編輯狀態的元素(不僅僅是 <input /> )
:placeholder-shown 選擇 placeholder text 顯示時的元素
:default 選擇在 <button>,<input type="checkbox" />, <input type="radio" />, 以及 <option> 上的默認狀態
第二類:輸出系(Input Value States)
選擇器 作用
:checked 選擇處于選中狀態的 <input type="radio" />
:indeterminate 選擇狀態不確定的表單元素與 <progress>
第三類:偵查系(Input Value-checking)
選擇器 作用
:blank 選擇處于空值時的 <input>,暫未被瀏覽器支持
:valid 選擇驗證通過的表單元素
:invalid 選擇驗證不通過的表單元素
:in-range 選擇處于指定范圍內的 <input />
:out-of-range 選擇不處于指定范圍內的 <input />
:required 選擇必填的表單元素
:optional 選擇選填的表單元素
:user-invalid 選擇用戶輸入但值非法時的 <input />,暫未被瀏覽器支持
可怕,除了選擇器,居然還跟這些屬性有關系
<input> 除了有很多相關的選擇器,結合不同的type還有不同的屬性可以供使用。他們的作用如下:
屬性 作用
maxlength 可輸入的最大長度
minlength 可輸入的最小長度
size 輸入框的長度
readonly 輸入框是否只讀
required 輸入框是否必填
multiple 輸入框是否可以多選
pattern 輸入框驗證規則
min 可輸入的最小值
max 可輸入的最大值
step 輸入框每次的增量
list 輸入框綁定的可選值數據
placeholder 輸入框預選文字
實戰
通過上面的三類說明,我們大致了解了 <input /> 標簽的相關信息,但是你們以為我是來列list的嗎?
當然不是,還有實操啊~
純CSS實現表單提交功能
首先我們來看個效果圖
上面的效果就是一個純CSS實現的表單提交功能,這是怎么實現的呢?下面我們直接看源碼,然后一步一步地來分拆(不想看的可以直接CV下面的源碼自己做測試~)
<style>
:root {
--error-color: red;
}
.form > input {
margin-bottom: 10px;
}
.form > .f-tips {
color: var(--error-color);
display: none;
}
input[type="text"]:invalid ~ input[type="submit"],
input[type="password"]:invalid ~ input[type="submit"] {
display: none;
}
input[required]:focus:invalid + span {
display: inline;
}
input[required]:empty + span {
display: none;
}
input[required]:invalid:not(:placeholder-shown) + span {
display: inline;
}
</style>
<form class="form" id="form" method="get" action="/api/form">
賬號:
<input data-title="賬號" placeholder="請輸入正確的賬號" pattern="\w{6,10}" name="account" type="text" required />
<span class="f-tips">請輸入正確的賬號</span>
<br />
密碼:
<input data-title="密碼" placeholder="請輸入正確的密碼" pattern="\w{6,10}" name="password" type="password" required />
<span class="f-tips">請輸入正確的密碼</span>
<br />
<input name="button" type="submit" value="提交" />
</form>
第一步:寫好基礎結構
首先我們來把基礎結構給寫好,代碼如下:
<style>
:root {
--error-color: red;
}
.form > input {
margin-bottom: 10px;
}
.form > .f-tips {
color: var(--error-color);
display: none;
}
</style>
<form class="form" id="form" method="get" action="/api/form">
賬號:
<input data-title="賬號" placeholder="請輸入正確的賬號" pattern="\w{6,10}" name="account" type="text" required />
<span class="f-tips">請輸入正確的賬號</span>
<br />
密碼:
<input data-title="密碼" placeholder="請輸入正確的密碼" pattern="\w{6,10}" name="password" type="password" required />
<span class="f-tips">請輸入正確的密碼</span>
<br />
<input name="button" type="submit" value="提交" />
</form>
掃一眼,嗯,挺簡單的,都是常用的東西。咦,不對,這個 pattern 是什么東西?
在這里我們重點分享下 pattern 這個屬性,這是一個用來驗證 input[value] 是否合法的屬性,里面的內容就是匹配value的,語法便是正則的語法,例子如下:
<label>
<!--
當前pattern的內容就是驗證input[name="part"]的value的,其規則如同里面的正則一樣,匹配input[name="part"]的value是否是一個數字+3個大寫字母
-->
<input pattern="[0-9][A-Z]{3}" name="part" />
</label>
當然,不同的 input[type] 也會默認帶有相應的 pattern ,例如 input[type="email"] 就是默認匹配了以下規則:
/^[a-zA-Z0-9.!#$%&'+\/=?^_`{|}~-]+@a-zA-Z0-9?(?:.a-zA-Z0-9?)$/
1
第二步:重點功能
input[type="text"]:invalid ~ input[type="submit"],
input[type="password"]:invalid ~ input[type="submit"] {
display: none;
}
input[required]:focus:invalid + span {
display: inline;
}
input[required]:empty + span {
display: none;
}
input[required]:invalid:not(:placeholder-shown) + span {
display: inline;
}
上面便是核心交互的實現。
首先第一個class就是保證了在兩個輸入框不通過的時候隱藏,就是當輸入框值為空或者不符合驗證規則,則隱藏提交按鈕。
第二個,第三個class則是控制當用戶在輸入框輸入內容時,如果不符合驗證規則,則顯示錯誤信息,否則則隱藏。
第四個class則是用過 placeholder 是否存在來控制錯誤信息的顯隱,如果 placeholder 不顯示,則證明用戶正在輸入,錯誤信息則根據用戶輸入的值來判斷是否顯隱,否則則隱藏。
狀態切換
上面我們有提到一個選擇器 :indeterminate ,這個是用于選擇狀態不確定的表單元素與 <progress> ,玩過掃雷的人都知道,右擊除了可以選擇紅旗,還可以選擇問號,就是選中,但不確定;又跟 promise 的 pending 狀態類型,介于 resolve 與 reject 之間。
多了 :indeterminate 會給我們帶來很多很有趣的體驗。
首先我們來看看它的使用案例。
基礎使用法
先看效果
代碼如下:
<style>
body {
background: #333;
color: #fff;
padding: 20px;
text-align: center;
}
input {
margin-right: .25em;
width: 30px;
height: 30px;
}
label {
position: relative;
top: 1px;
font-size: 30px;
}
</style>
<form>
<input type="checkbox" id="checkbox">
<label for="option">點擊左邊</label>
</form>
<script>
'use strict';
checkbox.addEventListener('click', ev => {
if (ev.target.readOnly) {
ev.target.checked = ev.target.readOnly = false;
} else if (!ev.target.checked) {
ev.target.readOnly = ev.target.indeterminate = true;
};
});
</script>
這里面其實沒有什么復雜的實現,只是做了個中間態的判斷,就非常輕松的實現了radio的三種狀態切換。
秀到頭皮發麻法
先看效果
(此天秀效果來自于 Ben Szabo 的 codepen,有興趣的可以仔細研究下,我何時才能有大佬這么優秀,嚶嚶嚶~)
輸入框綁定的可選值
先看效果
其實代碼很簡單:
<input type="text" list="names" multiple />
<datalist id="names">
<option value="kris">
<option value="陳大魚頭">
<option value="深圳金城武">
</datalist>
<input type="email" list="emails" multiple />
<datalist id="emails">
<option value="chenjinwen77@foxmail.com" label="kris">
<option value="chenjinwen77@gmail.com" label="kris">
</datalist>
<input type="date" list="dates" />
<datalist id="dates">
<option value="2019-09-03">
</datalist>
這里原理就是通過 <input list="dates" /> 來綁定需要下拉顯示的數據列表 <datalist id="dates"> 。
那么當我們要實現輸入聯想的時候,也可以通過修改 <datalist id="dates"> 的子元素來實現,而不是再寫一大堆的操作函數來實現。
總結
JS----預編譯及變量提升詳解
JS屬于解釋型語言,在執行過程中順序執行,但是會分塊先預編譯然后才執行。因此在JS中存在一種變量提升的現象。搞懂預編譯環節,變量提升自然而然也就懂了。本文講圍繞以下幾點進行介紹(變量提升會穿插在其中講解):
預編譯執行步驟
示例演示
預編譯執行步驟
預編譯發生在函數執行的前一刻,過程如下:
創建AO對象,執行期上下文(后面更新關于執行期上下文詳解)。
尋找函數的形參和變量聲明,將變量和形參名作為AO對象的屬性名,值設定為undefined.
將形參和實參相統一,即更改形參后的undefined為具體的形參值。
尋找函數中的函數聲明,將函數名作為AO屬性名,值為函數體。
至此,預編譯環節結束,函數中咯變量按照最終AO對象中的值開始執行。接下來,結合示例演示就會更加清晰。
作者:北海北方
鏈接:https://juejin.im/post/5aa6693df265da23884cb571
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
示例演示
我們先來看下面這段代碼:
function fn(a){
console.log(a);
var a = 123;
console.log(a);
function a(){};
console.log(a);
var b = function(){};
console.log(b);
function d(){};
}
//調用函數
fn(1);
作者:北海北方
鏈接:https://juejin.im/post/5aa6693df265da23884cb571
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
接下來我們來按照前面的步驟詳細分析它的預編譯執行過程:
創建AO對象
AO{
//空對象
}
復制代碼
找形參和變量聲明
AO{
a : undefined,
b : undefined
}
復制代碼
形參和實參相統一
AO{
a : 1,
b : undefined
}
復制代碼
找函數聲明
AO{
a : function a(){},
b : undefined,
d : function d(){}
}
復制代碼預編譯環節就此結束,此時的AO對象已經更新為:
AO{
a : function a(){},
b : undefined,
d : function d(){}
}
復制代碼函數開始逐行順序執行:
function fn(a){
console.log(a);// 輸出functiona(){}
var a = 123;//執行到這里重新對a賦,AO對象再一次更新
console.log(a);// 輸出123
function a(){};//預編譯環節已經進行了變量提升,故執行時不在看這行代碼
console.log(a);// 輸出123
var b = function(){};//這個是函數表達式不是函數聲明,故不能提升,會對AO中的b重新賦值
console.log(b);//輸出function(){}
function d(){};
}
復制代碼至此,函數執行完畢,銷毀AO對象。
我們再來看幾個例子,熟悉函數的預編譯過程。
示例一:
function test (a,b){
console.log(a);
c = 0;
var c;
a = 3;
b = 2;
console.log(b);
function b(){};
function d(){};
console.log(b);
}
//調用函數
test(1);
復制代碼它的AO創建過程如下(此處省略創建空AO對象的部分,下文同):
AO1{
a : undefined,
b : undefined,
c : undefined
}
AO2{
a : 1,
b : undefined,
c : undefined
}
AO3{
a : 1,
b : function b(){},
c : undefined,
d : function d(){}
}
復制代碼至此預編譯環節完成,開始執行:
function test (a,b){
console.log(a); //輸出1
c = 0; //給AO對象中的c重新賦值0
var c;//預編譯環節變量提升,不再讀此行代碼
a = 3;//給AO對象中的a重新賦值3
b = 2;//給AO對象中的b重新賦值2
console.log(b);//輸出2
function b(){};//預編譯環節變量提升,執行時不再讀這行代碼
function d(){};//預編譯環節變量提升,執行時不再讀這行代碼
console.log(b);//輸出2
}
//調用函數
test(1);
復制代碼示例二:
這個例子中我們引入全局對象GO。GO與AO的過程類似
function test(){
var a = b = 123;
}
test();
復制代碼此函數的執行過程:先把123賦給b,再聲明a,再把b賦給a。此時變量b未經聲明就賦值,為全局變量。預編譯環節如下:
GO1{
b : undefined
}
AO1{
a : undefined
}
GO2{
b : 123;
}
AO2{
a : 123;
}
復制代碼示例三 :
console.log(test);
function test(test){
console.log(test);
var test = 234;
console.log(test);
function test(){};
}
test(1);
var test = 123;
復制代碼我們來看它的預編譯過程:
//執行前(頁面加載完成時)生成GO對象
GO1{
test : undefined
}
GO2{
test : function(){}
}
//輸出 function test(){...}
//執行test()前生成它的AO對象
AO1{
test : undefined
}
AO2{
test : 1
}
AO3{
test : function test(){}
}
//預編譯結束開始執行test(1);
AO4{
test : 234
}
//輸出234
復制代碼示例四:
function demo(){
console.log(b);
if(a){
var b = 100;
}
console.log(b);
c = 234;
console.log(c);
}
var a;
demo();
a = 10;
console.log(c);
復制代碼我們來看它的預編譯過程:
//首先是全局對象GO
GO1{
a : undefined
}
G02{
a : undefined,
demo : function demo(){}
}
//執行demo()前預編譯,由于demo中的c未聲明就使用故為全局對象
//輸出undefined
GO3{
a : undefined,
demo : function demo(){}
c : undefined
}
//此時a還是undefined,故不執行if()代碼塊
//輸出還是undefined
GO4{
a : undefined,
demo : function demo(){}
c : 234;
}
//輸出234
GO5{
a : 10,
demo : function demo(){}
c : 234;
}
//輸出234
nodejs中使用別名映射,兼容webpack的@和best-require 的:xxx 別名映射
項目地址: https://github.com/langyuxiansheng/biu-server-admin
寫在前面
研究了很久,找了很多資料發現都沒有,只好自己去想辦法,查資料.才弄好的,凌晨發布的,轉載請注明出處.
在做nodejs項目開發的時候,你是不是也在為
require('./posts');
require('./controllers/posts');
require('../controllers/posts');
require('../../controllers/posts');
require('../../../apis/controllers/posts');
或者
require(ROOT_PATH + '/application/apis/controllers/posts');
// other require()...
require(ROOT_PATH + '/application/apis/controllers/users');
require(ROOT_PATH + '/application/apis/controllers/products');
require(ROOT_PATH + '/application/apis/services/rest');
require(ROOT_PATH + '/application/apis/config');
這樣的寫法而困擾;
那看完這篇文章,從此之后就可以告別這個煩惱了;
感謝一下 best-require 這個模塊包的作者,不然還需要自己去寫這個
npmjs 鏈接 https://www.npmjs.com/package/best-require
github 鏈接 https://github.com/yuezhihan/best-require
不廢話了,進入正題 往下看:
/server/src
);vue 生命周期
每個 Vue 實例在被創建時都要經過一系列的初始化過程。設置數據監聽、編譯模板、掛載等等。vue生命周期還是不太容易理解,這里就簡單地說一下它的整個過程。
1創建一個vue實例
new vue({
data () {
return {
}
}
})
2 初始化事件和生命周期 beforeCreate 創建實例之前執行的鉤子函數
3 初始化·注入和校驗 created 實例創建完成后執行的鉤子
new vue ({
data () {
return {
a: 1
}
},
created: function () {
console.log('created')
}
})
4 渲染頁面 編譯 beforeMount 將編譯完成的html掛載在虛擬dom時執行的鉤子
5 mouted鉤子 掛載完畢對數據進行渲染 會做一些ajax情求初始化數據 mounted整個實例過程中只執行一次
new vue ({
data () {
return {
a: 1
}
},
created: function () {
console.log('created')
},
// 一些鉤子函數
mouted: function () {
console.log('mounted')
}
})
6 修改數據 beforeUpdate 更新之前的鉤子
7 updated 修改完成重新渲染
8 準備解除綁定子組件以及事件監聽器 beforeDestroy
9 銷毀完成 destroyed
css:
*{margin:0;padding:0;}
background: rgb(201, 230, 128);text-align: center;}
html:
<body>
<div id="tu">
<P id="info"></P>
<img src="jiao.jpg" alt="冰棒">
<input type="button" id="yi" value="第一張">
<input type="button" id="er" value="第二張">
</div>
</body>
javascript:
window.onload = function(){
var yi = document.getElementById(“yi”);
var er = document.getElementById(“er”);
var img = document.getElementsByTagName(“img”)[0];
var imgArr = [ “jiao.jpg”,“san.jpg”,“bao.jpg”,“hua.jpg”,“pei.jpg”,“tu.jpg”,“xin.jpg”,“niu.jpg”]
var index = 0 ;
var info = document.getElementById(“info”);
info.innerHTML = "一共" + imgArr.length + "張圖片,當前第"+(index+1)+"張";
yi.onclick = function(){
index--;
if(index < 0){
index = imgArr.length - 1;
}
img.src = imgArr[index];
info.innerHTML = "一共" + imgArr.length + "張圖片,當前第"+(index+1)+"張";
}
er.onclick = function(){
index++;
if(index > imgArr.length - 1){
index = 0;
}
img.src = imgArr[index];
info.innerHTML = "一共" + imgArr.length + "張圖片,當前第"+(index+1)+"張";
}
}
1,從程序上看,遞歸表現為自己調用自己,遞推則沒有這樣的形式。
2,遞歸是從問題的最終目標出發,逐漸將復雜問題化為簡單問題,最終求得問題
是逆向的。遞推是從簡單問題出發,一步步的向前發展,最終求得問題。是正向的。
3,遞歸中,問題的n要求是計算之前就知道的,而遞推可以在計算中確定,不要求計算前就知道n。
4,一般來說,遞推的效率高于遞歸(當然是遞推可以計算的情況下)
最容易理解就是結合一個經典的例子:斐波那契數列
遞歸求解
int fib(n){
return n < 2 ? 1 : fib(n-1)+f(n-2);
}
遞推求解
int fib(int n){
int fn = 1;
int fn_1 = 0;
for(int i=0; i<n; i++) {
int t = fn
fn = fn + fn_1;
fn_1 = t;
}
return fn;
}
遞推 Inductive 是從1 往 n推(未知)
遞歸Recursive是從n(未知)往1推, 再層層返回
藍藍設計的小編 http://www.syprn.cn