api日常总结
异步加载JS和CSS
<script type="text/javascript">
(function () {
var s = document.createElement('script'); s.async = true;
s.type = 'text/javascript';
s.setAttribute('external_src', '//' + window.disqus_shortname + '.disqus.com/count.js');
(document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
}());
</script>
$(window).load(function() {
$('[external_src]').each(function() {
var external_src = $(this).attr("external_src");
$(this).attr("src", $(this).attr("external_src")).removeAttr("external_src");
});
});
微信分享图标:
<title>闪电借款 56秒极速到账</title>
<div id='wx_pic' style='margin:0 auto;display:none;'>
<img src="" />
</div>
在jquery中each循环中,要用return false代替break,return true代替continue。
$.each(data, function (n, value) {
if(value.status==1){
return false;//相当于break
}
else if(value.status==2){
return true;//相当于continue
}
})
JavaScript 深浅拷贝
JavaScript有五种基本数据类型(Undefined, null, Boolean, String, Number),还有一种复杂的数据类型,就是对象。
Undefined 其实是已声明但没有赋值的变量的输出结果,null其实就是一个不存在的对象的结果
对于简单的数据类型
它们值在占据了内存中固定大小的空间,并被保存在栈内存中。 当一个变量向另一个变量复制基本类型的值时,会创建这个值的副本,还有就是不能给基本类型的值添加属性。1234var a = 1;var b = a;a.attr = 'long';console.log(a.attr) //undefined
上边代码中a就是简单数据类型(number),b就是a的副本,它们两者都占有不同位置,但相等的内存空间。
对于复杂的数据类型
复杂的数据类型即引用类型,它的值是对象,保存在堆内存中,包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针。从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象。12345678var obj = { name: 'long', age: 0};var obj2 = obj;obj2.c = 5;console.log(obj); //Oject {name: 'long', age: 0, c: 5}console.log(obj2); //Oject {name: 'long', age: 0, c: 5}
我们可以看到obj赋值给obj2后,当我们更改其中一个对象的属性值,两个对象都发生了改变,究其原因是因为obj和obj2这两个变量都指向同一个指针,赋值是复制了指针,所以当我们改变其中一个值,就会影响另一个变量的值。
浅拷贝
其实上边的代码就是浅拷贝,有时候我们只是想备份数组,但是只是简单让它赋给一个变量,改变其中一个,另外一个就紧跟着改变,但很多时候这不是我们想要的。
12345678var obj = { name:'wsscat', age:0 } var obj2 = obj; obj2['c'] = 5; console.log(obj);//Object {name: "wsscat", age: 0, c: 5} console.log(obj2);////Object {name: "wsscat", age: 0, c: 5}
深拷贝数组
对于数组我们可以使用slice()和concat()方法来解决上面的问题。
slice
12345var arr = ['ge', 'yu', 'long'];var arrCopy = arr.slice(0);arrCopy[0] = 'gai';console.log(arr) // ['ge', 'yu', 'long']console.log(arrCopy) // ['gai', 'yu', 'long']
concat
1234var arr = ['ge', 'yu', 'long'];var arrCopy = arr.concat();arrCopy[0] = 'gai';//console跟上边一样
对象
对象我们可以定义一个新的对象并遍历新的属性去实现深拷贝
原始方法:
12345678910var obj = { name: 'geyulong', age: 0};var obj2 = new Object();obj2.name = obj.name;obj2.age = obj.age;obj.name = "gaiyulong";console.log(obj); // Object {name: 'geyulong', age: 0}console.log(obj2); // Object {name: 'gaiyulong', age: 0}
当然我们是要封装一个方法来实现深拷贝。
123456789101112var deepCopy = function(source) { var result = {}; for(var key in source) { if(typeof source[key] === 'object') { result[key] = deepCopy(source[key]) } else { result[key] = source[key] } } return result;}var obj2 = deepCopy(obj);
Meta基础知识:
H5页面窗口自动调整到设备宽度,并禁止用户缩放页面
//一、HTML页面结构
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
// width 设置viewport宽度,为一个正整数,或字符串‘device-width’
// height 设置viewport高度,一般设置了宽度,会自动解析出高度,可以不用设置
// initial-scale 默认缩放比例,为一个数字,可以带小数
// minimum-scale 允许用户最小缩放比例,为一个数字,可以带小数
// maximum-scale 允许用户最大缩放比例,为一个数字,可以带小数
// user-scalable 是否允许手动缩放
//二、JS动态判断
var phoneWidth = parseInt(window.screen.width);
var phoneScale = phoneWidth/640;
var ua = navigator.userAgent;
if (/Android (\d+\.\d+)/.test(ua)){
var version = parseFloat(RegExp.$1);
if(version>2.3){
document.write('<meta name="viewport" content="width=640, minimum-scale = '+phoneScale+', maximum-scale = '+phoneScale+', target-densitydpi=device-dpi">');
}else{
document.write('<meta name="viewport" content="width=640, target-densitydpi=device-dpi">');
}
} else {
document.write('<meta name="viewport" content="width=640, user-scalable=no, target-densitydpi=device-dpi">');
}
空白页基本meta标签
<!-- 设置缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui" />
<!-- 可隐藏地址栏,仅针对IOS的Safari(注:IOS7.0版本以后,safari上已看不到效果) -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<!-- 仅针对IOS的Safari顶端状态条的样式(可选default/black/black-translucent ) -->
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<!-- IOS中禁用将数字识别为电话号码/忽略Android平台中对邮箱地址的识别 -->
<meta name="format-detection"content="telephone=no, email=no" />
其他meta标签
<!-- 启用360浏览器的极速模式(webkit) -->
<meta name="renderer" content="webkit">
<!-- 避免IE使用兼容模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓 -->
<meta name="HandheldFriendly" content="true">
<!-- 微软的老式浏览器 -->
<meta name="MobileOptimized" content="320">
<!-- uc强制竖屏 -->
<meta name="screen-orientation" content="portrait">
<!-- QQ强制竖屏 -->
<meta name="x5-orientation" content="portrait">
<!-- UC强制全屏 -->
<meta name="full-screen" content="yes">
<!-- QQ强制全屏 -->
<meta name="x5-fullscreen" content="true">
<!-- UC应用模式 -->
<meta name="browsermode" content="application">
<!-- QQ应用模式 -->
<meta name="x5-page-mode" content="app">
<!-- windows phone 点击无高光 -->
<meta name="msapplication-tap-highlight" content="no">
常见问题:
移动端如何定义字体font-family
@ --------------------------------------中文字体的英文名称
@ 宋体 SimSun
@ 黑体 SimHei
@ 微信雅黑 Microsoft Yahei
@ 微软正黑体 Microsoft JhengHei
@ 新宋体 NSimSun
@ 新细明体 MingLiU
@ 细明体 MingLiU
@ 标楷体 DFKai-SB
@ 仿宋 FangSong
@ 楷体 KaiTi
@ 仿宋_GB2312 FangSong_GB2312
@ 楷体_GB2312 KaiTi_GB2312
@
@ 说明:中文字体多数使用宋体、雅黑,英文用Helvetica
body { font-family: Microsoft Yahei,SimSun,Helvetica; }
打电话发短信写邮件怎么实现
// 一、打电话
<a href="tel:0755-10086">打电话给:0755-10086</a>
// 二、发短信,winphone系统无效
<a href="sms:10086">发短信给: 10086</a>
// 三、写邮件
//注:在添加这些功能时,第一个功能以"?"开头,后面的以"&"开头
//1.普通邮件
<a href="mailto:863139978@qq.com">点击我发邮件</a>
//2.收件地址后添加?cc=开头,可添加抄送地址(Android存在兼容问题)
<a href="mailto:863139978@qq.com?cc=zhangqian0406@yeah.net">点击我发邮件</a>
//3.跟着抄送地址后,写上&bcc=,可添加密件抄送地址(Android存在兼容问题)
<a href="mailto:863139978@qq.com?cc=zhangqian0406@yeah.net&bcc=384900096@qq.com">点击我发邮件</a>
//4.包含多个收件人、抄送、密件抄送人,用分号(;)隔开多个邮件人的地址
<a href="mailto:863139978@qq.com;384900096@qq.com">点击我发邮件</a>
//5.包含主题,用?subject=
<a href="mailto:863139978@qq.com?subject=邮件主题">点击我发邮件</a>
//6.包含内容,用?body=;如内容包含文本,使用%0A给文本换行
<a href="mailto:863139978@qq.com?body=邮件主题内容%0A腾讯诚信%0A期待您的到来">点击我发邮件</a>
//7.内容包含链接,含http(s)://等的文本自动转化为链接
<a href="mailto:863139978@qq.com?body=http://www.baidu.com">点击我发邮件</a>
//8.内容包含图片(PC不支持)
<a href="mailto:863139978@qq.com?body=<img src="" />">点击我发邮件</a>
//9.完整示例
<a href="mailto:863139978@qq.com;384900096@qq.com?cc=zhangqian0406@yeah.net&bcc=993233461@qq.com&subject=[邮件主题]&body=腾讯诚邀您参与%0A%0Ahttp://www.baidu.com%0A%0A<img src="" />">点击我发邮件</a>
移动端touch事件(区分webkit和winphone)
/* 当用户手指放在移动设备在屏幕上滑动会触发的touch事件 */
// 以下支持webkit
touchstart——当手指触碰屏幕时候发生。不管当前有多少只手指
touchmove——当手指在屏幕上滑动时连续触发。通常我们再滑屏页面,会调用event的preventDefault()可以阻止默认情况的发生:阻止页面滚动
touchend——当手指离开屏幕时触发
touchcancel——系统停止跟踪触摸时候会触发。例如在触摸过程中突然页面alert()一个提示框,此时会触发该事件,这个事件比较少用
//TouchEvent说明:
touches:屏幕上所有手指的信息
targetTouches:手指在目标区域的手指信息
changedTouches:最近一次触发该事件的手指信息
touchend时,touches与targetTouches信息会被删除,changedTouches保存的最后一次的信息,最好用于计算手指信息
//参数信息(changedTouches[0])
clientX、clientY在显示区的坐标
target:当前元素
//事件响应顺序
ontouchstart > ontouchmove > ontouchend > onclick
// 以下支持winphone 8
MSPointerDown——当手指触碰屏幕时候发生。不管当前有多少只手指
MSPointerMove——当手指在屏幕上滑动时连续触发。通常我们再滑屏页面,会调用css的html{-ms-touch-action: none;}可以阻止默认情况的发生:阻止页面滚动
MSPointerUp——当手指离开屏幕时触发
移动端click屏幕产生200-300ms的延时响应
说明:移动设备上的web网页是有300ms延迟的,玩玩会造成按钮点击延迟甚至是点击失效。
以下是历史原因,来源一个公司内一个同事的分享:
2007年苹果发布首款iphone上IOS系统搭载的safari为了将适用于PC端上大屏幕的网页能比较好的展示在手机端上,使用了双击缩放(double tap to zoom)的方案,比如你在手机上用浏览器打开一个PC上的网页,你可能在看到页面内容虽然可以撑满整个屏幕,但是字体、图片都很小看不清,此时可以快速双击屏幕上的某一部分,你就能看清该部分放大后的内容,再次双击后能回到原始状态。
双击缩放是指用手指在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。
原因就出在浏览器需要如何判断快速点击上,当用户在屏幕上单击某一个元素时候,例如跳转链接<a href="#"></a>,此处浏览器会先捕获该次单击,但浏览器不能决定用户是单纯要点击链接还是要双击该部分区域进行缩放操作,所以,捕获第一次单击后,浏览器会先Hold一段时间t,如果在t时间区间里用户未进行下一次点击,则浏览器会做单击跳转链接的处理,如果t时间里用户进行了第二次单击操作,则浏览器会禁止跳转,转而进行对该部分区域页面的缩放操作。那么这个时间区间t有多少呢?在IOS safari下,大概为300毫秒。这就是延迟的由来。造成的后果用户纯粹单击页面,页面需要过一段时间才响应,给用户慢体验感觉,对于web开发者来说是,页面js捕获click事件的回调函数处理,需要300ms后才生效,也就间接导致影响其他业务逻辑的处理。
//解决方案:
fastclick可以解决在手机上点击事件的300ms延迟
zepto的touch模块,tap事件也是为了解决在click的延迟问题
Rentina显示屏原理及设计方案
说明:retina屏是一种具备超高像素密度的液晶屏,同样大小的屏幕上显示的像素点由1个变为多个,如在同样带下的屏幕上,苹果设备的retina显示屏中,像素点1个变为4个。
在高清显示屏中的位图被放大,图片会变得模糊,因此移动端的视觉稿通常会设计为传统PC的2倍。
那么,前端的应对方案是:设计稿切出来的图片长宽保证为偶数,并使用backgroud-size把图片缩小为原来的1/2
//例如图片宽高为:200px*200px,那么写法如下
.css{width:100px;height:100px;background-size:100px 100px;}
//其它元素的取值为原来的1/2,例如视觉稿40px的字体,使用样式的写法为20px
.css{font-size:20px}
//image-set设计Rentina背景图
image-set,webkit私有属性,也是CSS4的属性,为解决Rentina屏幕下的图像而生。
.css {
background: url(images/bg.jpg) no-repeat center;
background: -webkit-image-set(
url(images/bg.jpg) 1x, //支持image-set普通屏
url(images/bg-2x.jpg) 2x); //支持image-set的Rentinan
}
点击元素产生背景或边框怎么去掉
/ios用户点击一个链接,会出现一个半透明灰色遮罩, 如果想要禁用,可设置-webkit-tap-highlight-color的alpha值为0去除灰色半透明遮罩;
//android用户点击一个链接,会出现一个边框或者半透明灰色遮罩, 不同生产商定义出来额效果不一样,可设置-webkit-tap-highlight-color的alpha值为0去除部分机器自带的效果;
//winphone系统,点击标签产生的灰色半透明背景,能通过设置<meta name="msapplication-tap-highlight" content="no">去掉;
//特殊说明:有些机型去除不了,如小米2。对于按钮类还有个办法,不使用a或者input标签,直接用div标签
a,button,input,textarea {
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-user-modify:read-write-plaintext-only; //-webkit-user-modify有个副作用,就是输入法不再能够输入多个字符
}
// 也可以
* { -webkit-tap-highlight-color: rgba(0,0,0,0); }
//winphone下
<meta name="msapplication-tap-highlight" content="no">
美化表单元素
//一、使用appearance改变webkit浏览器的默认外观
input,select { -webkit-appearance:none; appearance: none; }
//二、winphone下,使用伪元素改变表单元素默认外观
//1.禁用select默认箭头,::-ms-expand修改表单控件下拉箭头,设置隐藏并使用背景图片来修饰
select::-ms-expand { display:none; }
//2.禁用radio和checkbox默认样式,::-ms-check修改表单复选框或单选框默认图标,设置隐藏并使用背景图片来修饰
input[type=radio]::-ms-check,
input[type=checkbox]::-ms-check { display:none; }
//3.禁用pc端表单输入框默认清除按钮,::-ms-clear修改清除按钮,设置隐藏并使用背景图片来修饰
input[type=text]::-ms-clear,
input[type=tel]::-ms-clear,
input[type=number]::-ms-clear { display:none; }
移动端字体单位font-size选择px还是rem
// 如需适配多种移动设备,建议使用rem。以下为参考值:
html { font-size: 62.5%; } //10*16 = 62.5%
//设置12px字体 这里注意在rem前要加上对应的px值,解决不支持rem的浏览器的兼容问题,做到优雅降级
body { font-size:12px; font-size:1.2rem; }
超实用的CSS样式
//去掉webkit的滚动条——display: none;
//其他参数
::-webkit-scrollba //滚动条整体部分
::-webkit-scrollbar-thumb //滚动条内的小方块
::-webkit-scrollbar-track //滚动条轨道
::-webkit-scrollbar-button //滚动条轨道两端按钮
::-webkit-scrollbar-track-piece //滚动条中间部分,内置轨道
::-webkit-scrollbar-corner //边角,两个滚动条交汇处
::-webkit-resizer //两个滚动条的交汇处上用于通过拖动调整元素大小的小控件
// 禁止长按链接与图片弹出菜单
a,img { -webkit-touch-callout: none }
// 禁止ios和android用户选中文字
html,body {-webkit-user-select:none; user-select: none; }
// 改变输入框placeholder的颜色值
::-webkit-input-placeholder { /* WebKit browsers */
color: #999; }
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
color: #999; }
::-moz-placeholder { /* Mozilla Firefox 19+ */
color: #999; }
:-ms-input-placeholder { /* Internet Explorer 10+ */
color: #999; }
input:focus::-webkit-input-placeholder{ color:#999; }
// android上去掉语音输入按钮
input::-webkit-input-speech-button {display: none}
// 阻止windows Phone的默认触摸事件
/*说明:winphone下默认触摸事件事件使用e.preventDefault是无效的,可通过样式来禁用,如:*/
html { -ms-touch-action:none; } //禁止winphone默认触摸事件
取消input在ios下,输入的时候英文首字母的默认大写
<input autocapitalize="off" autocorrect="off" />
手机拍照和上传图片
//IOS有拍照、录像、选取本地图片功能,部分Android只有选择本地图片功能。Winphone不支持
<input type="file" accept="images/*" />
<input type="file" accept="video/*" />
屏幕旋转的事件和样式
//JS处理
function orientInit(){
var orientChk = document.documentElement.clientWidth > document.documentElement.clientHeight?'landscape':'portrait';
if(orientChk =='lapdscape'){
//这里是横屏下需要执行的事件
}else{
//这里是竖屏下需要执行的事件
}
}
orientInit();
window.addEventListener('onorientationchange' in window?'orientationchange':'resize', function(){
setTimeout(orientInit, 100);
},false)
//CSS处理
//竖屏时样式
@media all and (orientation:portrait){ }
//横屏时样式
@media all and (orientation:landscape){ }
audio元素和video元素在ios和andriod中无法自动播放
//音频,写法一
<audio src="music/bg.mp3" autoplay loop controls>你的浏览器还不支持哦</audio>
//音频,写法二
<audio controls="controls">
<source src="music/bg.ogg" type="audio/ogg"></source>
<source src="music/bg.mp3" type="audio/mpeg"></source>
优先播放音乐bg.ogg,不支持在播放bg.mp3
</audio>
//JS绑定自动播放(操作window时,播放音乐)
$(window).one('touchstart', function(){
music.play();
})
//微信下兼容处理
document.addEventListener("WeixinJSBridgeReady", function () {
music.play();
}, false);
//小结
//1.audio元素的autoplay属性在IOS及Android上无法使用,在PC端正常
//2.audio元素没有设置controls时,在IOS及Android会占据空间大小,而在PC端Chrome是不会占据任何空间
重力感应事件
// 运用HTML5的deviceMotion,调用重力感应事件
if(window.DeviceMotionEvent){
document.addEventListener('devicemotion', deviceMotionHandler, false)
}
var speed = 30;
var x = y = z = lastX = lastY = lastZ = 0;
function deviceMotionHandler(eventData){
var acceleration = event.accelerationIncludingGravity;
x = acceleration.x;
y = acceleration.y;
z = acceleration.z;
if(Math.abs(x-lastX)>speed || Math.abs(y-lastY)>speed || Math.abs(z-lastZ)>speed ){
//这里是摇动后要执行的方法
yaoAfter();
}
lastX = x;
lastY = y;
lastZ = z;
}
function yaoAfter(){
//do something
}
微信浏览器用户调整字体大小后页面矬了,怎么阻止用户调整
//以下代码可使Android机页面不再受用户字体缩放强制改变大小,但是会有1S左右延时,期间可以考虑loading来处理
if (typeof(WeixinJSBridge) == "undefined") {
document.addEventListener("WeixinJSBridgeReady", function (e) {
setTimeout(function(){
WeixinJSBridge.invoke('setFontSizeCallback', { 'fontSize':0}, function(res){
alert(JSON.stringify(res));
})
}, 0)
});
}else{
setTimeout(function(){
WeixinJSBridge.invoke('setFontSizeCallback', { 'fontSize':0}, function(res){
alert(JSON.stringify(res));
})
}, 0)
}
//IOS下可使用 -webkit-text-size-adjust禁止用户调整字体大小
body { -webkit-text-size-adjust:100%!important; }
//最好的解决方案:最好使用rem或百分比布局
定位的坑
//fixed定位
//1.ios下fixed元素容易定位出错,软键盘弹出时,影响fixed元素定位
//2.android下fixed表现要比iOS更好,软键盘弹出时,不会影响fixed元素定位
//3.ios4下不支持position:fixed
//解决方案:使用[Iscroll](http://cubiq.org/iscroll-5),如:
<div id="wrapper">
<ul>
<li></li>
.....
</ul>
</div>
<script src="iscroll.js"></script>
<script>
var myscroll;
function loaded(){
myscroll=new iScroll("wrapper");
}
window.addEventListener("DOMContentLoaded",loaded,false);
</script>
//position定位
//Android下弹出软键盘弹出时,影响absolute元素定位
//解决方案:
var ua = navigator.userAgent.indexOf('Android');
if(ua>-1){
$('.ipt').on('focus', function(){
$('.css').css({'visibility':'hidden'})
}).on('blur', function(){
$('.css').css({'visibility':'visible'})
})
}
播放视频不全屏
<!--
1.ios7+支持自动播放
2.支持Airplay的设备(如:音箱、Apple TV)播放
x-webkit-airplay="true"
3.播放视频不全屏
webkit-playsinline="true"
-->
<video x-webkit-airplay="true" webkit-playsinline="true" preload="auto" autoplay src="http://"></video>
JS判断设备
function deviceType(){
var ua = navigator.userAgent;
var agent = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
for(var i=0; i<len,len = agent.length; i++){
if(ua.indexOf(agent[i])>0){
break;
}
}
}
deviceType();
window.addEventListener('resize', function(){
deviceType();
})
JS判断微信浏览器
function isWeixin(){
var ua = navigator.userAgent.toLowerCase();
if(ua.match(/MicroMessenger/i)=='micromessenger'){
return true;
}else{
return false;
}
}
android 2.3 bug
//1.@-webkit-keyframes 需要以0%开始100%结束,0%的百分号不能去掉
//2.after和before伪类无法使用动画animation
//3.border-radius不支持%单位,如要兼容,可以给radius设置一下较大的值
//4.translate百分比的写法和scale在一起会导致失效,例如:
-webkit-transform: translate(-50%,-50%) scale(-0.5, 1)
android 4.x bug
//1.三星 Galaxy S4中自带浏览器不支持border-radius缩写
//2.同时设置border-radius和背景色的时候,背景色会溢出到圆角以外部分
//3.部分手机(如三星),a链接支持鼠标:visited事件,也就是说链接访问后文字变为紫色
//4.android无法同时播放多音频audio
消除transition闪屏
.css {
-webkit-transform-style: preserve-3d;
-webkit-backface-visibility: hidden;
-webkit-perspective: 1000;
}
开启硬件加速
//目前,像Chrome/Filefox/Safari/IE9+以及最新版本Opera都支持硬件加速,当检测到某个DOM元素应用了某些CSS规则时就会自动开启,从而解决页面闪白,保证动画流畅。
.css {
-webkit-transform: translate3d(0,0,0);
-moz-transform: translate3d(0,0,0);
-ms-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
渲染优化
//1.禁止使用iframe(阻塞父文档onload事件)
//2.禁止使用gif图片实现loading效果(降低CPU消耗,提升渲染性能)
//使用CSS3代码代替JS动画;
//开启GPU加速;
//使用base64位编码图片(不小图而言,大图不建议使用)
// 对于一些小图标,可以使用base64位编码,以减少网络请求。但不建议大图使用,比较耗费CPU。小图标优势在于:
//1.减少HTTP请求;
//2.避免文件跨域;
//3.修改及时生效;
JavaScript中比较运算的详细总结
目录
前言
在javascript开发中,比较操作是十分常见的。由于显式/隐式强制类型转换机制的存在,我们在使用比较运算时显得过于随意,也许表面上看并没有什么错误(比如在if()语句中判断两值相等时顺手就写成 == ),但是这可能会埋下很多不易发现的隐患。对于比较操作(相等关系和不等关系),在javascript中其实是有一套完善的机制的。本文依据ES5规范《ECMAScript Language Specification ECMA-262
5.1 Edition / June 2011》的11.8节和11.9节:
对javascript中比较操作相关内容进行系统总结。
相等比较
严格相等
严格相等指“===”,它不允许比较双方进行强制类型转换。因此,问题考虑变得简单,对于x === y
,javascript引擎在进行判断时所遵循的算法如下:
- 如果x和y的数据类型不同,返回false。
- 如果x是undefined,返回true。
- 如果x是null,返回true。
- 如果x是number类型:
4.1 如果x是NaN,返回false。
4.2 如果y是NaN,返回false。
4.3 如果x和y的值相等,返回true。
4.4 如果x是+0,y是-0,返回true。
4.5 如果x是-0,y是+0,返回true。
4.6 否则,返回false。 - 如果x是string类型,如果x和y是长度相等且对应位置上字符相同的序列,返回true;否则返回false。
- 如果x是Boolean类型,如果x和y均为true或者x和y均为false,返回true;否则,返回false。
- 如果x是对象(普通对象,函数,数组等),那么如果x和y指向同一个对象(是内存中同一个对象的引用),返回true;否则,返回false。
宽松相等
宽松相等指“==”,它会对比较双方进行隐式强制类型转换。下面先根据ES5规范进行系统说明:
- 如果x和y的数据类型相同:
1.1 如果x的类型是undefined,返回true。
1.2 如果x的类型是null,返回true。
1.3 如果x的类型是number:
1.3.1 如果x是NaN,返回false。
1.3.2 如果y是NaN,返回false。
1.3.3 如果x和y的值相同,返回true。
1.3.4 如果x是+0,y是-0,返回true。
1.3.5 如果x是-0,y是+0,返回true。
1.3.6 否则,返回false。
1.4 如果x是string类型,如果x和y是长度相等且对应位置上字符相同的序列,返回true;否则返回false。
1.5 如果x是Boolean类型,如果x和y均为true或者x和y均为false,返回true;否则,返回false。
1.6 如果x是对象(普通对象,函数,数组等),那么如果x和y指向同一个对象(是内存中同一个对象的引用),返回true;否则,返回false。 - 如果x是null,y是undefined,返回true。
- 如果x是undefined,y是null,返回true。
- 如果x是number类型,y是string类型,则对y进行类型转换,转换为number类型,返回 x == ToNumber(y) 的结果(参见上述1.3)。
- 如果x是string类型,y是number类型,则对x进行类型转换,转换为number类型,返回 ToNumber(x) == y 的结果(参见上述1.3)。
- 如果x是Boolean类型,则对x进行类型转换,转换为number类型,返回 ToNumber(x) == y 的结果(此时y的类型还是不确定的,应将其转换为number类型后进行比较)。
- 如果y是Boolean类型,则对y进行类型转换,转换为number类型,返回 x == ToNumber(y) 的结果(此时x的类型还是不确定的,应将其转换为number类型后进行比较)。
- 如果x是string类型或者number类型,y是一个对象(普通对象,函数,数组等),则对y进行类型转换—使用内置的[[ToPrimitive]]方法转换(该方法简单来说,就是先调用该对象上的
valueOf()
方法,如果有该方法且返回基本类型值,就使用该值进行强制类型转换;如果不存在,就调用该对象上的toString()
方法,如果有该方法,就使用其返回值来进行强制类型转换;如果这两个方法都不存在,就产生TypeError错误。),返回 x == ToPrimitive(y) 的结果。 - 如果x是一个对象(普通对象,函数,数组等),y是string类型或者number类型,则对x进行类型转换—使用内置的[[ToPrimitive]],返回 ToPrimitive(x) == y 的结果。
- 否则(非上述所有情况),返回false。
注:针对以上10条有几点注意事项:
1.对a,b强制按字符串string类型进行比较,可采用如下方法:"" + a == "" + b
2.对a,b强制按数字number类型进行比较,可采用如下方法:
+a == +b
3.对a,b强制按布尔值boolean类型进行比较,可采用如下方法:
!!a == !!b
4.相等比较操作有以下恒等性:
- A != B 等价于 !(A == B)
- A == B 等价于 B == A (除非A B有顺序上的互相计算关系)
5.相等操作并不是总具有传递性。
new String(“a”) == “a” 和 “a” == new String(“a”) 结果都返回true;
new String(“a”) == new String(“a”) 却返回false。
因为new String(“a”)是一个对象,按照上述规则会被转换为”a”,因此相等;而new String(“a”) == new String(“a”),==左右两边是两个不同的对象,在内存中位于不同地址,因此结果返回false。
由于强制类型转换的存在,宽松相等的情况变得复杂,比较容易出现问题的有以下几种:
更改内置原生原型后的相等比较
12345Number.prototype.valueOf = function(){ return 3;}new Number(2) == 3; // true
这只是为了说明这种情况的存在,应该不会有人这么去改原型上的方法。
假值的相等比较
这部分应该是比较复杂的:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152"0" == null; //false"0" == undefined; //false"0" == false; //true"0" == NaN; //false"0" == 0; //true"0" == ""; //falsefalse == null; //falsefalse == undefined; //falsefalse == NaN; //falsefalse == 0; //truefalse == ""; //truefalse == []; //truefalse == {}; //false"" == null; //false"" == undefined; //false"" == NaN; //false"" == 0; //true"" == []; //true"" == {}; //false0 == null; //false0 == undefined; //false0 == NaN; //false0 == []; //true0 == {}; //false
解释如下:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051"0" == null; //false:null转换为"null""0" == undefined; //false:undefined转换为"undefined""0" == false; //true:false转换为0;"0"转换为0"0" == NaN; //false:NaN转换为"NaN""0" == 0; //true:"0"转换为0"0" == ""; //false:都是字符串,值不同false == null; //false:false转换为0;null转换为"null",进而转换为数字,得到NaNfalse == undefined; //false:false转换为0;undefined转换为"undefined",进而转换为数字,得到NaNfalse == NaN; //false:false转换为0;与NaN不同false == 0; //true:false转换为0false == ""; //true:false转换为0;""转换为0false == []; //true:false转换为0;[]转换为0false == {}; //false:false转换为0;{}转换为NaN"" == null; //false:""转换为0;null转换为"null",进而转换为数字,得到NaN"" == undefined; //false:""转换为0;undefined转换为"undefined",进而转换为数字,得到NaN"" == NaN; //false:""转换为0;与NaN不同"" == 0; //true:""转换为0"" == []; //true:""转换为0;[]转换为0"" == {}; //false:""转换为0;{}转换为NaN0 == null; //false:null转换为"null",进而转换为数字,得到NaN0 == undefined; //false:undefined转换为"undefined",进而转换为数字,得到NaN0 == NaN; //false:0与NaN不同0 == []; //true:[]转换为00 == {}; //false:{}转换为NaN
一些极端情况
a. [] == ![]; //true
[]被转换为0,![]被转换为false,进而被转换为0。
b. 2 == [2]; //true
[2]调用数组的valueOf()
方法,返回”2”,进而被转换为2。
c. “” == [null]; //true
[null]调用数组的valueOf()
方法,进而调用toString()
方法,返回””。
一些选用的原则
- 如果==两边有true或者false(指本身,不是经过类型转换以后的),绝对不使用==。
- 如果==两边有[],””,0,尽量不使用==。
- ==和===选取哪一个取决于是否允许比较双方进行强制类型转换。
- 不应该一味地使用===来避免考虑这些可能的问题,因为有时候隐式类型转换可以让代码更加简洁,只要用的对。‘
typeof x == "function"
、typeof x != "undefined"
这样的用法是完全正确且安全的,开发中也常用。
经典的相等比较关系图
GitHub上有一个经典的比较关系图,http://dorey.github.io/JavaScript-Equality-Table/,以表格的形式系统总结了宽松相等==,严格相等===,if()条件语句中使用不同数据类型对应的结果,很有意义:
不等比较
!= 和 !==
只要搞清楚上面详细描述的==和===,对应取反即可。
> < >= <=
这些比较的基础是 x < y:
x < y会返回true或者false或者undefined。如果返回undefined,说明x,y两者至少有一个是NaN。比较算法中需要使用一个布尔值的标记LeftFirst作为参数。这个参数的作用是控制可能具有副作用的操作作用于x和y的顺序。这个标志是有必要的,因为在ECMAScript指定了从左到右的运算顺序,LeftFirst的默认值是true,表示x的表达式是在y的表达式左边的。如果LeftFirst值为false,情况相反,表明关于y的操作必须先于x进行。据此,比较操作规则如下:
- 如果LeftFirst值为true:
1.1 ToPrimitive(x, hint Number)的结果记为px
1.2 ToPrimitive(y, hint Number)的结果记为py- 否则,运算顺序改为从右向左:
2.1 ToPrimitive(y, hint Number)的结果记为py
2.2 ToPrimitive(x, hint Number)的结果记为px- 经过如上转换,如果px和py至少有一个的类型不为string:
3.1 ToNumber(px)的结果记为nx
3.2 ToNumber(py)的结果记为ny
3.3 如果nx是NaN,返回undefined
3.4 如果ny是NaN,返回undefined
3.5 如果nx和ny的值相同,返回false
3.6 如果nx是+0,ny是-0,返回false
3.7 如果nx是-0,ny是+0,返回false
3.8 如果nx是+Infinity,返回false
3.9 如果ny是+Infinity,返回true
3.10 如果ny是-Infinity,返回false
3.11 如果nx是-Infinity,返回true
3.12 如果nx的值小于ny,返回true;否则,返回false- 如果px和py都是string:
4.1 如果py是px的前缀,返回false
4.2 如果px是py的前缀,返回true
4.3 设置变量k,k表示px和py对应位上出现不同值时,位置的索引。
4.4 记px的位置k上的字母对应的字符编码值为m
4.5 记py的位置k上的字母对应的字符编码值为n
4.6 如果m < n,返回true;否则返回false
因此,> < >= <=就有如下的规则:
RelationalExpression > ShiftExpression
- 记lref为RelationalExpression的运算结果值。
- 记lval为lref类型转换后获取的value值。
- 记rref为ShiftExpression的运算结果值。
- 记rval为rref类型转换后获取的value值。
- 设LeftFirst为false,将rval < lval按照上述规则进行运算,结果为r。
- 如果r是undefined返回false,否则返回r。
RelationalExpression < ShiftExpression
- 记lref为RelationalExpression的运算结果值。
- 记lval为lref类型转换后获取的value值。
- 记rref为ShiftExpression的运算结果值。
- 记rval为rref类型转换后获取的value值。
- 将lval < rval按照上述规则进行运算,结果为r。
- 如果r是undefined返回false,否则返回r。
RelationalExpression >= ShiftExpression
- 记lref为RelationalExpression的运算结果值。
- 记lval为lref类型转换后获取的value值。
- 记rref为ShiftExpression的运算结果值。
- 记rval为rref类型转换后获取的value值。
- 设LeftFirst为false,将rval < lval按照上述规则进行运算,结果为r。
- 如果r是true或者undefined返回false,否则返回true。
RelationalExpression <= ShiftExpression
- 记lref为RelationalExpression的运算结果值。
- 记lval为lref类型转换后获取的value值。
- 记rref为ShiftExpression的运算结果值。
- 记rval为rref类型转换后获取的value值。
- 将lval < rval按照上述规则进行运算,结果为r。
- 如果r是true或者undefined返回false,否则返回true。
文章主要介绍了数组Array.prototype方法的使用,需要的朋友可以参考下,如果你是大神,请直接无视。
- 在ES5中,一共有9个Array方法 http://kangax.github.io/compat-table/es5/
- Array.prototype.indexOf
- Array.prototype.lastIndexOf
- Array.prototype.every
- Array.prototype.some
- Array.prototype.forEach
- Array.prototype.map
- Array.prototype.filter
- Array.prototype.reduce
- Array.prototype.reduceRight
在ES6即将普及的时代,我相信这些方法对FE开发者是非常实用的必备技能,接下来我将通过实例帮大家替换for循环大法,更高效的来操作数组。
1、indexOf
indexOf()方法返回在该数组中第一个找到的元素位置,如果它不存在则返回-1。
使用for:
- var arr = ['apple','orange','pear'],
- found = false;
- for(var i= 0, l = arr.length; i< l; i++){
- if(arr[i] === 'orange'){
- found = true;
- }
- }
- console.log("found:",found);
使用indexOf:
- var arr = ['apple','orange','pear'];
- console.log("found:", arr.indexOf("orange") != -1);
2、lastindexOf
lastIndexOf() 方法返回在该数组中最后一个找到的元素位置,和 indexof相反。
3、every()
evety()可是检测数组中的每一项是否符合条件
使用for:
- var ary = [12,23,-24,42,1];
- var result = function(){
- for (var i = 0; i < ary.length; i++) {
- if(ary[i] > 0){
- return false;
- }
- }
- return true; //需全部满足
- }
- console.log(result())
使用every:
- var ary = [12,23,-24,42,1];
- var result = ary.every(function(item, index){
- return item > 0
- })
- console.log(result)
4、some()
some()可以检测数组中是否有某一项符合条件
使用for:
- var ary = [12,23,-24,42,1];
- var result = function(){
- for (var i = 0; i < ary.length; i++) {
- if(ary[i] < 0){
- return false;
- }
- }
- return true; //只需满足一个
- }
- console.log(result())
使用some:
- var ary = [12,23,-24,42,1];
- var result = ary.some(function(item, index){
- return item < 0
- })
- console.log(result)
5、 forEach()
forEach为每个元素执行对应的方法
使用for:
- var arr = [1,2,3,4,5,6,7,8];
- for(var i= 0, l = arr.length; i< l; i++){
- console.log(arr[i]);
- }
使用forEach():
- var arr = [1,2,3,4,5,6,7,8];
- arr.forEach(function(item,index){
- console.log(item);
- });
forEach是用来替换for循环的
6、 map()
map()对数组的每个元素进行一定操作(映射)后,会返回一个新的数组,
使用for:
- var oldArr = [{first_name:"Colin",last_name:"Toh"},{first_name:"Addy",last_name:"Osmani"},{first_name:"Yehuda",last_name:"Katz"}];
- function getNewArr(){
- var newArr = [];
- for(var i= 0, l = oldArr.length; i< l; i++){
- var item = oldArr[i];
- item.full_name = [item.first_name,item.last_name].join(" ");
- newArr[i] = item;
- }
- return newArr;
- }
- console.log(getNewArr());
使用map:
- var oldArr = [{first_name:"Colin",last_name:"Toh"},{first_name:"Addy",last_name:"Osmani"},{first_name:"Yehuda",last_name:"Katz"}];
- function getNewArr(){
- return oldArr.map(function(item,index){
- item.full_name = [item.first_name,item.last_name].join(" ");
- return item;
- });
- }
- console.log(getNewArr());
map()是处理服务器返回数据时是一个非常实用的函数。
forEach 与map的区别:
语法:forEach和map都支持2个参数:一个是回调函数(item,index,list)和上下文;
forEach:用来遍历数组中的每一项;这个方法执行是没有返回值的,对原来数组也没有影响;数组中有几项,那么传递进去的匿名回调函数就需要执行几次;每一次执行匿名函数的时候,还给其传递了三个参数值:数组中的当前项item,当前项的索引index,原始数组list;理论上这个方法是没有返回值的,仅仅是遍历数组中的每一项,不对原来数组进行修改;但是我们可以自己通过数组的索引来修改原来的数组;
forEach方法中的this是ary,匿名回调函数中的this默认是window;
- var ary = [12,23,24,42,1];
- var res = ary.forEach(function (item,index,input) {
- input[index] = item*10;
- })
- console.log(res);//-->undefined;
- console.log(ary);//-->会对原来的数组产生改变;
map: 和forEach非常相似,都是用来遍历数组中的每一项值的,用来遍历数组中的每一项;
区别:map的回调函数中支持return返回值;return的是啥,相当于把数组中的这一项变为啥(并不影响原来的数组,只是相当于把原数组克隆一份,把克隆的这一份的数组中的对应项改变了);
不管是forEach还是map 都支持第二个参数值,第二个参数的意思是把匿名回调函数中的this进行修改。
- var ary = [12,23,24,42,1];
- var res = ary.map(function (item,index,input) {
- return item*10;
- })
- console.log(res);//-->[120,230,240,420,10];
- console.log(ary);//-->[12,23,24,42,1];
7、 filter
该filter()方法创建一个新的匹配过滤条件的数组。
使用for:
- var arr = [
- {"name":"apple", "count": 2},
- {"name":"orange", "count": 5},
- {"name":"pear", "count": 3},
- {"name":"orange", "count": 16},
- ];
- var newArr = [];
- for(var i= 0, l = arr.length; i< l; i++){
- if(arr[i].name === "orange" ){
- newArr.push(arr[i]);
- }
- }
- console.log("Filter results:",newArr);
使用 filter():
- var arr = [
- {"name":"apple", "count": 2},
- {"name":"orange", "count": 5},
- {"name":"pear", "count": 3},
- {"name":"orange", "count": 16},
- ];
- var newArr = arr.filter(function(item){
- return item.name === "orange";
- });
- console.log("Filter results:",newArr);
8、 reduce()
reduce()可以实现一个累加器的功能,将数组的每个值(从左到右)将其降低到一个值。 说实话刚开始理解这句话有点难度,它太抽象了。
场景: 统计一个数组中有多少个不重复的单词
使用for:
- var arr = ["apple","orange","apple","orange","pear","orange"];
- function getWordCnt(){
- var obj = {};
- for(var i= 0, l = arr.length; i< l; i++){
- var item = arr[i];
- obj[item] = (obj[item] +1 ) || 1;
- }
- return obj;
- }
- console.log(getWordCnt());
让我先解释一下我自己对reduce的理解。reduce(callback, initialValue)会传入两个变量。回调函数(callback)和初始值(initialValue)。假设函数它有个传入参数,prev和next,index和array。prev和next你是必须要了解的。一般来讲prev是从数组中第一个元素开始的,next是第二个元素。但是当你传入初始值(initialValue)后,第一个prev将是initivalValue,next将是数组中的第一个元素。
- /*
- * 二者的区别,在console中运行一下即可知晓
- */
- var arr = ["apple","orange"];
- function noPassValue(){
- return arr.reduce(function(prev,next){
- console.log("prev:",prev);
- console.log("next:",next);
- return prev + " " +next;
- });
- }
- function passValue(){
- return arr.reduce(function(prev,next){
- console.log("prev:",prev);
- console.log("next:",next);
- prev[next] = 1;
- return prev;
- },{});
- }
- console.log("No Additional parameter:",noPassValue());
- console.log("----------------");
- console.log("With {} as an additional parameter:",passValue());
9.reduceRight()
reduceRight的语法以及回调函数的规则和reduce方法是一样的,区别就是在与reduce是升序,即角标从0开始,而reduceRight是降序,即角标从arr.length-1开始。
方法可应用于字符串。
- /*
- * 使用此方法反转字符串中的字符
- */
- var word = "retupmoc";
- function AppendToArray(previousValue, currentValue) {
- return previousValue + currentValue;
- }
- var result = [].reduceRight.call(word, AppendToArray, "the ");
- console.log(result); // the computer
一个不会写文章的程序猿,希望对你有帮助~
api日常总结的更多相关文章
- api日常总结:前端常用js函数和CSS常用技巧
我的移动端media html{font-size:10px} @media screen and (min-width:321px) and (max-width:375px){html{font- ...
- 阿里云API网关(1)服务网关的产品概述
网关指南: https://help.aliyun.com/document_detail/29487.html?spm=5176.doc48835.6.550.23Oqbl 网关控制台: https ...
- 浅析阿里云API网关的产品架构和常见应用场景
自上世纪60年代计算机网络发展开始,API(Application Programming Interface )随之诞生,API即应用程序接口,是实现系统间衔接的桥梁.时至今日,API市场已经形成了 ...
- 日常英语---二、注册google的api的key
日常英语---二.注册google的api的key 一.总结 一句话总结:register google api key register_google_api_key 1.请通过电子邮件向我发送有关 ...
- 日常API之百度翻译
百度翻译是什么,可以吃吗?相信很多人都熟悉,它是我们生活中必不可少的一只东东. 但是,百度翻译开发平台只有每月只能翻译200万个字符,多出的要按照49.00/百万字符来算.对于我酱紫的乞丐程序员来说, ...
- 日常API之QQ登录
这次的QQ登录我研究了好久惹,今天终于可以和大家分享啦! 大家都知道,QQ登录有很多方法,例如使用账号密码登录,手机版企鹅扫码登录等等(这些方法只能验证QQ是否成功登录,并没有聊天等功能) 首先就来使 ...
- 日常API之C#百度人脸识别
最近看到一只我家徒儿发来的链接,原来是一堆百度AI的SDK,于是一时兴起就做了一只人脸识别,喵喵喵(●'◡'●) 一.准备工作 首先,当然是下载SDK啦:http://ai.baidu.com/sdk ...
- immutable日常操作之深入API
写在前面 本文只是个人在熟悉Immutable.js的一些个人笔记,因此我只根据我自己的情况来熟悉API,所以很多API并没有被列举到,比如常规的push/map/filter/reduce等等操作, ...
- 日常API之图灵聊天机器人
机器人是什么?可以吃吗? 嗯,他可以和你聊天,不能吃哦. 首先需要到www.tuling123.com注册一只KEY,你才能调用机器人API哦 一.布局 (控制台程序可以跳过这一步)本文以WPF为示例 ...
随机推荐
- javascript 练习题目答案1
以下是这个教程的答案 https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/00143 ...
- PHP共享内存详解
前言 在PHP中有这么一族函数,他们是对UNIX的V IPC函数族的包装. 它们很少被人们用到,但是它们却很强大.巧妙的运用它们,可以让你事倍功半. 它们包括: 信号量(Semaphores) 共享内 ...
- Lodash JavaScript 实用工具库
地址:https://www.lodashjs.com/ Lodash 是一个一致性.模块化.高性能的 JavaScript 实用工具库.
- 树形插件 --- zTree
地址:http://www.treejs.cn/v3/api.php
- python 全栈开发,Day141(flask之应用上下文,SQLAlchemy)
一.flask之应用上下文 由于时间关系,详细过程略... 草稿图 参考链接: http://www.cnblogs.com/zhaopanpan/p/9457343.html 总结: 上下文管理(应 ...
- 《剑指offer》-判断平衡二叉树
题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 考察平衡树的概念和递归的使用.平衡树是指,树中的每个节点的左右子树的高度差小于等于1. class Solution { public: bo ...
- A 暴力搜索 剪枝是关键
Description 盖伦是个小学一年级的学生,在一次数学课的时候,老师给他们出了一个难题:老师给了一个正整数 n,需要在不大于n的范围内选择三个正整数(可以是相同的),使它们三个的最小公倍数尽可能 ...
- hdu 1542
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1542 题意: 求所给矩形的覆盖面积 题解: 利用扫描线的思想,先将坐标离散化,之后以y轴分成多个矩形求解, ...
- [转]ubuntu安装skype
Skype 4.3 Released, How to Install it in Ubuntu 14.04/12.04 June 19, 2014 — 107 Comments Skype for ...
- openstack时间不同步问题
一.出现的问题 我们在安装openstack的时候如果没有设置计算节点和控制节点的的时间同步,当你虚拟机开机之后会存在控制节点和计算节点的时间 不一样,导致opstack无法登陆,报如下错误: 二.设 ...