笨鸟也能展翅高飞

多渠道分享实践

多渠道分享实践

财富云平台的业务场景有包括海外渠道等多渠道的分享需求,结合了国内微信的分享,设计了一套多渠道分享方案。

本文涉及到的分享渠道有微信、微信朋友圈、Facebook、Twitter、Messenger、Instagram、Viber 和 Whatsapp。主要介绍了以上渠道的分享方式,以及根据业务场景设计的分享方案。

以上渠道分享出来的卡片样式大同小异,主要包括:titledescriptionimage,以及分享文案,我们可以命名为 content

Node.js, TC-39, and Modules

Node.js, TC-39, and Modules

James M Snell

Katherina Miao(翻译)

原文链接


本周我第一次参加了 TC-39 会议。可能有人还不知道,TC-39 是 ECMA 工作组的编号,是他们定义了 ECMAScript 语言(或者更常见的“JavaScript”)。这个工作组敲定(通常是纠结的)并通过了 JavaScript 语言的细枝末节,以确保 JavaScript 编程语言不断发展并继续满足开发人员的需求。

我本周参加 TC-39 会议的原因非常简单:TC-39 定义的一种较新的 JavaScript 语言 - 即模块 - 导致 Node.js 核心团队遇到了一些困扰。我们(主要是Bradley Farias - Twitter@bradleymeck)一直试图弄清楚如何以最好的方式使 Node.js 支持 ECMAScript 模块(ESM),又不会弄出乱子。

问题实际上并不是我们无法按照目前规范定义的方式在 Node.js 中实现 ESM,而是如果完全按照规范的要求做意味着需要减少 Node.js 预期功能,开发者只能得到次优体验。我们非常希望确保 Node.js 中的 ESM 实现最优又可用。由于这个问题比较复杂,为了得到最有效的解决方案,我们与 TC-39 成员坐下来面谈。幸运的是,我认为我们取得了一些重大进展。

为了能更好的解释当前的问题和我们的计划,让我花一些时间来解释一些引起了我们最关注的基本问题。

【练手】Webpack+React开发Todolist项目

主流框架在不停的更新换代。在使用了gulp+Jquery半年以上的时候,为了跟上步伐,决定尝试用webpack+react做一个简单的app来熟悉下。

Todo List 就决定是你了!

Webpack

第一次接触webpack的时候觉得这个想法惊为天人!通过各种loader将所有的资源打包,然后插入到html里面:所有非js的资源,都需要通过相对应的loader。而且webpack内还整合很多功能,模块加载啊,ES6支持啊。。。

clipboard.pngWhat is webpack?

安装

安装过程不做赘述,webpack官网有更详细的描述,通过npm安装:$ npm install webpack --save-dev

Loader&Plugins

之前说webpack是把非js资源转换成js,那么就有各式各样的loader,在List of Loaders可以查看列表,但一般来说,会用到如下几种:

1
2
3
css-loader //将css打包
style-loader //将样式输出
url-loader //解析url,小图片会被解析为data-url

除此之外,还有file、json、sass等loader。这里因为使用了React,所以除了这些,还安装了babel-loader。
通过babel,可以将jsx转换成js,也可以使用es6语法。

安装:$ npm install style-loader css-loader url-loader babel-loader sass-loader --save-dev

插件可以提供打包相关的功能:比如将指定打包的CommonsChunkPlugin;压缩js代码的UglifyJsPlugin;限制合并的LimitChunkCountPlugin

具体的插件说明可以在List of Plugins中查看。

Config

要用webpack,需要配置webpack.config.js。在这个配置里面,entry就是这个app的入口在src文件夹,output是编译之后的脚本在dist文件夹。因为这里用了CommonsChunkPlugin,将引用的库单独导出,所以在entry里配置了vendor,编译过之后会生成vendor.js。在module里面可以配置loader,jsx用到babel的es2015和react,scss用到了style、css和sass。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var webpack = require('webpack');

module.exports = {
output: {
filename: './dist/[name].js'
},
entry: {
vendor: [
'react',
'react-dom'
],
app: './src/index.jsx'
},

module: {
loaders: [
{test: /\.jsx$/, exclude: /node_modules/, loader: 'babel', query: {presets: ['es2015', 'react']}},
{test: /\.scss$/, exclude: /node_modules/, loaders: ["style", "css", "sass"]}
]
},

plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor', './dist/vendor.js'),
]
}

需要建立一个html文件,将编译过的脚本引入,如果使用脚本分块的话,一定要注意先后依赖:比如vendor.js要在app.js之前。

1
2
<script src="./dist/vendor.js"type="text/javascript"></script>
<script src="./dist/app.js"type="text/javascript"></script>

React

第一次接触React,我是懵逼的。

长时间函数式、关注分离,第一次接触MVVM,表示很纠结、也很方便。纠结的是事件绑定与组建通信的方式;方便的是不用在意界面的修改,而专注于业务。

推荐入门阅读我大神男友的博文:初识 React

JSX

在.jsx文件中,可以直接返回一个元素:

1
2
3
4
5
6
/* 这里的className,是样式class。*/
return (
<div className="content">
Hello, world!
</div>
);

直接写标签,简单易读~!
如果要建组件,要用到React.createClass,注意的一点是组件名一定要首字母大写。
React会建立虚拟DOM树,再之后的更改中会建立另一个DOM树,然后通过有效的比对,在界面上做出相应的更改。

state&props

在react组件中,有stateprops这两个接口。

props是可以为组件组件传递参数,比如:

1
2
3
4
5
6
7
8
var Greating = React.createClass({
render: function(){
return (
<h1>Hello, I'm {this.props.name}! </h1>
);
}
});
React.render(<Greating name="Katherina" />, document.body);

父组件向子组件的参数传递通过props,除此之外可以通过getDefaultProps这个生命周期方法来设置。

state参数是用来实现交互的。起初使用getInitialState获得最初的一个state状态,之后使用setState来控制。

比起手动维护交互时界面的变化,用state就方便多了,然而子组件若是想要改变父组件的state,只能通过事件向父组件传递。

这里就先不举例啦,到Todo List的时候仔细说说。

####生命周期方法####

这次生命周期方法只用到了getInitialState,对于各个方法使用心得和避坑还是参考初识 React

React组件是一个从加载-》更新-》卸载的过程,一共提供10个生命周期方法:

  • getDefaultProps:第一次实例化调用,给组件提供props
  • getInitialState:创建实例时调用,给组件提供初始state
  • componentWillMount:加载组件前调用,可以访问到props和state,并可以修改state
  • render:创建虚拟DOM,每个组件必需的方法
  • componentDidMount:组件加载完成后调用,此时可以通过this.getDOMNode()访问到DOM。
  • componentWillReceiveProps:组件props更新前调用,将props作为参数传入
  • shouldComponentUpdate:组件将要更新时调用,新的 props 和 state 将会作为该方法的参数,该方法应当返回一个布尔值,返回false表示跳过后续的生命周期方法。
  • componentWillUpdate:组件更新之前调用,接收到新的props或者state作为参数,此时不能更新state。
  • componentDidUpdate:组件渲染完成后调用,此时可以访问到新的DOM元素。
  • componentWillUnmount:卸载组件之前调用,可以做一些清理工作,比如清除计时器。

组件加载的时候,会依次调用: (getDefaultProps)、 getInitialStatecomponentWillMountrendercomponentDidMount
其中,只有getDefaultProps是只有第一次实例化才会调用的,之后props会保存下来为组件直接调用。

更新的时候,会依次调用: componentWillReceivePropsshouldComponentUpdatecomponentWillUpdaterendercomponentDidUpdate

卸载的时候,会调用componentWillUnmount

TODOS

Todo List是一个经典入门练习。
第一次做webpack+react,因为这两个对我来说都是新东西,所以一开始有点困难。遇到问题也找不到是webpack配置问题,还是react语法问题(对不起,我真的很笨。。。

Anyway,这个入门是在react官网上demo的基础上延伸的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var TodoList = React.createClass({
render: function() {
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});

React.render(<TodoApp />, mountNode);

A JavaScript library for building user interfaces | React

这个demo把一个Todo List的基础功能实现了:可以将新任务添加到任务列表中。

TodoApp是父组件,它用了生命周期方法getInitialState来设置文本框的状态text,列表内的项目items
每次改变文本框内容,setState会改变text的值。在提交的时候,会给items数组多添加一项,同时置空text和文本框。
按钮上面的数字是目前任务条目的个数,当items数组多添加一项的时候,按钮也会随之加1。
TodoApp是父组件中引入了TodoList子组件,并传递了items。在TodoList组建中,对props.itemsmap方法添加了条目。

在此基础上,我除了把界面做漂亮之外(对不起,视觉系的人不能对着毫无样式的页面做功能。。)还增加了勾选完成和删除任务功能。

clipboard.png
——最后效果。(用了sass,需要在入口文件中require

为了让内容记录可以保存,这里使用localStorage来储存。在一开始getInitialState中读取到items,然后每次提交添加、修改和删除条目的时候,都会修改statelocalStorage

1
2
3
4
getInitialState: function() {
var itemsFromStorage = localStorage.getItem('TODOs')?window.JSON.parse(localStorage.getItem('TODOs')):[];
return {items: itemsFromStorage, text: ''};
}

在添加列表条目的基础上,条目上的勾选完成和删除也是修改state。起初添加条目的时候,为条目添加状态 active:

1
2
3
4
5
6
7
8
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([{text: this.state.text, status:'active'}]);
var nextText = '';
var itemJson = JSON.stringify(nextItems);
localStorage.setItem('TODOs',itemJson);
this.setState({items: nextItems, text: nextText});
}

之后在勾选完成的时候,会修改status为done:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
changeStatus: function(e) {
var key = e.target.parentNode.getAttribute('id');
var items = this.state.items;
var status = this.state.items[key]['status'];
if (status === 'done'){
items[key]['status'] = 'active';
}else{
items[key]['status'] = 'done';
}
this.changeStates(items);
},
changeStates: function(items) {
this.setState({items: items});
localStorage.setItem('TODOs',JSON.stringify(items));
}

除此之外,删除条目的时候,根据条目的索引,删除state中对应项。

1
2
3
4
5
6
7
8
9
10
removeItem: function(e) {
var key = e.target.parentNode.getAttribute('id');
var items = this.state.items;
items.splice(key, 1);
this.changeStates(items);
},
changeStates: function(items) {
this.setState({items: items});
localStorage.setItem('TODOs',JSON.stringify(items));
}

另外,加了一个用作筛选的选项卡,在子组件中添加一个额外的state:all的时候显示所有;active的时候显示未完成的;complete的时候显示已完成。点击标签的时候,切换state,然后在render的时候做筛选:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var TodoList = React.createClass({
changeTab : function(e){
var type = e.target.getAttribute('type');
this.setState({type:type})
},
getInitialState: function() {
return {type:'all'};
},
render: function() {
var style = {};
if(this.props.items.length === 0){
style = {display:'none'};
}

return <div className="itemList">
<div className="clearfix">
{this.props.items.length} items
<nav style={style}>
<a className={this.state.type === 'all'?'navTab active':'navTab'} type='all' onClick={this.changeTab}>ALL</a>
<a className={this.state.type === 'active'?'navTab active':'navTab'} type='active' onClick={this.changeTab}>ACTIVE</a>
<a className={this.state.type === 'done'?'navTab active':'navTab'} type='done' onClick={this.changeTab}>COMPLETE</a>
</nav>
</div>
<ul>
{this.props.items.map((item, index)=>{
if(this.state.type === 'all'||this.state.type === item.status){
return <li key={index} id={index} className={item.status}>
<input type="checkbox" className="toggle" onChange={this.props.changeStatus} checked={item.status === 'done'}/>
<lable>{item.text}</lable>
<button className="remove" onClick={this.props.removeItem}></button>
</li>;
}
})
}
</ul>
</div>;
}
});

最后要说的是组件之间的数据传递。
父组件向子组件传递,通过props

1
<TodoList items={this.state.items} changeStatus={this.changeStatus} removeItem={this.removeItem}/>

而子组件向父组件传递,通过事件。事件定义在父组件,通过props传递到子组件,绑定触发时可以改变父组件的state

下面就是index.jsx的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
var TodoList = React.createClass({
changeTab : function(e){
var type = e.target.getAttribute('type');
this.setState({type:type})
},
getInitialState: function() {
return {type:'all'};
},
render: function() {
var style = {};
if(this.props.items.length === 0){
style = {display:'none'};
}

return <div className="itemList">
<div className="clearfix">
{this.props.items.length} items
<nav style={style}>
<a className={this.state.type === 'all'?'navTab active':'navTab'} type='all' onClick={this.changeTab}>ALL</a>
<a className={this.state.type === 'active'?'navTab active':'navTab'} type='active' onClick={this.changeTab}>ACTIVE</a>
<a className={this.state.type === 'done'?'navTab active':'navTab'} type='done' onClick={this.changeTab}>COMPLETE</a>
</nav>
</div>
<ul>
{this.props.items.map((item, index)=>{
if(this.state.type === 'all'||this.state.type === item.status){
return <li key={index} id={index} className={item.status}>
<input type="checkbox" className="toggle" onChange={this.props.changeStatus} checked={item.status === 'done'}/>
<lable>{item.text}</lable>
<button className="remove" onClick={this.props.removeItem}></button>
</li>;
}
})
}
</ul>
</div>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
var itemsFromStorage = localStorage.getItem('TODOs')?window.JSON.parse(localStorage.getItem('TODOs')):[];
return {items: itemsFromStorage, text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([{text: this.state.text, status:'active'}]);
var nextText = '';
var itemJson = JSON.stringify(nextItems);
localStorage.setItem('TODOs',itemJson);
this.setState({items: nextItems, text: nextText});
},
changeStatus: function(e) {
var key = e.target.parentNode.getAttribute('id');
var items = this.state.items;
var status = this.state.items[key]['status'];
if (status === 'done'){
items[key]['status'] = 'active';
}else{
items[key]['status'] = 'done';
}
this.changeStates(items);
},
changeStates: function(items) {
this.setState({items: items});
localStorage.setItem('TODOs',JSON.stringify(items));
},
removeItem: function(e) {
var key = e.target.parentNode.getAttribute('id');
var items = this.state.items;
items.splice(key, 1);
this.changeStates(items);
},
render: function() {
return (
<div className="pure-u-11-12">
<header>
<h3>TODOs</h3>
</header>
<form onSubmit={this.handleSubmit} className='submitForm'>
<input type='text' className='submitInput pure-u-3-4 pure-u-md-5-6 pure-u-lg-7-8' onChange={this.onChange} value={this.state.text} placeholder="What needs to be done?"/>
<button className='submitBtn pure-u-1-4 pure-u-md-1-6 pure-u-lg-1-8' disabled={this.state.text===''}>{'Add'}</button>
</form>
<TodoList items={this.state.items} changeStatus={this.changeStatus} removeItem={this.removeItem}/>
</div>
);
}
});

完整代码请戳>>github: todolist-react

【经验总结】pushState+ajax应用

在即将度过试用期的时候,借着今天学习的文章,整理一下前段时间做用户主页时用到的pjax。

近期我站的个人主页改版,将用户发布的各类作品放到一个页面上切换,附加筛选功能。
在做筛选及翻页的过程中,我们用到了pjax。即通过ajax获取数据,然后改变url链接,这样用户可以前进后退。就像这样→

clipboard.png

上面的二级导航就是筛选用哒~

clipboard.png

这个就是翻页。

翻页是由php直接渲染的,所以里面的链接也是php回传的。在筛选之后ajax回传数据里面,也带了翻页的数据,就可以直接添到里面。因为pushState和replaceState不能很好的兼容低版本IE,这里我们用了jquery.history插件,在使用pushState或replaceState的时候触发 ‘statechange’事件。

因为ajax参数是作为a标签的链接形式写入翻页里,所以在翻页时需要将参数提取出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$('.pager').on('click', 'a', function(e) {
e.preventDefault();
var href = $(this).attr('href');
if (!href) {
return;
};

if ($(this).closest('.pager').hasClass('ajaxPage')) {
changeState(href);
} else {
window.location.href = href;
};

});

先阻止a标签的跳转,然后取出链接。这里有一处判断是否是ajax页,因为页面交互涉及到其他页面跳转过来的情况,所以ajaxPage作为判断[请忽略。。。

接下来就是使用pushState:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var changeState = function changeState(href) {
var parmas;
var href = href;
//取出参数(使用插件)
href = href.substring(href.indexOf('?'));
parmas = $.query.parseNew(href).get();
var subParmas = {
/* state对象 */
};
var subHref;
/*
各种判断处理
subHref为url要与后台约定
*/
History.pushState(subParmas, $('title').text(), subHref);
};

然后等到pushState的时候会触发’statechange’,我们再根据参数ajax取数据,最后呈现出来:

1
2
3
4
5
6
7
8
9
//当页面url更新时更新数据
History.Adapter ? History.Adapter.bind(window,'statechange',function(){
var parmas = History.getState().data,
$.when($.get(ajaxlink, parmas)).done(function(result) {
/*
各种呈现处理
*/
});
}) : '';

得益于pushState和replaceState的出现,以及jquery.history插件,这几行代码就可以有pjax效果。
嘛~~更多有关于pjax的应用,可以阅读学习浅析Web开发中前端路由实现的几种方式

【面试次体验】堆糖前端面试

在上一次失败的经历之后,本菜鸟非常努力的梳理羽毛(整理知识点),在两天后参加了堆糖的视频面试。这次面试经历总体来说比较好,可能是查漏补缺起到了效果,大部分的题目都能大体回答出来,加上面试官很nice~~~
Anyway,还是把面试过程整理出来。


首先还是从CSS开始,有遇到与上次面试类似题目的请参考今日头条前端实习生面试

CSS题目

1.如何实现一个三栏布局

三栏布局,问到的时候我以为是左右固定,中间自适应,后来发现他就是想考我float。

1
2
3
4
5
6
7
8
9
10
*{
margin: 0;
padding: 0;
}
.left,.right,.middle{
float:left;
border: solid 1px #777;
width: 30%;
margin:1.55%;
}

然后面试官问我,这三个元素float了之后,对下面的元素有影响要怎么处理。

清除浮动问题我发现是前端面试CSS方面经常问的一个问题,详细可参考文章:那些年我们一起清除过的浮动

主要的方法有添加多余标签,然后clear: both一下

1
2
3
4
<div class="left"></div>
<div class="middle"></div>
<div class="right"></div>
<div class="clear"></div>

然后CSS改一下

1
2
3
.clear{
clear: both;
}

第二个方法是在父层容器添加overflow:auto属性

1
2
3
4
5
<div class="overflow">
<div class="left"></div>
<div class="middle"></div>
<div class="right"></div>
</div>
1
2
3
.overflow{
overflow: auto;
}

第三个方法是用伪类::after

1
2
3
4
5
<div class="clearfix">
<div class="left"></div>
<div class="middle"></div>
<div class="right"></div>
</div>

由于IE6-7不支持:after,使用 zoom:1触发 hasLayout。

1
2
3
4
5
6
7
8
9
.clearfix::after{
display: block;
content: "";
visibility: hidden;
clear: both;
}
.clearfix{
*zoom:1;
}

2.position的几种属性

我发现这个面试官的问题都不算刁钻,position也是在学布局的时候一定要接触到的。常用的position有static、fix、absolute和relative。

position:static
这个static是默认的,对块级框的块格式化,对行级框的行格式化,元素按照块格式化上下文或行格式化上下文正常排版。

position:relative
相对定位,元素正常排版,并可以用top left right bottom进行位置的偏移,然而后面的元素不会调整位置去适应这个相对定位的元素。

position:absolute
绝对定位,元素脱离正常排版,使用top left right bottom相对于第一个非static的父层定位。

position:fixed
与absolute类似,不过使用top left right bottom定位是在视口的固定位置上。


3.如何实现一个响应式布局

这个是一个经验题。我原以为他要问我对Bootstrap等类库的使用,加之我从没接触过移动端,对于除了使用过Bootstrap和使用百分比宽度没再尝试过别的。

面试官说明是要自己写响应式,然后我就说我就是用百分比设置宽度。然后他问半分比相对于谁?

所以说还是基础硬伤,因为自己做的时候没注意过相对于谁,而且大多是外框,所以就说相对于窗口。面试官一笑,窗口?我说好吧,我再查查。。。顺便查了一下盒模型设置的各个百分比都是相对于谁的。

CSS中:width、margin和padding百分比是相对于包含块的!
一般情况下包含块是父元素,当position: fixed的时候,包含块为视口;当position: absolute的时候,包含块为最近的position不是static的祖先。

除此之外,响应式布局的方法还有:
CSS3 Media Query

通过在里面插入这段内容,使分辨率在小于1024分辨率的情况下和大于1024分辨率的情况下能响应不同的css文件。

<meta name="viewport" content="width=device-width; initial-scale=1.0">

设置原始缩放比例和视窗的大小

1
2
3
<link rel="stylesheet" type="text/css" href="common.css" media="all" />
<link rel="stylesheet" type="text/css" href="normalScreen.css" media="screen and (max-width: 1024px)" />
<link rel="stylesheet" type="text/css" href="widthScreen.css" media="screen and (min-width: 1024px)" />

同样,可以在样式表中镶嵌@media,文章推荐参考响应式布局这件小事


Javascript题目

1.Jquery中$(‘’).click()和$(‘’).on(‘click’)的分别

我为了学基础,学原生的Javascript,已经很久没碰Jquery了。问到这个问题的时候,我只能很诚实的说我没有考虑过这个问题,如果让我说的话,可能是原生的JS中onclick事件与addEventListener(“click”)的区别。

在面试完之后,我立刻又上网查了这两个的分别,其实在效果上作用不大,就是说下列代码在实现上并没有很大差距。

1
2
3
$("#myDiv").click(function(){
alert("clicked");
})
1
2
3
$("#myDiv").on("click",function(){
alert("clicked");
})

不同的是,on()方法可以实现动态绑定。可以将所有的点击事件绑在一个父层元素上,也可以用off()方法解绑定。

1
2
3
4
5
$("body").on("click","button",function(){
var btnValue = this.val();
alert(btnValue+"clicked");
});
//为每一个按钮绑定点击事件
1
$("#myDiv").off("click");//解除点击事件绑定

2.JS实现一个类

我发现,这个问题也是面试常会问到的,而且我觉得这也是我在学习JS的时候遇到的一个难点。
说起类,就要提起原型链的问题,这里直接上例子。对于原型,可以直接参考我大神男友的博文Javascript 面向对象特性(1)——抛弃类Javascript 面向对象特性(2)——找回类

《Javascript高级程序设计》中讲到继承的时候提到了六种方式:原型链、借用构造函数、组合继承、原型式继承、寄生式继承、寄生组合式集成。

原型链方式的具体用法是创建一个构造函数,然后创建继承这个构造函数的引用类型,将这个引用的原型指向构造函数。在原型链的问题是,Student的原型为Person的实例,那么所有Student的实例会共享它引用类型的属性,所以实例在修改这个属性的时候会导致其他实例的实例都被修改。

1
2
3
4
5
6
7
8
9
10
11
function Person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){
alert(this.name);
}
}
function Student(school){
this.school=school;
}
Student.prototype= new Person();

借用构造函数的用法是在一个引用类型中调用构造函数,使用apply()和call()方法可以在函数内部调用构造函数。问题是,对于Student的实例找不到哪些是Person中定义的。

1
2
3
4
5
6
7
8
9
10
11
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayName=function(){
alert(this.name);
}
function Student(name,age,school){
Person.call(this,name,age);
this.school=school;
}

组合方式是综合了原型链与借用构造函数两种方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.sayName=function(){
alert(this.name);
}
function Student(name,age,school){
Person.call(this,name,age);
this.school=school;
}
Student.prototype= new Person();
Student.prototype.constructor=Student;

Student.prototype.saySchool=function(){
alert(this.school);
}
var instance1=new Student("Kathy","23","UQ");
var instance2=new Student("Visper","24","KMUST");

instance1.saySchool();//UQ
instance2.sayName();//Visper

原型式继承,方法是通过一个现有的对象创建一个新的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
//引自《Javascript高级程序设计》
function object(o){
function F(){}
F.prototype = o;
return new F();
}
var person = {
name: "Kathy",
age: "23"
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Visper";
anotherPerson.age = "24";

寄生式继承的方法是将原型式继承封装在一个方法内,然后再用工厂式的创建对象的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//引自《Javascript高级程序设计》
function object(o){
function F(){}
F.prototype = o;
return new F();
}
var person = {
name: "Kathy",
age: "23"
};
function createPerson(original){
var clone=object(original);
clone.sayName=function(){
alert(this.name);
};
return clone;
}
var anotherPerson = createAnother(person);
anotherPerson.name = "Visper";
anotherPerson.age = "24";

2.如何统计一个字符串中哪个字母出现的次数最多

这个问题又是算法题,而算法题是我相当不擅长的。。。不过有上次选取“字符串中第一个只出现一次的字符”的经验,这次也是比较快的想出了一个方法。不过,也还是不知道这个问题的最快解决办法是什么。

思路还是将字符串变成数组,然后再sort()排序,之后为每一组字符计算数字,将目前出现次数最多的字符以及其次数记下来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function maxCountValue(string){
var letterArray=Array.prototype.slice.apply(string);
var sortArray=letterArray.sort();
var arrayLen=letterArray.length;
var countNow=0,count=0,value=sortArray[0],maxCountValue="";
for(var i=0;i<arrayLen;i++){
//判断是否进入下个字符组
if(value==sortArray[i]){
countNow=countNow+1;
//判断是否为最后一组并且判断当前次数以及目前最大次数
if(i==arrayLen-1&count<countNow){
maxCountValue=value;
}
}
else{
//如果当前次数大于目前最大次数
if(count<countNow){
count=countNow;
maxCountValue=value;
}
value=sortArray[i];
countNow=0
countNow=countNow+1;
}
}
alert(maxCountValue);
}

HTTP题目以及新技术题目

HTTP题目继续问到了AJAX的问题,上一篇面试题中有整理。

面试官紧接着有问到form提交和ajax提交的区别?
Form在提交的时候,会跳转页面或者原页面刷新。而AJAX是异步的,无需页面刷新而只是部分刷新。
Form提交时,JS不是必需的,而且数据按照表单结构提交。AJAX需要JS来实现,而且数据也要用程序处理。


之后面试官问到开发环境问题:

1、目前的开发环境是什么
曾用PC开发,使用EverEdit;这几个月转用Mac,使用HBuilder

2、shell操作 命令行用过什么
简单的前进到文件目录cd
列出当前文件夹内的文件 ls -li
改变所有人权限 chown
该文件读写模式 chmod

3、chrome的调试方法
Elements中查询元素结构、以及元素的样式
Network中查看每个资源的请求反馈时间
Sources中调试程序,在左栏程序文件上打断点,并在右侧输入变量名查看变量值的变化
Resource中查询Session、Cookie、本地存储及缓存的状态
Console中执行函数并查看错误行号或错误代码

4、代码管理工具使用过么?做哪些操作呢?
使用过Git:建立远程仓库,拉取本地仓库,更改之后submit,之后push到远程仓库。曾经Fork过别人的,然后修改过后也是submit再push。
说实话我做的个人项目比较多,对于GitHub的使用仅限于提交推送更新代码。所以也是大概的说了一下我的使用情况。


新技术题目

接触过什么比较前沿的技术?js框架、模块化 、自动化工具、预编译语言?
呃。。呃。。呃。。。

当时我的反应好尴尬,不过也是实话实说我没接触过什么,只不过听说过angular.js、backbone.js之类的。其余的都没太听说过。。。汗

不过我有表示我以后会努力学习,迎头赶上!


最后问道面试官对我的印象:1、经验不足,2、新技术接触的太少。

因为我目前时间有限,只有每天晚上整理面试题,所以这短短的几个题整理了接近一个星期,很多问题也期待大神们的补充。。。目前也算是找到了工作,今后会继续努力,让我的羽翼丰满起来!

【面试初体验】今日头条面试

回国半个月的准海带,今天体验了一下第一场技术面试。首先是一套题目,30分钟左右的时间根据自己的水平选难题回答,答完之后之后才进行面试。面试官问了很多基础问题,也很多没能回答上。略受打击。。。


后悔试题没拍照,根据回忆整理:

CSS题目

1.上下左右居中

这个题面试前才看过,但问到还是不能说清楚,还是基础有硬伤
思路为绝对定位,在宽高一定的情况下,上下设置为50%。之后设置margin为负值,margin-top和margin-left为自身高度和宽度的一半,将位置拉回中间。

1
2
3
4
5
6
7
8
9
.content{
position: absolute;
width:400px;
height:400px;
top:50%;
left:50%;
margin:-200px 0 0 -200px;
border:1px solid #008800;
}

2.两栏,左边固定100px,右边自适应

其实做布局这个用的最多最常见,我也说的蛮自信的,可惜我把两个思路的属性说混了。
第一个思路是左边用float:left,右边用margin-left为左栏的宽度。

1
2
3
4
5
6
7
8
9
10
11
12
13
*{
margin: 0;
padding: 0;
}
.left{
float:left;
width:100px;
border: solid #999 1px;
}
.right{
margin-left: 100px;
border: solid #777 1px;
}

第二个思路略复杂,右栏width:100%,float:left,但是这样就会超出屏幕的宽度,于是用负值margin拉回来。但这就出现另一个问题,由于右栏缩到左栏里,所以左栏会遮盖右栏的一部分,所以在右栏内部再镶嵌一层,margin-left为左栏的宽度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
*{
margin: 0;
padding: 0;
}
.left{
float:left;
width:100px;
border: solid #999 1px;
margin-right: -100%;
}
.right{
float:left;
width:100%;
border: solid #777 1px;
}
.content{
margin-left: 100px;
}

补充:第三种方式是在整理下面题目的时候发现的,用display:table-cell的自适应。
思路还是,因为table-cell的自适应,将右栏设的尽可能的大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
*{
margin: 0;
padding: 0;
}
.left{
float:left;
width:100px;
border: solid #999 1px;
}
.right{
display: table-cell;
width:3000px;
border: solid #777 1px;
}

3.盒模型

盒模型可以帮助块级元素的定位和计算面积,简单来说W3C的盒模型包括content、padding、border。而IE得盒模型则有些不同,表现在设置宽高的时候,W3C模型下只包含content区域,而IE包括所有区域。
这样,在IE下,设置了宽高之后可以很放心的设定padding,但是对于W3C模型下,设置了宽高之后还要计算padding和border的区域。因此有一个方法叫做box-sizing:border-box。border-box将border和padding区域一同算进设定的宽高内。这样的话添加如下代码,就可以设定padding而用不担心超出设置区域。

1
2
3
4
5
div{
box-sizing:border-box;
-moz-box-sizing:border-box; /* Firefox */
-webkit-box-sizing:border-box; /* Safari */
}

4.几种方法生成三个并排的大小相等的元素

这个问题让我有点蒙,首先想到的是三个元素都用百分比控制

1
2
3
4
5
6
7
8
*{
margin: 0;
padding: 0;
}
.left,.right,.middle{
float:left;
width:33.3%
}

其实还有可以用display:inline-block。回家以后尝试了一下,发现问题是如果这几个元素没有在代码里写在一行,那么最后展现出来的是最后一个元素会被挤到下面一行。
大神男友告诉我hack方法为:

1
margin-right: -4px//可以按照实际调整。

关于inline-block缝隙,推荐一篇文章:去除间距

还有一个方法为tabel-cell,这种方法就是将每一个元素设置的尽可能的大,但由于自适应的特性,这三个元素还是会乖乖地等分的排在一行里。

1
2
3
4
5
6
7
8
*{
margin: 0;
padding: 0;
}
.left,.middle, .right{
display:table-cell;
width:3000px;
}

接着昨天的继续补充

5.CSS3的新特性用过哪些

对于我来说最常用当然是border-radius,让圆角成为方便的存在。
然而他问第二个问题:怎样将方的通过圆角变成圆的?
其实只要

border-radius:50%;

就好了。可惜当时我怕这个不能用百分比,于是说如果50的宽高,半径为25就好。其实这个也没错啦,但也看的出我基础不扎实。

除此之外,box-shadow也是咱常用的属性之一。设置水平、垂直的位移,阴影的模糊、尺寸还有颜色,就可以为元素添加阴影。

box-shadow: 2px 2px 3px #777;

然后我也提了一下border-image这个好用又漂亮的属性。通过设置图片路径,图片的切割方式、还有边框的宽度、溢出,以及平铺或拉伸等呈现方式,可以做出一个漂亮精致的边框。

1
border-image: url(border-image.png) 30/10px/10px round;

关于border-image的每一项属性的用法和限制,可以参考我大神男友的文章CSS 3 border-image 属性详解

面试官还问到了动画:transition、transform和animation的区别?
可惜我目前并没有用CSS做过动画【摊手。。
他专门问到了这个问题,说明CSS3的动画是应用较多的,回来立刻学习。

transform是指转换,可以将元素移动、旋转、倾斜、拉伸。

translate(),从当前位置移动到由给定left和top值的位置。这个例子中,div向右下移动,如果想要往左上移动,则要设置为负值。

1
2
3
4
5
6
7
div{
transform: translate(50px,100px);
-ms-transform: translate(50px,100px); /* IE 9 */
-webkit-transform: translate(50px,100px); /* Safari and Chrome */
-o-transform: translate(50px,100px); /* Opera */
-moz-transform: translate(50px,100px); /* Firefox */
}

rotate(),将元素旋转到给定角度,单位为deg(degree角度),在这个例子中为顺时针30度。负值的话为逆时针旋转。

1
2
3
4
5
6
7
div{
transform: rotate(30deg);
-ms-transform: rotate(30deg); /* IE 9 */
-webkit-transform: rotate(30deg); /* Safari and Chrome */
-o-transform: rotate(30deg); /* Opera */
-moz-transform: rotate(30deg); /* Firefox */
}

skew() ,将元素倾斜到给定角度,单位也是deg,分别围绕着X轴和Y轴翻转。这个例子中,在X轴顺时针翻转30度,Y轴顺时针翻转20度,负值为逆时针翻转。

1
2
3
4
5
6
7
div{
transform: skew(30deg,20deg);
-ms-transform: skew(30deg,20deg); /* IE 9 */
-webkit-transform: skew(30deg,20deg); /* Safari and Chrome */
-o-transform: skew(30deg,20deg); /* Opera */
-moz-transform: skew(30deg,20deg); /* Firefox */
}

scale(),将元素拉伸到指定的倍数,同样设定了X与Y两个方向。这个例子中把宽度拉伸2倍,高度拉伸4倍。有意思的是,负值不是缩小,而是翻转,既水平翻转和垂直翻转。

1
2
3
4
5
6
7
div{
transform: scale(2,4);
-ms-transform: scale(2,4); /* IE 9 */
-webkit-transform: scale(2,4); /* Safari 和 Chrome */
-o-transform: scale(2,4); /* Opera */
-moz-transform: scale(2,4); /* Firefox */
}

transform并没有变化的过程,而是直接生成最终效果。transform还有3D方法,就是多了一个Z轴(Opera不支持)

  • transition是指过渡,可以动画般显示出一个从样式到样式之间的过渡。

上面说过transform没有变化的过程,这样一来就可以动画显示出各种酷炫的效果。
transform的属性包括一个你设定过渡的CSS属性,持续时间,时间曲线还有过渡开始的时间。

比较麻烦的写法,每一项属性单独写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
div{
transition-property: width;
transition-duration: 1s;
transition-timing-function: linear;
transition-delay: 2s;
/* Firefox 4 */
-moz-transition-property:width;
-moz-transition-duration:1s;
-moz-transition-timing-function:linear;
-moz-transition-delay:2s;
/* Safari 和 Chrome */
-webkit-transition-property:width;
-webkit-transition-duration:1s;
-webkit-transition-timing-function:linear;
-webkit-transition-delay:2s;
/* Opera */
-o-transition-property:width;
-o-transition-duration:1s;
-o-transition-timing-function:linear;
-o-transition-delay:2s;
}

简单的写法就是将属性都写在transition中。下面例子中包括了之前我们将transform的旋转,效果比较酷炫。(背景用色有点小私心,不过过渡很漂亮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
div{
width:100px;
height:100px;
background:lavender;
transition:width 2s, height 2s, background 2s;
-moz-transition:width 2s, height 2s, -moz-transform 2s; /* Firefox 4 */
-webkit-transition:width 2s, height 2s, -webkit-transform 2s, background 2s; /* Safari and Chrome */
-o-transition:width 2s, height 2s, background 2s, -o-transform 2s; /* Opera */
}

div:hover{
width:200px;
height:200px;
background:LightSeaGreen;
transform:rotate(180deg);
-moz-transform:rotate(180deg); /* Firefox 4 */
-webkit-transform:rotate(180deg); /* Safari and Chrome */
-o-transform:rotate(180deg); /* Opera */
}

  • animation,用@keyframes规则做动画效果。

如果说transition是过渡中的动画效果,那么animation就是专门做动画的。transition是animation的简化,是当属性发生变化的时候,触发过渡动画。就是上面例子中hover的时候,css的属性变化了,那么在transition里面绑定过得属性动画过渡过去。
animation就复杂多了:@keyframes可以有from to,也可以是百分比表示时间帧。

from-to

1
2
3
4
@keyframes myfirst{
from {background: lavender;}
to {background: LightSeaGreen;}
}

百分比

1
2
3
4
5
6
7
@keyframes myfirst{
0% {background: purple;}
25% {background: lavender;}
50% {background: cyan;}
75% {background: LightSeaGreen;}
100% {background:purple;}
}

然后再将这个@keyframes绑定到一个元素上。
animation提供了更多属性,包括@keyframes名字、持续时间、速度曲线、开始时间、播放次数等。

1
2
3
4
5
6
7
8
9
div{
animation: myfirst 5s linear 2s infinite alternate;
/* Firefox: */
-moz-animation: myfirst 5s linear 2s infinite alternate;
/* Safari 和 Chrome: */
-webkit-animation: myfirst 5s linear 2s infinite alternate;
/* Opera: */
-o-animation: myfirst 5s linear 2s infinite alternate;
}

有了这几个属性,css可以在很多时候取代flash和动图了。


JavaScript题目

可能因为笔试中JS题目较多,所以面试的时候没有问太多。

1.优化代码,事件委托

笔试的题目上给了一段代码让我优化。具体代码记不清,大概是获取的元素,然后循环为每一个添加mouseover和mouseout事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var elementList=document.querySelectorAll("a");
var listLen=elementList.length;
function handler (element,color){
return function(){
element.style.color=color;
};
}
//添加事件方法
function addEvent(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}
else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}
}
//循环为元素添加事件
for(i=0;i<listLen;i++){
addEvent(elementList[i],"mouseover",handler(elementList[i],"red"));
addEvent(elementList[i],"mouseout",handler(elementList[i],"black"));
}

然后面试官又问我为什么事件委托效率更高

还是基础硬伤,我只是大概知道什么是事件委托,对于原理还不了解。于是回来又查参考书《Javascript高级程序设计》

事件委托利用了事件冒泡,指定一个事件处理程序,就可以管理某一个类型的所有事件。

书上解释,在DOM树中尽量高的层次上添加一个事件处理程序。那么在他的子孙元素中的事件,最终都会冒泡到这个层次中。例如,列表里有两个项目,我们不要分别设置每一个项目点击时的事件,而是在上层中建立事件委托。

1
2
3
4
<ul id="link">
<li id="home">首页</li>
<li id="resume">简历</li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var link = document.querySelector("#link");
//添加事件
function addEvent(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}
else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}
}
//获取事件对象,因为奇葩的IE,事件对象要用window.event
function getEvent(event){
return event ? event : window.event;
}
//获取事件目标
function getTarget(event){
return event.target || event.srcElement;
}
//为外层ul添加事件委托
addEvent(link,"click",function(event){
event = getEvent(event);
var target = getTarget(event);
//为每一个li添加事件
switch(target.id){
case "home":
target.innerHTML="首页,<a href='http://myj.name/'>Welcome to my home</a>"
break;
case "resume":
location.href = "http://katherina-miao.github.io/resume/";
break;
}
})

2.添加一个类似email中checklist的双向联动

起初,我的内心是崩溃的。。。
这个题刚开始都没搞清楚是做什么的,一直到做完整个卷子我才又回头看了看这个题。时间有限,都没来得及想就往纸上写。
其实这个题是让我做一个类似email里选择发件人的东西,勾选的时候添加到发件人,取消勾选的时候从发件人里面删除。由于是双向联动,因此要在发件人里点击的时候可以删除,并且取消勾选。好像说的有点乱,看下面代码。

其实主要还是事件绑定,然后添加节点,改变属性。

1
2
3
4
5
<div class="container">
<div class="showbox"></div>
<input type="checkbox" name="email" value="i@myj.name">Kathy</input>
<input type="checkbox" name="email" value="i@zjy.name">Visper</input>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
var emails=document.querySelectorAll("[name=email]");
var showBox=document.querySelector(".showbox");
var length=emails.length;
//添加事件
function addEvent(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}
else if(element.attachEvent){
element.attachEvent("on"+type,handler);
}
}
//循环,为每个checkbox添加事件
for(var i=0;i<length;i++){
addEvent(emails[i],"click",function(){
//判断是否被选中
if(this.checked){
var Emailvalue = this.value;
var newBtn=document.createElement("input");
newBtn.setAttribute("type","button");

newBtn.value=Emailvalue;
showBox.appendChild(newBtn);
}
//若取消勾选,则删除相应的button
else{
var Emailvalue = this.value;
var emailBtn=document.querySelector("[type=button][value='"+Emailvalue+"']");
showBox.removeChild(emailBtn);
}
//每次重新调用以更新button
startBtn();
})
}
//获取button列表,为button添加事件
function startBtn(){
var emailBtn=document.querySelectorAll("[type=button]");
if(emailBtn.length){
for(var i=0;i<emailBtn.length;i++){
addEvent(emailBtn[i],"click",function(){
var btnvalue=this.value;
var cancelChoose=document.querySelectorAll("[value='"+btnvalue+"']");
//删除button并取消勾选相应checkbox
for(var j=0;j<cancelChoose.length;j++){
if(cancelChoose[j].getAttribute("type")=="button"){
showBox.removeChild(cancelChoose[j]);
}
else if(cancelChoose[j].getAttribute("type")=="checkbox"){
cancelChoose[j].checked=false;
}
}
})
}
}
}

3.Array的方法

面试官问到Array有几种方法,这个我在面试前曾经简单整理过,所以感觉回答也比较满意。

isArray(),判断是否为Array类型。
join(),将Array类型转换为字符串类型,并用join内的符号间隔开。

队列,栈的用法
push(),将数据项放到数组的后面。
pop(),将数组的最后一项删除并返回。
shift(),将数组的第一项删除并返回。
unshift(),将数据项放到数组的前面。

重排序的方法
reverse(),将数组逆序排列。
sort(),将数组由小到大排列。默认是按照字符串比较,也就是说15比5小。要按照数字排列的话,要设定方法:

1
2
3
4
5
6
var numlist = [0, 5, 15, 20, 10];
function compare (value1, value2){
value2-value1;
}
var listsort = numlist.sort(compare);
alert( listsort );//0, 5, 10, 15, 20 按照数值从小到大

操作Array的方法
concat(),在原有meat数组上创建新数组
slice(),截取从指定位置开始到结束为止(不包含结束位置)的数组项赋给新数组,如果没有第二个参数,则截取从指定位置开始到结束的数组项赋给新数组
splice(),从指定位置(第一个参数)删除指定个数(第二个参数)并插入指定项目(’第三个参数”)

位置方法
indexOf(),从前向后查找位置索引。
lastIndexOf(),从后向前查找位置索引。
这两个方法支持第二个参数,既指定位置然后按照方法向前或向后查找位置。

遍历方法
every()中,每一项运行给定函数,每一项都返回true,否则返回true。
filter()中,每一项运行给定函数,返回true的项。
forEach()中,每一项运行给定函数,无返回。
map()中,每一项运行给定函数,返回调用结果组成的数组。
some()中,每一项运行给定函数,任意一项返回true,否则返回true。

归并方法
reduce()和reduceRight()都接受四个参数:前一个值,当前值,索引和数组对象。
reduce()是从左向右归并,当第一个值和第二个值运行过之后,结果将作为下一次运行的前一个值。
reduceRight()则是从右向左,归并方式与reduce()一样。


算法题

算法题就一个,给定字符串“abcba”,处理得到第一个不重复字母。

对算法实在只是了解个皮毛,所以这个问题估计最后还是写错了。回来后自己写了一个,可以得到答案,但也不知道是不是最优。
思路是首先将字符转换成数组,然后从小到大排序,然后循环对每一个数组项比较前后。因为曾经排序,如果是不重复的那么前后应该都与它不同。

1
2
3
4
5
6
7
8
9
var letterString="abcba";
var letterArray=Array.prototype.slice.apply(letterString);
var arrayLen=letterArray.length;
var sortArray=letterArray.sort();
for(var i=0;i<arrayLen;i++){
if(sortArray[i]!=sortArray[i+1]&&sortArray[i]!=sortArray[i-1]){
alert(sortArray[i]);
}
}

HTTP

1.AJAX过程,AJAX中如何区分get和post,get和post的区别

面试官在这个方面一气儿问了三个问题,可能因为我没一个问题都没能答的全面。

AJAX算是听人说的最多的一个技术,无需加载整个页面的情况下进行局部更新。
再用AJAX技术与服务器交换数据时,首先创建一个XMLHttpRequest对象,在特立独行的IE里是ActiveXObject。

接着我们用open()方法和send()方法,使用get和post会有些区别。然后,请求会发送给服务器,服务器会响应触发onreadystatechange事件,当readyState=4(请求完成)且status=200时,说明服务器已经就绪。然后用responseText或responseXML属性去获得字符串或XML格式的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var xmlhttp;
if (window.XMLHttpRequest){
xmlhttp=new XMLHttpRequest();
}
else{
//奇葩的IE5、6
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET","response.php",true);
xmlhttp.send();

xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
document.getElementById("showBox").innerHTML=xmlhttp.responseText;
}
}

在AJAX中区别get和post,就要详谈open()方法和send()方法。

get请求常用于向服务器查询信息。

在get请求中,open()的第一个参数为“get”,第二个参数为URL,第三个参数为是否为异步。由于get请求的URL中通常带有参数,这些参数必须要使用encodeURIComponent()方法进行编码,否则会有格式错误。这样send()方法内部的参数可以传入null,传入的参数也会转到URL中。

post请求常用于向服务器提交信息。

在post请求中,open()的第一个参数为“post”,URL中不含有参数,第三个参数一样。post请求的send()方法中需要传入提交数据,可以使用XML或者字符串。在提交表单时,首先要设定请求头部的Content-Type为 application/x-www-form-urlencoded。

xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 

然后将传入的表单信息序列化,方便后端的程序获取数据。对于表单数据序列化,FormData对象可以直接放入send()内:

1
2
3
4
<form id=“user-info”>
用户名:<input type="text" name="username"/>
年龄:<input type="text" name="年龄"/>
</form>
1
2
var form = document.getElementById("user-info");
xhr.send(new FormData(form));

前面提过,get方式中url包含参数,而post中url是“干干净净”的。因为用处不同,post在提交内容的时候没有大小限制。而且post在发送时有内容体,那些提交的信息放在内容体内。


2.JSONP 跨域的特性

说实话我只在用JQuery的时候用过JSONP,只知道可以跨域,但不知道为什么可以。所以在被问到的时候,脑子又是一蒙。

由于跨域安全策略,XMLHttpRequest不能跨域请求,这是AJAX的一个主要限制。
JSONP是通过<script>元素。因为我们知道,<script>可以跨域引用js文件。返回的数据格式为JSON,创建一个回调函数来相应返回的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
var script = document.createElement("script");
script.src = "http://tryjson.org?callback=handler";
document.body.appendChild(script);

//回调函数
function handler(response){
var responseContent={
diatotime:response.datetime,
usedEnergy:response.used.Energy,

}
return responseContent;
}

除了JSONP,用img标签也可以跨域通信。

1
2
var img = new Image();
img.src = "http://www.example.com/id=123";

CORS是跨域请求的另一个方法。在IE浏览器中为XDomainRequest对象,而在其他浏览器中则继续用XMLHttpRequest。区别在于,XDomainRequest对象的open()只有两个参数:请求类型和URL,因为都是异步执行。而其他浏览器在使用open()时,URL要使用绝对路径,既可以跨域请求。

3.提高网页性能的方法

因为刚刚问完AJAX,所以首先反应的是使用AJAX部分刷新。然后我想到了CSS精灵,将小图片放在一起减少HTTP请求。然后想到将script和css从外部引入,然后script放在文档底部。

除此之外,总结一下:在内容上压缩,在请求上减少。压缩图片、脚本和CSS,使用CSS精灵,优化图片,尽量使用引用CSS。在请求上避免重定向,尽量使用AJAX尽量使用GET请求。

在优化性能方面,我没什么研究,还是由于对底层原理不熟【汗。。


这次的面试经历整理了一天多,一是因为实在是基础差,每一题都要查资料,编程题要自己试,二是要激励自己。因为自己是很懒的人,不喜欢上网找文章资料。这次面试算是一个鞭策,将自己漏洞补起来。

希望各路大神评论补充!!本菜鸟道谢~~