GithubHelp home page GithubHelp logo

blog's Introduction

blog's People

Contributors

harleywang93 avatar

Stargazers

Fatboy avatar  avatar 那只喵的憨雄 avatar JoeyLin avatar Lisen avatar Ning Cheng avatar  avatar Zachery Liu avatar lncircle avatar  avatar ly avatar Alex Blue avatar  avatar 周坚 avatar  avatar 程序员小明 avatar mvilplss avatar  avatar  avatar  avatar  avatar SPARON avatar  avatar Maizi avatar willa avatar hurc咸鱼 avatar xieqingtian avatar chentt avatar jimdeng92 avatar  avatar  avatar KodSen avatar  avatar Steven Liang avatar kawav avatar

Watchers

James Cloos avatar  avatar

blog's Issues

DOM0、DOM1、DOM2级事件

DOM(Document Object Model,文档对象模型)是针对HTML文档和XML文档的一个API。DOM描绘了一个层次化的节点树,允许开发人员添加、移出和修改页面的某一部分,DOM 脱胎于Netscape 及微软公司创始的 DHTML(动态HTML)。但现在它已经成为表现和操作页面标记的真正跨平台、语言中立的方式。

Netscape Navigator 4 和 IE4 分别发布于 1997 年的 6 月和 10 月发布的 DHTML,由于 IE4 和 Netscape Navigator4 分别支持不同的 DHTML,为了统一标准,W3C开始制定 DOM。1998 年10 月 W3C 总结了 IE 和 Navigator4 的规范,制定了 DOMLevel 1即 DOM1,之前 IE 与 Netscape 的规范则被称为 DOMLevel 0 即 DOM0 。

DOM0级事件

假设页面中存在一个 btn 的按钮,并且给 btn 添加一个点击事件

btn.onclick = function(){
   console.log('this is a click event')
}

事件就是用户或浏览器自身执行的某种操作,如click、load、mouseover等,都是事件的名字,而响应某个事件的函数就被称为事件处理程序。

click事件过程

在上述的例子中,click 事件并没有像其他函数一样,必须要调用才可以执行,click 事件并不确定什么时候发生,而当浏览器发现用户点击该按钮时,浏览器就检测btn.onclick是否有值,如果有,就会执行btn.onclick.call(btn,event),此时函数执行,call() 方法接收两个参数,第一个指向调用当前方法的对象,也就是this

需要注意的是,指定的 this 值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象。

另一个参数则是事件对象 event,该对象也可以通过 arguments[0] 来访问,它包含了事件相关的所有信息,如本例子中,则包含了点击事件的全部信息。可以通过给函数传参来获取事件信息。

btn.onclick = function(e){
   console.log('this is a click event');
   console.log(e);  //  事件对象
    
}

但是在 IE 中,在使用 DOM0 级方法添加事件处理程序时,event 是作 window 对象的一个属性而存在的。此时访问事件对象需要通过 window.event

btn.onclick = function(){
   console.log(window.event);  //  IE中事件对象    
}

在 DOM0级中,如果想要实现一个对象绑定多个函数,可以这样实现。

function fn1(){
    // do something
}
function fn2(){
    // do something
}
btn.onclick = function(e){
  fn1.call(this,xxx);
  fn2.call(this.yyy);
}

DOM2级事件

W3C 后来将 DOM1 升级为 DOM2,DOM2级规范开始尝试以一种符合逻辑的方式来标准化 DOM事件。DOM0级 可以认为 onclick 是 btn 的一个属性,DOM2级 则将属性升级为队列。

DOM2级 事件定义了两个方法,用于处理指定和删除事件处理程序的操作,addEventListener()removeEventListener(),所有的 DOM 节点中都包含这两个方法,它们都接收 3 个参数。

  1. 要处理的事件名

  2. 作为事件处理程序的函数

  3. 布尔值,true 代表在捕获阶段调用事件处理程序,false 表示在冒泡阶段调用事件处理程序,默认为 false。

btn.addEventListener('click',function(){
  //  do something
})
btn.addEventListener('click',function(){
  //  do something else
})

addEventListener()将事件加入到监听队列中,当浏览器发现用户点击按钮时,click 队列中依次执行匿名函数1、匿名函数2。

function fn1(){
  //  do something
}
function fn1(){
  //  do something else
}
btn.addEventListener('click',fn1)
btn.addEventListener('click',fn2)

如果这样写,click 队列中依次fn1.call(btn,event)fn2.call(btn,event)

通过addEventListener()添加的事件只能由removeEventListener()来移除,并且removeEventListener()只能移除具名函数,不能移除匿名函数。

IE 中 DOM2级事件

IE8 及之前,实现类似addEventListener()removeEventListener()的两个方法是attachEvent()detachEvent(),这两个方法接受相同的两个参数。

  1. 要处理的事件名

  2. 作为事件处理程序的函数

IE8 之前的只支持事件冒泡,所以通过attachEvent()添加的事件处理程序只能添加到冒泡阶段。

btn.attachEvent('click',fn1)
btn.attachEvent('click',fn2)

当用户点击时,click 队列依次fn1.call(undefined,undefined)fn2.call(undefined,undefined)

类似的detachEvent()也只能移除具名函数,不能移除匿名函数。

兼容处理

if(typeof btn.addEventListener === 'function'){
  btn.addEventListener('click',fn);
}else if(typeof btn.attachEvent === 'function'){
  btn.attachEvent('onclick',fn)
}else{
  btn.onclick=function(){
    // do something
  }
}

Mac Terminal - iTerm2 配置

前言

作为一名开发者,我们会花很多时间在终端上,所以对终端的效率、颜值有很高的要求。

本文会分享 Terminal 在颜值(个性化)和效率( Oh-My-Zsh )两方面的配置。

Iterm2

简介 Introduction

iTerm2 is a replacement for Terminal and the successor to iTerm. It works on Macs with macOS 10.8 or newer. iTerm2 brings the terminal into the modern age with features you never knew you always wanted.

iTerm2 是一款免费的,专为 Mac OS 用户打造的命令行工具。作为系统自带 Terminal 的替代,它提供了很多方便的功能。比如隔离的面板、透明窗口、强大的正则表达式搜索、自动补全等功能,了解更多 iTerm2 的特性见 iTerm2 Features

安装 Installation

下载安装见 Iterm2 Downloads

iTerm2 个性化配置

对于命令行的使用,一般有两种情景:

  • 短时间使用。执行几条命令就关闭,比如打开文件,启动服务等。

  • 长时间使用。比如使用 Vim 编程。

所以,我们可以尝试配置两个 Profile 。

第一种配置

iTerm2_hotkey_1

  • 新建 Profile

首先,在 Preperences → Profiles,新建一个 Profile,命名为 Hotkey Window,如图:

hotkey_window_profile

  • 背景设置

Preperences → Profiles → Window → Window Appearance 进行设置,如图:

window

  • 窗口设置

Preperences → Profiles → Window → Settings for New Windows 进行设置,如图:

settings_window

  • HotKey 设置

Preperences → Profiles → Keys → HotKey 进行设置,如图:

hotkey_window

第一种 Profile 配置完成,您可以按下你设置的 HotKey 来方便快速打开和隐藏命令行。

第二种配置

iTerm2_hotkey_2

第二种配置在第一种配置的基础上稍微修改一下就可以了。

  • 复制 Hotkey Window

Preperence → Profiles,复制 Hotkey Window, 取名 Default of Hotkey Window,如图:

copy-hotkey-window1

copy-hotkey-window2

  • 窗口配置

Preperences → Profiles → Default of Hotkey Window → Window → Settings for New Windows 进行设置,如图:

dofw_window

  • 设置默认

如果要使用这种风格,设置为默认即可。

Preperences → Profiles 中,选中 Default of HotKey Window, 点击下方 Other Actions → Set as Default,如图所示:

set_default

至此,两种 Profile 配置完毕。

oh-my-zsh

简介 Introduction

目前常用的 Linux 系统和 OS X 系统的默认 Shell 都是 bash,但是真正强大的 Shell 是深藏不露的 zsh,这货绝对是马车中的跑车,跑车中的飞行车,是「终极 Shell 」,但是由于配置过于复杂,所以初期无人问津,很多人跑过来看看 zsh 的配置指南,什么都不说转身就走了。直到有一天,国外有个穷极无聊的程序员开发出了一个能够让你快速上手的zsh项目,叫做「oh my zsh」,Github 网址是: https://github.com/robbyrussell/oh-my-zsh

oh-my-zsh 是用来管理 zsh 配置的,自带很多好用的基本配置,基本都是 zsh 的标配了,我们不用再一步步重新配置 zsh ,节约了宝贵时间。

安装 Installation

  • via curl
sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
  • via wget
sh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)"

详情见 oh-my-zsh

主题配置 Themes

zsh 的配置主要集中在用户当前目录的 .zshrc 里,用 vim 或者你喜欢的其他编辑器打开这个文件,在里面可以修改主题、插件以及定义自己的环境变量等操作。

打开 .zshrc ,找到这行语句:

ZSH_THEME="robbyrussell"

修改 ZSH_THEME 的值就可以更换主题。

默认的主题是 robbyrussell ,不喜欢的话可以自行更换。

更多主题见 oh-my-zsh_themes,还有很多不是自带的主题 oh-my-zsh_External-themes

Powerline Fonts

这里踩了个小坑,如果您选择了其中一款主题,打开终端或者 Vim 之后发现这样问号的情况:

no_poweline_fonts

原因是依赖于一种字体 powerline fonts ,powerline fonts 是 vim 增强组件 vim powerline 附属的字体,所以要在系统中安装该字体。

解决办法:

  • 安装 powerline fonts
# clone
git clone https://github.com/powerline/fonts.git --depth=1
# install
cd fonts
./install.sh
# clean-up a bit
cd ..
rm -rf fonts
  • 配置iTerm2

Preperences → Profiles → Text → FontNon-ASCII Font
将字体改为 Source Code Pro for Powerline ,如图:

font

插件 Plugins

打开 ~/.zshrc 找到这行语句:

plugins=(git)

修改 plugins 的值就可以添加或者删除插件,比如:

plugins=(git autojump osx zsh-autosuggestions)

下面是几个常用插件,更多插件见 Plugins

autojump

A cd command that learns - easily navigate directories from the command line

一款快捷跳转目标路径的插件,支持模糊匹配,自动补全,历史记录等功能。

  • 安装 INSTALLATION

Homebrew is the recommended installation method for Mac OS X:

brew install autojump
  • 然后修改 plugins 的值即可。

更多内容见 autojump

osx

This plugin provides a few utilities to make it more enjoyable on OSX.

一款增加了一些在 OSX 上实用的命令插件。

  • osx 插件是内置插件,不需要安装,修改 plugins 的值即可。

下面是一些命令:

Command Description
tab Open the current directory in a new tab
split_tab Split the current terminal tab horizontally
vsplit_tab Split the current terminal tab vertically
ofd Open the current directory in a Finder window
pfd Return the path of the frontmost Finder window
pfs Return the current Finder selection
cdf cd to the current Finder directory
pushdf pushd to the current Finder directory
quick-look Quick-Look a specified file
man-preview Open a specified man page in Preview app
showfiles Show hidden files
hidefiles Hide the hidden files
itunes Control iTunes. User itunes -h for usage details
spotify Control Spotify and search by artist, album, track and etc.

zsh-autosuggestions

Fish-like autosuggestions for zsh

一款自动建议插件

  • 安装 Installation

Clone this repository into $ZSH_CUSTOM/plugins (by default ~/.oh-my-zsh/custom/plugins)

cd ~/.oh-my-zsh/custom/plugins
git clone git://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions
  • 修改 plugins 的值,然后重启终端即可。

常用 iTerm2 功能快捷键

  • cmd + n : 新建窗口

  • cmd + t : 新建TAB

  • cmd + d : 垂直新建TAB

  • cmd + shift + d : 水平新建TAB

感谢

谢谢很多前辈的分享,让我学习到了很多,非常感谢!

校园霸凌

前言

前段时间看了很多关于校园霸凌的新闻和评论,以及受到霸凌的受害者和施暴者关于经历或参与校园霸凌的描述,让我联想起了自己参与或经历的校园霸凌,所以写了这么一篇随感。

亲身经历

小学

小学时期,班上有几位农村来的同学,跟班里大多数同学在穿着或行为上有很多不一样的地方。

一些同学拿他们取笑或作弄他们,这些同学里面就包括我,在我的印象中有以下的行为:

  1. 口头上编一些有关他们的歌词或者顺口溜来取笑他们。
  2. 说他们身上有“病毒”,谁碰了他们就要被“传染”。我们就故意去碰他们,然后去碰下一个同学,就这样追逐打闹。
  3. ...

我写下这些是给自己看的,目的是牢记自己做过这样的罪行,并忏悔一辈子。

初中

初中时期,我有一些家住着很近的同学,每天上学放学都一起,基本上每个周末也都约着去打篮球,自然也发展成很要好的朋友。其中有一位 L 同学,他性格开朗,篮球也打的不错。

当时班上有 2 个同学对他人有霸凌行为。他们喜欢挑衅并狠踹他人,有把同学的脑袋按在垃圾堆里的行为。我有多次被他们挑衅并狠踹的经历。

L 和我一样,都受到过那 2 个同学的霸凌。我小时候很胆小,性格懦弱,有过反抗但没用。L 不一样,他选择跟他们死磕到底,甚至后来选择带刀子上学。

有一天下午放学后,那 2 个同学叫来了几个其他学校的霸凌者和几个社会闲散人员,羞辱并殴打了 L。后来这件事的处理我不太记得了。

我只记得这件事之后,L 同学有些变了,有了一些奇怪的行为。(后来,L 在升入跟我不同的高中之后,开始参与打架,也惹出了事。)在这之后,我们这一帮要好的朋友,都开始疏远 L,并嘲笑他的那些奇怪行为。

在读大学放假回家期间,我们那一帮老同学老朋友出来聚餐,还时不时会拿 L 的奇怪行为来取笑。

现在想来,L 的奇怪行为都是因为受到霸凌而产生的一些反应,他是个受害者,但我们却拿他取笑。

所以,在我初中的时候,受到过霸凌,也做了霸凌者的帮凶。

之前我跟 L 聊过一次,我真诚的道了歉,他也接受了道歉。虽然他嘴上说无所谓,但我知道原谅是不可能的,这些事改变了他的人生轨迹。

不知道这些以前跟 L 要好但后来疏远嘲笑L的老同学有没有认识到这一点,虽然羞辱殴打 L 的不是我们,但后来我们的疏远嘲笑也一样霸凌了 L,而且在大学期间都还没有意识到这个错误。甚至现在,好些老同学可能都没有悔悟。

高中 & 大学

很幸运的是,我高中时期和大学时期没有经历或参与任何校园霸凌。

美国处理霸凌的方式

据一位在美国教书的**教师观察,如果一个孩子以“我不是故意的”为借口逃脱掉一件主观做错事的责任,他“不小心”欺负人的情况会越来越多。今天不小心打了一下其他人,过几天不小心拿铅笔扔到别人的身上,甚至,不小心把其他孩子的大把头发扯下来!

那么美国小学是如何处理这些事情的呢?这位自称“小杨老师”的人举了个例子。

有一天在操场的时候,他突然看到几个孩子围在一起,地上有一大把头发。他捡起头发立马询问怎么回事。旁边有孩子跟老师解释:“ Mark 把 Betty 的头发扯下来了!”看着那一大把被生生扯下来的头发,Mark 一开始声称这只是个游戏,但看到老师的表情很严肃,也开始有点害怕。“小杨老师”告诉他:“第一,没有扯人头发的游戏。第二,你所谓的“游戏”不好玩,甚至给他人造成了痛苦! 第三,你需要为这件事情负责。”

随后“小杨老师”把那一撮头发保存到一个塑料袋子里作为证据。然后立马报告校长。鉴于 Mark 在学校近期其他欺负孩子的表现,校长找两个孩子谈话,并且当场打电话给两个孩子的父母。Betty 的妈妈很快赶过来,把 Betty 接去家庭医生那里做检查。Mark 在和校长一番谈话后接受了以下处理:

  1. 书面写信给 Betty 道歉。
  2. 因为涉及到身体伤害,以及近段时间其他表现,Mark 被正式停学一天。
  3. Mark 失去一个星期的 Recess 休息时间,在休息时间被派去食堂帮忙。

这次痛的教训让 Mark 意识到:对别人的伤害,有时候不是一句“对不起”就能解决的。如果孩子在犯错不接受应有的处罚,下次再犯的概率很高,因为孩子发现可以找机会侥幸逃脱。而每一次犯错都自己承担了责任和后果,孩子会学会对自己的行为负责。就算是不小心,也需要弥补对他人造成的伤害,哪怕是一句真心诚意的“对不起”!

美国纽约洛克菲勒大学研究人员发现,遭受霸凌过的老鼠,脑部掌管情绪的部位会改变,容易对压力产生敏感,长期下来会出现“社交障碍”,恐惧任何新环境,即便是安全的环境,老鼠还是会害怕结交新朋友。

同样的,许多遭受过霸凌的孩子,也都会产生退缩怯懦、缺乏自信的后遗症。而新闻中的孩子就出现失眠、恐惧上学等症状,后被诊断为急性应激反应。这种症状还与学校老师处理事情的方式相关。如果事件不能得到有效处理,霸凌他人的孩子不能从根本认识到自己行为严重性并且承担相应的后果,被欺负的孩子会持续生活在再次受霸凌的恐慌中。

所以,老师的一个小小的举动和认同,对孩子却是一个世界的肩膀可以依靠。起码,他在学校是感到安全的。

在美国小学,关于防止校园霸凌的项目有很多,而小杨老师所在学校使用的是 Second Step 情商教育,目前美国小学有很多学校都在用这个体系,里面有专门防止校园霸凌的教材。

  • 下面的一段文字是转自 心路独舞 的公众号文章。

很多网友留言问我,在美国这样的案子会怎样处理?我去搜索了近期在美国发生的相似案例,还真找到了一些。

2015年9月,美国内布拉斯加(Nebraska)州Springfield市Platteview高中的一名16岁学生,从学校的化学实验室里拿了一些硫酸铜,投放到另一名同学的饮料瓶中,并在午餐的时候激将那位学生喝下去(注意这里的情节比黄山那个要轻些,并不是偷偷下药)。后者喝下去后有恶心的感觉,好在到医院处理后证实对健康不会有什么影响,但那位投药的同学却被当地Sarpy郡治安官调查并短暂收容进郡少年拘留所,后被指控并定罪“2级未遂袭击”(attempted 2nd degree assault)。在内布拉斯加州,取决后果的严重程度“2级袭击罪”的惩罚结果也不一样,较轻的属Class IIIA级犯罪,惩罚从1万美元罚款到5年监禁或者二者并罚;较重的是Class III级犯罪,惩罚从2.5万美元罚款到20年监禁或二者同罚。这位犯法的孩子属未成年人,又是“未遂”的2级袭击,判罚会轻一些,但触犯法律的定罪和几个月的少管所时间却是不可避免的。

另一个案子曾被新闻广泛报道,加利福尼亚州斯坦福大学一名亚裔前女研究生由于秘密给其他同学的水瓶里面加化学物质而遭到4项重罪的指控,后被证实患有精神病,但是还是被判决一年监狱时间后驱逐出境。这位来自新加坡的欧阳湘玉(Xiangyu Ouyang,音译)只有26岁,从2014年9月开始往同一个实验室里工作的医学院学生水瓶中投放仲甲醛,这是一种主要用于杀虫剂和清洁剂的化学品,部分同学说喝水之后难受和有烧灼感,但没有造成更严重的伤害。

从上面两个案例来看,在美国只要是有意投药,不管涉事人是否是未成年人,不管是否造成严重的身体伤害,都是作为违法行为来审判的,最终都要依法惩罚,哪怕犯事的人有某种精神障碍。在美国如果是下春药的话,麻烦可能更多,除了投毒的指控外,还难逃想性侵女生的嫌疑,另外“威胁如果找老师或报警会用砒霜毒死”受害人,在美国还犯了“恐吓”罪(intimidation, terror)等,指控的罪名可能就会更多了。

写到这里我突然想起了不久前三名**留学生因欺凌同学,被美国加州法院判处6年至13年的有期徒刑的案子。在这个案子中,三名嫌犯翟芸瑶、章鑫磊和杨玉菡伙同其他**留学生,对两名分别16及18岁的**女同学施以虐待,包括强迫脱光衣服、用烟头烫伤乳头、掌掴、剃头发等,其中18岁的受害者是被绑架至公园施暴,过程持续五小时。最后,三人以绑架、严重人身伤害、攻击罪以及攻击导致的严重人身伤害等罪名分别被判处13年、6年和10年监禁,服刑期满后将被遣返回**。这个案子在**民间引起广泛的热议,不少人评论说,这些在**顶多被行政警告的恶行,在美国却是违法,如果按美国法律判决的话,**的很多校园欺凌案件中的施虐人都该进监狱了。

而回到黄山黄家炳实验中学的案子(具体情况见文末最后一条外链),我不仅对受害女生受到的诽谤和压力感到悲哀,对施害者能轻易逃脱惩罚感到无比的气愤,更对黄山教育局的回应感到吃惊,药不是买的,连店主也找不到了,既然他自己喝过没反应为什么还要多此一举倒女生瓶里?这种回应让人不免觉得把责任推得很干净,就这样的调查回应还是女生发帖之后的压力下产生的,更多的还需要我去说破吗?

道歉

上述几位小学同学已经联系不上了,如果碰巧遇到了你们,而我还能认出来你们,我一定会向你们道歉。

我在网上看到这样的一段话,是关于一个霸凌者想找到被霸凌者道歉的评论(详情见见倒数第二个外链),我觉得说的很对:你觉得对不起他,你觉得自己做错了。
那你在午夜自己好好反省就好了。如果碰巧遇到了他,请给他道歉。但是,请不要主动的再去打扰他的生活了,你在对方心里不过是永远不想提起的伤疤制造者,不是每个人都能够坚强到揭开伤疤的。

所以,我在这里想对几位受到我欺凌或者间接受到欺凌的同学说:对不起!现在我知道了这些行为对你们造成的伤害是无法想象的,这些伤害产生的影响是一生的,我在这里写下这些屁话也算是忏悔的一种方式吧。我明白你们心里不会原谅我,我也不应该被你们原谅,更不配被你们原谅,我只奢求你们能接受我的道歉,对不起!

随感

不管什么形式,霸凌就是霸凌。

孩子的世界是最简单也是最直接粗暴的,某种程度上比成年人的世界还残忍。

作为霸凌者或者帮凶,我们应该对受到霸凌的同学真诚的道歉,如果碰巧遇到他们,那么请真诚道歉,但不要主动去找他们。他们心里是不会原谅我们的,我们的行为改变了他们的人生轨迹,这是无法原谅的,我们也不应该被他们原谅,更不配被他们原谅,我们应该为此忏悔一辈子。

最可恨的是那些霸凌者多年之后,忘记了这些事情,或者他们心里根本不认为这是对他人造成的莫大伤害。这是非常恶心的,就像下面的第一条外链提到的事情一样恶心。

这些事情跟学校和老师的处理脱不开干系,学校的领导和老师遇到这些事情的时候,思考的根本不是学生,而想的是“维稳”,对学生本身压根不够重视,采取的措施就是“和稀泥”,往往就扯什么同学间开玩笑啊之类的。开玩笑有把同学的头按到垃圾桶里羞辱的吗?开玩笑有逼迫同学吃屎的吗?开玩笑有给同学下春药的吗?最可笑的也是最经典的屁话是“一个巴掌拍不响,TA 为什么不欺负别人就欺负你?是不是你也有问题?”,这是什么逻辑?!关于这个我看过一个很好的回答“这种时候,你就应该抽那人一耳光,然后问 TA 一个巴掌到底响不响?”。

有关校园霸凌

如何看待“33年后首次同学聚会,全班向当年欺负过的女生道歉”?
你遭受「霸凌」或「校园暴力」的经历是怎样的?事后如何处理?
校园霸凌:怕麻烦的人,只会流血
谈谈校园霸凌(school bullying)
校园欺凌可以有多严重?
应该如何看待校园欺凌事件?
如何看待西南财经大学通博楼占座者及其男友发起的校园霸凌事件?
长大后,想对曾经遭受校园欺凌的自己说些什么?
该怎么向被我欺凌过的同桌道歉?
如何看待「高三男生对同班女生下春药,并在事后威胁如果告发就下砒霜」的欺凌事件?

常见的几种排序算法总结

程序,了解一下

一个程序主要包括以下两方面的信息:

  • 对数据的描述,在程序中要指定用到哪些数据类型以及这些数据的类型和数据的组织形式,这就是数据结构。

  • 对操作的描述,即要求计算机进行操作的步骤,就是算法

  • 对于程序设计人员来说,算法,数据结构,程序化设计方法和程序语言是其所应的知识,算法是灵魂,数据结构是加工对象,语言是工具。

几种常见排序

关于排序算法通常我们所说的往往指的是内部排序算法,即数据记录在内存中进行排序。

排序算法大体可分为两种:

一种是比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等。

另一种是非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序等。

冒泡排序

介绍

冒泡排序它重复地走访过要排序的元素,一次比较相邻两个元素,如果他们的顺序错误就把他们调换过来,直到没有元素再需要交换,排序完成。这个算法的名字由来是因为越小(或越大)的元素会经由交换慢慢“浮”到数列的顶端。

代码实现

function swap(myArray, p1, p2){
  var temp = myArray[p1];
  myArray[p1] = myArray[p2];
  myArray[p2] = temp;
}
function bubbleSort(myArray){
  var len = myArray.length;
  
  for (var i = 0; i < len - 1; i++){
    for (var j = 0; j < len - 1 - i; j++){
      if (myArray[j] > myArray[j + 1]){
        swap(myArray, j, j + 1);
      }
    }
  }
  return myArray;
}
bubbleSort([1,2,4,5,3]) //[1,2,3,4,5]

选择排序

介绍

选择排序类似于冒泡排序,只不过选择排序是首先在未排序的序列中找到最小值(最大值),放到序列的起始位置,然后再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾,以此类推,直到所有元素均排序完毕。

代码实现

function swap(myArray, p1, p2){
    var temp = myArray[p1];
    myArray[p1] = myArray[p2];
    myArray[p2] = temp;
}
function selectionSort(myArray){
    var len = myArray.length;
    var min;
    for (var i = 0; i < len; i++){
        // 将当前位置设为最小值
        min = i;
        // 检查数组其余部分是否更小
        for (var j = i+1; j < len; j++){
            if (myArray[j] < myArray[min]){
                min = j;
            }
        }
        // 如果当前位置不是最小值,将其换为最小值
        if (i !== min){
            swap(myArray, i, min);
        }
    }
    return myArray;
}
selectionSort([1,2,4,5,3])  //[1,2,3,4,5]

插入排序

介绍

插入排序比冒泡排序和选择排序更有效率,插入排序类似于生活中抓扑克牌来。

插入排序具体算法描述,以数组[3, 2, 4, 5, 1]为例。

  1. 将数组分成[3]和[2, 4, 5, 1]两部分,前者是已排序的,后者是未排序的。

  2. 取出未排序部分的第一个元素“2”,与已排序部分最后一个元素“3”比较,因为2小于3,所以2排在3前面,整个数组变成[2, 3]和[4, 5, 1]两部分。

  3. 取出未排序部分的第一个元素“4”,与已排序部分最后一个元素“3”比较,因为4大于3,所以4排在3后面,整个数组变成[2, 3, 4]和[5, 1]两部分。

  4. 取出未排序部分的第一个元素“5”,与已排序部分最后一个元素“4”比较,因为5大于4,所以5排在4后面,整个数组变成[2, 3, 4, 5]和[1]两部分。

  5. 取出未排序部分的第一个元素“1”,与已排序部分最后一个元素“5”比较,因为1小于5,所以再与前一个元素“4”比较;因为1小于4,再与前一个元素“3”比较;因为1小于3,再与前一个元素“2”比较;因为小于1小于2,所以“1”排在2的前面,整个数组变成[1, 2, 3, 4, 5]。

代码实现

function insertionSort(myArray) {
    var len = myArray.length,     // 数组的长度
        value,                      // 当前比较的值
        i,                          // 未排序部分的当前位置
        j;                          // 已排序部分的当前位置
    
    for (i=0; i < len; i++) {    
        // 储存当前位置的值
        value = myArray[i];        
        /*
         * 当已排序部分的当前元素大于value,
         * 就将当前元素向后移一位,再将前一位与value比较
         */
        for (j=i-1; j > -1 && myArray[j] > value; j--) {
            myArray[j+1] = myArray[j];
        }
        myArray[j+1] = value;
    }    
    return myArray;
}
 insertionSort([1,2,4,5,3]) // [1,2,3,4,5]

归并排序

介绍

前面三种排序算法只有教学价值,因为效率低,很少实际使用。

归并排序(Merge sort)则是一种被广泛使用的排序方法。

它的基本**是,将两个已经排序的数组合并,要比从头开始排序所有元素来得快。因此,可以将数组拆开,分成n个只有一个元素的数组,然后不断地两两合并,直到全部排序完成。

以对数组[3, 2, 4, 5, 1] 进行从小到大排序为例,步骤如下:

  1. 将数组分成[3, 2, 4]和[5, 1]两部分。

  2. 将[3, 2, 4]分成[3, 2]和[4]两部分。

  3. 将[3, 2]分成[3]和[2]两部分,然后合并成[2, 3]。

  4. 将[2, 3]和[4]合并成[2, 3, 4]。

  5. 将[5, 1]分成[5]和[1]两部分,然后合并成[1, 5]。

  6. 将[2, 3, 4]和[1, 5]合并成[1, 2, 3, 4, 5]。

代码实现

function merge(left, right){
    var result  = [],
        il = 0,
        ir = 0;
    while (il < left.length && ir < right.length){
        if (left[il] < right[ir]){
            result.push(left[il++]);
        } else {
            result.push(right[ir++]);
        }
    }
    return result.concat(left.slice(il)).concat(right.slice(ir));
}
//上面的merge函数,合并两个已经按升序排好序的数组
  • 有了 merge 函数,就可以对任意数组排序了。基本方法是将数组不断地拆成两半,直到每一半只包含零个元素或一个元素为止,然后就用 merge 函数,将拆成两半的数组不断合并,直到合并成一整个排序完成的数组。
function mergeSort(myArray){
    if (myArray.length < 2) {
        return myArray;
    }
    var middle = Math.floor(myArray.length / 2),
        left    = myArray.slice(0, middle),
        right   = myArray.slice(middle),
        params = merge(mergeSort(left), mergeSort(right));
    
    // 在返回的数组头部,添加两个元素,第一个是0,第二个是返回的数组长度
    params.unshift(0, myArray.length);
	// splice用来替换数组元素,它接受多个参数,
	// 第一个是开始替换的位置,第二个是需要替换的个数,后面就是所有新加入的元素。
	// 因为splice不接受数组作为参数,所以采用apply的写法。
	// 这一句的意思就是原来的myArray数组替换成排序后的myArray
    myArray.splice.apply(myArray, params);
	// 返回排序后的数组
    return myArray;
}
mergeSort([1,2,4,5,3]) // [1,2,3,4,5]

快速排序

介绍

快速排序(quick sort)是公认最快的排序算法之一,有着广泛的应用。

快速排序算法步骤:

  1. 从序列中挑出一个元素,作为”基准”(pivot)。

  2. 把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基 准的后面(相同的数可以到任一边),这个称为分区(partition)操作。

  3. 对每个分区递归地进行步骤1~3,递归的结束条件是序列的大小是0或1,这时整体已经被排好序了。

代码实现

// 部署一个swap函数,用于互换两个位置的值。
function swap(myArray, firstIndex, secondIndex){
    var temp = myArray[firstIndex];
    myArray[firstIndex] = myArray[secondIndex];
    myArray[secondIndex] = temp;
}
// 部署一个partition函数,用于完成一轮排序。
function partition(myArray, left, right) {
    var pivot = myArray[Math.floor((right + left) / 2)],
        i = left,
        j = right;
    while (i <= j) {
        while (myArray[i] < pivot) {
            i++;
        }
        while (myArray[j] > pivot) {
            j--;
        }
        if (i <= j) {
            swap(myArray, i, j);
            i++;
            j--;
        }
    }
    return i;
}
// 递归上面的过程,完成整个排序。
function quickSort(myArray, left, right) {
	if (myArray.length < 2) return myArray;
	left = (typeof left !== "number" ? 0 : left);
	right = (typeof right !== "number" ? myArray.length - 1 : right);
	var index  = partition(myArray, left, right);
	 if (left < index - 1) {
            quickSort(myArray, left, index - 1);
     }
	 if (index < right) {
            quickSort(myArray, index, right);
      }
	 return myArray;
}
quickSort([1,2,4,5,3]) // [1,2,3,4,5]

推荐阅读

排序算法 - 阮一峰

常用排序算法总结

回调函数

在 JavaScript 中我们常听说回调函数,回调函数是一段可执行的代码段,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。通俗来说,回调函数就是就是一个可执行的函数,在需要的地方调用它。

我们知道,对象可以作为参数传递给函数,而函数实际上也是对象,那么函数也可以作为参数传递给函数,在实际使用中,我们可以很容易找到回调函数的例子,例如 jQuery 中

$('div').click(function(){     
  console.log('hello')   
})

其实 click 中的 function 就是个匿名的回调函数,当然,回调函数也可以是具名的

$('div').click(callback)
function callback(){
  console.log('hello')
}

这个回调函数不会立即执行,在传参的过程中,此时传入了回调函数的指针,它并不会立即执行,而是需要等到被调用的时候才会执行。

关于回调函数的 this

在回调函数调用时 this 的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。

例如:

var obj = {
	sum: 0,
	add: function(num1, num2){
		this.sum = num1 + num2;
	}
};
function add(num1, num2, callback){
	callback(num1, num2);
};
add(1,2, obj.add);
console.log(obj.sum);			//=>0
console.log(window.sum);		//=>3

上述代码调用回调函数的时候是在全局环境下,因此 this 指向的是 window,所以 sum 的值是赋值给 window 的。

允许多重回调函数

一个典型的例子就是 jQuery 中 ajax 的使用

function successCallback(){
    //在发送之前做点什么
}     
function successCallback(){
  //在信息被成功接收之后做点什么
}
function completeCallback(){
  //在完成之后做点什么
}
function errorCallback(){
    //当错误发生时做点什么
}
$.ajax({
    url:"http://xxx.com/",
    success:successCallback,
    complete:completeCallback,
    error:errorCallback
})

通过回调函数获取数据

在进行跨域的时候,我们需要获取对应接口的数据并对数据进行相应的处理,那么就可以通过接口所提供的回调函数通过传参来获取数据。

例如 JSONP 跨域时,提供的接口为 http://platform.sina.com.cn/slide/album_tech?jsoncallback=func&app_key=1271687855&num=3&page=4

<script>
  function func(data){
    console.log(data)   //  获取数据
  }
</script>
<script src="http://platform.sina.com.cn/slide/album_tech?jsoncallback=func&app_key=1271687855&num=3&page=4"></script>

一个回调函数中可以嵌入另一个回调函数,对于这种情况出现多层嵌套时,代码会难以阅读和维护,这个时候可以采用命名回调函数的方式调用,或者采用模块化管理函数。

异步编程

我们知道,JavaScript 的执行模式分为两种,异步(Asynchronous)和同步(Synchronous),同步模式指的是代码按照顺序自上而下执行,生活中排队就是同步的一个例子,异步模式指的是代码顺序与执行顺序不保证一致,就好比正好好排队的时候,突然有一个人插队了,这就是异步。但是在浏览器端,异步模式是很重要的,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是 Ajax 操作。在服务器端,”异步模式”甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有 http 请求,服务器性能会急剧下降,很快就会失去响应。而回调函数就是实现异步编程的一个方式。

定时器就是异步实现的一个例子。

var a = 1
setTimeout(function () {
  console.log(a)   // a打印出来是多少呢? 
}, 1000)

我们看到 1s 之后传入一个匿名的回调函数,并打印出 a 的值,你也许会说,a 是 1 。可是你怎么知道后续的 a 会不会被修改呢?假如 a 的值在 1000 行的代码处被修改为 233 或者其他的值,此时 a 的值就不确定了,不管是 setTimeout,还是 setInterval,当我们没有完整的看完代码——也就是代码在之后执行的时候没有确定 a 的值的时候,a 在 1s 后的值在目前的代码片段是无法确定的,异步编程的特点在于当前可执行的代码不阻塞后面代码的执行,而回调函数是实现异步编程的基本方法。

除了定时器,还有事件监听、http 请求、promise 对象中使用回调函数都可以实现异步编程。

推荐阅读

理解与使用Javascript中的回调函数

Javascript异步编程的4种方法

Markdown 基础语法

什么是 Markdown?

Markdown 是一个简单的标记语言,这些标记和 HTML 的一些标签对应
通过一些转换库可以把 Markdown 转换成 HTML 或者把 HTML 转换成 Markdown

Markdown 有什么作用?

用来在网页上展示文章,省去排版布局的烦恼

基本语法

标题

# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题

列表

  • 有序列表
1. 有序列表
2. 有序列表
3. 有序列表
  • 无序列表
- 无序列表
- 无序列表
- 无序列表

超链接

  • 插入链接
[显示文本](链接地址)
  • 插入图片
![](图片链接地址)

引用

  • 单行
> 引用测试

> > 引用内引用测试

效果

引用测试

引用内引用测试

  • 多行

```
var a = 1
var b = 2
```

效果

var a = 1
var b = 2

强调

**粗体测试**
*斜体测试*
~~删除线测试~~

效果

粗体测试
斜体测试
删除线测试

表格

Markdown 使用 |- 来绘制表格,: 可控制左对齐、居中及右对齐。

| 表头测试 | 表头测试 | 表头测试 |
| :--- | :---: | ---: |
| 内容 | 内容 | 内容 |
| 内容 | 内容 | 内容 |

效果

表头测试 表头测试 表头测试
内容 内容 内容
内容 内容 内容

特殊符号

Markdown 利用 \ 字符来转义一些在语法中有特殊意义的符号。

推荐阅读

Markdown 语法说明 (简体中文版)
Markdown 语法手册

如何在没有实际项目经验的情况下找到工作

原文 - How to Land a Development Job Without Experience
译文 - 如何在没有实际项目经验的情况下找到工作

许多开发人员在找工作的时候,虽然满足了对方所要求的理论技能,但是缺乏实际的开发经验,这让雇主在是否雇用你这个问题上犹豫不决。在这篇文章中,具有丰富招聘经验的 Paddy Sherry 为你提供了一些指南,来帮助你提高找到工作的体会。

对于许多年轻的计算机或 IT 相关专业毕业生,在没有实际项目开发经验的情况下找到第一份全职工作可能是你在职业生涯中遇到的最大的挑战。

当你刚毕业的时候,你很容易认为自己知道超级多德有关软件开发的知识,并且能够顺利地找到一份工作。你可能在学校的成绩很高,做了一个了不起的毕业设计,并且研究了最流行的技术和 JavaScript 库。

然而,除非你在十几岁的时候就展现出了超凡的编程能力,并且已经与一些雇主建立了联系或者有在某公司的实习经历。否则你在找第一份工作的时候都是要花费一些时间和精力的。

为什么找第一份工作这么难?

现实中的 Web 和软件开发和你在学校那种环境下所学的是完全不同的。

可能你已经在学校和同学们做过周期长达12周的项目,但是真正工作中的团队协作与你在学校中经历的协作有着很多不同的地方。在团队中,每一位成员都彼此团结一致,为项目的交付而努力。这就意味着你必须要理解好在项目中负责的那部分,并按时向代码库中提交符合规范的代码,在规定时间内进行交付,同时需要确保你负责的功能在所有环境中都能很好地发挥作用,而不仅仅是你的本地机器。

在实际工作中,很多项目是前人做好的,那么即使对一个经验丰富的工程师来说马上去修改和使用这些代码也是十分困难的。学习项目结构,理解前人的代码并在不破坏项目其他地方的基础上修改代码是有难度的。公司在花钱和时间来找新员工,所以他们不想在试用期的时候就在你这样一个初级开发人员身上花费精力。

在你自己或者学校的项目中对 bug 的容忍度还是比较大的。但是这些 bug 在现实的工作中则是绝对不能容忍的。公司的整个软件发布流程的任何一个环节都涉及到公司的利益,在整个软件发布完成前,你的任何 bug 都可能影响公司的收入和形象。产品的每一个版本都需要进行相关测试,要保证产品具有较高的性能,较高的安全性和可拓展性。这也就需要你对产品所涉及的领域和产品开发过程中必要的测试等内容有很好的把控。在你职业生涯之初时你很难全面的掌握这些知识。

综上所述,你可以去认真的提高自己以上几个方面的能力。当你再发现有公司招聘初级开发人员的时候,你所掌握的这些将会成为你的优势。

决定好你主攻的编程语言

五花八门的信息、计算等相关课程只能让你对 web 开发、软件工程、数据库、网络、用户体验、架构和业务发展等方面浅尝辄止。刚毕业的求职者们的一个常见错误就是把这技术统统罗列出来。

任何一个招聘人员,当看到写着精通 Java、c#、Python、PHP、Ruby、Javascript 等的时候都会毫不犹豫的将注意力转移到下一份简历。你可能擅长其中的一个或两个,但是精通那么多种编程语言远不是你一个初级开发者所能达到的。

缩小你的技能范围,决定好你要主攻哪种编程语言。

因为后端语言不太容易改变,所以如果你选择软件开发方向,选择一个语法严格的语言(如 Java、c# 或 Python)比较好。如果你想选择 Web 开发方向,那就选择 PHP 和 Ruby。这些将是你职业生涯的基础。再学习一些前端的知识如 JavaScript,你将成为一名全栈工程师。

你缩小了你的技能范围,但是这表明了你对某项技术更加的专注,同时在你接下来的职业生涯中,你可以去选择专攻前端还是后端。

雇主更喜欢能够灵活应变的员工(也就是让你干啥你就能干啥呗)。

Employee

为你自己搭建一个展示个人作品的网站

面试官不会问你在大学学习了那几个方面的只是,因为这样很难在面试中真正了解你的技术水平。他们想知道你的开发经验,虽然可能你的经验并不丰富,但是你为自己做作品集的过程也是一个开发经验提升的过程。

如果你没有任何作品来展示在你的作品集上那也没关系。注册一个域名,搭建一个服务器,安装一个 CMS,创建一个账户,搜索引擎提交,添加谷歌分析等都是建立一个网站的必要组成部分。在这个阶段,网站流量的获取和影响力不是你主要考虑的东西。

如果你已经建立了一个个人网站,那么很可能你的朋友或者家人了解到也有别人有类似的需求,所以这就又是一个丰富你作品的机会啊。这将让你有机会在为别人干活的同时不断历练自己。这也让你更接近实际的项目开发,同时还提供了一个相对没有风险的提高技能的机会。

通过以上这些经历,将使你在面试中有东西可说,同时如果面试官认为他们有一些小网站你可以来做,那你的表现就更有可能给面试官留下深刻的印象。

制作一个产品或者小插件

在你搭建个人网站的过程中可能会遇到一些这样或那样的问题,而这些问题通常可以通过一个小插件或者 JavaScript 库解决。然而如果你没找到可用的解决方案或者你有需要进一步开发的解决方法,那么这就是一个超过与你同水平的开发人员的好机会。

然后你需要找到一种方法来解决这个问题,再对其进行包装,让用户可以很方便的使用。例如一个 WordPress 插件或者开源的 GitHub 项目。

这将会进一步提升你你的技能水平。因为你已经从为自己做小东西转变到了独立创建项目。能够去思考其他产品或者设计师的需求。你的插件和代码有机会被收入到更大的项目当中。

这就是能够吸引面试官的一个亮点,因为你做的这些更接近实际的开发。在一个真正的开发团队中进行开发时,你会负责一个大的应用程序的一小部分,你要确保你的代码在集成到项目中时不会出现任何问题。

另外的一个好处就是,你可以对你插件的使用进行收费,作为你努力赚取的一点额外收入。

view

参与网络社区

除了技术能力,雇主还希望看到能够证明热爱你的工作的证据,因为现在技术变化的太快了,你必须能够多多的投入个人时间,来提升你的知识。你需要能够了解到新兴的框架或工具等的变化趋势。最好的方法是阅读博客,多逛一逛软件开发网站。当你在其中能够有一定的贡献的时候,你就可以去做一些评论,提供一些你的想法。如果你能帮别人解决一些问题,那你的社区贡献度就会飙升。

你可能会认为雇主在筛选候选人时不会在意这一点,然而他们会。去查看你在 LinkedIn、GitHub、Stack Overflow 等账号致力于你的研究方向的证据的公司并不罕见。顶级团队甚至会要求求职者在 Stack Overflow 社区中贡献度不能低于多少值。因为没有什么能够比从你为其他工程师提供的建议中更能证明你的水平了。

将你的代码都开源到 GitHub 上

把你所有的代码都放到网上,让全世界都可以看到。这将会让你有更强的批判性思维和分析能力。同行对你代码的评论和提出的问题是你们开发团队来发现错误并及时修复错误来保证产品质量的重要方式。如果你早就习惯了这一点,你就会发现在进行代码审查时不会有任何问题,并且你会在商业环境中有建设性地反馈意见。

同时,这也提供了一种来看你写的代码的方式。你解决过的问题和你做过的项目说来容易,但当一个高级工程师看了你的代码后能够在另一个层面上增加他对你开发能力的信任程度。

你潜在的团队成员也有机会去阅读和检查你的代码,你的设计模式,和你清晰地代码提交记录。最重要的是,它表明你熟悉版本控制,软件产品的核心以及团队的建立。

结论

毕业后在没有实际项目开发经验的情况下找到你的第一份工作是一个艰巨的任务。但这也有行之有效的方法来提高和吸引雇主的注意力。
选择一个后端和前端语言,创建一个网站,然后做一个插件。多看一些博客和网站,并做一些评论,提供一些你的想法,不要害怕别人会说什么。
把你所做的项目和实用的小工具等都放到 GitHub 上。
记住每个人都一样,拥有等量的资源和机会。参照本文中的要点,在你有耐心的时候好好坚持下去,最终一定会得到高薪的工作机会的。

编码规范

命名技巧

语义化

  • 语义化标签优先

  • 基于功能命名、基于内容命名、基于表现命名

  • 简略、明了、无后患

HTML规范:

  • 用两个空格来代替制表符(tab)

  • 嵌套元素应当缩进一次(即两个空格)

  • 对于属性的定义,确保全部使用双引号,绝不要使用单引号

  • 不要在自闭合(self-closing)元素的尾部添加斜线 -- HTML5-规范中明确说明这是可选的。

  • 不要省略可选的结束标签(closing tag)

  • 遵循 HTML 标准和语义,减少标签的数量,尽量避免多余的父元素。

CSS规范:

  • 用两个空格来代替制表符(tab)--这是唯一能保证在所有环境下获得一致展现的方法。

  • 为了代码的易读性,在每个声明块的左花括号前添加一个空格。

  • 换行,声明块的右花括号应当单独成行。

  • 每条声明语句的 : 后应该插入一个空格。

  • 所有声明语句都应当以分号结尾。

  • 对于属性值或颜色参数,省略小于 1 的小数前面的 0(例如,.5 代替 0.5-.5px 代替 -0.5px)。

  • 十六进制值应该全部小写,并采用简写形式,例如#fff

  • 为选择器中的属性添加双引号,例如input[type="text"]

  • 避免为 0 值指定单位,例如,用 margin: 0;代替margin: 0px;

  • 使用简写形式的属性声明,例如margin:15px 16px;代替margin: 15px 16px 15px 16px;

常见命名

.wrap.wrapper -- 用于外侧包裹
.container.ct -- 包裹容器
.header -- 用于头部
.body -- 页面 body
.footer -- 页面尾部
asidesidebar -- 用于侧边栏
.content -- 和header footer 对应,用于主要内容
.navigation -- 导航元素
.pagination -- 分页
.tabs > .tab -- tab 切换
.breadcrumbs -- 导航列表、面包屑
.dropdown -- 下拉菜单
.article -- 文章
.main -- 用于主体
.thumbnail -- 头像,小图像
.media -- 媒体资源
.panel -- 面板
.tooltip -- 鼠标放置上去的提示
.popup -- 鼠标点击弹出的提示
.button.btn -- 按钮
.ad -- 广告
.subnav -- 二级导航
.menu -- 菜单
.tag -- 标签
.message或者.notice -- 提示消息
.summary -- 摘要
.logo -- logo
.search -- 搜索框
.login -- 登录
.register -- 注册
.username -- 用户名
.password -- 密码
.banner -- 广告条
.copyright -- 版权
.modal或者 .dialog -- 弹窗

var 名字 = {
  状态: [
    'inverse',
    'toggled',
    'switched',
    'original',
    'initial',
    'identified',
    'disabled',
    'loading',
    'pending',
    'syncing',
    'default'
  ],
  修饰: [
    'dark',
    'light',
    'shaded',
    'flat',
    'ghost',
    'maroon',
    'pale',
    'intense',
    'twisted',
    'narrow',
    'wide',
    'smooth',
    'separate',
    'clean',
    'sharp',
    'aligned'
  ],
  元素: [
    'pagination',
    'modal',
    'popup',
    'article',
    'story',
    'flash',
    'status',
    'state',
    'media',
    'block',
    'card',
    'teaser',
    'badge',
    'label',
    'sheet',
    'poster',
    'notice',
    'record',
    'entry',
    'item',
    'figure',
    'square',
    'module',
    'bar',
    'button',
    'action',
    'knob'
  ],
  布局: [
    'navigation',
    'wrapper',
    'inner',
    'header',
    'footer',
    'aside',
    'section',
    'divider',
    'content',
    'container',
    'panel',
    'pane',
    'construct',
    'composition',
    'spacing',
    'frame'
  ]
}

推荐阅读

google html css编码规范

bootstrap 编码规范

命名这货真难

new

通常我们创建一个自定义对象可以使用以下两种方法:

  • 使用对象字面量
var obj = {name: 'xxx',  sex: 'female', run: function(){ /* do something  */ }}
  • 使用构造函数
var obj = new Object()
obj.name = 'xxx'
obj.sex = 'female'

以第一种方法为例(为啥不选第二种?就不选第二种!),当我们需要创建 100 个有 run 方法的对象时,那么我们需要这样写:

var person1 = {
  name: 'xxx',
  sex: 'female', 
  run: function(){ 
    // do something  
  }
}
var person2 = {
  name: 'yyy',
  sex: 'female', 
  run: function(){ 
    // do something  
  }
}
......
var person100 = {
  name: 'zzz',
  sex: 'male', 
  run: function(){ 
    // do something  
  }
}

是不是感觉很难受?我只有一个 run 方法想和你一样竟然要写这么多代码,不能忍,为了解决这个写很多遍重复代码的问题,机智的程序员提出了一种设计模式:工厂模式

function createPerson(name, sex){
  var person = new Object()
  person.name = name
  person.sex = sex
  person.run = function(){
     // do something  
  }
  return person
}
var person1 = createPerson('xxx', 'female')
var person2 = createPerson('yyy', 'male')

现在你总不会说代码多又重复了吧?每个有不同的属性值又都有 run 方法,JS 之父看我们又是写 var 又是 return 的,为了让我们再少些几行代码,给我们提供了 new 这个关键字,同时我们使用了 this 。

function Person(name, sex){
  this.name = name
  this.sex = sex
  this.run = function(){
     // do something  
  }
}
var person1 = new Person('xxx', 'female')
var person2 = new Person('yyy', 'male')

在这里 new 帮我们做了如下的事情:

  1. 不用显示的创建对象,因为 new 会帮你做(使用「this」就可以访问到临时对象)

  2. 直接将属性和方法赋给了 this 对象

  3. 不用 return 对象,因为 new 会帮你做

这个模式被称为构造函数模式,按照惯例,构造函数始终都应该以一个大写字母开头,并且使用new操作符来创建构造函数的实例。

那么问题又来了,我们不需要每次创建对象的时候再重写 run 方法,因为我们发现
person1.run === person2.run // false,我们想要 person1 的 run 和 person2 的 run 是一样的,这样就没必要每次创建 person 的时候重复创建 run 方法了,而用原型链可以解决这个问题。

Person.prototype = {       
  run: function(){
    // do something
  }
}

注意:如果重新给 Person.prototype 赋值,那么你的 constructor 属性就没了,所以需要这么写:

Person.prototype = {
  constructor: Person
  run: function(){
    // do something
  }
}

或者这么写

Person.prototype.run = function(){
  // do something
}

至此,整个代码是这样的

function Person(name, sex){
  this.name = name
  this.sex = sex
}
Person.prototype.run = function(){
  // do something
}
var person1 = new Person('xxx', 'female')
var person2 = new Person('yyy', 'male')

这个模式被称为原型模式,在这里 new 总共帮我们做了如下的事情:

  1. 不用显示的创建对象,因为 new 会帮你做(使用「this」就可以访问到临时对象)

  2. 直接将属性和方法赋给了 this 对象

  3. 不用 return 对象,因为 new 会帮你做

  4. 不用绑定原型,new 指定原型的名字为 prototype

推荐阅读

JS 的 new 到底是干什么的?

Hexo + GitHub (Coding) Pages 搭建博客

前言

喜欢写 Blog 的人,会经历三个阶段。

第一阶段,刚接触 Blog,觉得很新鲜,试着选择一个免费空间来写。
第二阶段,发现免费空间限制太多,就自己购买域名和空间,搭建独立博客。
第三阶段,觉得独立博客的管理太麻烦,最好在保留控制权的前提下,让别人来管,自己只负责写文章。

这是阮一峰在博客中写到的关于 Blog 的想法,而这里的第三阶段的实现就是利用 GitHub Pages 搭建博客。

使用 GitHub Pages 功能搭建博客的好处有:

  • 免费,GitHub 提供无限流量。
  • 都是静态文件,世界各地都有理想的访问速度。(访问速度可以进一步优化)
  • 拥有绝对的管理权,又享受 Git 的版本管理功能,不用担心文章遗失。

Hexo 是基于 Node.js 的一款静态博客框架,如果想要搭建博客,不想自己写页面的话可以考虑用 Hexo,其中有很多的简洁主题可供选择,同时 Hexo 支持 Markdown 语法,编辑文章更加方便,快捷。

注:此篇分享以 Mac 为例

环境配置

Hexo 文档有对 Hexo 安装及使用的详细介绍,推荐阅读。这里我主要写自己安装的步骤、踩过的坑以及一些优化的方法。

在正式安装 Hexo 之前,我们需要确认电脑中是否已安装下列应用程序:

当然,我们还需要 GitHub 账号,如果没有请注册
注册之后记得一定要前往邮箱确认注册,否则无法使用 GitHub Pages。

安装 Hexo

所有必备的应用程序安装完成后,即可使用 npm 安装 Hexo。终端输入如下命令:

sudo npm install -g hexo-cli

输入管理员密码( Mac 登录密码)即开始安装。

这里有个小坑:Hexo 官网上的安装命令是npm install -g hexo-cli,安装时不要忘记前面加上sudo,否则会因为权限问题报错。

初始化

使用终端cd到一个您指定的目录,执行以下命令(命令中的blog是您将要建立的文件夹的名称):

hexo init blog

使用终端cdblog文件夹下,执行以下命令,安装npm:

npm install

好了,现在可以试试看是否已经初始化成功,执行如下命令,开启本地 Hexo 服务器:

hexo s

此时,浏览器中打开网址(默认是4000端口) http://localhost:4000 ,能看到如下页面:

Hexo 本地

这里我踩了个不算坑的坑,终端输入hexo s后没有成功,我也在网上搜到了很多解决办法,但是都没有奏效,后来我尝试改了下端口就成功了,也就是说默认的4000端口无法连接。如果您的情况跟我一样,尝试了网上的很多解决办法之后依然无效,那您也许可以尝试输入命令hexo s -p 5000改一下端口试试看。

关联 GitHub

创建仓库

  1. 登录您的 GitHub 账号,新建名称为您的用户名.github.io的仓库。假设我的 GitHub 账号的用户名是 luxun,那么我就应该新建名称为luxun.github.io的仓库。
  2. Description可写可不写,随意。
  3. 勾上Initialize this repository with a README
  4. 点击Create Repository完成创建。

开启 GitHub Pages

点击Settings,你将会打开这个库的 Settings 页面,向下拖动,直到看见 GitHub Pages,如图:

gh-p

修改全局配置文件

Hexo 官方文档中有对全局配置的详细说明,推荐阅读。

小坑提醒,修改本地所有的配置文件时,注意所有的冒号:后边都要加一个空格,否则执行 Hexo 命令时会报错,一定注意。

找到本地blog文件夹下_config.yml,打开后滑到最后,修改成下边的样子:

deploy:
  type: git
  repository: https://github.com/xxx/xxx.github.io.git
  branch: master

您需要将repository后的所有xxx换成你自己的用户名,或者也可以在下图位置获取:

https 地址

然后,您需要为自己配置身份信息,终端输入yournameyouremail换成您自己的 GitHub 用户名和邮箱):

git config --global user.name "yourname"
git config --global user.email "youremail"

终端cdblog文件夹下执行生成静态页面命令:

hexo g

此时若出现如下报错:

ERROR Local hexo not found in ~/blog
ERROR Try runing: 'npm install hexo --save'

尝试执行命令:

npm install hexo --save

若无报错,自行忽略此步骤。

然后在当前目录下,终端输入:

hexo d

这里踩了个坑,如果您执行命令hexo d仍然报错:无法连接 git 或找不到 git,则执行如下命令来安装hexo-deployer-git

npm install hexo-deployer-git --save

完成安装之后,再次执行hexo ghexo d命令。

随后按照提示,分别输入自己的 GitHub 用户名和密码,开始上传。

完成上传之后,通过http://xxx.github.io/ (xxx换成您自己的仓库名,也就是用户名)来访问自己刚刚上传的网站。

  • 为避免每次输入 GitHub 用户名和密码的麻烦,可参照后文 优化(个性化设置) 添加ssh key 到 GitHub进行优化

常用指令和发布文章

  • 常用指令
hexo new "postName"        //新建文章
hexo new page "pageName"        //新建页面
hexo g          //生成静态页面至public目录
hexo server         //开启预览访问端口(默认端口4000,'ctrl + c'关闭server)
hexo deploy         //将.deploy目录部署到GitHub
  • 常用组合
hexo clean
hexo g
hexo d
hexo d -g #生成部署
hexo s -g #生成预览
  • 发布文章

终端cdblog文件夹下,执行如下命令新建文章:

hexo new "xxx"

名为xxx.md的文件会建在目录.../blog/source/_posts下。

所有的文章都会以md形式保存在_post文件夹中,只要在_post文件夹中新建md类型的文档,就能在执行hexo g的时候被渲染。新建的文章头需要添加一些信息,如下所示:

---
title: xxx    //在此处添加你的标题。
date: 2016-10-07 13:38:49   //在此处输入编辑这篇文章的时间。
tags: xxx    //在此处输入这篇文章的标签。
categories: xxx    //在此处输入这篇文章的分类。
---

文章编辑完成后,终端cdblog文件夹下,依次执行如下命令来发布:

hexo g
hexo d

至此,Mac 上搭建基于 GitHub Pages + Hexo 的博客就完成了。

下面的内容是介绍安装 Themes 、个性化设置以及优化。

设置 Themes

如果您喜欢 Hexo 默认的主题的话,可以跳过这部分。如果您想换一个主题的话,可以到Hexo 主题挑选自己中意的主题。

这里以nexT 主题为例。

终端cdblog目录下执行如下命令(这是目前的稳定版本,不是最新版。最新版有一些新特性,但是可能会不稳定,所以这里推荐选择安装稳定版):

git clone --branch v5.1.2 https://github.com/iissnan/hexo-theme-next themes/next

如果想尝试最新版,请执行如下命令:

git clone https://github.com/iissnan/hexo-theme-next themes/next

打开blog目录下的_config.yml,找到theme: landscape修改为theme: next

终端cdblog目录下,依次执行如下命令(每次部署文章的步骤):

hexo clean

hexo g

hexo d

至于更改博客的名称、描述、头像等,只需要修改blog/_config.yml文件和blog/themes/next/_config.yml文件中对应的属性名称即可(不要忘记冒号:后加空格)。

个性化设置

为博客加上 GitHub 丝带

这里以 Next 主题为例(其他主题也差不多),添加 GitHub 丝带:在blog\themes\next\layout\_layout.swig中加入相关代码,记得修改自己的链接。

添加 README.md

每个项目仓库下一般都有一个 README.md 文件,但是使用 hexo 部署到仓库后,项目仓库中是没有 README.md 文件的。

blog 目录下的 source 目录下添加一个 README.md 文件,修改站点配置文件 _config.yml,将 skip_render 参数的值设置为 README.md

skip_render: README.md

保存退出即可。

为博客添加 LICENSE

在主题配置文件中添加下面这段代码(添加之前先看看您的主题配置文件是否已经包含这段代码,已经包含就不用添加了,因为重复会报错),LICENSE 会显示在侧边栏。

# Creative Commons 4.0 International License.
# http://creativecommons.org/
# Available: by | by-nc | by-nc-nd | by-nc-sa | by-nd | by-sa | zero
creative_commons: by-nc-sa
#creative_commons:

修改文章底部的带 # 号的标签

如果您觉得#不好看,想改成图标,那么请按照下面修改。
打开/themes/next/layout/_macro/post.swig,搜索(组合键command+f)rel="tag">#,将#换成<i class="fa fa-tag"></i>

将阅读量改为热度

很多人将文章标题下的 阅读次数 改为了 热度,如果您喜欢的话可以这样修改。

打开blog/themes/next/languages/zh-Hans文件,查找阅读次数这几个字,可以看到,在post中的visitors被定义为阅读次数,把这里的阅读次数改为热度

visitors: 热度

那么怎么在页面中显示呢?打开Next主题文件夹中blog/themes/next/layout/_macro/post.swig,在这个文件里加上摄氏度的标志,在<span class="leancloud-visitors-count"></span>下面增加一行<span>℃</span>即可:

<span class="leancloud-visitors-count"></span>
<span>℃</span>

博文置顶

  • 修改 hexo-generator-index 插件

替换文件blog/node_modules/hexo-generator-index/lib/generator.js 内的代码为:

'use strict';
var pagination = require('hexo-pagination');
module.exports = function(locals){
  var config = this.config;
  var posts = locals.posts;
    posts.data = posts.data.sort(function(a, b) {
        if(a.top && b.top) { // 两篇文章top都有定义
            if(a.top == b.top) return b.date - a.date; // 若top值一样则按照文章日期降序排
            else return b.top - a.top; // 否则按照top值降序排
        }
        else if(a.top && !b.top) { // 以下是只有一篇文章top有定义,那么将有top的排在前面(这里用异或操作居然不行233)
            return -1;
        }
        else if(!a.top && b.top) {
            return 1;
        }
        else return b.date - a.date; // 都没定义按照文章日期降序排
    });
  var paginationDir = config.pagination_dir || 'page';
  return pagination('', posts, {
    perPage: config.index_generator.per_page,
    layout: ['index', 'archive'],
    format: paginationDir + '/%d/',
    data: {
      __index: true
    }
  });
};
  • 设置文章置顶

在文章 Front-matter 中添加 top 值,数值越大文章越靠前,如:

---
title: xxx
date: 2015-04-02 14:36:04
categories: xxx
tags: xxx
top: 10
---

NexT 首页文章 加载更多 设置

这里只说一个方法:编辑文章时,在您希望显示 加载更多 按钮的地方,加上<!--more-->

more

首页分割线

  在 \themes\next\source\css\_custom\custom.styl 文件中添加以下代码,可以修改博客首页中每篇文章的分割线样式,width是长度,height是宽度。

//index页面中每篇文章相隔的那条线
.posts-expand {
  .post-eof {
    display: block;
    margin: $post-eof-margin-top auto $post-eof-margin-bottom;
    width: 100%;
    height: 3px;
    background: $grey-light;
    text-align: center;
  }
}

小图标设置

博客中一切小图标都可以在fontawesome 图标库自行搜索(qq、微博、微信等图标是有的,但知乎、豆瓣等图标目前还没有)。

  • 修改网页底部的桃心图标
    打开blog/themes/next/layout/_partials/footer.swig,找到这段代码进行修改(还是在fontawesome 图标库找自己喜欢的图标):
<span class="with-love">
  <i class="fa fa-share-alt"></i>
</span>

设置网站的图标Favicon

准备一张 icon 图标文件,放在 source 目录下就可以了,在主题配置文件中找到 favicon 的设置:

# Put your favicon.ico into `hexo-site/source/` directory.
favicon: /favicon.ico

主页文章添加阴影效果

具体实现方法

打开blog\themes\next\source\css\_custom\custom.styl文件,添加以下代码:

//主页文章添加阴影效果
 .post {
   margin-top: 60px;
   margin-bottom: 60px;
   padding: 25px;
   -webkit-box-shadow: 0 0 5px rgba(202, 203, 203, .5);
   -moz-box-shadow: 0 0 5px rgba(202, 203, 204, .5);
  }

隐藏网页底部 powered By Hexo / 强力驱动

打开blog/themes/next/layout/_partials/footer.swig,使用<!-- -->隐藏符号之间的代码即可,或者直接将这段代码删除。位置如图:

隐藏底部 Hexo

添加 SSH key 到 GitHub

  • 检查 SSH keys 是否已经存在

终端执行如下命令,检查SSH keys是否存在。

ls ~/.ssh

如果显示如下信息(下面是我个人的显示,也许您跟我显示的不一样,但重点是只要有id_rsaid_rsa.pub),就说明 SSH keys 已经存在了:

id_rsa	   id_rsa.pub	  known_hosts

如果存在,则直接跳过下一个步骤进入将 SSH key 添加到 GitHub 中,否则请继续下一步骤 生成新的 SSH key

  • 生成新的 SSH key

终端执行如下命令生成新的 SSH key,注意将[email protected]换成你自己注册 GitHub 的邮箱地址。

ssh-keygen -t rsa -C "[email protected]"

默认会在相应路径~/.ssh下生成id_rsaid_rsa.pub两个文件。

1.3.将 SSH key 添加到 GitHub 中

终端依次输入:

cd ~/.ssh
cat id_rsa.pub

复制所有显示的内容

进入GitHub –> Settings –> SSH and GPG keys –> NEW SSH key,如下图所示:

settings

ssh_key

Title 里任意添一个标题,将复制的内容粘贴到 Key 里,点击下方 Add SSH key 绿色按钮即可,如下图所示:

add_ssh_key

绑定独立域名

  • 首先,需要注册一个域名。可以选择GoDaddy万网中的任意一家。

  • 然后,我们需要配置一下域名解析。推荐使用DNSPod的服务,免费稳定,解析速度也比较快。在域名注册商处(Godaddy 或万网)修改 NS 服务器地址为:

f1g1ns1.dnspod.net
f1g1ns2.dnspod.net

域名解析详细的步骤这里我就不写了,给个图:

dnspod

图中设置国内国外的原因是想让博客加载速度更快,方法请阅读后文 将个人博客同时部署到 GitHub 和 Coding

  • 如果将域名指向另一个域名,实现与被指向域名相同的访问效果,需要增加 CNAME 记录。

进入 blog/source 目录下,添加并打开 CNAME 文件,输入您的域名,重新上传您的博客。

在 GitHub 中打开您自己的博客仓库,进入库的 Settings 界面,如果看到了如下提示,说明配置成功了。

CNAME

在这一系列的操作中,修改 NS 服务器、设置解析等等,都需要一定的时间。短则10分钟,长则24小时,最长不会超过72小时。如果超过72小时,请检查自己的配置过程,或者修改自己本地的 DNS 服务器。

优化

将个人博客同时部署到 GitHub 和 Coding

Coding 可以理解为国内的 GitHub。通过将博客同时部署到 GitHub 和 Coding,可以提升博客的加载速度。

这里有个提醒,Coding 会强制用户在网站上挂推广图标,通过 Coding 加载博客还会有5s广告,如果觉得这样不好,可以选择放弃部署在 Coding 上。还有一个选择,就是花钱成为 Coding 的会员,这样上述的问题就都没有了。

  • 首先到Coding注册。创建仓库跟 GitHub 上创建仓库的要求一样。

  • 打开本地 blog 目录下的 _config.yml 文件,滑到最下面,修改如下(xxx换成自己的用户名):

deploy:
  type: git
  repository:
    github: https://github.com/xxx/xxx.github.io.git
    coding: https://git.coding.net/xxx/xxx.git
  branch: master
  • 然后执行命令hexo clean hexo g hexo d

  • 个人域名添加两条 CNAME 解析。将 GitHub 的地址 解析为 国外 ,将 Coding 的地址 解析为 国内

dnspod

  • 如果您有个人域名的话,到 Coding 博客仓库的 Pages 服务 界面,添加域名绑定,输入个人域名就 OK 了,可能需要耐心等待几分钟。

这样就可以实现从国内访问就通过 Coding 加载博客项目,从国外访问就通过 GitHub 加载博客项目,从而提升加载博客的速度。

压缩代码

压缩代码也是一个优化加载速度的方法。

目前知道的有两个插件可以压缩博文,hexo-all-minifier 插件和 gulp 插件。hexo-all-minifier 使用比较简单,也可以压缩图片,不过对文章缩进不支持。如果您对文章缩进有要求,可以暂时使用 gulp 压缩手段。

  • hexo-all-minifier 使用方法

安装 hexo-all-minifier,在站点的根目录下执行以下命令:

npm install hexo-all-minifier --save

hexo g编译的时候就会自动压缩 HTML、JS、图片。详情参考插件介绍 hexo-all-minifier

  • glup 使用方法

Hexo 依赖 gulp 插件安装,在站点的根目录下执行以下命令:

npm install gulp -g
npm install gulp-minify-css gulp-uglify gulp-htmlmin gulp-htmlclean gulp --save

blog目录下,新建 gulpfile.js 并填入以下内容:

var gulp = require('gulp');
var minifycss = require('gulp-minify-css');
var uglify = require('gulp-uglify');
var htmlmin = require('gulp-htmlmin');
var htmlclean = require('gulp-htmlclean');
// 压缩 public 目录 css
gulp.task('minify-css', function() {
    return gulp.src('./public/**/*.css')
        .pipe(minifycss())
        .pipe(gulp.dest('./public'));
});
// 压缩 public 目录 html
gulp.task('minify-html', function() {
  return gulp.src('./public/**/*.html')
    .pipe(htmlclean())
    .pipe(htmlmin({
         removeComments: true,
         minifyJS: true,
         minifyCSS: true,
         minifyURLs: true,
    }))
    .pipe(gulp.dest('./public'))
});
// 压缩 public/js 目录 js
gulp.task('minify-js', function() {
    return gulp.src('./public/**/*.js')
        .pipe(uglify())
        .pipe(gulp.dest('./public'));
});
// 执行 gulp 命令时执行的任务
gulp.task('default', [
    'minify-html','minify-css','minify-js'
]);

生成博文时执行 hexo g && gulp 就会根据 gulpfile.js 中的配置,对 public 目录中的静态资源文件进行压缩。

SEO(搜索引擎优化)

网站验证

验证方式有几种,推荐最简单的两种:文件验证和 CNAME 验证。

  • 文件验证
  1. 登录百度站长选择添加网站,使用方式为文件验证
  2. 将下载的文件放到source文件下
  3. 由于 hexo 自动会对 html 文件进行渲染,所以在站点配置文件中找到skip_render:
  4. 在后面添加文件名字,若有多个用这样的形式[xxx.html, xxx.html],比如: skip_render: [googleff0226f76d5f451b.html, baidu_verify_vHC5EAW09E.html]
  5. 重新渲染文件: hexo clean hexo d -g
  6. 点击站长的验证按钮,完成验证。
  • CNAME 验证
  1. 去站长添加网站选择 CNAME 验证
  2. 把地址解析到zz.baidu.com
  3. 完成验证

添加并提交sitemap

安装 Hexo 的 sitemap 网站地图生成插件,终端cdblog

npm install hexo-generator-sitemap --save
npm install hexo-generator-baidu-sitemap --save

站点配置文件中任意位置添加如下代码,但要看清您的 Hexo 版本。

如果您的 Hexo 版本是 2.x.x

sitemap:
    path: sitemap.xml
baidusitemap:
    path: baidusitemap.xml

如果您的 Hexo 版本是 3.x.x

sitemap:
path: sitemap.xml
baidusitemap:
path: baidusitemap.xml

配置成功后,会生成sitemap.xmlbaidusitemap.xml,前者适合提交给谷歌搜素引擎,后者适合提交百度搜索引擎。

百度 sitemap 提交如图,Google 也是一样的:

sitemap_yz

验证成功之后就可以开始推送了。Google 的收录比较快,通常第二天就能搜得到,百度就比较慢了。

主动推送

安装主动推送插件,终端cdblog

npm install hexo-baidu-url-submit --save

在根目录下,把以下内容配置到站点配置文件中:

baidu_url_submit:
  count: 3     ## 比如3,代表提交最新的三个链接
  host: xxx     ## 在百度站长平台中注册的域名
  token: xxx       ## 请注意这是您的秘钥,请不要发布在公众仓库里!
  path: baidu_urls.txt       ## 文本文档的地址,新链接会保存在此文本文档里

至于上面提到的token可在百度站长如下位置找到:

token

其次,记得查看站点配置文件中的url,必须包含站长平台注册的域名,比如:

url: http://harleywang93.com
root: /
permalink: :year/:month/:day/:title/

接下来添加一个新的 deploy 类型:

deploy:
- type: baidu_url_submitter
- type: git
  repository:
    github: https://github.com/xxx/xxx.github.io.git
    coding: https://git.coding.net/xxx/xxx.git
  branch: master

执行hexo d的时候,新的链接就会被推送了。原理:

  • 新链接的产生,hexo g会产生一个文本文件,里面包含最新的链接。
  • 新链接的提交,hexo d会从上述文件中读取链接,提交至百度搜索引擎。

自动推送

把 NexT 主题配置文件中的baidu_push:设置为true,就可以了。

添加蜘蛛协议

blog/source/目录下新建一个robots.txt文件,添加下面的一段代码(可根据自己的需要修改):

#hexo robots.txt
User-agent: *

Allow: /
Allow: /archives/

Disallow: /vendors/
Disallow: /js/
Disallow: /css/
Disallow: /fonts/
Disallow: /vendors/
Disallow: /fancybox/

Sitemap: http://xxx/sitemap.xml
Sitemap: http://xxx/baidusitemap.xml

然后到站长(这里以百度为例,Google 一样)更新一下,就像这样:

robots

感谢

自己搭建博客时,很是折腾了一番,也学习到了很多知识,阅读了很多相关的博文,在此向相关博文的作者表示感谢,谢谢你们的文章让我进步。同时,我看到有很多人想拥有自己的博客或者优化自己的博客,期间可能会遇到很多问题,我也是这么一个坑一个坑踩过来的,所以我就写了这么一篇自己折腾博客的分享,希望可以在帮助自己的同时帮助到别人。(如果踩了新坑的话,一定会更新的。)

  • 目前已经弃掉了此方法写博客,因为想回到回归初心,写博客最初的目的 - 总结梳理知识以及分享一些感受,不在乎形式。( 其实是因为懒得折腾了,Hexo 有一些不稳定性,所以目前弃掉了,喜欢折腾的可以继续尝试 lol )

推荐阅读

搭建一个免费的,无限流量的Blog-阮一峰
Hexo搭建博客教程
基于Hexo+Node.js+github+coding搭建个人博客——进阶篇(从入门到入土)

七种方式实现垂直居中

  • 如果 .parentheight 不写,只需要 padding: 10px 0; 或者 line-height 就能将 .child 垂直居中。

  • 如果 .parentheight 写死了,就很难把 .child 居中 ( 所以,能不写 height 就千万别写 height。)

  • 如果你 .parent 不得不定高 ( 如全屏高度 ),以下是几种垂直居中的方法。

方法:

1. table 自带功能

//HTML
<table class="parent">
    <tr>
      <td class="child">
      我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中
      </td>
    </tr>
  </table>
//CSS
.parent{
  border: 1px solid red;
  height: 600px;
}

.child{
  border: 1px solid green;
}

效果图1

2. 100% 高度的 after before 加上 inline block

//HTML
<div class="parent">
    <span class=before></span><div class="child">
      我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中
    </div><span class=after></span>
  </div>
//CSS
.parent{
  border: 3px solid red;
  height: 600px;
  text-align: center;
}

.child{
  border: 3px solid black;
  display: inline-block;
  width: 300px;
  vertical-align: middle;
}

.parent .before{
  outline: 3px solid red;
  display: inline-block;
  height: 100%;
  vertical-align: middle;
}
.parent .after{
  outline: 3px solid red;
  display: inline-block;
  height: 100%;
  vertical-align: middle;
}

效果图2

  • 优化
//HTML
<div class="parent">
    <div class="child">
      我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中
    </div>
  </div>
//CSS
.parent{
  border: 3px solid red;
  height: 600px;
  text-align: center;
}

.child{
  border: 3px solid black;
  display: inline-block;
  width: 300px;
  vertical-align: middle;
}

.parent:before{
  content:'';
  outline: 3px solid red;
  display: inline-block;
  height: 100%;
  vertical-align: middle;
}
.parent:after{
  content:'';
  outline: 3px solid red;
  display: inline-block;
  height: 100%;
  vertical-align: middle;
}

效果图2-2

3. div 装成 table

//HTML
<div class="table">
      <div class="td">
        <div class="child">
          我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中
        </div>
    </div>
  </div>
//CSS
div.table{
  display: table;
  border: 1px solid red;
  height: 600px;
}

div.tr{
  display: table-row;
  border: 1px solid green;
}

div.td{
  display: table-cell;
  border: 1px solid blue;
  vertical-align: middle;
}
.child{
  border: 10px solid black;
}

效果图3

4. margin-top: -50%;

//HTML
<div class="parent">
    <div class="child">
      我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中
    </div>
</div>
//CSS
.parent{
  height: 600px;
  border: 1px solid red;
  position: relative;
}
.child{
  border: 1px solid green;
  width: 300px;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-left: -150px;
  height: 100px;
  margin-top: -50px;
}

效果图4

5. translate: -50%;

//HTML
<div class="parent">
    <div class="child">
      我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中
    </div>
</div>
//CSS
.parent{
  height: 600px;
  border: 1px solid red;
  position: relative;
}
.child{
  border: 1px solid green;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
}

效果图5

6. absolute margin auto

//HTML
<div class="parent">
    <div class="child">
      一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
    </div>
</div>
//CSS
.parent{
  height: 600px;
  border: 1px solid red;
  position: relative;
}
.child{
  border: 1px solid green;
  position: absolute;
  width: 300px;
  height: 200px;
  margin: auto;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

效果图6

7. flex

//HTML
<div class="parent">
    <div class="child">
      一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字一串文字
    </div>
</div>
//CSS
.parent{
  height: 600px;
  border: 3px solid red;
  
  display: flex;
  justify-content: center;
  align-items: center;
}
.child{
  border: 3px solid green;
  width: 300px;
}

效果图7

浏览器兼容

什么是浏览器兼容问题?

同一份代码,有的浏览器效果正常,有的不正常

  • 不正常的原因是什么?(不支持? bug?)
  • 如何让它展示正常?(条件注释? 单独Hack?)

为什么会有浏览器兼容问题?

  • 同一产品,版本越老 bug 越多
  • 同一产品,版本越新,功能越多
  • 不同产品,不同标准,不同实现方式

什么是 CSS hack?

  • 由于不同厂商的浏览器,比如 Internet Explorer,Safari,Mozilla Firefox,Chrome 等,或者是同一厂商的浏览器的不同版本,如 IE6 和 IE7,对 CSS 的解析认识不完全一样,因此会导致生成的页面效果不一样,得不到我们所需要的页面效果。

这个时候我们就需要针对不同的浏览器去写不同的CSS,让它能在不同的浏览器中也能得到我们想要的页面效果。

谈一谈浏览器兼容的思路

要不要做

  • 产品的角度(产品的受众、受众的浏览器比例、效果优先还是基本功能优先)
  • 成本的角度 (有无必要做某件事)

做到什么程度

  • 让哪些浏览器支持哪些效果

如何做

渐进增强和优雅降级
渐进增强(progressive enhancement): 针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验
优雅降级 (graceful degradation): 一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

stackoverflow-渐进增强和优雅降级的区别

几种浏览器兼容的写法

  • 条件注释

eg:

<!--[if IE 6]-->
<p>You are using Internet Explorer 6.</p>
<![endif]-->
  • CSS 属性前缀

eg:

div{
            color:blue;
            *color:red; /*如果在IE6或者7中,字体颜色为红色,否则为蓝色*/
}
  • 选择器前缀(即选择器 Hack)

eg:

div\9{
                color:yellow
}
  • 条件注释结合选择器

eg:

<!DOCTYPE html>
    <!--[if IEMobile 7 ]> <html dir="ltr" lang="en-US"class="no-js iem7"> <![endif]-->
    <!--[if lt IE 7 ]> <html dir="ltr" lang="en-US" class="no-js ie6 oldie"> <![endif]-->
    <!--[if IE 7 ]> <html dir="ltr" lang="en-US" class="no-js ie7 oldie"> <![endif]-->
    <!--[if IE 8 ]> <html dir="ltr" lang="en-US" class="no-js ie8 oldie"> <![endif]-->
    <!--[if (gte IE 9)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html dir="ltr" lang="en-US" class="no-js"><!--<![endif]-->
  • Modernizr工具
<html class=" js no-flexbox canvas canvastext no-webgl no-touch geolocation 
         postmessage no-websqldatabase no-indexeddb hashchange no-history 
   draganddrop no-websockets rgba hsla multiplebgs backgroundsize 
   no-borderimage borderradius boxshadow no-textshadow opacity 
   no-cssanimations no-csscolumns no-cssgradients no-cssreflections
   csstransforms no-csstransforms3d no-csstransitions fontface 
   generatedcontent video audio localstorage sessionstorage 
   no-webworkers no-applicationcache svg inlinesvg smil svgclippaths">
  • 浏览器兼容写法
transition: transform 1s;
-moz-transition: transform 1s; /* Firefox 4 */
-webkit-transition: transform 1s; /* Safari 和 Chrome */
-o-transition: transform 1s; /* Opera */

常见属性的兼容情况

inline-block: >=ie8
min-width/min-height: >=ie8
:before,:after: >=ie8
div:hover: >=ie7
inline-block: >=ie8
background-size: >=ie9
圆角: >= ie9
阴影: >= ie9
动画/渐变: >= ie10

以下工具/名词是做什么的?

条件注释

  • 条件注释 (conditional comment) 是于HTML源码中被IE有条件解释的语句。条件注释可被用来向IE提供及隐藏代码。
        <!--[if IE 6]>
        <p>You are using Internet Explorer 6.</p>
        <![endif]-->
        <!--[if !IE]><!-->
        <script>alert(1);</script>
        <!--<![endif]-->
        <!--[if IE 8]>
        <link href="ie8only.css" rel="stylesheet">
        <![endif]-->
  • 使用了条件注释的页面在 Windows Internet Explorer 9 中可正常工作,但在 Internet Explorer 10 中无法正常工作。

  • IE10不再支持条件注释

项目 范例 说明
[if !IE] 非IE
lt [if lt IE 5.5] 小于IE 5.5
lte [if lte IE 6] 小于等于IE6
gt [if gt IE 5] 大于 IE5
gte [if gte IE 7] 大于等于IE7

IE Hack

针对IE浏览器编写不同的CSS,使得在IE浏览器中也能得到我们想要的页面效果

js 能力检测

能力检测的目标不是识别特定的浏览器,而是识别浏览器的能力。使用这种方式无需顾及浏览器如何如何,只需确定浏览器是否支持特定的能力,就可以给出相关的方案。

用于解决IE9以下版本浏览器对HTML5新增标签不识别,并导致CSS不起作用的问题,使用Javascript来使不支持HTML5的浏览器支持HTML标签。

让不支持 css3 Media Query 的浏览器包括 IE6-IE8 等其他浏览器支持查询。

早期的 CSS Reset 的作用就是清除所有浏览器默认样式,这样更易于保持各浏览器渲染的一致性。
这样暴力清零会带来一些问题,eg:

  1. *{ margin:0; padding:0; }会带来性能问题
  2. 使用通配符存在隐性问题
  3. 过渡的标签重置等于画蛇添足
  4. 过渡的标签重置导致语言元素失效

是一个可定制的 CSS 文件,使浏览器呈现的所有元素,更一致和符合现代标准;是在现代浏览器环境下对于CSS reset的替代。
该项目依赖于研究浏览器默认元素风格之间的差异,精确定位需要重置的样式。相比于传统的CSS Reset,Normalize.css是一种现代的、为HTML5准备的优质替代方案。Normalize.css现在已经被用于Twitter Bootstrap、HTML5 Boilerplate、GOV.UK、Rdio、CSS Tricks 以及许许多多其他框架、工具和网站上。
作用:

  1. 保护有用的浏览器默认样式而不是完全去掉它们
  2. 为大部分HTML元素提供一般化的样式
  3. 修复浏览器自身的bug并保证各浏览器的一致性
  4. 用一些小技巧优化CSS可用性
  5. 用注释和详细的文档来解释代码

一个 JavaScript 库,用于检测用户浏览器的 HTML5 与 CSS3 特性。Modernizr 会在页面加载后立即检测特性;然后创建一个包含检测结果的 JavaScript 对象,同时在 html 元素加入方便你调整 CSS 的 class 名。

PostCSS 是一个使用 JS 插件来转换 CSS 的工具。这些插件可以支持使用变量,混入(mixin),转换未来的 CSS 语法,内联图片等操作。

一般在哪个网站查询属性兼容性?

Vim 基础使用

Vim 是什么?

  • Vim 是从 vi 发展出来的一个高效率的文本编辑器,在程序员中被广泛使用,有很多支持程序员们更快更好写代码的强大功能。

为什么要学习使用 Vim?

  • 作为一个程序员,不管是前端还是后端,工作中基本上都要跟服务器打交道,而现在服务器里面运行的操作系统基本上都是 Linux。
  • 当你远程跟服务器通信的时候,基本上都要通过终端来跟服务器建立连接。
  • 当你要修改服务器的某个配置文件的时候,在一个黑漆漆的终端里,你能用的几乎只有 Vim。不会使用 Vim,就没办法工作。

如何学习使用 Vim?

  • 学习任何东西都要循序渐进,不要想一口吃成个胖子。现在很多教程上来就列一堆快捷键,基本上看几眼就放弃了。
  • 刚开始只需要学习一些最基本的操作,后面随着代码写的越来越多,进阶的操作每次练一两个,慢慢也就会了,很简单。
  • 工具是拿来用的,不要让它成为一个负担!

新手最大的问题

  • 大小写看错
  • 空格写漏
  • 把多个命令当成一个命令

安装和配置

  • Windows 要安装 Git Bash
  • Linux 无需安装
  • macos 无需安装

基础使用

Vim 的两种模式:编辑模式&命令模式

  • 输入vim a.md 初始进入编辑器命令模式
    注意 vima.md 之间有一个空格
  • i 进入编辑模式
  • 键盘左上角 esc 进入命令模式
  • :wq 保存退出
  • :q! 不保存强制退出

常见的文本操作

按键 功能
h或← 光标左移
l或→ 光标右移
k或↑ 光标上移
j或↓ 光标下移
gg 快速定位到文本开头
G(shift+g) 快速定位到文本最后一段
0或home 快速定位到当前段首
$或end 快速定位到当前段首
Ctrl+f 向下翻一页
Ctrl+b 向上翻一页
Ctrl+d 向下翻半页
Ctrl+u 向上翻一页
/string 查找文本中光标位置下方的string字符串,如要查找kabc则直接输入/kabc即可
?string 查找文本中光标位置上方的string字符串,如要查找kabc则直接输入?kabc即可
yy 复制光标所在的段落
y0 复制光标位置到段首的所有字符
y$ 复制光标位置到段尾的所有字符
nyy 从光标位置向下复制n段,包含光标所在的段落
ygg 复制光标位置所在段落到文本开头的所有内容
yG 复制光标位置所在段落到文本结尾的所有内容
p 将复制的内容粘贴为光标所在段落的下一段
P 将复制的内容粘贴为光标所在段落的上一段
dd 删除光标所在的段落
d0 删除光标位置到段首的所有字符
d$ 删除光标位置到段尾的所有字符
dgg 删除光标位置所在段落到文本开头的所有内容
dG 删除光标位置所在段落到文本结尾的所有内容
ndd 从光标位置向下删除n段,包含光标所在的段落
u 撤销上一个操作
Ctrl+r 重复上一个操作
:w 保存数据
:w! 保存时强制写入数据,不管文件是否为只读文件
) 移至下一个句子(sentence)首 (sentence 是以 . ! ? 为区格)
( 移至上一个句子(sentence)首
} 移至下一个段落(paragraph)首 (paragraph 是以空白行为区格)
{ 移至上一个段落(paragraph)首

跨域

同源策略(Same origin Policy)

说起跨域,我们就要先了解一下同源策略。同源策略是针对浏览器的,不是针对 JS。

同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。

换句话说,浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。

本域

同协议:如都是http或者https
同域名:如都是http://github.com/ahttp://github.com/b
同端口:如都是80端口

http://harleywang93.com/为例
该 url 的协议为 http,域名是 harleywang93.com,端口为443,默认端口可以省略。

http://harleywang93.com/http://harleywang93.com/about (同源,协议、域名、端口都相同)
http://harleywang93.com/https://harleywang93.com (不同源,协议不同)
http://harleywang93.com/http://other.harleywang93.com (不同源,域名不同)
http://harleywang93.com/http://harleywang93.com:442 (不同源,端口不同)

需要注意的是: 对于当前页面来说页面存放的 JS 文件的域不重要,重要的是加载该 JS 页面所在什么域

那么,我就是想访问其他域(非同源)的资源 — 即跨域,该怎么做呢?

常见的跨域方法

  • JSONP
  • CORS
  • 降域
  • window.postMessage

JSONP(JSON Padding)

HTML 中 Script 通过 src 属性可以引入其他域下的 JS,比如引入线上的 jQuery库。也可以引入非 JS 的文件,利用这个特性,可实现跨域访问接口。该方法需要后端支持。

  1. 定义数据处理函数 _fun
  2. 创建 Script 标签,src 的地址执行后端接口,最后加个参数 callback=_fun
  3. 服务端在收到请求后,解析参数,计算返还数据,输出 fun(data) 字符串。
  4. fun(data)会放到 Script 标签做为 JS 执行。此时会调用 fun 函数,将 data 做为参数。

实现方式

//b.html
<ul>
  <li>十九大10月18日在北京召开 喜迎十九大</li>
  <li>***欢迎塔吉克斯坦总统 世界之问的**答案</li>
  <li>丁来杭中将履新空军司令员 接替马晓天上将</li>
</ul>
<button>换一组</button>
<script>
  var $ = function (str) {
    return document.querySelector(str);
  };
  $('button').addEventListener('click', function () {
    var script = document.createElement('script');
    script.src = 'http://a.jrg.com:8080/getNews?callback=fn';
    document.head.appendChild(script);
    document.head.removeChild(script);
  });

  var fn = function (news) {
    for(var i=0; i<3; i++)
      $('ul>li').remove();
    for(var i=0; i<news.length; i++){
      var li = document.createElement('li');
      li.innerText = news[i];
      $('ul').appendChild(li);
    }
  };
</script>
//router.js
app.get('/getNews', function (req, res) {
    var news = [
        '**公民在美购敏感材料获刑3年 疑遭钓鱼执法',
        '十九大10月18日在北京召开 喜迎十九大',
        '***欢迎塔吉克斯坦总统 世界之问的**答案',
        '丁来杭中将履新空军司令员 接替马晓天上将',
        '上海警察"一招擒拿"抱娃妇女摔落孩子 已被停职',
        '技校学生拳击玻璃致身亡 家属抬棺材堵校门',
        '女博士被人推下几十级台阶 男子自首称"想听尖叫"'
    ];

    var data = [];
    for(var i=0; i<3; i++){
        var index = parseInt(Math.random()*news.length);
        data.push(news[index]);
        news.splice(index, 1);
    }

    var cb = req.query.callback;
    if(cb)
        res.send(cb + '(' + JSON.stringify(data) + ')');
    else
        res.send(data);
});

CORS(Cross-Origin Resource Sharing)

CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 ajax 跨域请求资源的方式,支持现代浏览器,IE支持10以上。 实现方式很简单,当你使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin; 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。所以 CORS 的表象是让你觉得它与同源的 ajax 请求没啥区别,代码完全一样。

CORS 有简单请求和复杂请求之分

  • 简单请求
    比如发送了一个 origin 的头部
    Origin : http://www.nczonline.net
    如果服务器认为这个请求可以接受,就在Access-Control-Allow-Origin头部回发相同的源信息
    Access-Control-Allow-Origin : http://www.nczonline.net

  • 复杂请求
    复杂请求是那种对服务器有特殊要求的请求,比如请求方法是 PUT 或 DELETE ,或者Content-Type 字段的类型是 application/json 。
    复杂请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为“预检”请求(preflight)。
    浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的 XMLHttpRequest 请求,否则就报错。

实现方式

//a.html
<ul>
  <li>十九大10月18日在北京召开 喜迎十九大</li>
  <li>***欢迎塔吉克斯坦总统 世界之问的**答案</li>
  <li>丁来杭中将履新空军司令员 接替马晓天上将</li>
</ul>
<button>换一组</button>
<script>
    var $ = function (str) {
        return document.querySelector(str);
    };
    $('button').addEventListener('click', function () {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function () {
            if(xhr.readyState === 4 && xhr.status === 200)
                fn(JSON.parse(xhr.responseText));
        };
        xhr.open('get', 'http://b.jrg.com:8080/getNews', true);
        xhr.send();
    });
    var fn = function (news) {
        for(var i=0; i<3; i++)
            $('ul>li').remove();
        for(var i=0; i<news.length; i++){
            var li = document.createElement('li');
            li.innerText = news[i];
            $('ul').appendChild(li);
        }
    };
</script>
//router.js
app.get('/getNews', function (req, res) {
    var news = [
        '**公民在美购敏感材料获刑3年 疑遭钓鱼执法',
        '十九大10月18日在北京召开 喜迎十九大',
        '***欢迎塔吉克斯坦总统 世界之问的**答案',
        '丁来杭中将履新空军司令员 接替马晓天上将',
        '上海警察"一招擒拿"抱娃妇女摔落孩子 已被停职',
        '技校学生拳击玻璃致身亡 家属抬棺材堵校门',
        '女博士被人推下几十级台阶 男子自首称"想听尖叫"'
    ];

    var data = [];
    for(var i=0; i<3; i++){
        var index = parseInt(Math.random()*news.length);
        data.push(news[index]);
        news.splice(index, 1);
    }

    res.header('Access-Control-Allow-Origin', 'http://a.jrg.com:8080');
    res.send(data);
});

降域

当两个页面不同域,但是它们的父域之上都相同(端口),那么可以使用降域的方法来实现跨域。
对于主域相同而子域不同的情况下,可以通过设置 document.domain 的办法来解决,
具体做法是可以在 url 为 a.github.com:8080 下的 a.htmlb.github.com:8080 下的 b.html 两个文件分别加上 document.domain = “github.com”;然后通过 a.html 文件创建一个 iframe,去控制 iframewindow,从而进行交互,当然这种方法只能解决主域相同、而二级域名不同的情况。

实现方式

//a.html
<input type="text">
<iframe src="http://b.jrg.com:8080/b.html" frameborder="0"></iframe>
<script>
    var $ = function (str) {
        return document.querySelector(str);
    };
    $('input').addEventListener('input', function () {
        window.frames[0].$('input').value = this.value;
    });

    document.domain = 'jrg.com';
</script>
//b.html
<input type="text">
<script>
    var $ = function (str) {
        return document.querySelector(str);
    };
    $('input').addEventListener('input',function () {
        window.parent.$('input').value = this.value;
    });
    document.domain = 'jrg.com';
</script>

window.postMessage

window.postMessage 是一个安全的跨源通信的方法。一般情况下,当且仅当执行脚本的页面使用相同的协议(通常都是 http )、相同的端口( http 默认使用80端口)和相同的 host(两个页面的 document.domain 的值相同)时,才允许不同页面上的脚本互相访问。 window.postMessage 提供了一个可控的机制来安全地绕过这一限制,当其在正确使用的情况下。

window.postMessage 解决的不是浏览器与服务器之间的交互,解决的是浏览器不同的窗口之间的通信问题,可以做的就是同步两个网页,当然这两个网页应该是属于同一个基础域名。

实现方式

//a.html
<input type="text">
<iframe src="http://b.jrg.com:8080/b.html" frameborder="0"></iframe>
<script>
    var $ = function (str) {
        return document.querySelector(str);
    };
    $('input').addEventListener('input', function () {
        window.frames[0].postMessage(this.value, 'http://b.jrg.com:8080');
    });
</script>
//b.html
<input type="text">
<script>
    var $ = function (str) {
        return document.querySelector(str);
    };
    window.addEventListener('message',function (e) {
        $('input').value = e.data;
    });
</script>

推荐阅读

关于跨域,你想知道的全在这里
阮一峰 - CORS通信
跨域解决方案大全
Web开发中跨域的几种解决方案

Flex 布局

CSS Flexbox

传统的网页布局中,我们经常使用display:block + margin + padding + float + position来实现,但随着网页越来越复杂,对于某些不确定宽高的元素想要实现垂直居中实际上是比较困难的,而随着 CSS3 中 Flex 布局的出现,解决了传统布局中一些不易实现的问题。

Flex 是 Flexible Box 的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。

Flex 布局具有以下特点:

  1. Flex 布局与方向无关。
  2. Flex 布局可以实现空间的自动分配、自动对齐,具有很强的灵活性。
  3. Flex 布局试用于简单的线性布局,更复杂的布局可以考虑用 JS 或者 grid 实现。

Flex 布局的概念

flex_concept

在容器内,默认有两条轴,水平方向为主轴( main axis ),垂直方向为侧轴( cross axis ),主轴方向起始位置为主轴起点( main start ),主轴方向终点位置为主轴终点( main end ),侧轴方向起始位置为侧轴起点( cross start ),主轴方向终点位置为主轴终点( cross end ),容器的主尺寸为 main size ,侧尺寸为 cross size ,容器内的子元素为 flex item ,当给 container 设置display:flex,每一个子元素都是 container 的 item 。

Flex container 的属性

  • flex-direction
  • flex-wrap
  • flex-flow
  • justify-content
  • align-items
  • align-content

flex-direction 属性

flex-direction 表示主轴的方向。

flex-direction: row(默认值) | row-reverse | column | column-reverse;
  • row:主轴方向为水平方向,从左至右。

flex-direction:row;

  • row-reverse:主轴方向为水平方向,从右至左。

flex-direction:row-reverse;

  • column:主轴方向为垂直方向,从上至下。

flex-direction:column;

  • column-reverse:主轴方向为垂直方向,从下至上。

flex-direction:column-reverse;

flex-wrap 属性

flex-wrap: no-wrap(默认)| wrap | wrap-reverse;

flex-wrap 是指在主轴方向上如果项目过多排列不下时是否换行,默认情况下是不换行的。

  • wrap:轴线方向换行,第一行在上。

flex-wrap:wrap;

  • wrap-reverse:轴线方向换行,第一行在下

flex-wrap:wrap-reverse;

flex-flow 属性

flex-flowflex-directionflex-wrap 的简写,默认为 rowno-wrap

flex-flow: <flex-direction> || <flex-wrap>;

justify-content 属性

justify-content是指主轴方向对齐方式。

justify-content:flex-start(默认) | flex-end | center | space-between | space-around;
  • flex-start:主轴方向从左端对齐。

justify-content:flex-start;

  • flex-end:主轴方向从末端对齐。

justify-content:flex-end;

  • center:主轴方向从中间对齐。

justify-content:center;

  • space-between:主轴方向两端对齐,项目之间的空间均匀分配。

justify-content:space-between;

  • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

justify-content:space-around;

align-items 属性

align-items 是指侧轴方向对齐方式。

align-items: flex-start | flex-end | center | baseline | stretch;
  • flex-start:侧轴方向上端对齐。

align-items:flex-start;

  • flex-end:侧轴方向底端对齐。

align-items:flex-end;

  • center:侧轴方向中心对齐。

align-items:center;

  • baseline:侧轴方向上文字基线对齐。

align-items:baseline;

  • strech(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

align-items:strech;

align-content 属性

align-content属性定义了多根轴线(多行或是多列)的对齐方式。如果项目只有一根轴线(一行或是一列),该属性不起作用。

align-content: flex-start | flex-end | center | space-between | space-around | stretch;
  • flex-start:与交叉轴的起点对齐。

align-conten:flex-start;

  • flex-end:与交叉轴的终点对齐。

align-content: flex-end;

  • center:与交叉轴的中点对齐。

align-content: center;

  • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。

align-content: space-between;

  • space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。

align-content: space-around;

  • stretch(默认值):轴线占满整个交叉轴。

align-content: strech;

Flex 项目的属性

  • align-self
  • flex-grow
  • flex-shrink
  • flex-basis
  • flex
  • order

align-self 属性

align-self 属性定义 flex 的单个项目在侧轴上的对齐方式,默认为 auto,表示继承父元素的 align-items,否则将覆盖 align-items 属性。

align-self: auto | flex-start | flex-end | center | baseline | stretch;
  • center 属性:

align-self: center;

其他值与align-items属性值用法相同。

flex-grow 属性

flex-grow 定义项目的扩展比率(一般使用在空间过多时),默认为 0,即便存在剩余空间,也不放大。

flex-grow: <number> ;   /* default 0 */
  • 如果所有的项目不为 0,如 1/2/3,则等分剩余空间。

  • 如果 item1 项目为 1,其他项目为 0,则 item1 占据所有剩余空间。

.item1{flex-grow: 1;}

  • 如果 item1 项目为 1,item2 项目为 2,则 item2 占据的剩余空间比 item1 多一倍。

.item1{flex-grow: 1;} .item2{flex-grow: 2;}

flex-shrink 属性

flex-shrink 定义项目的收缩比率(一般使用在空间不足时),默认为 1,即空间不足时,项目缩小,为 0 表示不缩小。

该属性对负值无效。

flex-shrink: <number>; /* default 1 */
  • 如果所有项目均为 1,则所有项目同比例缩小。

.item{flex-shrink: 1;}

  • 如果 item1 为 0,其他 item 均为 1,则 item1 不缩小,其他 item 同比例缩小。

.item1{flex-shrink: 0;}

flex-basis 属性

flex-basis 定义了在分配多余空间之前,项目占据的主轴空间( main size )。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。

flex-basis: <length> | auto; /* default auto */

.item1{flex-basis: 400px;}

flex 属性

flex 属性是 flex-grow, flex-shrinkflex-basis 的简写,默认值为 0 1 auto。后两个属性可选。

flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]

order 属性

order 属性定义项目的排列顺序。数值越小,排列越靠前,默认为 0

该属性对负值有效。

order: <integer>;

小贴士

关于各属性浏览器的支持程度可以在 Can I use 查看。

推荐阅读

Flex 布局教程:语法篇

学习 JavaScript:阻碍你进步的 9 个常见错误

原文 - Learning JavaScript: 9 Common Mistakes That Are Holding You Back
译文 - 学习 JavaScript:阻碍你进步的 9 个常见错误

前言

很多人尝试学习 JavaScript ,但是不久就放弃了。然后他们就告诉自己,“JavaScript 太复杂了”,更有甚者说,“我不是前端开发的料”。

这种情况挺让人悲伤的。其实根本不必放弃,所要做的仅仅是换一种不同的学习方法。

在这篇文章中,我们将介绍一些最常见的错误学习方法,并了解如何避免这些错误。许多技巧不仅适用于 JavaScript,甚至可以用到 web 开发上,所以也算是一种福利。

我们来吧!

错误 1:开始学习之前过度分析

开始学习 JavaScript 之前,你可以找到很多相关的信息。如果你去看,就会发现一些 JavaScript 是最好的或者是最坏的、你是需要这个框架还那个框架的相关信息。你也可能会听到你需要以某种方式编写 JavaScript,否则你永远不会成为“真正”的开发人员等。

不管这些说的正确与否,没有什么比浪费六个月到一年还没有开始更糟糕。

开始敲代码吧,它不一定完美,可能很糟糕。但如果你开始了,就通过了阻碍很多人的障碍之一了。

错误 2:学习原生 JavaScript 之前学习框架

JavaScript 框架建立在原生 JavaScript 之上,因此如果你理解了 JavaScript,你也就自然而然的知道如何使用任何 JavaScript 框架的基本原理。

然而,如果你直接学习一个框架,最后也只是记住了它的语法却不理解它的原理。这就像在不知道词语意思的情况下造句,最终你只是随便地记住了一些词语,却不知道这些词语的意思并且不会组织这些词语来学以致用。

如果你直接进入一个框架,那将会更难学习,当你需要另一个框架你会更难适应。如果你首先学习基础的 JavaScript,那么你将有一个坚实的基础来了解所有的框架。

错误 3:好高骛远

最常见的错误之一就是在理解概念之后立即采取行动。

我一直在努力解决这个问题,因为一旦了解某些东西,你就想更进一步。

像对待新玩具一样对待每个概念是很有帮助的;这意味着你需要花一些时间来享受你刚学到的东西。玩耍、实验,看看你能不能做一些新的事情。你会学到很多,你会记得更好。

当你感觉自己闭着眼睛都能运用自如的时候再继续向下学习。可能在达到这一步之前,你需要更多的时间,但是这将是你接下来的学习变得更快。

另一方面,如果你过于急躁,你就不会太注意细节。但令人沮丧的是,这会使你之后的学习成本大幅提升。其实这也是人们常说要放弃学习 JavaScript 的常见原因之一。

错误 4:没有将概念理解透彻

学习就像爬楼梯:如果你能走一步,你可以继续采取更多的步骤,直到你达到目标。当有些东西难以理解时,往往是因为你想要进行一次飞跃,而不是一次走一步。当然这是痴心妄想!

在实际场景中,我看到人们对某段代码不理解的时候,我会请他们解释一下,他们会试图一下解释清整个问题。那我会请他们再一行一行的解释一遍,这样是有道理的。

如果有些部分很让人费解,那经常是因为跳过了某些东西,那么这也将有助于你去关注细节,直到找出症结所在。如果一个概念在分解之后仍然没有意义,那你也会有更容易找到相关解决方法,因为查找特定的主题比胡乱搜索更容易。

错误 5:太早尝试复杂的项目

刚开始学习 JavaScript 的人经常会说“我就随便定个小目标,写一个 Facebook 那样的网站算了”,没有意识到项目所涉及的深度。当项目逐渐深入时,他们就放弃学习 JavaScript 了。

我更详细地介绍了关于项目,但是在学习的时候,从一些基本概念开始会更容易。当你开始做项目时,你可以在工具包中添加一些构建工具。

更明确地说,我不是要那种越旷日持久的项目。我刚刚发现,如果我先做了一些简单的部分,比如在浏览器中显示一些文本或响应一个按钮,那么就可以更轻松地启动项目。

错误 6:不在真实环境下练习

当你学习 JavaScript 时,你可能会在不符合真实环境下进行练习。例如,你可能在网站的内置代码编辑器中输入内容,或者你可能依赖于教程中的粘贴文件。

这些方法对于学习来说可能是非常好的,但是你也可以尝试自己搭建环境。这意味着使用你自己的文本编辑器,并从头开始编写项目。

如果你不自己独立练习每一个概念,那你会依赖于训练环境。你最终会遇到这样的情况:你已经花了很多时间来学习,但你一个都无法掌握。

错误 7:将自己与大神进行比较

让自己更沮丧的最简单的方法之一就是和大神进行比较。因为你总是看他们在那里,而不是看他们如何到达那里。

举个例子,人们看到我的教程,并问我如何写这么干净的代码。他们说他们无法编写像这样的干净的代码,所以也许他们根本就不是 JavaScript 的那块料。

事实是我的过程是一团糟。我不断试验、犯错、查阅资料,写下丑陋的代码,最后把所有的内容都细化成一个可呈现的教程。人们看了优秀的版本,并且假设整个过程就是这样的。我也做过关于教程作者的这些假设,直到我开始写我自己的教程。

关键点是,认真学习你正在学习的东西,你会得到进步。继续重复这个过程,很快别人就会好奇你是如何达到那种高度的。

错误 8:只看教程不写代码

你会自然而然的花费大量的时间来观看视频和教程,但是除非你自己动手编写代码,否则你不能真的学会。

光看而不采取实际行动是很危险的,你会有一种你正在学习的错觉。六个月后,你会发现自己什么都没学会。

写 15 分钟的代码比上你光看一小时的教程有用多了。

错误 9:没有事先理解或自行尝试就盲目跟从教程

阅读教程时,很容易陷入照葫芦画瓢的情况。这种教程并不会教你如何解决一个问题,例如需要进行怎样的测试,如何一步一步的探索可能出问题的方向。因此,只会跟着教程走的人往往学不到真正的知识。

那么解决方案是什么?

不要只知道跟着教程一步步走,而是要花点儿时间去自己实现。例如,如果您正在学习幻灯片教程,请尝试显示和隐藏 div,然后尝试计时,然后尝试另一个小部分。相对于跟着教程一步步地走,通过亲身尝试并拓展你将学到更多知识,并且有可能将它应用得更好。

小贴士

在你读完这篇文章后,如果你问我最想让你记住什么,那就是通过采取最小的步骤来取得最大的进步。

无论你在学习什么,都要好好学习它本质上的东西。尝试你学到的东西,并乐在其中。

有时可能很困难,但这没关系。挑战意味着你正在提升个人能力,这将使你进步。如果一切总是太容易,这可能意味你需要进行些改变了。

21 步 教你成为一名成功的 Web 开发工程师

原文 - 21 Steps to Becoming a Successful Web Developer
译文 - 21 步助你成为成功的 Web 开发者

前言

随着 Web 开发的蓬勃发展,许多人都在问这样一个问题:我如何才能成为一名 Web 开发者?我认为这个问题不应该这样问,而应该是:我如何才能成为一名成功的 Web 开发者?这样的问题是很有必要的,因为世界有许多 Web 开发者,但是他们当中又有多少人是成功的呢?

我写这篇文章的目的是帮助你们提升心态、知识和技能,让你们能够从人群中脱颖而出,并让你无论是在的网站开发行业还是在公司或自由职业者,都能够取得成功。写这篇文章的目的是鼓励那些 Web 开发者和那些正在挣扎着冲破 “平庸障碍” 的 Web 开发者们。

以下这 21 歩将会帮助你在 Web 开发甚至 Web 开发之外的领域取得成功。

这是你真正热爱的吗?

“激情” 这个词常常被提及,人们已经在滥用中歪曲了它的原意,而它的实际意思就是“一种强烈且无法控制的情感”。

激情不是被动的:它是一种对行动起来的追求。大多数人讨厌他们的日常工作,但于此同时工作也很少能让他们喜爱并且爆发热情。

向你自己提出以下这三个重要的问题:

  • 创建网站和网页应用的想法是否会令我兴奋?

  • 这个会是适合于我的激动人心的事业吗?

  • 成为一名网页开发者能让我的工作同我自己(以及我的家人)的生活方式保持协调吗?

如果针对如上问题你的回答都是肯定的,那么对你成为一名网页开发者就是一条正确的路。

你做这些的原因是什么?

这是一个你要问自己的最重要的问题。为什么你会喜欢成为一名(成功的)网页开发者?

  • 为了与众不同然后改善其他人的生活?

  • 为其他人构建项目?

  • 构建属于自己的项目?

  • 挣到一份不错的收入?

我给自己提供的理由之一就是能给其他人创造条件,并使他们的生活整个产生正向变化, 这样他们就能为更多的其他人做同样的事情。

当你感到疲惫、分心,不安或者没有动力的时候,如果你的理由够充分的,那么它们就会让你行动起来。

你感兴趣的是什么?

你喜欢逻辑,解决问题,设计和视觉?

如果你喜欢逻辑和解决问题,那么你会喜欢 Web 后端开发。

如果你喜欢设计和视觉,你会喜欢 Web 前端开发。

后端开发就是做那些你在网站上看不到的东西。你可以想象成在这个世上某个角落有这样一台服务器(带着一个足够大到放下网站所有信息内容的硬盘),它在处理着网站上的数据,然后发送给访问者的浏览器。

而前端开发,就是做你在网站上能看到的那些展示出来的内容,点击,交互。

有可能以上两者你都有兴趣?

建立一个行动计划

一旦你找到了让你感兴趣的东西,那就马上做一个行动计划吧。你有多少可以用来学习的时间?你在学习的欲望有多强烈?

马上开始为你自己创建一个学习计划吧,这个计划看起来可能就像下面这样:

我每天只有 2 个小时的学习时间。我的预算不超过 $500,我对前端开发很有兴趣。

在做过一些调查,你知道了该学习什么以及去哪里学习之后,你的学习计划看起来可能就像下面这样:

  • 第一个月: 学习 HTML 和 CSS

  • 第二个月: 学习 Bootstrap 框架和基础设计原则

  • 第三个月: 练习用 HTML,CSS,Bootstrap 搭建网站

  • 第四个月: 学习 JavaScript 入门

  • 第五个月: 深入学习 Javascript 的高级用法

  • 第六个月: 练习用 HTML,CSS 和 JavaScript 搭建网站

  • 第七个月:专注在搭建一个个人作品集网站,打造个人品牌

  • 第八个月:尝试找一些公司和机构,为他们搭建网站(可以是免费的,就当时积攒实战经验)

  • 第九个月:磨练提高记忆,找到自己需要补足的有欠缺的地方

  • 第十个月: 到这个时候,我们的个人作品集里,最起码也应该有五个客户网站了

  • 第十一个月:学习一些接单技巧,了解商业上的基本运作

  • 第十二个月:寻找接触更多意向客户,推销你的业务,向客户收钱,或者是去找个工作

你需要注意的是,这仅仅是一份通用的例子,并不是一份确定的学习计划。

行动

如果你想做得更出色,那就必须学会妥协和牺牲。

如果你想成为一名成功的 Web 开发者或自由职业者,但你又抱怨没有时间。这时候你只需要看看你 每天/每周/每月 的日常习惯,找找哪些可以去掉,这样就可以腾出更多学习时间,用来提升你的开发技能等。

在我的生命里,我改掉的最使我分散注意力的一个习惯就是看电视:到现在为止,我已经两年没看过电视了,我感觉简直爽歪歪!这么做的 “怪人” 并不是只有我一个。Seth Godin,我们这个时代最伟大的营销和**领袖之一,他大力提倡不要浪费时间看电视

你自身的驱动力到底有多大?别再拖延也别再给自己找借口了,赶紧干活啦!

自律胜过外部动力

有动力很好,但有的时候动力也只是暂时的。当你不想做任何事情或者你没激情的时候,自律能让你赶紧行动起来。

社交媒体

打造线上的形象是必不可少的,社交媒体是其中的一个途径。

保证你在 LinkedIn, Twitter, Instagram 和 Facebook 上面都有一份个人资料。

创建一个作品集站点

你的作品集就是你的在线简历。我总是说,你向别人展示你做过什么总比你跟别人说你能做什么重要得多。

下面给你展示三个成功的作品集:

Robby Leonardi

Adam Dannaway

Denise Chandler

看看上面列出来的三个网站。他们一开始也都是初学者,但你看看他们是怎么展示他们专业知识和做过的作品的。

如果你的作品集足够好,那么客户和潜在的雇主会主动找你。建立一个好的项目列表/网站 - 即使你必须安排出来一段时间来做。

为开源项目做贡献

参与开源项目可以向别人展示你的以下方面:

  • 你对自己所做的方向充满激情

  • 团队合作的能力

  • 你的技术栈

最让人高兴的就是,如果你真的做得很好,你会在社区里被广泛认可,这也会提高你的整体可信度。

jQuery 的创始人 John Resig 说:当提及招聘,我总是把 GitHub 的 commit 记录放进简历。

这儿有五个你可以贡献代码的开源项目:

你够执着吗?

为什么要执着?

Web 开发者在开发过程中经常面临很大的挑战。如果你的代码中有错误,或者代码运行结果和预期不一样,那么在没有找到解决办法之前别再添加新功能了,你不要放弃任何角落。你必须得执着,把问题解决掉。

为了找出问题,你可能会花很长的时间,但你的执着是非常值得的,这也有助于你在未来的项目中快速的找出有效的解决方案。

磨刀不误砍柴功

给我六个小时砍树,我会用前 4 个小时磨斧头。

Abraham Lincoln (亚伯拉罕·林肯) 的名言对我影响很大影响。只专注于做技术或经营自由贸易的人往往会忽略如何更好、更快的做事。成功的开发者用很巧妙的方式工作,并能获得很好的成绩。

不仅仅是知道多少

我知道更多编程语言,所以我会比你更成功。

成功不在于你知道多少,而在于你能用所知道的东西什么。就像在面试中,知道的多的人总能得到工作,是吗?

生活在 “现实世界” 中的人都知道这不对。面试需要的不仅是工作技能(或者作为自由职业者的技能),还有很多其它方面。

成长

任何行业的专家都会不断的学习来提高自身的知识和技能。作为 Web 开发者,保持不断的学习是很重要的事儿。

如果你在 20 年前学过 C++,但并没有跟随新版本,那么在今天你在这方面的知识已经没什么用了。

不要在乎你的经验处于什么水平,持续学习就对了。

经验

你宁愿雇用谁?

  • 一个 35 岁,刚刚获得 MBA 学位的商业顾问。
  • 一个 30 岁,高中辍学,没有证书,但负责着 3 个几百万美元的业务,其中 2 个已经高价卖出,赚了不少钱,有着丰富经验的商业顾问?

经验比理论更重要。不要只说 “我可以做这做那”:这些是每个普通的 Web 开发者都能做的事情。你一定要展示出你做了什么。

薪酬

一旦你开始变得更加自信,积累了更多经验,你就需要将你的工资定得更高。

这可能会成就你,也可能阻碍你。你是愿意拿每年 5 万美元的工资还是 9 万美元的工资?用 500 美元的网站和 5000 美元创建的网站有啥区别?

如果我要卖给你一瓶酒,我告诉你我现在有两瓶,一瓶定价 5 美元,另一瓶定价 55 美元,你可能会认为定价 5 美元的有问题,不是吗?

对于你来说也是同样的道理。虽然这主要适用于自由职业者,但这个原则同样适用于谈判你的薪酬。

效率

比起被各种短信消息、YouTube、有趣的图片所吸引的情况下工作 4 个小时,高度集中注意力工作 1.5 个小时要好的多。

尽可能不要分散注意力,集中注意力让工作变得更有效率。

看看 Pomodoro Technique 对于效率的讨论.

技能

限制自己只学习某个编程语言是不对的。

为了提高成功的机率,学习其他的像市场、谈判技巧、沟通以及社会技能等对你很重要。

看看那些最成功的开发者们吧:他们的技术都很全面,也有着很好的天分,他们并没有把所有精力都用在编程上。

在网上和别人交流

加入编程社区,Facebook,Twitter 以及其他的平台,别怕问 “愚蠢的” 问题。

在像 Stack Overflow,Reddit,Quora 以及博客等提问和回答问题。

参加聚会同时多参加其他社交活动

这个方法可以和别人取得联系。

我性格非常内向。真的,人们叫我寄居蟹。猜猜我在组织中的位置在哪儿?在某个角落里…

如果你性格内向 (和我一样,你应该意识到,在某种意义上,你需要走出舒适区和别人打成一片。

看下你附近是否有聚会,使用 Meetup 这样的服务。

顺便说下,不要只去为开发者准备的聚会和一些活动。如果你是自由职业者,也可以去参加商业活动。毕竟,有多少开发者雇佣其他开发者呢?

要有发散性思维

你需要像网站访问者和网站的所有者一样思考。

作为站点访问者,你需要思考:在这个网站上的每一步操作,给我留下印象的是什么?我有没有在这里得到想要的答案?这个站点满足了我的需求吗?我相信这个网站吗?等等。

作为网站所有者,你需要思考:我的网站是否在此刻解答了用户的问题和怀疑?为了回答这些问题,我可以做什么?为了方便网站的访问者按照我的想法来操作,我可以在网站上做些什么?

永不放弃

成功的人常常经历失败,但是不可否认的是,他们都有永不放弃和寻找新奇事物以处理事情的特质。

有时候,你和成功失败的距离就差一步,那就是是否愿意继续。

永远不要放弃你的梦想,你的欲望和你的目标。

总结

永远不要认为你不能成功,如果你需要一个相信你的人,我相信你行!

我希望在这篇简短的文章里能给你带来积极的影响。

JS原型链、__proto__ 和 prototype

不知道你有没有想过这样一些问题:

为什么我定义一个数组,它就有 push、join、pop、shift 等方法,我明明什么也没写啊?
为什么我定义一个函数,它就有 call、apply、length 等属性/方法,我也什么都没有做呀?!
为什么我定义一个对象,它就有 toString、valueOf 等方法,我更是什么都没有做呀?

我们先来说说对象

当我们定义一个对象时

var obj = {}

proto

我们发现 obj 下面有一个属性名叫__proto__,(它是个对象)

obj.__proto__里面又有很多属性,包括 valueOf、toString、constructor 等。当我们需要找obj.valueOf这个属性时,发现 obj 本身没有没有,那么它就会去查找obj.__proto__是否有这个属性,如果还没有,它去找obj.__proto__.__proto__,直到找到这个属性或 null 为止,在这个读取属性的过程中,是沿着__proto__组成的链子来搜索的,这个链子我们称为原型链

如果 obj 自身定义了一个 valueOf 属性,那么它找到自身的 valueOf 之后就不再沿着__proto__来找,因为已经找到了,没有必要继续找了,也就是说

  • 新增的属性不会沿着__proto__查找

  • 读取属性会沿着__proto__,直到找到这个属性,或者是 null 为止。

那么obj.__proto__到底是什么呢?

__proto__是一个简单的访问器属性,它总是指向它的构造函数的 prototype ,即原型对象。

所有的对象都继承了Object.prototype的属性和方法,
它们可以被覆盖(除了以null为原型的对象,如 Object.create(null))。
例如,新的构造函数的原型覆盖原来的构造函数的原型,提供它们自己的 toString() 方法.。对象的原型的改变会传播到所有对象上,除非这些属性和方法被其他对原型链更里层的改动所覆盖。

所有的对象会动态生成一个__proto__指向它构造函数的原型 ( prototype )

当我们去查找obj.valueOf这个属性时,他会沿着原型链去查找obj.__proto__.valueOf,而obj.__proto__指向obj.constructor.prototype。即

obj.__proto__ === obj.constructor.prototype  // true
obj.__proto__.toString=== obj.constructor.prototype.toString  // true

我们知道 obj 的构造函数就是 Object,那么我们也可以这么写

obj.__proto__ === Object.prototype  // true
obj.__proto__.toString === Object.prototype.toString  // true

对于数组

以 push 方法为例,我们知道当我们定义一个空数组时,我们可以直接调用 push 方法,根据上面的解释,他会沿着这个数组的__proto__去查找这个方法。

var array = []
array.push === array.__proto__.push // true

array.__proto__指向它构造函数的prototype,那么

array.__proto__.push === array.constructor.prototype.push // true
array.__proto__.push === Array.prototype.push  // true

终于找到 push 方法了。

问题来了,我们知道 array 也是对象,那么 JS 是怎么知道 array 也是对象的呢?

  • 通过__proto__

我们先看Array.__proto__指向谁

Array.__proto__ === Function.prototype   // true

你也许会纳闷,怎么Array.__proto__指向的怎么是函数的原型对象呢?因为 Array 的构造函数就是函数,可以通过console.log(Array.constructor)验证。而函数的原型对象的__proto__最终指向 Object

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

或者我们也可以这样写

Array.prototype.__proto__ === Object.prototype  // true

最终归宿都是 Object。

至此我们看下一个小小的 array 都经历了什么

array.__proto__  ----> Array.prototype ----> Array.prototype.__proto__---->Object.prototype

这样也就不难理解为什么数组(函数)也是个对象了。

一切皆对象?

也许你会迷惑,既然Array.__proto__.__proto__ = Object.prototype,那么

Number.__proto__.__proto__ === Object.prototype   // true
Boolean.__proto__ .__proto__ === Object.prototype   // true
String.__proto__.__proto__ === Object.prototype   // true

为什么我们不可以说数字/布尔/字符串也是对象呢?

这要看这个数字/布尔/字符串是怎么创建的了。

以数字为例

var a = 1
var b = new Number(1)

我们知道基本类型是没有属性的,即便可以访问到这个属性,也是访问的临时对象的属性,访问完就销毁了,即使你发现a.__proto__.__proto__指向是 Object,也是 new Number 指向的,跟 a 没有半毛钱关系,因为 a 就是个 number。

b 就不一样了,b 是构造函数 Number 构造出来的一个对象,只不过他的值是 1,它可是有__proto__属性的,那么 b 就可以愉快的指来指去了。

b.__proto__.__proto__ === Object.prototype  // true

所以 a 是一个 number,而 b 是一个 object。

同理字符串和布尔也是如此。

最后,我们来一起念一遍 JS 的七种数据类型:

number , string , boolean , undefined , null , object , symbol

那么 JS 一切皆对象 的说法不攻自破了。

推荐阅读

什么是 JS 原型链?

Object.prototype.proto

Promise

在了解 Promise 之前,我们先来看一下一般实现异步编程的方法。

回调函数

在 ajax 中,我们经常用到回调函数来表示当请求成功之后所进行的操作,例如

ajax({
  method: 'GET',
  url: '/xxx',
  success: function(responseText){
    console.log(responseText)
  }
  error: function(status){
   console.log('请求失败,状态码为' + status)
  }
})
function ajax(options){
  let {method, url, success, error} = options
  if(!method){ throw new Error('Error!没有传入method') } 
  url = url || location.href
  let xhr = new XMLHttpRequest()
  xhr.open(method, url)
  xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
      if(xhr.status>=200 && xhr.status < 400){
        success && success.call(null, xhr.responseText, xhr)
      }else if(xhr.status >= 400){
        error && error.call(null, xhr.status, xhr)
      }
    }
  }
  xhr.send()
}

上面的例子中,通过自己封装的一个 ajax 方法,当请求成功/失败之后,通过回调函数,实现成功/失败的事件,这样看似乎没有什么问题,但是当请求成功时,需要执行多个回调函数,该怎样呢?

正常情况下,可以这样实现:

ajax({
  method: 'GET',
  url: '/xxx',
  success: function(responseText){
    success1()
    success2()
  }
  error: function(status){
    console.log('请求失败,状态码为' + status)
  }
})

在成功的调用后,我们可能需要把返回的结果用在另一个 Ajax 请求中,也可能请求多次,这样就可能会出现回调函数之间层层嵌套的情况(函数连环套),当这种嵌套的回调函数变得难以理解,开发人员需要仔细分析哪些代码用于应用的业务逻辑,而哪些代码处理异步函数调用的,代码结构支离破碎。错误处理也分解了。

Promise

为了降低异步编程的复杂性,开发人员一直寻找简便的方法来处理异步操作。其中一种处理模式称为 promise(注意:promise 是一种处理模式),它代表了一种可能会长时间运行而且不一定必须完整的操作的结果。这种模式不会阻塞和等待长时间的操作完成,而是返回一个代表了承诺的(promised)结果的对象。

jQuery 中的 ajax 对象返回了一个类似 promise 的对象,并提供了 then 方法来实现异步处理

let promise = $.ajax({ method: 'GET', url: '/xxx'})
promise.then(success1).then(success2)

then 方法提供了两个参数,resolvedHandler,rejectedHandler。resolvedHandler 回调函数在 promise 对象进入完成状态时会触发,并传递结果;rejectedHandler 函数会在拒绝状态下调用。then 方法会返回另一个 promise 对象,以便于形成 promise 管道。后来 ES6 对其进行了规范,并提供了原生的 Promise 对象。

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点

对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Fulfilled(已成功)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从 Pending 变为 Fulfiled(resolve) 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 Resolved(已定型)。如果改变已经发生了,你再对 Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

ES6 规定,Promise 是一个构造函数,可以通过它来创建 Promise 实例:

let promise = new Promise(function(resolve,reject){
  if( /* 异步操作成功 */){
    resolve()
  }else{
    reject()
  }
})

Promise 构造函数接受一个函数作为参数,该函数的两个参数也是函数,分别是 resolve 和 reject。它们由 JavaScript 引擎提供,不用自己部署。

resolve 函数的作用是,将 Promise对象的状态从“未完成”变为“成功”(即从 Pending 变为 Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将 Promise对象的状态从“未完成”变为“失败”(即从 Pending 变为 Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise 实例生成以后,可以用 then 方法分别指定 Resolved 状态和 Rejected 状态的回调函数。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

then 方法可以接受两个回调函数作为参数。第一个回调函数是 Promise 对象的状态变为Resolved 时调用,第二个回调函数是 Promise对象的状态变为 Rejected 时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受 Promise对象传出的值作为参数。

针对于我们一开始的例子,用 Promise 可以这样来实现:

function ajax(options){
  return new Promise(function(resolve, reject){ // 1
    let {method, url} = options
    if(!method){ throw new Error('Error!没有传入method') } 
    url = url || location.href
    let xhr = new XMLHttpRequest()
    xhr.open(method, url)
    xhr.onreadystatechange = function(){
      if(xhr.readyState === 4){
        if(xhr.status>=200 && xhr.status < 400){
          resolve.call(null, xhr.responseText) // 2
        }else if(xhr.status >= 400){
          reject.call(null, xhr) // 3
        }
      }
    }
    xhr.send()
  })
}
 ajax({ method: 'GET', url:'/x'}).then(success1, error).then(success2,error)

有了 Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

推荐阅读

ES6 Promise - 阮一峰

JavaScript 异步编程的 Promise 模式

从 URL 输入到页面展现

流程

  1. 在浏览器输入URL
  2. 域名解析
  3. 服务器处理
  4. 网站处理流程
  5. 浏览器处理
  6. 绘制网页

流程简述

1. 在浏览器输入 URL

什么是 URL?

  • 统一资源定位符(Uniform / Universal Resource Locator,常缩写为 URL),俗称网页地址(网址)。
  • 用于定位互联网上的资源。
  • URL 包含协议部分。协议是浏览器和 www万维网之间的沟通方式,它会告诉浏览器在网络上正确找到资源位置。常见的协议有 http、https、ftp、file 等。其中 http 是最常见的网络传输协议,而 https 则是进行加密的网络传输。

2. 域名解析

  • 域名解析是把域名指向网站空间IP,让人们通过注册的域名可以方便地访问到网站的一种服务。
  • 对于 https://github.com/HarleyWang93/blog, github.com 就是域名。
  • 对于 http://github.com 的 URL,浏览器实际上不知道 github.com 到底是什么东西,需要查找 github.com 网站所在服务器的IP地址,才能找到目标。
  • IP 地址很难记,所以发明了域名。语义化的域名更利于人们记忆,可是电脑收到用户输入的网址后,要想让电脑理解这一串字符串,就有些困难了,因为计算机更擅长处理一长串数字,那么 IP 地址和 DNS服务就显得非常重要了。

IP 地址是什么?

  • IP 地址是指互联网协议地址,每个处于互联网中的设备都有 IP 地址,形如192.168.0.1
  • 局域网 IP 和公网 IP 是有差别的
  • 127.0.0.1代表本机的 IP

域名解析的流程

  1. 浏览器缓存 – 浏览器会缓存 DNS 记录一段时间
  2. 系统缓存 - 从 Hosts 文件查找是否有该域名和对应 IP
  3. 路由器缓存 – 一般路由器也会缓存域名信息
  4. ISP DNS 缓存 – 比如到电信的 DNS 上查找缓存
  5. 如果都没有找到,则向根域名服务器查找域名对应 IP,根域名服务器把请求转发到下一级,直到找到 IP
  • 8.8.8.8 —— Google 提供的免费 DNS 服务器的 IP 地址
    114.114.114.114 —— 是国内第一个、全球第三个开放的 DNS 服务地址,又称114DNS

  • DNS劫持:DNS劫持又称域名劫持,是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能反应或访问的是假网址。

3. 服务器处理

什么是服务器?

  • 服务器是一台机器安装系统的机器,每个服务器里都安装有处理请求的应用——web server。
  • 常见的web server产品有 apache、nginx、IIS 或 Lighttpd 等。
  • web服务器接收用户的 Request 交给网站代码,或者接受请求反向代理到其他 web服务器。

4. 网站处理流程

  • 网站处理,就是实际后台处理的工作。后台开发现在有很多框架,但大部分都还是按照MVC设计模式进行搭建的。
  • MVC是一个设计模式,将应用程序分成三个核心部件:模型(model)-- 视图(view)--控制器(controller),它们各自处理自己的任务,实现输入、处理和输出的分离。
  1. 视图(view)
    视图是用户看到并与之交互的界面。这是前端工作的主力部分。

  2. 模型(model)
    模型是将实际开发中的业务规则和所涉及的数据格式模型化,应用于模型的代码只需写一次就可以被多个视图重用。在MVC的三个部件中,模型拥有最多的处理任务。一个模型能为多个视图提供数据。

  3. 控制器(controller)
    控制器接受用户的输入并调用模型和视图去完成用户的需求。Controller处于管理角色,从视图接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示模型处理返回的数据。

  • 总结,首先控制器接收用户的请求,并决定应该调用哪个模型来进行处理,然后模型用业务逻辑来处理用户的请求并返回数据,最后控制器用相应的视图格式化模型返回html字符串给浏览器,浏览器呈现网页给用户。因此,下一步就来到浏览器处理阶段。

5. 浏览器处理

  • HTML 字符串被浏览器接受后被一句句读取解析
  • 解析到 link 标签后重新发送请求获取 css
  • 解析到 script 标签后发送请求获取 js,并执行代码
  • 解析到 img 标签后发送请求获取图片资源

6. 绘制网页

  • 浏览器根据 HTML 和 CSS 计算得到渲染树(DOM 树的可视化表示),绘制到屏幕上,js 也会被执行,最终完成了整个页面的展示。

推荐阅读

说说从URL输入到页面展现的过程
从URL输入到页面展现的全过程

Recommend Projects

  • React photo React

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

  • Vue.js photo Vue.js

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

  • Typescript photo Typescript

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

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

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

Recommend Topics

  • javascript

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

  • web

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

  • server

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

  • Machine learning

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

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

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

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.