备战秋招,复习基础。如有错误,欢迎批评指正,共同进步!
写在最前
整理自自己的面试经验+网络资料
部分资料参考自网络,在具体内容前均有标出~感恩大家~
由于篇幅原因,拆成两篇~前端面试梳理(一)
JavaScript
this
参考资料:JavaScript 的 this 原理
参考资料:JS this指向总结
在函数体内部,指代函数当前的运行环境。
哪个对象调用函数,函数里面的this指向哪个对象。
- 普通函数调用:没特殊意外,就是指向全局对象-window。
let username='cn'
function fn(){
alert(this.username);//undefined
}
fn();
- 对象函数调用:哪个函数调用,this指向哪里
window.b=2222
let obj={
a:111,
fn:function(){
alert(this.a);//111
alert(this.b);//undefined
}
}
obj.fn();
- 构造函数调用
let TestClass=function(){
this.name='111';
}
let subClass=new TestClass();
subClass.name='cn';
console.log(subClass.name);//cn
let subClass1=new TestClass();
console.log(subClass1.name)//111
- apply和call调用:改变传入函数的this
let obj1={
a:222
};
let obj2={
a:111,
fn:function(){
alert(this.a);//222
}
}
obj2.fn.call(obj1)
- 箭头函数调用:箭头函数里面的 this 是继承外面的环境
let obj={
a:222,
fn:function(){
setTimeout(function(){console.log(this.a)})
}
};
obj.fn();//undefined ← 传给setTimeout的是普通函数,this 指向是 window
let obj={
a:222,
fn:function(){
setTimeout(()=>{console.log(this.a)});
}
};
obj.fn();//222 ← 传给setTimeout的是箭头函数,setTimeout的上层作用域是fn。fn里面的this指向 obj,所以setTimeout里面的箭头函数的this指向obj。
普通函数和箭头函数的区别
1. 箭头函数是匿名函数,不能作为构造函数,不能使用new
2. 箭头函数不绑定arguments,取而代之用rest参数...解决
3. 箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
4. 箭头函数通过call()或apply()方法调用一个函数时,只传入了一个参数,对this并没有影响。
5. 箭头函数没有原型属性
6. 箭头函数不能当做Generator函数,不能使用yield关键字
new的机理
参考资料:js中的new()到底做了些什么??
创建一个新对象,将新对象的_proto_指向构造函数的原型对象,然后将构造函数的this指向新对象。调用构造函数。
1. 创建一个新对象;
2. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
3. 执行构造函数中的代码(为这个新对象添加属性) ;
4. 返回新对象。
闭包
参考资料:彻底搞懂JS闭包各种坑
闭包:能够访问另一个函数作用域的变量的函数。
function outer() {
var a = '变量1'
var inner = function () {
console.info(a)
}
return inner // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}
由于闭包会携带包含它的函数的作用域,因为会比其他函数占用更多内容,过度使用闭包,会导致内存占用过多。
原型链 prototype _proto_
参考资料:帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)
1. __proto__和constructor属性是对象所独有的;
2. prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。
3. __proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。
4. prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.__proto__ === Foo.prototype。
5. constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function。
js执行机制(宏任务 微任务)
let data = [];
$.ajax({
url:www.javascript.com,
data:data,
success:() => {
console.log('发送成功!');
}
})
console.log('代码执行结束');
1. ajax进入Event Table,注册回调函数success。
2. 执行console.log('代码执行结束')。
3. ajax事件完成,回调函数success进入Event Queue。
4. 主线程从Event Queue读取回调函数success并执行。
macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick
怎样让setTimeOut执行两次
setTimeout(code,等待毫秒数)
多次调用: 1 code自身再次调用setTimeout()
2 用setInterval(code,时间间隔)
通过DOM切换图片
1. 定义图片路径数组存放图片路径。定义字符串数组存放文字描述。
2. 封装prev和next方法
3. 绑定按钮的点击事件
自执行函数(立即执行函数)
具体含义待补充!!!
1 var fn1 = function(){
}();
2 (function (){
}());
3 (function(){
}();
4 !function(){
}();
5 +function(){
}();
相等操作符
==先转换再比较bolean → 数值对象 → valueOf()/toString()字符串 → 数值===不转换直接比较null===undefined//falseobject.is(a,b)比较规则与===相同
| 比较 | == | === | object.is() |
|---|---|---|---|
| null undefined | true | false | false |
| NaN NaN | false | false | true |
| undefined 0 | false | false | false |
| null 0 | false | false | false |
| -0 +0 | true | true | false |
for-in 和 for-of
- for...in :以原始插入顺序迭代对象的可枚举属性
- for...of:遍历可迭代对象定义要迭代的数据(非自定义属性)
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
let arr = ['a','b','c'];
arr.hobby = 'foosball';
for (let i in arr) { ← for in 用键key遍历
console.log(i); // 0, 1, 2, "hobby", "arrCustom", "objCustom" ← 可循环出自定义的属性
}
for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); //0, 1, 2, "hobby" ← 过滤非实例属性
}
}
for (let i of iterable) { ← for of 用值value遍历
console.log(i); // 'a', 'b', 'c' ← 无自定义属性
}
for(var key of Object.keys(arr)){ ← 使用Object.keys()方法获取对象key的数组
console.log(arr[key]);
}
检测数据类型
1. typeof 返回number/boolean/symbol/string/object/undefined/function
2. instanceof 判断A是不是B的实例
3. constructor [].constructor == Array //true
4. toString Object.prototype.toString.call([]);
其中差异待补充!!!
class继承和es5继承
参考资料:ES6中的类继承和ES5中的继承模式详解
ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。
ES6的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。
类的继承可以看做是寄生组合式继承的语法糖
使对象属性不可修改
Object.freeze 操作一个对象后, 就会使这个对象不可以新增属性, 删除属性, 对于已经存在的属性也不可以重新配置(The descriptor for the property), 或者被修改.如果属性是一个对象值的, 那么这个属性还是可以被修改的, 除非再次让这个属性为 freeze.
缺陷:freeze 操作只是针对于 window.openApi 对象本身, 而非针对 window 对象本身, 所以你仍旧可以重新定义 window 的 openApi 属性.
通过 Object.defineProperty 分别设置其 configurable 和 writable 为 false.
手写代码
实现一个sleep()
<script type="text/javascript">
//方法一
function sleep1(ms, callback) {
setTimeout(callback, ms)
}
//sleep 1s
sleep1(1000, () => {
console.log(1000)
})
//方法二
function sleep2(ms) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, ms)
})
}
sleep2(1000).then(() => {
console.log(2000)
})
//方法三
function sleep3(ms) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, ms)
})
}
async function init() {
await sleep3(1000);
}
init().then(() => {
console.log(3000)
})
</script>
用原生js写splice
参考资料:对js数组的splice实现
Array.prototype.splice = function(start,deleteCount){
var max = Math.max,
min = Math.min,
delta,
element,
insertCount = max(arguments.length - 2,0), //插入的元素的个数,最小为0
k = 0,
len = this.length,
new_len,
result = [], //返回的数组,包函了被删除的元素
shift_count;
start = start || 0; //如何start不存在,则从0开始
if(start < 0){
start += len; //确保start为正数
}
start = max(min(start,len),0); //确保start为正数
deleteCount = max(min(typeof deleteCount === 'number' ? deleteCount : len,len-start),0); //要删除元素的个数
// 1.如果deleteCount存在则deleteCount,否则len
// 2.用1的结果与len-start对比,取较小者(因为最大可删除的元素个数为len-start)
// 3.用2的结果与0比对,取大者,防止为负数
delta = insertCount - deleteCount;
alert(delta);
/*
* 1.如果delta大于0,说明数组长度会增加
* 2.如果delat小于0,说明数组长度会减少
*/
new_len = len + delta; //数组的新长度
while(k < deleteCount){ //这个while循环的作用是保存要返回的result,即保存被删除的元素
element = this[start + k];
if(element != undefined){
result[k] = element;
}
k++;
}
shift_count = len - start - deleteCount;
/*
* 1. len-start ,start前端的元素不用动
* 2. 用1的结果再减去deleteCount,是start后面要保留的元素的个数
* 3. 通过shift_count次的遍历,就可以把要保留的元素向前移动,达到删除的目的
*/
if(delta <= 0){ //在数组长度减少的情况下
k = start + insertCount; //k的初始下标为要保留的元素新的开始下标
while(shift_count){
this[k] = this[k - delta]; //后面的替换前面的要删除的元素
k += 1;
shift_count -= 1;
}
this.length = new_len;
}else if(delta > 0){ //在数组长度增加的情况下
k = 1;
while(shift_count){
this[new_len - k] = this[len - k]; //从数组的最后一个元素开始,倒着进行替换
k += 1;
shift_count -= 1;
}
this.length = new_len;
}
for(k = 0; k < insertCount; k+=1){
this[start + k] = arguments[k+2]; //插入替换元素
}
return result;
};
用原生JS写<div>
var divE = document.createElement('div');
var divId = document.createAttribute("id");
divId.value = 'name';
divE.setAttributeNode(divId); //为节点添加属性
给原型添加方法
Object(原型类名).prototype.sss = function(){
console.log(this);
}
写一个js继承
寄生组合式继承:通过借用构造函数来继承属性,通过原型链的方式来继承方法,而不需要为子类指定原型而调用父类的构造函数,我们需要拿到的仅仅是父类原型的一个副本。因此可以通过传入子类和父类的构造函数作为参数,首先创建父类原型的一个复本,并为其添加constrcutor,最后赋给子类的原型。这样避免了调用两次父类的构造函数,为其创建多余的属性。
//父:person
function Person(name){
this.name=name;
}
Person.prototype.sayName=function(){
console.log(this.name+' '+this.gender+' '+this.age);
}
//借用构造函数来继承属性的方法
function inheritPrototype(Female,Person){
var protoType=Object.create(Person.prototype);
protoType.constructor=Female;
Female.prototype=protoType;
}
//调用继承方法
inheritPrototype(Female,Person);
//子:female 新增方法
Female.prototype.sayAge=function(){
console.log(this.name+' '+this.age);
}
//尝试调用
var fm=new Female('skila','female',19);
fm.sayName();//skila female 19
fm.sayAge();skila 19
原生Js实现Bind
1. 不会立即执行函数,需要返回一个待执行的函数
2. 作用域绑定,使用apply或者call方法
3. 参数传递,由于参数的不确定性,用apply传递数组
Function.prototype.bind = function(newThis) {
var aArgs = Array.prototype.slice.call(arguments, 1) //拿到除了newThis之外的预置参数序列
var that = this
return function() {
return that.apply(newThis, aArgs.concat(Array.prototype.slice.call(arguments)))
//绑定this同时将调用时传递的序列和预置序列进行合并
}
}
手写一个计时器
<html>
<head>
<script type="text/javascript">
var c=0
var t
function timedCount()
{
document.getElementById('txt').value=c
c=c+1
t=setTimeout("timedCount()",1000)
}
function stopCount()
{
clearTimeout(t)
}
</script>
</head>
<body>
<form>
<input type="button" value="Start count!" onClick="timedCount()">
<input type="text" id="txt">
<input type="button" value="Stop count!" onClick="stopCount()">
</form>
</body>
</html>
手写一个深拷贝
function isObj(obj) {
//判断是否为对象或者函数,但不是null
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj) {
let newObj = Array.isArray(obj) ? [] : {}
for(let key in obj) {
newObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
}
return newObj
}
或
函数库lodash,提供_.cloneDeep()
获取当前Url中某个参数
function getQuery(name) {
// 正则:[找寻'&' + 'url参数名字' = '值' + '&']('&'可以不存在)
let reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
let r = window.location.search.substr(1).match(reg);
if(r != null) {
// 对参数值进行解码
return unescape(r[2]);
}
return null;
}
// 调用方法,注意需要传入String类型的数据,输出结果为String类型
getQuery('id'); // '123'
手写正则匹配手机号
function isPoneAvailable($poneInput) {
var myreg=/^[1][3,4,5,7,8][0-9]{9}$/;
if (!myreg.test($poneInput.val())) {
return false;
} else {
return true;
}
}
手写随机打乱数组
var arr = [1, 2, 3, 4, 5];
arr.sort(functon() {
return Math.random() - 0.5;
})
手写判断两个网络地址是否同一子网
参考资料:javascript判断两个IP地址是否在同一个网段的实现思路
要判断两个IP地址是否在同一个网段,将它们的IP地址分别与子网掩码做与运算,得到的结果为网络号,如果网络号相同,就在同一子网,否则,不在同一子网。
function isEqualIPAddress (addr1,addr2,mask){
if(!addr1 || !addr2 || !mask){
console.log("各参数不能为空");
return false;
}
var res1 = [], res2 = [];
addr1 = addr1.split(".");
addr2 = addr2.split(".");
mask = mask.split(".");
for(var i = 0,ilen = addr1.length; i < ilen ; i += 1){
res1.push(parseInt(addr1[i]) & parseInt(mask[i]));
res2.push(parseInt(addr2[i]) & parseInt(mask[i]));
}
if(res1.join(".") == res2.join(".")){
console.log("在同一个网段");
return true;
}else{
console.log("不在同一个网段");
return false;
}
}
手写indexOf
function indexOf(str, val){
var strLen = str.length, valLen = val.length
for(var i = 0; i < strLen; i++){
var matchLen = i + valLen
var matchStr = str.slice(i, matchLen)
if(matchLen > strLen){
return -1
}
if(matchStr === val){
return i
}
}
return -1
}
手写实现call
Function.prototype.call2 = function (context) {
var context = Object(context) || window
context.fn = this
var args = []
for (var i = 1; i < arguments.length; i++) {
args.push('arguments[' + i +']')
}
var res = eval('context.fn(' + args + ')')
delete context.fn
return res
}
写ES5扩展类
CSS
盒模型
盒模型:包含了元素内容(content)、内边距(padding)、边框(border)、外边距(margin)几个要素。
隐藏元素
用途:
1 对文本的隐藏
2 隐藏超链接
3 对统计代码隐藏
4 隐藏超出的图片
5 CSS隐藏滚动条
6 CSS隐藏div层
方法:
1 display:none → 不占位置
2 overflow:hidden / visibility:hidden
3 opacity:0
4 position:absolute;top:-9999px;left:-9999px
水平垂直居中
1. 双层 - 使用absolute定位居中
.container{
position:relative;
height:100px; ← 否则无高度,会向上偏移覆盖上方元素
}
.child{
transform:translate(-50%,-50%); ← 图像大小的一半
top:50%;left:50%; ← 容器位置的一半
position:absolute;
}
2. 双层 - 使用flexbox居中(主流)
.container{
display:flex;
justify-content:center;
align-items:center;
}
3. 双层 - 使用calc基于当前页面布局计算尺寸
.container{
position:relative;
height:100px; ← 没设定高度则找不到盒子
width:100px; ← 没设定宽度则默认页面居中
}
.child{ ← 使盒子居中,盒子内部不一定居中!
position:absolute;
width:40%;
height:40%;
top:calc(50%-20%);
left:calc(50%-20%);
}
45. 图片子元素
.child{
display:inline-block;
vertical-align:middle; ← 需要有一个兄弟文字元素 设置居中 使图片和文字的基线对齐
}
5. 双层 - margin auto 定位块级元素
.container{
height: 200px;
width: 200px;
position: relative;
}
.child{
margin: auto; ← 必须设定四周位置和宽高
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
height: 100px;
width: 100px;
position: absolute;
}
6. 单层 - 使用text-align水平居中(非垂直居中)
.container{
text-align:center;
}
7. 单层 - 单行文本元素 使用text-align水平居中 + line-height垂直居中
.div{
display: inline-block;
height: 100px;
line-height: 100px;
text-align: center;
width: 200px; ← 不设宽度的话,宽度自适应为文字宽度
}
8. 单层 - 多行文本元素
.div{
display:table-cell;
text-align:center;
vertical-align: middle;
}
常见布局方式
1. 传统盒模型布局方式:使用display(文档流)+position属性(定位布局)+float属性(浮动布局)
2. flex弹性布局:display:flex
3. Grid网格布局:实现二维布局 display:grid / inline-grid / subgrid
grid-template-columns 列宽 grid-template-rows 行高 有几个数字就几列/行
grid-column-gap 列与列的距离
4. 圣杯布局:两边定宽,中间自适应。
三个元素都是float:left
.left{margin-left:-100%}
.right{margin-left:-(width)px}
.mid{padding:width}
5. 双飞翼布局:与圣杯类似,只是中间防遮挡不同
在中间的div内部创建子div放置内容,子div里用margin留出位置
BFC
块级格式化上文,是一个独立的布局环境,其中的元素布局是不受外界影响。
position:absolute / fixed
或
display: inline-block / table-cell / table / caption
或
float != null / overflow != visible
特性:
1 子元素margin重叠
2 会自动清除内部浮动
防止外边距重叠解决方案:
1 外层元素padding代替
2 内层元素透明边框 border:1px solid transparent;
3 内层元素绝对定位 postion:absolute:
4 外层元素 overflow:hidden;
5 内层元素 加float:left;或display:inline-block;
6 内层元素padding:1px;
如何解决子元素造成的父元素高度塌陷问题:
1 给父元素再添加一个高度为0的子元素,并且让它清除浮动【clear:both;】
2 给父元素设置display:inline-block;
3 给父元素设置overflow:hidden;
4 给父元素设置固定的高度
5 对父元素使用伪元素:after,并且清除浮动【clear:both;】
尺寸
px:绝对尺寸,不能适用浏览器缩放
em:相对尺寸,相对于当前对象内文本的font-size,但需要知道父元素文本的font-size和对象内文本的font-size
rem:相对尺寸,相对于根元素<html>的font-size
margin-left:auto
参考资料:[margin-left: auto;为什么可以使的元素靠右](https://segmentfault.com/q/1010000008431088)
div {
width: 100px;
margin-left: auto;
}
因为
'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' +'border-right-width' + 'margin-right' = width of containing block
所以
margin-left = width of containing block - width(div)
页面滚动 导航栏固定在顶部
当页面滚动超出设置距离时,改变样式属性 切换定位状态fixed,给top设置一个值
--- css ---
.box{
position: relative;
height: 80px;
width: 100%;
z-index: 999;
}
.box-active{
position: fixed;
top: 0;
}
--- js ---
// 监听事件
window.addEventListener('scroll', function(){
let t = $('body, html').scrollTop(); // 目前监听的是整个body的滚动条距离
if(t>0){
$('.box').addClass('box-active')
}else{
$('.box').removeClass('box-active')
}
})
body自适应浏览器高度
document.getElementsByTagName('body')[0].style.height = window.innerHeight+'px';
html{
height:100%
}
body{
height:100%;
}
为body设置高度,只是IE6下有作用。而代码中除了给body应用以外,还给html对象也应用了相同的样式。这样做的好处是使IE与Firefox都能够实现高度自适应。另外,FirefoxFirefox中的HTML标签不是100%高度,因此给两个标签都定义为height:100%;以保证两个浏览器下均能够正常显示。
分栏高度相等
不管中间内容怎么撑高,两边侧栏都会跟着等高
<style>
body{margin: 0;padding: 0;}
.wrap{
overflow: hidden; ← 必须的,否则会显示溢出的内容!
}
.left{
width: 200px;
background-color: #C5C5C5;
float: left;
margin-bottom: -3000px;
padding-bottom: 3000px;
}
.right{
width: 300px;
background-color: yellow;
float: right;
margin-bottom: -3000px; ← 使父级宽度不被撑开成3000px
padding-bottom: 3000px;
}
.main{
height: 500px;
background-color: lightpink;
margin: 0 310px 0 210px ;
}
</style>
画椭圆
<div class="ellipse"></div>
<style>
.ellipse {
width: 400px;
height: 200px;
border-radius: 50%;
background-color: #000;
}
</style>
<svg width="800" height="400">
<ellipse rx="200" ry="100" cx="400" cy="200"></ellipse>
</svg>
<svg width="800" height="400" id="J_SvgWrap"></svg>
<script>
var svg = document.getElementById('J_SvgWrap');
var ell = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
ell.setAttribute("cx", 400);
ell.setAttribute("cy", 200);
ell.setAttribute("rx", 200)
ell.setAttribute("ry", 100)
svg.appendChild(ell);
</script>
<canvas width="800" height="400" id="J_MyCanvas"></canvas>
<script>
var cvs = document.getElementById('J_MyCanvas');
var ctx = cvs.getContext('2d');
ctx.scale(1, 0.5);
ctx.arc(400, 200, 200, 0, Math.PI * 2);
ctx.fill();
</script>
CSS3新属性
1. 边框 border-radius box-shadow border-image
2. 背景 background-size background-origin
3. 文本效果 text-shadow word-wrap
4. 字体:CSS3 @font-face 规则可以自定义字体。
5. 2D 转换(transform)
6. 3D 转换
7. transition:过渡效果,使页面变化更平滑
8. animation:动画
HTML
Xhtml和html的区别
XHTML 元素必须被正确地嵌套。
XHTML 元素必须被关闭。
标签名必须用小写字母。
XHTML 文档必须拥有根元素。
React
写一下向子组件传状态
父:
this.state = {name:'xx'}
<Person data = {this.state.name}/>
子:
<div data = {this.porps.name}/>
Vue
框架对比
参考资料:Vue.js与React的全面对比
| 对比点 | 框架 | 区别 |
|---|---|---|
| 数据流 | ||
| - | Angular | 双向绑定。界面操作实时反映到数据,数据变更实时展现到页面 |
| - | Vue | 默认单向绑定,通过依赖追踪支持双向绑定:通过 Object.defineProperty 把data对象的属性全部转为 getter/setter |
| - | React | 函数式编程,单向数据流:在View层直接写JS代码Model层中的数据拿过来渲染 |
| 视图渲染 | ||
| - | Angular | 在DOM加载后遍历生成NG指令 |
| - | Vue | 使用真实DOM作为模板,数据绑定到真实节点,改变多少更新多少 |
| - | React | 渲染虚拟DOM,再给真实DOM打补丁。超大量数据的首屏渲染有优势 |
| 事件绑定 | ||
| - | html | onclick =" listen() "; |
| - | Vue | @click = " listen($event,参数) "; |
| - | React | onClick={this.deleteRow.bind(this, id) 或 onClick={(e) => this.deleteRow(id, e) |
| 数据获取 | ||
| - | 原生js | var xhr = creatXHR(); |
| - | Vue | axios.get(url).then(function(response){...}).catch(function(error){...}); |
| - | React | axios/reqwest |
| 路由 | ||
| - | Vue | <router-link to="/foo">Go to Foo</router-link> |
| - | React | <Route path="/repos" component={Repos}/> |
| 开发模式 | ||
| - | Vue | Vue是MVVM模式的一种方式实现 |
| - | React | React本身,是严格的view层,MVC模式 |
待补充……!!!
H5
H5是一个解决方案,是一系列技术的集合
H5主要技术
1 页面预加载:使用createJS中的preloadJS
2 音乐加载播放:使用createJS中的soundJS
3 可滑动页面:swiper.js插件,touch系列事件
4 可涂抹擦除:使用canvas叠加层
5 动态文字和图片:css3动画和Js动画
6 可填表报名
7 分享自定义文案和图片:使用微信jssdk
8 包含:audio标签、canvas拖拽特性、本地存储、websocket通讯、盒模型、绝对定位
移动H5自适应布局
1 分辨率resolution适配:使用rem。针对不同屏幕宽度,调整高宽比、文字大小、元素间距
2 单位英寸像素数PPI适配:标题用rem(会发虚),段落用px,用media query或Js适配
3 设备像素比例DPR适配