harleywang93 / blog Goto Github PK
View Code? Open in Web Editor NEWblog - 走过的路,踩过的坑 ( 目录见下方 )
blog - 走过的路,踩过的坑 ( 目录见下方 )
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 。
假设页面中存在一个 btn 的按钮,并且给 btn 添加一个点击事件
btn.onclick = function(){
console.log('this is a click event')
}
事件就是用户或浏览器自身执行的某种操作,如click、load、mouseover等,都是事件的名字,而响应某个事件的函数就被称为事件处理程序。
在上述的例子中,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);
}
W3C 后来将 DOM1 升级为 DOM2,DOM2级规范开始尝试以一种符合逻辑的方式来标准化 DOM事件。DOM0级 可以认为 onclick 是 btn 的一个属性,DOM2级 则将属性升级为队列。
DOM2级 事件定义了两个方法,用于处理指定和删除事件处理程序的操作,addEventListener()
和removeEventListener()
,所有的 DOM 节点中都包含这两个方法,它们都接收 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()
只能移除具名函数,不能移除匿名函数。
IE8 及之前,实现类似addEventListener()
和removeEventListener()
的两个方法是attachEvent()
和detachEvent()
,这两个方法接受相同的两个参数。
要处理的事件名
作为事件处理程序的函数
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
}
}
作为一名开发者,我们会花很多时间在终端上,所以对终端的效率、颜值有很高的要求。
本文会分享 Terminal 在颜值(个性化)和效率( Oh-My-Zsh )两方面的配置。
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。
下载安装见 Iterm2 Downloads
对于命令行的使用,一般有两种情景:
短时间使用。执行几条命令就关闭,比如打开文件,启动服务等。
长时间使用。比如使用 Vim 编程。
所以,我们可以尝试配置两个 Profile 。
首先,在 Preperences → Profiles
,新建一个 Profile,命名为 Hotkey Window,如图:
在 Preperences → Profiles → Window → Window Appearance
进行设置,如图:
在 Preperences → Profiles → Window → Settings for New Windows
进行设置,如图:
在 Preperences → Profiles → Keys → HotKey
进行设置,如图:
第一种 Profile 配置完成,您可以按下你设置的 HotKey
来方便快速打开和隐藏命令行。
第二种配置在第一种配置的基础上稍微修改一下就可以了。
Hotkey Window
在 Preperence → Profiles
,复制 Hotkey Window
, 取名 Default of Hotkey Window
,如图:
在 Preperences → Profiles → Default of Hotkey Window → Window → Settings for New Windows
进行设置,如图:
如果要使用这种风格,设置为默认即可。
在 Preperences → Profiles
中,选中 Default of HotKey Window
, 点击下方 Other Actions → Set as Default
,如图所示:
至此,两种 Profile 配置完毕。
目前常用的 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 ,节约了宝贵时间。
sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
sh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)"
详情见 oh-my-zsh。
zsh 的配置主要集中在用户当前目录的 .zshrc
里,用 vim 或者你喜欢的其他编辑器打开这个文件,在里面可以修改主题、插件以及定义自己的环境变量等操作。
打开 .zshrc
,找到这行语句:
ZSH_THEME="robbyrussell"
修改 ZSH_THEME
的值就可以更换主题。
默认的主题是 robbyrussell
,不喜欢的话可以自行更换。
更多主题见 oh-my-zsh_themes,还有很多不是自带的主题 oh-my-zsh_External-themes 。
这里踩了个小坑,如果您选择了其中一款主题,打开终端或者 Vim 之后发现这样问号的情况:
原因是依赖于一种字体 powerline fonts ,powerline fonts 是 vim 增强组件 vim powerline 附属的字体,所以要在系统中安装该字体。
解决办法:
# clone
git clone https://github.com/powerline/fonts.git --depth=1
# install
cd fonts
./install.sh
# clean-up a bit
cd ..
rm -rf fonts
在 Preperences → Profiles → Text → Font
和 Non-ASCII Font
将字体改为 Source Code Pro for Powerline
,如图:
打开 ~/.zshrc
找到这行语句:
plugins=(git)
修改 plugins
的值就可以添加或者删除插件,比如:
plugins=(git autojump osx zsh-autosuggestions)
下面是几个常用插件,更多插件见 Plugins
A cd command that learns - easily navigate directories from the command line
一款快捷跳转目标路径的插件,支持模糊匹配,自动补全,历史记录等功能。
Homebrew is the recommended installation method for Mac OS X:
brew install autojump
plugins
的值即可。更多内容见 autojump 。
This plugin provides a few utilities to make it more enjoyable on 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. |
Fish-like autosuggestions for zsh
一款自动建议插件
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
的值,然后重启终端即可。cmd + n : 新建窗口
cmd + t : 新建TAB
cmd + d : 垂直新建TAB
cmd + shift + d : 水平新建TAB
谢谢很多前辈的分享,让我学习到了很多,非常感谢!
前段时间看了很多关于校园霸凌的新闻和评论,以及受到霸凌的受害者和施暴者关于经历或参与校园霸凌的描述,让我联想起了自己参与或经历的校园霸凌,所以写了这么一篇随感。
小学时期,班上有几位农村来的同学,跟班里大多数同学在穿着或行为上有很多不一样的地方。
一些同学拿他们取笑或作弄他们,这些同学里面就包括我,在我的印象中有以下的行为:
我写下这些是给自己看的,目的是牢记自己做过这样的罪行,并忏悔一辈子。
初中时期,我有一些家住着很近的同学,每天上学放学都一起,基本上每个周末也都约着去打篮球,自然也发展成很要好的朋友。其中有一位 L 同学,他性格开朗,篮球也打的不错。
当时班上有 2 个同学对他人有霸凌行为。他们喜欢挑衅并狠踹他人,有把同学的脑袋按在垃圾堆里的行为。我有多次被他们挑衅并狠踹的经历。
L 和我一样,都受到过那 2 个同学的霸凌。我小时候很胆小,性格懦弱,有过反抗但没用。L 不一样,他选择跟他们死磕到底,甚至后来选择带刀子上学。
有一天下午放学后,那 2 个同学叫来了几个其他学校的霸凌者和几个社会闲散人员,羞辱并殴打了 L。后来这件事的处理我不太记得了。
我只记得这件事之后,L 同学有些变了,有了一些奇怪的行为。(后来,L 在升入跟我不同的高中之后,开始参与打架,也惹出了事。)在这之后,我们这一帮要好的朋友,都开始疏远 L,并嘲笑他的那些奇怪行为。
在读大学放假回家期间,我们那一帮老同学老朋友出来聚餐,还时不时会拿 L 的奇怪行为来取笑。
现在想来,L 的奇怪行为都是因为受到霸凌而产生的一些反应,他是个受害者,但我们却拿他取笑。
所以,在我初中的时候,受到过霸凌,也做了霸凌者的帮凶。
之前我跟 L 聊过一次,我真诚的道了歉,他也接受了道歉。虽然他嘴上说无所谓,但我知道原谅是不可能的,这些事改变了他的人生轨迹。
不知道这些以前跟 L 要好但后来疏远嘲笑L的老同学有没有认识到这一点,虽然羞辱殴打 L 的不是我们,但后来我们的疏远嘲笑也一样霸凌了 L,而且在大学期间都还没有意识到这个错误。甚至现在,好些老同学可能都没有悔悟。
很幸运的是,我高中时期和大学时期没有经历或参与任何校园霸凌。
据一位在美国教书的**教师观察,如果一个孩子以“我不是故意的”为借口逃脱掉一件主观做错事的责任,他“不小心”欺负人的情况会越来越多。今天不小心打了一下其他人,过几天不小心拿铅笔扔到别人的身上,甚至,不小心把其他孩子的大把头发扯下来!
那么美国小学是如何处理这些事情的呢?这位自称“小杨老师”的人举了个例子。
有一天在操场的时候,他突然看到几个孩子围在一起,地上有一大把头发。他捡起头发立马询问怎么回事。旁边有孩子跟老师解释:“ Mark 把 Betty 的头发扯下来了!”看着那一大把被生生扯下来的头发,Mark 一开始声称这只是个游戏,但看到老师的表情很严肃,也开始有点害怕。“小杨老师”告诉他:“第一,没有扯人头发的游戏。第二,你所谓的“游戏”不好玩,甚至给他人造成了痛苦! 第三,你需要为这件事情负责。”
随后“小杨老师”把那一撮头发保存到一个塑料袋子里作为证据。然后立马报告校长。鉴于 Mark 在学校近期其他欺负孩子的表现,校长找两个孩子谈话,并且当场打电话给两个孩子的父母。Betty 的妈妈很快赶过来,把 Betty 接去家庭医生那里做检查。Mark 在和校长一番谈话后接受了以下处理:
- 书面写信给 Betty 道歉。
- 因为涉及到身体伤害,以及近段时间其他表现,Mark 被正式停学一天。
- 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]为例。
将数组分成[3]和[2, 4, 5, 1]两部分,前者是已排序的,后者是未排序的。
取出未排序部分的第一个元素“2”,与已排序部分最后一个元素“3”比较,因为2小于3,所以2排在3前面,整个数组变成[2, 3]和[4, 5, 1]两部分。
取出未排序部分的第一个元素“4”,与已排序部分最后一个元素“3”比较,因为4大于3,所以4排在3后面,整个数组变成[2, 3, 4]和[5, 1]两部分。
取出未排序部分的第一个元素“5”,与已排序部分最后一个元素“4”比较,因为5大于4,所以5排在4后面,整个数组变成[2, 3, 4, 5]和[1]两部分。
取出未排序部分的第一个元素“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] 进行从小到大排序为例,步骤如下:
将数组分成[3, 2, 4]和[5, 1]两部分。
将[3, 2, 4]分成[3, 2]和[4]两部分。
将[3, 2]分成[3]和[2]两部分,然后合并成[2, 3]。
将[2, 3]和[4]合并成[2, 3, 4]。
将[5, 1]分成[5]和[1]两部分,然后合并成[1, 5]。
将[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函数,合并两个已经按升序排好序的数组
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)是公认最快的排序算法之一,有着广泛的应用。
快速排序算法步骤:
从序列中挑出一个元素,作为”基准”(pivot)。
把所有比基准值小的元素放在基准前面,所有比基准值大的元素放在基 准的后面(相同的数可以到任一边),这个称为分区(partition)操作。
对每个分区递归地进行步骤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 的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。
例如:
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 对象中使用回调函数都可以实现异步编程。
Markdown 是一个简单的标记语言,这些标记和 HTML 的一些标签对应
通过一些转换库可以把 Markdown 转换成 HTML 或者把 HTML 转换成 Markdown
用来在网页上展示文章,省去排版布局的烦恼
# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
1. 有序列表
2. 有序列表
3. 有序列表
- 无序列表
- 无序列表
- 无序列表
[显示文本](链接地址)
![](图片链接地址)
> 引用测试
> > 引用内引用测试
效果
引用测试
引用内引用测试
```
var a = 1
var b = 2
```
效果
var a = 1
var b = 2
**粗体测试**
*斜体测试*
~~删除线测试~~
效果
粗体测试
斜体测试
删除线测试
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,你将成为一名全栈工程师。
你缩小了你的技能范围,但是这表明了你对某项技术更加的专注,同时在你接下来的职业生涯中,你可以去选择专攻前端还是后端。
雇主更喜欢能够灵活应变的员工(也就是让你干啥你就能干啥呗)。
面试官不会问你在大学学习了那几个方面的只是,因为这样很难在面试中真正了解你的技术水平。他们想知道你的开发经验,虽然可能你的经验并不丰富,但是你为自己做作品集的过程也是一个开发经验提升的过程。
如果你没有任何作品来展示在你的作品集上那也没关系。注册一个域名,搭建一个服务器,安装一个 CMS,创建一个账户,搜索引擎提交,添加谷歌分析等都是建立一个网站的必要组成部分。在这个阶段,网站流量的获取和影响力不是你主要考虑的东西。
如果你已经建立了一个个人网站,那么很可能你的朋友或者家人了解到也有别人有类似的需求,所以这就又是一个丰富你作品的机会啊。这将让你有机会在为别人干活的同时不断历练自己。这也让你更接近实际的项目开发,同时还提供了一个相对没有风险的提高技能的机会。
通过以上这些经历,将使你在面试中有东西可说,同时如果面试官认为他们有一些小网站你可以来做,那你的表现就更有可能给面试官留下深刻的印象。
在你搭建个人网站的过程中可能会遇到一些这样或那样的问题,而这些问题通常可以通过一个小插件或者 JavaScript 库解决。然而如果你没找到可用的解决方案或者你有需要进一步开发的解决方法,那么这就是一个超过与你同水平的开发人员的好机会。
然后你需要找到一种方法来解决这个问题,再对其进行包装,让用户可以很方便的使用。例如一个 WordPress 插件或者开源的 GitHub 项目。
这将会进一步提升你你的技能水平。因为你已经从为自己做小东西转变到了独立创建项目。能够去思考其他产品或者设计师的需求。你的插件和代码有机会被收入到更大的项目当中。
这就是能够吸引面试官的一个亮点,因为你做的这些更接近实际的开发。在一个真正的开发团队中进行开发时,你会负责一个大的应用程序的一小部分,你要确保你的代码在集成到项目中时不会出现任何问题。
另外的一个好处就是,你可以对你插件的使用进行收费,作为你努力赚取的一点额外收入。
除了技术能力,雇主还希望看到能够证明热爱你的工作的证据,因为现在技术变化的太快了,你必须能够多多的投入个人时间,来提升你的知识。你需要能够了解到新兴的框架或工具等的变化趋势。最好的方法是阅读博客,多逛一逛软件开发网站。当你在其中能够有一定的贡献的时候,你就可以去做一些评论,提供一些你的想法。如果你能帮别人解决一些问题,那你的社区贡献度就会飙升。
你可能会认为雇主在筛选候选人时不会在意这一点,然而他们会。去查看你在 LinkedIn、GitHub、Stack Overflow 等账号致力于你的研究方向的证据的公司并不罕见。顶级团队甚至会要求求职者在 Stack Overflow 社区中贡献度不能低于多少值。因为没有什么能够比从你为其他工程师提供的建议中更能证明你的水平了。
把你所有的代码都放到网上,让全世界都可以看到。这将会让你有更强的批判性思维和分析能力。同行对你代码的评论和提出的问题是你们开发团队来发现错误并及时修复错误来保证产品质量的重要方式。如果你早就习惯了这一点,你就会发现在进行代码审查时不会有任何问题,并且你会在商业环境中有建设性地反馈意见。
同时,这也提供了一种来看你写的代码的方式。你解决过的问题和你做过的项目说来容易,但当一个高级工程师看了你的代码后能够在另一个层面上增加他对你开发能力的信任程度。
你潜在的团队成员也有机会去阅读和检查你的代码,你的设计模式,和你清晰地代码提交记录。最重要的是,它表明你熟悉版本控制,软件产品的核心以及团队的建立。
毕业后在没有实际项目开发经验的情况下找到你的第一份工作是一个艰巨的任务。但这也有行之有效的方法来提高和吸引雇主的注意力。
选择一个后端和前端语言,创建一个网站,然后做一个插件。多看一些博客和网站,并做一些评论,提供一些你的想法,不要害怕别人会说什么。
把你所做的项目和实用的小工具等都放到 GitHub 上。
记住每个人都一样,拥有等量的资源和机会。参照本文中的要点,在你有耐心的时候好好坚持下去,最终一定会得到高薪的工作机会的。
语义化标签优先
基于功能命名、基于内容命名、基于表现命名
简略、明了、无后患
用两个空格来代替制表符(tab)
嵌套元素应当缩进一次(即两个空格)
对于属性的定义,确保全部使用双引号,绝不要使用单引号
不要在自闭合(self-closing)元素的尾部添加斜线 -- HTML5-规范中明确说明这是可选的。
不要省略可选的结束标签(closing tag)
遵循 HTML 标准和语义,减少标签的数量,尽量避免多余的父元素。
用两个空格来代替制表符(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
-- 页面尾部
aside
、sidebar
-- 用于侧边栏
.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'
]
}
通常我们创建一个自定义对象可以使用以下两种方法:
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 帮我们做了如下的事情:
不用显示的创建对象,因为 new 会帮你做(使用「this」就可以访问到临时对象)
直接将属性和方法赋给了 this 对象
不用 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 总共帮我们做了如下的事情:
不用显示的创建对象,因为 new 会帮你做(使用「this」就可以访问到临时对象)
直接将属性和方法赋给了 this 对象
不用 return 对象,因为 new 会帮你做
不用绑定原型,new 指定原型的名字为 prototype
喜欢写 Blog 的人,会经历三个阶段。
第一阶段,刚接触 Blog,觉得很新鲜,试着选择一个免费空间来写。
第二阶段,发现免费空间限制太多,就自己购买域名和空间,搭建独立博客。
第三阶段,觉得独立博客的管理太麻烦,最好在保留控制权的前提下,让别人来管,自己只负责写文章。
这是阮一峰在博客中写到的关于 Blog 的想法,而这里的第三阶段的实现就是利用 GitHub Pages 搭建博客。
使用 GitHub Pages 功能搭建博客的好处有:
Hexo 是基于 Node.js 的一款静态博客框架,如果想要搭建博客,不想自己写页面的话可以考虑用 Hexo,其中有很多的简洁主题可供选择,同时 Hexo 支持 Markdown 语法,编辑文章更加方便,快捷。
注:此篇分享以 Mac 为例
Hexo 文档有对 Hexo 安装及使用的详细介绍,推荐阅读。这里我主要写自己安装的步骤、踩过的坑以及一些优化的方法。
在正式安装 Hexo 之前,我们需要确认电脑中是否已安装下列应用程序:
当然,我们还需要 GitHub 账号,如果没有请注册。
注册之后记得一定要前往邮箱确认注册,否则无法使用 GitHub Pages。
所有必备的应用程序安装完成后,即可使用 npm 安装 Hexo。终端输入如下命令:
sudo npm install -g hexo-cli
输入管理员密码( Mac 登录密码)即开始安装。
这里有个小坑:Hexo 官网上的安装命令是
npm install -g hexo-cli
,安装时不要忘记前面加上sudo
,否则会因为权限问题报错。
使用终端cd
到一个您指定的目录,执行以下命令(命令中的blog
是您将要建立的文件夹的名称):
hexo init blog
使用终端cd
到blog
文件夹下,执行以下命令,安装npm:
npm install
好了,现在可以试试看是否已经初始化成功,执行如下命令,开启本地 Hexo 服务器:
hexo s
此时,浏览器中打开网址(默认是4000端口) http://localhost:4000 ,能看到如下页面:
这里我踩了个不算坑的坑,终端输入hexo s
后没有成功,我也在网上搜到了很多解决办法,但是都没有奏效,后来我尝试改了下端口就成功了,也就是说默认的4000端口无法连接。如果您的情况跟我一样,尝试了网上的很多解决办法之后依然无效,那您也许可以尝试输入命令hexo s -p 5000
改一下端口试试看。
您的用户名.github.io
的仓库。假设我的 GitHub 账号的用户名是 luxun
,那么我就应该新建名称为luxun.github.io
的仓库。Description
可写可不写,随意。Initialize this repository with a README
。Create Repository
完成创建。点击Settings
,你将会打开这个库的 Settings 页面,向下拖动,直到看见 GitHub Pages,如图:
Hexo 官方文档中有对全局配置的详细说明,推荐阅读。
小坑提醒,修改本地所有的配置文件时,注意所有的冒号
:
后边都要加一个空格,否则执行 Hexo 命令时会报错,一定注意。
找到本地blog
文件夹下_config.yml
,打开后滑到最后,修改成下边的样子:
deploy:
type: git
repository: https://github.com/xxx/xxx.github.io.git
branch: master
您需要将repository
后的所有xxx
换成你自己的用户名,或者也可以在下图位置获取:
然后,您需要为自己配置身份信息,终端输入yourname
和youremail
换成您自己的 GitHub 用户名和邮箱):
git config --global user.name "yourname"
git config --global user.email "youremail"
终端cd
到blog
文件夹下执行生成静态页面命令:
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 g
和hexo d
命令。
随后按照提示,分别输入自己的 GitHub 用户名和密码,开始上传。
完成上传之后,通过http://xxx.github.io/
(xxx
换成您自己的仓库名,也就是用户名)来访问自己刚刚上传的网站。
添加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 #生成预览
终端cd
到blog
文件夹下,执行如下命令新建文章:
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 //在此处输入这篇文章的分类。
---
文章编辑完成后,终端cd
到blog
文件夹下,依次执行如下命令来发布:
hexo g
hexo d
至此,Mac 上搭建基于 GitHub Pages + Hexo 的博客就完成了。
下面的内容是介绍安装 Themes 、个性化设置以及优化。
如果您喜欢 Hexo 默认的主题的话,可以跳过这部分。如果您想换一个主题的话,可以到Hexo 主题挑选自己中意的主题。
这里以nexT 主题为例。
终端cd
到blog
目录下执行如下命令(这是目前的稳定版本,不是最新版。最新版有一些新特性,但是可能会不稳定,所以这里推荐选择安装稳定版):
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
。
终端cd
到blog
目录下,依次执行如下命令(每次部署文章的步骤):
hexo clean
hexo g
hexo d
至于更改博客的名称、描述、头像等,只需要修改blog/_config.yml
文件和blog/themes/next/_config.yml
文件中对应的属性名称即可(不要忘记冒号:
后加空格)。
这里以 Next 主题为例(其他主题也差不多),添加 GitHub 丝带:在blog\themes\next\layout\_layout.swig
中加入相关代码,记得修改自己的链接。
每个项目仓库下一般都有一个 README.md
文件,但是使用 hexo 部署到仓库后,项目仓库中是没有 README.md
文件的。
在 blog
目录下的 source
目录下添加一个 README.md
文件,修改站点配置文件 _config.yml
,将 skip_render
参数的值设置为 README.md
。
skip_render: README.md
保存退出即可。
在主题配置文件中添加下面这段代码(添加之前先看看您的主题配置文件是否已经包含这段代码,已经包含就不用添加了,因为重复会报错),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>
替换文件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
---
这里只说一个方法:编辑文章时,在您希望显示 加载更多
按钮的地方,加上<!--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>
准备一张 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);
}
打开blog/themes/next/layout/_partials/footer.swig
,使用<!--
-->
隐藏符号之间的代码即可,或者直接将这段代码删除。位置如图:
终端执行如下命令,检查SSH keys是否存在。
ls ~/.ssh
如果显示如下信息(下面是我个人的显示,也许您跟我显示的不一样,但重点是只要有id_rsa
和id_rsa.pub
),就说明 SSH keys 已经存在了:
id_rsa id_rsa.pub known_hosts
如果存在,则直接跳过下一个步骤进入将 SSH key 添加到 GitHub 中
,否则请继续下一步骤 生成新的 SSH key
。
终端执行如下命令生成新的 SSH key,注意将[email protected]
换成你自己注册 GitHub 的邮箱地址。
ssh-keygen -t rsa -C "[email protected]"
默认会在相应路径~/.ssh
下生成id_rsa
和id_rsa.pub
两个文件。
1.3.将 SSH key 添加到 GitHub 中
终端依次输入:
cd ~/.ssh
cat id_rsa.pub
复制所有显示的内容
进入GitHub –> Settings –> SSH and GPG keys –> NEW SSH key,如下图所示:
Title 里任意添一个标题,将复制的内容粘贴到 Key 里,点击下方 Add SSH key 绿色按钮即可,如下图所示:
然后,我们需要配置一下域名解析。推荐使用DNSPod的服务,免费稳定,解析速度也比较快。在域名注册商处(Godaddy 或万网)修改 NS 服务器地址为:
f1g1ns1.dnspod.net
f1g1ns2.dnspod.net
域名解析详细的步骤这里我就不写了,给个图:
图中设置国内国外的原因是想让博客加载速度更快,方法请阅读后文 将个人博客同时部署到 GitHub 和 Coding。
进入 blog/source
目录下,添加并打开 CNAME
文件,输入您的域名,重新上传您的博客。
在 GitHub 中打开您自己的博客仓库,进入库的 Settings 界面,如果看到了如下提示,说明配置成功了。
在这一系列的操作中,修改 NS 服务器、设置解析等等,都需要一定的时间。短则10分钟,长则24小时,最长不会超过72小时。如果超过72小时,请检查自己的配置过程,或者修改自己本地的 DNS 服务器。
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 的地址 解析为 国内
:
Pages 服务
界面,添加域名绑定,输入个人域名就 OK 了,可能需要耐心等待几分钟。这样就可以实现从国内访问就通过 Coding 加载博客项目,从国外访问就通过 GitHub 加载博客项目,从而提升加载博客的速度。
压缩代码也是一个优化加载速度的方法。
目前知道的有两个插件可以压缩博文,hexo-all-minifier 插件和 gulp 插件。hexo-all-minifier 使用比较简单,也可以压缩图片,不过对文章缩进不支持。如果您对文章缩进有要求,可以暂时使用 gulp 压缩手段。
安装 hexo-all-minifier,在站点的根目录下执行以下命令:
npm install hexo-all-minifier --save
hexo g
编译的时候就会自动压缩 HTML、JS、图片。详情参考插件介绍 hexo-all-minifier。
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 目录中的静态资源文件进行压缩。
验证方式有几种,推荐最简单的两种:文件验证和 CNAME 验证。
source
文件下站点配置文件
中找到skip_render:
[xxx.html, xxx.html]
,比如: skip_render: [googleff0226f76d5f451b.html, baidu_verify_vHC5EAW09E.html]
hexo clean
hexo d -g
zz.baidu.com
安装 Hexo 的 sitemap 网站地图生成插件,终端cd
到blog
:
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.xml
和baidusitemap.xml
,前者适合提交给谷歌搜素引擎,后者适合提交百度搜索引擎。
百度 sitemap 提交如图,Google 也是一样的:
验证成功之后就可以开始推送了。Google 的收录比较快,通常第二天就能搜得到,百度就比较慢了。
安装主动推送插件,终端cd
到blog
:
npm install hexo-baidu-url-submit --save
在根目录下,把以下内容配置到站点配置文件中:
baidu_url_submit:
count: 3 ## 比如3,代表提交最新的三个链接
host: xxx ## 在百度站长平台中注册的域名
token: xxx ## 请注意这是您的秘钥,请不要发布在公众仓库里!
path: baidu_urls.txt ## 文本文档的地址,新链接会保存在此文本文档里
至于上面提到的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 一样)更新一下,就像这样:
自己搭建博客时,很是折腾了一番,也学习到了很多知识,阅读了很多相关的博文,在此向相关博文的作者表示感谢,谢谢你们的文章让我进步。同时,我看到有很多人想拥有自己的博客或者优化自己的博客,期间可能会遇到很多问题,我也是这么一个坑一个坑踩过来的,所以我就写了这么一篇自己折腾博客的分享,希望可以在帮助自己的同时帮助到别人。(如果踩了新坑的话,一定会更新的。)
搭建一个免费的,无限流量的Blog-阮一峰
Hexo搭建博客教程
基于Hexo+Node.js+github+coding搭建个人博客——进阶篇(从入门到入土)
如果 .parent
的 height
不写,只需要 padding: 10px 0;
或者 line-height
就能将 .child
垂直居中。
如果 .parent
的 height
写死了,就很难把 .child
居中 ( 所以,能不写 height
就千万别写 height
。)
如果你 .parent
不得不定高 ( 如全屏高度 ),以下是几种垂直居中的方法。
//HTML
<table class="parent">
<tr>
<td class="child">
我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中我要居中
</td>
</tr>
</table>
//CSS
.parent{
border: 1px solid red;
height: 600px;
}
.child{
border: 1px solid green;
}
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;
}
//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;
}
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;
}
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;
}
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%);
}
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;
}
//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;
}
这个时候我们就需要针对不同的浏览器去写不同的CSS,让它能在不同的浏览器中也能得到我们想要的页面效果。
根据兼容需求选择技术框架/库(jquery)
根据兼容需求选择兼容工具(html5shiv.js、respond.js、css reset、normalize.css、Modernizr
条件注释、CSS Hack、js 能力检测做一些修补
渐进增强和优雅降级
渐进增强(progressive enhancement): 针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验
优雅降级 (graceful degradation): 一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
eg:
<!--[if IE 6]-->
<p>You are using Internet Explorer 6.</p>
<![endif]-->
eg:
div{
color:blue;
*color:red; /*如果在IE6或者7中,字体颜色为红色,否则为蓝色*/
}
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]-->
<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
<!--[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 中无法正常工作。
项目 | 范例 | 说明 |
---|---|---|
! | [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浏览器编写不同的CSS,使得在IE浏览器中也能得到我们想要的页面效果
能力检测的目标不是识别特定的浏览器,而是识别浏览器的能力。使用这种方式无需顾及浏览器如何如何,只需确定浏览器是否支持特定的能力,就可以给出相关的方案。
用于解决IE9以下版本浏览器对HTML5新增标签不识别,并导致CSS不起作用的问题,使用Javascript来使不支持HTML5的浏览器支持HTML标签。
让不支持 css3 Media Query 的浏览器包括 IE6-IE8 等其他浏览器支持查询。
早期的 CSS Reset 的作用就是清除所有浏览器默认样式,这样更易于保持各浏览器渲染的一致性。
这样暴力清零会带来一些问题,eg:
是一个可定制的 CSS 文件,使浏览器呈现的所有元素,更一致和符合现代标准;是在现代浏览器环境下对于CSS reset的替代。
该项目依赖于研究浏览器默认元素风格之间的差异,精确定位需要重置的样式。相比于传统的CSS Reset,Normalize.css是一种现代的、为HTML5准备的优质替代方案。Normalize.css现在已经被用于Twitter Bootstrap、HTML5 Boilerplate、GOV.UK、Rdio、CSS Tricks 以及许许多多其他框架、工具和网站上。
作用:
一个 JavaScript 库,用于检测用户浏览器的 HTML5 与 CSS3 特性。Modernizr 会在页面加载后立即检测特性;然后创建一个包含检测结果的 JavaScript 对象,同时在 html 元素加入方便你调整 CSS 的 class 名。
PostCSS 是一个使用 JS 插件来转换 CSS 的工具。这些插件可以支持使用变量,混入(mixin),转换未来的 CSS 语法,内联图片等操作。
vim a.md
初始进入编辑器命令模式vim
和 a.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)首 |
说起跨域,我们就要先了解一下同源策略。同源策略是针对浏览器的,不是针对 JS。
同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。
换句话说,浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。
同协议:如都是http
或者https
同域名:如都是http://github.com/a
和http://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 页面所在什么域
那么,我就是想访问其他域(非同源)的资源 — 即跨域,该怎么做呢?
HTML 中 Script 通过 src 属性可以引入其他域下的 JS,比如引入线上的 jQuery库。也可以引入非 JS 的文件,利用这个特性,可实现跨域访问接口。该方法需要后端支持。
_fun
callback=_fun
fun(data)
字符串。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),是一种 ajax 跨域请求资源的方式,支持现代浏览器,IE支持10以上。 实现方式很简单,当你使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin
; 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。所以 CORS 的表象是让你觉得它与同源的 ajax 请求没啥区别,代码完全一样。
简单请求
比如发送了一个 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.html
和b.github.com:8080
下的 b.html
两个文件分别加上 document.domain = “github.com”
;然后通过 a.html
文件创建一个 iframe
,去控制 iframe
的 window
,从而进行交互,当然这种方法只能解决主域相同、而二级域名不同的情况。
//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 是一个安全的跨源通信的方法。一般情况下,当且仅当执行脚本的页面使用相同的协议(通常都是 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>
传统的网页布局中,我们经常使用display:block
+ margin
+ padding
+ float
+ position
来实现,但随着网页越来越复杂,对于某些不确定宽高的元素想要实现垂直居中实际上是比较困难的,而随着 CSS3 中 Flex 布局的出现,解决了传统布局中一些不易实现的问题。
Flex 是 Flexible Box 的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。
Flex 布局具有以下特点:
在容器内,默认有两条轴,水平方向为主轴( main axis ),垂直方向为侧轴( cross axis ),主轴方向起始位置为主轴起点( main start ),主轴方向终点位置为主轴终点( main end ),侧轴方向起始位置为侧轴起点( cross start ),主轴方向终点位置为主轴终点( cross end ),容器的主尺寸为 main size ,侧尺寸为 cross size ,容器内的子元素为 flex item ,当给 container 设置display:flex
,每一个子元素都是 container 的 item 。
flex-direction
表示主轴的方向。
flex-direction: row(默认值) | row-reverse | column | column-reverse;
row
:主轴方向为水平方向,从左至右。row-reverse
:主轴方向为水平方向,从右至左。column
:主轴方向为垂直方向,从上至下。column-reverse
:主轴方向为垂直方向,从下至上。flex-wrap: no-wrap(默认)| wrap | wrap-reverse;
flex-wrap
是指在主轴方向上如果项目过多排列不下时是否换行,默认情况下是不换行的。
wrap
:轴线方向换行,第一行在上。wrap-reverse
:轴线方向换行,第一行在下flex-flow
是 flex-direction
和 flex-wrap
的简写,默认为 row
和 no-wrap
。
flex-flow: <flex-direction> || <flex-wrap>;
justify-content
是指主轴方向对齐方式。
justify-content:flex-start(默认) | flex-end | center | space-between | space-around;
flex-start
:主轴方向从左端对齐。flex-end
:主轴方向从末端对齐。center
:主轴方向从中间对齐。space-between
:主轴方向两端对齐,项目之间的空间均匀分配。space-around
:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。align-items
是指侧轴方向对齐方式。
align-items: flex-start | flex-end | center | baseline | stretch;
flex-start
:侧轴方向上端对齐。flex-end
:侧轴方向底端对齐。center
:侧轴方向中心对齐。baseline
:侧轴方向上文字基线对齐。strech
(默认值):如果项目未设置高度或设为auto
,将占满整个容器的高度。align-content
属性定义了多根轴线(多行或是多列)的对齐方式。如果项目只有一根轴线(一行或是一列),该属性不起作用。
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
flex-start
:与交叉轴的起点对齐。flex-end
:与交叉轴的终点对齐。center
:与交叉轴的中点对齐。space-between
:与交叉轴两端对齐,轴线之间的间隔平均分布。space-around
:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。stretch
(默认值):轴线占满整个交叉轴。align-self
属性定义 flex 的单个项目在侧轴上的对齐方式,默认为 auto
,表示继承父元素的 align-items
,否则将覆盖 align-items
属性。
align-self: auto | flex-start | flex-end | center | baseline | stretch;
center
属性:其他值与align-items
属性值用法相同。
flex-grow
定义项目的扩展比率(一般使用在空间过多时),默认为 0
,即便存在剩余空间,也不放大。
flex-grow: <number> ; /* default 0 */
如果所有的项目不为 0,如 1/2/3,则等分剩余空间。
如果 item1 项目为 1,其他项目为 0,则 item1 占据所有剩余空间。
flex-shrink
定义项目的收缩比率(一般使用在空间不足时),默认为 1,即空间不足时,项目缩小,为 0 表示不缩小。
该属性对负值无效。
flex-shrink: <number>; /* default 1 */
flex-basis
定义了在分配多余空间之前,项目占据的主轴空间( main size )。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto
,即项目的本来大小。
flex-basis: <length> | auto; /* default auto */
flex
属性是 flex-grow
, flex-shrink
和 flex-basis
的简写,默认值为 0
1
auto
。后两个属性可选。
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
order
属性定义项目的排列顺序。数值越小,排列越靠前,默认为 0
。
该属性对负值有效。
order: <integer>;
关于各属性浏览器的支持程度可以在 Can I use 查看。
原文 - Learning JavaScript: 9 Common Mistakes That Are Holding You Back
译文 - 学习 JavaScript:阻碍你进步的 9 个常见错误
很多人尝试学习 JavaScript ,但是不久就放弃了。然后他们就告诉自己,“JavaScript 太复杂了”,更有甚者说,“我不是前端开发的料”。
这种情况挺让人悲伤的。其实根本不必放弃,所要做的仅仅是换一种不同的学习方法。
在这篇文章中,我们将介绍一些最常见的错误学习方法,并了解如何避免这些错误。许多技巧不仅适用于 JavaScript,甚至可以用到 web 开发上,所以也算是一种福利。
我们来吧!
开始学习 JavaScript 之前,你可以找到很多相关的信息。如果你去看,就会发现一些 JavaScript 是最好的或者是最坏的、你是需要这个框架还那个框架的相关信息。你也可能会听到你需要以某种方式编写 JavaScript,否则你永远不会成为“真正”的开发人员等。
不管这些说的正确与否,没有什么比浪费六个月到一年还没有开始更糟糕。
开始敲代码吧,它不一定完美,可能很糟糕。但如果你开始了,就通过了阻碍很多人的障碍之一了。
JavaScript 框架建立在原生 JavaScript 之上,因此如果你理解了 JavaScript,你也就自然而然的知道如何使用任何 JavaScript 框架的基本原理。
然而,如果你直接学习一个框架,最后也只是记住了它的语法却不理解它的原理。这就像在不知道词语意思的情况下造句,最终你只是随便地记住了一些词语,却不知道这些词语的意思并且不会组织这些词语来学以致用。
如果你直接进入一个框架,那将会更难学习,当你需要另一个框架你会更难适应。如果你首先学习基础的 JavaScript,那么你将有一个坚实的基础来了解所有的框架。
最常见的错误之一就是在理解概念之后立即采取行动。
我一直在努力解决这个问题,因为一旦了解某些东西,你就想更进一步。
像对待新玩具一样对待每个概念是很有帮助的;这意味着你需要花一些时间来享受你刚学到的东西。玩耍、实验,看看你能不能做一些新的事情。你会学到很多,你会记得更好。
当你感觉自己闭着眼睛都能运用自如的时候再继续向下学习。可能在达到这一步之前,你需要更多的时间,但是这将是你接下来的学习变得更快。
另一方面,如果你过于急躁,你就不会太注意细节。但令人沮丧的是,这会使你之后的学习成本大幅提升。其实这也是人们常说要放弃学习 JavaScript 的常见原因之一。
学习就像爬楼梯:如果你能走一步,你可以继续采取更多的步骤,直到你达到目标。当有些东西难以理解时,往往是因为你想要进行一次飞跃,而不是一次走一步。当然这是痴心妄想!
在实际场景中,我看到人们对某段代码不理解的时候,我会请他们解释一下,他们会试图一下解释清整个问题。那我会请他们再一行一行的解释一遍,这样是有道理的。
如果有些部分很让人费解,那经常是因为跳过了某些东西,那么这也将有助于你去关注细节,直到找出症结所在。如果一个概念在分解之后仍然没有意义,那你也会有更容易找到相关解决方法,因为查找特定的主题比胡乱搜索更容易。
刚开始学习 JavaScript 的人经常会说“我就随便定个小目标,写一个 Facebook 那样的网站算了”,没有意识到项目所涉及的深度。当项目逐渐深入时,他们就放弃学习 JavaScript 了。
我更详细地介绍了关于项目,但是在学习的时候,从一些基本概念开始会更容易。当你开始做项目时,你可以在工具包中添加一些构建工具。
更明确地说,我不是要那种越旷日持久的项目。我刚刚发现,如果我先做了一些简单的部分,比如在浏览器中显示一些文本或响应一个按钮,那么就可以更轻松地启动项目。
当你学习 JavaScript 时,你可能会在不符合真实环境下进行练习。例如,你可能在网站的内置代码编辑器中输入内容,或者你可能依赖于教程中的粘贴文件。
这些方法对于学习来说可能是非常好的,但是你也可以尝试自己搭建环境。这意味着使用你自己的文本编辑器,并从头开始编写项目。
如果你不自己独立练习每一个概念,那你会依赖于训练环境。你最终会遇到这样的情况:你已经花了很多时间来学习,但你一个都无法掌握。
让自己更沮丧的最简单的方法之一就是和大神进行比较。因为你总是看他们在那里,而不是看他们如何到达那里。
举个例子,人们看到我的教程,并问我如何写这么干净的代码。他们说他们无法编写像这样的干净的代码,所以也许他们根本就不是 JavaScript 的那块料。
事实是我的过程是一团糟。我不断试验、犯错、查阅资料,写下丑陋的代码,最后把所有的内容都细化成一个可呈现的教程。人们看了优秀的版本,并且假设整个过程就是这样的。我也做过关于教程作者的这些假设,直到我开始写我自己的教程。
关键点是,认真学习你正在学习的东西,你会得到进步。继续重复这个过程,很快别人就会好奇你是如何达到那种高度的。
你会自然而然的花费大量的时间来观看视频和教程,但是除非你自己动手编写代码,否则你不能真的学会。
光看而不采取实际行动是很危险的,你会有一种你正在学习的错觉。六个月后,你会发现自己什么都没学会。
写 15 分钟的代码比上你光看一小时的教程有用多了。
阅读教程时,很容易陷入照葫芦画瓢的情况。这种教程并不会教你如何解决一个问题,例如需要进行怎样的测试,如何一步一步的探索可能出问题的方向。因此,只会跟着教程走的人往往学不到真正的知识。
那么解决方案是什么?
不要只知道跟着教程一步步走,而是要花点儿时间去自己实现。例如,如果您正在学习幻灯片教程,请尝试显示和隐藏 div,然后尝试计时,然后尝试另一个小部分。相对于跟着教程一步步地走,通过亲身尝试并拓展你将学到更多知识,并且有可能将它应用得更好。
在你读完这篇文章后,如果你问我最想让你记住什么,那就是通过采取最小的步骤来取得最大的进步。
无论你在学习什么,都要好好学习它本质上的东西。尝试你学到的东西,并乐在其中。
有时可能很困难,但这没关系。挑战意味着你正在提升个人能力,这将使你进步。如果一切总是太容易,这可能意味你需要进行些改变了。
原文 - 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 上面都有一份个人资料。
你的作品集就是你的在线简历。我总是说,你向别人展示你做过什么总比你跟别人说你能做什么重要得多。
下面给你展示三个成功的作品集:
看看上面列出来的三个网站。他们一开始也都是初学者,但你看看他们是怎么展示他们专业知识和做过的作品的。
如果你的作品集足够好,那么客户和潜在的雇主会主动找你。建立一个好的项目列表/网站 - 即使你必须安排出来一段时间来做。
参与开源项目可以向别人展示你的以下方面:
你对自己所做的方向充满激情
团队合作的能力
你的技术栈
最让人高兴的就是,如果你真的做得很好,你会在社区里被广泛认可,这也会提高你的整体可信度。
jQuery 的创始人 John Resig 说:当提及招聘,我总是把 GitHub 的 commit 记录放进简历。
这儿有五个你可以贡献代码的开源项目:
为什么要执着?
Web 开发者在开发过程中经常面临很大的挑战。如果你的代码中有错误,或者代码运行结果和预期不一样,那么在没有找到解决办法之前别再添加新功能了,你不要放弃任何角落。你必须得执着,把问题解决掉。
为了找出问题,你可能会花很长的时间,但你的执着是非常值得的,这也有助于你在未来的项目中快速的找出有效的解决方案。
给我六个小时砍树,我会用前 4 个小时磨斧头。
Abraham Lincoln (亚伯拉罕·林肯) 的名言对我影响很大影响。只专注于做技术或经营自由贸易的人往往会忽略如何更好、更快的做事。成功的开发者用很巧妙的方式工作,并能获得很好的成绩。
我知道更多编程语言,所以我会比你更成功。
成功不在于你知道多少,而在于你能用所知道的东西做什么。就像在面试中,知道的多的人总能得到工作,是吗?
生活在 “现实世界” 中的人都知道这不对。面试需要的不仅是工作技能(或者作为自由职业者的技能),还有很多其它方面。
任何行业的专家都会不断的学习来提高自身的知识和技能。作为 Web 开发者,保持不断的学习是很重要的事儿。
如果你在 20 年前学过 C++,但并没有跟随新版本,那么在今天你在这方面的知识已经没什么用了。
不要在乎你的经验处于什么水平,持续学习就对了。
你宁愿雇用谁?
经验比理论更重要。不要只说 “我可以做这做那”:这些是每个普通的 Web 开发者都能做的事情。你一定要展示出你做了什么。
一旦你开始变得更加自信,积累了更多经验,你就需要将你的工资定得更高。
这可能会成就你,也可能阻碍你。你是愿意拿每年 5 万美元的工资还是 9 万美元的工资?用 500 美元的网站和 5000 美元创建的网站有啥区别?
如果我要卖给你一瓶酒,我告诉你我现在有两瓶,一瓶定价 5 美元,另一瓶定价 55 美元,你可能会认为定价 5 美元的有问题,不是吗?
对于你来说也是同样的道理。虽然这主要适用于自由职业者,但这个原则同样适用于谈判你的薪酬。
比起被各种短信消息、YouTube、有趣的图片所吸引的情况下工作 4 个小时,高度集中注意力工作 1.5 个小时要好的多。
尽可能不要分散注意力,集中注意力让工作变得更有效率。
看看 Pomodoro Technique 对于效率的讨论.
限制自己只学习某个编程语言是不对的。
为了提高成功的机率,学习其他的像市场、谈判技巧、沟通以及社会技能等对你很重要。
看看那些最成功的开发者们吧:他们的技术都很全面,也有着很好的天分,他们并没有把所有精力都用在编程上。
加入编程社区,Facebook,Twitter 以及其他的平台,别怕问 “愚蠢的” 问题。
在像 Stack Overflow,Reddit,Quora 以及博客等提问和回答问题。
这个方法可以和别人取得联系。
我性格非常内向。真的,人们叫我寄居蟹。猜猜我在组织中的位置在哪儿?在某个角落里…
如果你性格内向 (和我一样,你应该意识到,在某种意义上,你需要走出舒适区和别人打成一片。
看下你附近是否有聚会,使用 Meetup 这样的服务。
顺便说下,不要只去为开发者准备的聚会和一些活动。如果你是自由职业者,也可以去参加商业活动。毕竟,有多少开发者雇佣其他开发者呢?
你需要像网站访问者和网站的所有者一样思考。
作为站点访问者,你需要思考:在这个网站上的每一步操作,给我留下印象的是什么?我有没有在这里得到想要的答案?这个站点满足了我的需求吗?我相信这个网站吗?等等。
作为网站所有者,你需要思考:我的网站是否在此刻解答了用户的问题和怀疑?为了回答这些问题,我可以做什么?为了方便网站的访问者按照我的想法来操作,我可以在网站上做些什么?
成功的人常常经历失败,但是不可否认的是,他们都有永不放弃和寻找新奇事物以处理事情的特质。
有时候,你和成功失败的距离就差一步,那就是是否愿意继续。
永远不要放弃你的梦想,你的欲望和你的目标。
永远不要认为你不能成功,如果你需要一个相信你的人,我相信你行!
我希望在这篇简短的文章里能给你带来积极的影响。
不知道你有没有想过这样一些问题:
为什么我定义一个数组,它就有 push、join、pop、shift 等方法,我明明什么也没写啊?
为什么我定义一个函数,它就有 call、apply、length 等属性/方法,我也什么都没有做呀?!
为什么我定义一个对象,它就有 toString、valueOf 等方法,我更是什么都没有做呀?
当我们定义一个对象时
var obj = {}
我们发现 obj 下面有一个属性名叫__proto__
,(它是个对象)
obj.__proto__
里面又有很多属性,包括 valueOf、toString、constructor 等。当我们需要找obj.valueOf
这个属性时,发现 obj 本身没有没有,那么它就会去查找obj.__proto__
是否有这个属性,如果还没有,它去找obj.__proto__.__proto__
,直到找到这个属性或 null 为止,在这个读取属性的过程中,是沿着__proto__
组成的链子来搜索的,这个链子我们称为原型链。
如果 obj 自身定义了一个 valueOf 属性,那么它找到自身的 valueOf 之后就不再沿着__proto__
来找,因为已经找到了,没有必要继续找了,也就是说
新增的属性不会沿着__proto__
查找
读取属性会沿着__proto__
,直到找到这个属性,或者是 null 为止。
__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 一切皆对象 的说法不攻自破了。
在了解 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 是一种处理模式),它代表了一种可能会长时间运行而且不一定必须完整的操作的结果。这种模式不会阻塞和等待长时间的操作完成,而是返回一个代表了承诺的(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对象代表一个异步操作,有三种状态: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 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
github.com
就是域名。github.com
到底是什么东西,需要查找 github.com 网站所在服务器的IP地址,才能找到目标。8.8.8.8 —— Google 提供的免费 DNS 服务器的 IP 地址
114.114.114.114 —— 是国内第一个、全球第三个开放的 DNS 服务地址,又称114DNS
DNS劫持:DNS劫持又称域名劫持,是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能反应或访问的是假网址。
视图(view)
视图是用户看到并与之交互的界面。这是前端工作的主力部分。
模型(model)
模型是将实际开发中的业务规则和所涉及的数据格式模型化,应用于模型的代码只需写一次就可以被多个视图重用。在MVC的三个部件中,模型拥有最多的处理任务。一个模型能为多个视图提供数据。
控制器(controller)
控制器接受用户的输入并调用模型和视图去完成用户的需求。Controller处于管理角色,从视图接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示模型处理返回的数据。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.