如果说 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.lanlanwork.com