Giter Club home page Giter Club logo

my-blog's People

Contributors

kangxueliang avatar

Stargazers

 avatar  avatar

Watchers

 avatar

my-blog's Issues

关于LazyMan

LazyMan是一道非常经典的面试题,题目如下:

实现一个 LazyMan,可以按照以下方式调用:

  • LazyMan("Hank")

    输出

    Hi! This is Hank!

  • LazyMan("Hank").sleep(10).eat("dinner")

    输出

    Hi! This is Hank!

    等待10秒..

    Wake up after 10
    Eat dinner

  • LazyMan("Hank").eat("dinner").eat("supper")

    输出

    Hi This is Hank!
    Eat dinner
    Eat supper

  • LazyMan("Hank").sleepFirst(5).eat("supper")

    等待5秒

    Wake up after 5
    Hi This is Hank!
    Eat supper

题目解析

  • 链式调用,返回调用对象即可
  • 存在一个任务队列,使lazyMan的任务依次执行,并在结束后开始执行下一次任务
  • sleepFirst在任务队列的首位

实现代码

function LazyMan(name) {
  return new _LazyMan(name)
}

function _LazyMan(name) {
  this.tasks = []
  var fn = (n => () => {
    console.log('hi ' + n )
    this.next()
  })(name)
  this.tasks.push(fn)
  setTimeout(() => {
    this.next()
  }, 0);
}

_LazyMan.prototype.next = function() {
  let fn = this.tasks.shift()
  fn && fn()
}

_LazyMan.prototype.eat = function(name) {
  let fn = (n => () => {
    console.log('eat ' + n)
    this.next()
  })(name)
  this.tasks.push(fn)
  return this
}
_LazyMan.prototype.sleep = function (delay) {
  let fn = (d => () => {
    setTimeout(() => {
      console.log('Wake up after ' + d)
      this.next()
    }, d * 1000)
  })(delay)
  this.tasks.push(fn)
  return this
}
_LazyMan.prototype.sleepFirst = function(delay) {
  let fn = (d => () => {
    setTimeout(() => {
      console.log('等待 ' + d)
      this.next()
    }, d * 1000)
  })(delay)
  this.tasks.unshift(fn)
  return this
}

解决input光标不居中问题

大家应该都知道,在Input里让文字垂直居中有很多种方案,最直接了当的一种:

input {
  height: 20px;
  line-height: 20px;
}

但移动端很明显的有一种bug就是,未输入文字时Input里的光标与input一样高,输入文字后,光标行为也变得很怪异,见图:
[input光标样式bug
[input光标样式bug
原因分析:
首先
IE:不管该行有没有文字,光标高度与font-size一致。

FF:该行有文字时,光标高度与font-size一致。该行无文字时,光标高度与input的height一致。

Chrome:该行无文字时,光标高度与line-height一致;该行有文字时,光标高度从input顶部到文字底部(这两种情况都是在有设定line-height的时候),如果没有line-height,则是与font-size一致。

解决方法:
1、给input设置一个较小的height,然后上下用padding填充起来

input{
    height: 16px;
    padding: 4px 0px;
    font-size: 12px;
}

2、 外面包一层,然后给input设置一个较小的line-height

div {
  height: 20px;
  line-height: 20px;
}
div input {
  height: 20px;
  line-height: 10px
}

这样,光标就跟input的line-height一样了,看起来也和谐很多。
效果如下:
[input光标样式bug
[input光标样式bug

利用axios去除重复请求

根据文档上的第二种方法,我们可以在拦截器里统一处理取消重复请求

let pending = []; //声明一个数组用于存储每个ajax请求的取消函数和ajax标识
let cancelToken = axios.CancelToken;
let removePending = (config) => {
    for(let p in pending){
        if(pending[p].u === config.url + '&' + config.method) { //当当前请求在数组中存在时执行函数体
            pending[p].f(); //执行取消操作
            pending.splice(p, 1); //把这条记录从数组中移除
        }
    }
}

//添加请求拦截器
axios.interceptors.request.use(config=>{
     removePending(config); //在一个ajax发送前执行一下取消操作
     config.cancelToken = new cancelToken((c)=>{
        // 这里的ajax标识我是用请求地址&请求方式拼接的字符串,当然你可以选择其他的一些方式
        pending.push({ u: config.url + '&' + config.method, f: c });  
    });
     return config;
   },error => {
     return Promise.reject(error);
   });

//添加响应拦截器
axios.interceptors.response.use(response=>{
      removePending(res.config);  //在一个ajax响应后再执行一下取消操作,把已经完成的请求从pending中移除
      return response;
   },error =>{
      return { data: { } }; 返回一个空对象,主要是防止控制台报错
   });

关于在移动端使用h5的video标签的全攻略

最近完成了一个移动端活动页面里插入视频播放的需求,现在来说一下自己的经验与碰到的坑。
既然是插入视频,用的肯定是h5的video标签了,没啥异议,直接开干!对了,先来看一下效果图:

六小龄童领全民拜年 👈点击这里

先来简单分析一下video标签。

通用属性

MDN上的属性有以下几个:(只说主要的、常用的)

  • autoplay 自动播放属性。实践起来没什么卵用。
  • controls 加上控制条,允许用户控制视频的播放,包括音量,跨帧,暂停/恢复播放。当然,也没什么卵用。
  • loop 是否自动循环播放。
  • preload 告诉浏览器作者认为达到最佳的用户体验的方式是什么。
    • none 浏览器不缓存视频
    • metadata 不缓存视频,但会取得视频的一些数据,如视频长度
    • auto 优先下载视频,不管用户看不看
    • 空字符串 指auto
      ps:这里需要注意一点,如果设置了autoplay,但是preload的值为none,也会自动缓存视频,因为autoplay的优先级高于proload
  • poster 海报,就是视频还没开始播放的时候的标签所展示的图片。
  • src 资源路径
  • height/weight 高、宽

私有属性

iOS

  • webkit-playsinline和playsinline:视频播放时局域播放,不脱离文档流 。但是这个属性比较特别, 需要嵌入网页的APP比如WeChat中UIwebview 的allowsInlineMediaPlayback = YES webview.allowsInlineMediaPlayback = YES,才能生效。换句话说,如果APP不设置,你页面中加了这标签也无效,这也就是为什么安卓手机WeChat 播放视频总是全屏,因为APP不支持playsinline,而ISO的WeChat却支持。
    这里就要补充下,如果是想做全屏直播或者全屏H5体验的用户,IOS需要设置删除 webkit-playsinline 标签,因为你设置 false 是不支持的 ,安卓则不需要,因为默认全屏。但这时候全屏是有播放控件的,无论你有没有设置control。 做直播的可能用得着播放控件,但是全屏H5是不需要的,那么去除全屏播放时候的控件,需要以下设置:同层播放
  • x-webkit-airplay="allow":这个属性应该是使此视频支持ios的AirPlay功能。使用AirPlay可以直接从使用iOS的设备上的不同位置播放视频、音乐还有照片文件,也就是说通过AirPlay功能可以实现影音文件的无线播放,当然前提是播放的终端设备也要支持相应的功能

android

  • x5-video-player-type: 启用同层H5播放器,就是在视频全屏的时候,div可以呈现在视频层上,也是WeChat安卓版特有的属性。同层播放别名也叫做沉浸式播放,播放的时候看似全屏,但是已经除去了control和微信的导航栏,只留下"X"和"<"两键。目前的同层播放器只在Android(包括微信)上生效,暂时不支持iOS。至于为什么同层播放只对安卓开放,是因为安卓不能像ISO一样局域播放,默认的全屏会使得一些界面操作被阻拦,如果是全屏H5还好,但是做直播的话,诸如弹幕那样的功能就无法实现了,所以这时候同层播放的概念就解决了这个问题。不过在测试的过程中发现,不同版本的IOS和安卓效果略有不同
  • x5-video-orientation: 声明播放器支持的方向,可选值landscape 横屏, portraint竖屏。默认值portraint。无论是直播还是全屏H5一般都是竖屏播放,但是这个属性需要x5-video-player-type开启H5模式
  • x5­-video­-player­-fullscreen:全屏设置。它又两个属性值,ture和false,true支持全屏播放,false不支持全屏播放。其实,IOS 微信浏览器是Chrome的内核,相关的属性都支持,也是为什么X5同层播放不支持的原因。安卓微信浏览器是X5内核,一些属性标签比如playsinline就不支持,所以始终全屏。

方法

再来说两个方法:

  • ended 结束播放的回调
  • timeupdate 检测播放时间的回调

实践

所以一般来说,一个video标签长这样:(vue框架)

          <video id="video" 
                 src="/video1.mp4"                   // 资源路径
				 controls = "true"                      // 是否有控制条
                 poster="/begin.jpg9"               // 视频封面
                 preload="auto"                        // 加载的形式
                 x-webkit-airplay="allow"          // 使此视频支持ios的AirPlay功能
                 webkit-playsinline="true"         // 视频播放时局域播放,不脱离文档流.需要APP支持,目前ios支持
                 playsinline="true"
				 x5-video-player-type="h5"        // 启用H5播放器,是wechat安卓版特性
				 x5-video-player-fullscreen="true"   // 安卓机全屏设置
                 @timeupdate="update()"         // 检测播放时间回调
                 @ended="playEnd()">              // 播放结束回调
            您的设备或浏览器暂不支持 video 标签。
          </video>

全屏播放及隐藏控制条

对于iOS来说

<video id="video" src="xx.mp4" playsinline webkit-playsinline></video>

即可。

对于安卓来说,有两种办法
第一种

<div>
	<video id="video" 
		src="video.mp4"  
		x5-video-player-type="h5"
		x5-video-player-fullscreen="true"
		x5-video-orientation="portraint"
		style="object-fit:fill">
	</video>
</div>

优点:全屏显示,无控制条
缺点:导航栏消失,某些机型退出播放时会有短暂黑屏。

第二种

<div ontouchmove="{return false}" onclick="{return false}">
	<video id="video" playsinline="true">
	</video>
</div>

然后设置视频video标签样式:

        video {
          margin: 0;
          padding: 0;
          width: 100%;
          //height: 100%;   ios设置该属性可以达到全屏显示,但稍有压缩,android会屏幕两边留有黑边。
          object-fit: fill;
          object-position: top;
          z-index: 2;
          position: relative;
          user-select: none;
          outline: none;
        }

优点:有导航栏,退出播放无黑屏
缺点:其实是有控制条的,但是通过禁止用户触摸和点击,把控制条隐藏在界面外,导致界面缺失。
如果设置高度,可能导致android机型屏幕两边留有黑边,与视屏的尺寸有一定关系。而且不同的机型的表 现不同。

自动播放

这个就不用说了,安卓始终不支持自动播放。ios本来也不行,必须引导用户点击,但微信提供了一个事件WeixinJSBridgeReady,在微信嵌入webview全局的这个事件触发后,视频仍可以自动播放。

document.addEventListener("WeixinJSBridgeReady", function (){ 
		video.play();
}, false)

关于播放控制

尽量只用timeupdate与ended两个事件,其他的诸如'loadstart','canplay','canplaythrough','ended','timeupdate'.....等等,会产生不一致的效果,不建议使用。

支持的格式

当前,video 元素支持三种视频格式:

Ogg = 带有 Theora 视频编码和 Vorbis 音频编码的 Ogg 文件

MPEG4 = 带有 H.264 视频编码和 AAC 音频编码的 MPEG 4 文件

WebM = 带有 VP8 视频编码和 Vorbis 音频编码的 WebM 文件

当然了,最好使用mp4的,移动端支持较好。
如果你想保险起见,可以写成这样:

<video autoplay>
    <source src="video.mp4" type="video/mp4">
    <source src="video.webm" type="video/webm">
    <source src="video.ogv" type="video/ogg">
</video>

done.

下面附上文章最开始案例的code:
需求:全屏展示,保留title,不显示控制条,可嵌入APP
vue如下:

      <div class="page-two" v-el:pagetwo v-if="showTwo" ontouchmove="{return false}" onclick="{return false}">
        <div class="video-wrap">
          <video id="video" src="//img.dsimg.cn/m_station/special_events/new_year_friend_circle/video1.mp4"
                 poster="//img.dsimg.cn/m_station/special_events/new_year_friend_circle/begin.jpg"
                 preload="auto"
                 x-webkit-airplay="allow"
                 webkit-playsinline="true"
                 playsinline="true"
                 @timeupdate="update()"
                 @ended="playEnd()"
                 v-el:video
                 class="translate">
            您的设备或浏览器暂不支持 video 标签。
          </video>
          <!--<div class="ignore ignore2" @click="playEnd()" v-if="showIgnore && isiOS && !isQQ">跳过</div>-->
          <div class="ignore" @click="playEnd()" v-if="showIgnore && isiOS && !isQQ"><img src="//img.dsimg.cn/m_station/special_events/new_year_friend_circle/jump.png"></div>
        </div>
      </div>

样式如下:

    .page-two {
      position: absolute;
      z-index: 3;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-color: black;
      .video-wrap {
        position: relative;
        width: 100%;
        height: 100%;
        video {
          margin: 0;
          padding: 0;
          //height: 100%;  // 暂时保留,ios设置该属性可以达到全屏显示,但稍有压缩,android会屏幕两边留有黑边。
          object-fit: fill;
          object-position: top;
          z-index: 2;
          position: relative;
          user-select: none;
          outline: none;
        }
      }
    }

JS数据类型的判断

先来说一下,ECMAScript中,规定了如下:
六种 基本 数据类型:
String:表示字符串,例如:"Howdy"
Number: 表示数字,例如: 42 或者 3.14159
Boolean: 布尔值,true 和 false
null:一个表明 null 值的特殊关键字。 JavaScript 是大小写敏感的,因此 null 与 Null、NULL或其他变量完全不同
undefined: 变量未定义时的属性
Symbol ( 在 ECMAScript 6 中新添加的类型).。一种数据类型,它的实例是唯一且不可改变的
以及 Object 对象
这里Object除了单纯的常说的对象外,还包括:Array, Function, RegExp, Date等等。

这里判断数据类型的方法如下:

一、typeof
typeof操作符返回一个字符串,表示未经计算的操作数的类型。返回的值包括:number、boolean、symbol、string、object、undefined、function等

typeof('')  // "string"  可行
typeof(1)  // "number" 可行
typeof(NaN) // "number" 不可行
typeof(false) // "boolean" 可行
typeof(null) // "object" 不可行
typeof(undefined)  // "undefined" 可行
typeof(Symbol()) // "symbol" 可行
typeof({a:1}) // "object" 可行
typeof([1,2,3]) // "object" 不可行
typeof(() => {console.log(1)}) // "function" 不可行
typeof(new Set()) // "object" 不可行
typeof(new Map()) // "object" 不可行
typeof(new Date()) // "object" 不可行
typeof(new RegExp()) // "object" 不可行

实践证明,
1、typeof适用于:string、number、boolean、function、undefined的判断,也就是除了null以外的其余基本类型。
2、NaN,返回number,对于需要判断是否NaN的时候不实用
3、null 返回object类型
3、function 引用类型里唯一返回正确的,其余都返回object

所以,typeof的适用范围就是判断除null以外的基本类型和function,number类型还要判断NaN的时候。

二.、isNaN,Array.isArray()

isNaN(NaN) // true
Array.isArray([1,2,3]) // true

isNaN是判读NaN的唯一的可靠方法
Array.isArray本质是检测对象的[[class]]值。[[Class]] 是对象的一个内部属性,里面包含了对象的类型信息,其格式为 [object Xxx] ,Xxx 就是对应的具体类型 。对于数组而言,[[Class]] 的值就是 [object Array] 。

三、Object.prototype.toString().call
toString是Object的原型上的一个方法,返回当前对象的[[class]]。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用

Object.prototype.toString.call('') // "[object String]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call(NaN) // "[object Number]"
Object.prototype.toString.call(false) // "[object Boolean]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(Symbol()) // "[object Symbol]"
Object.prototype.toString.call({a: 1}) // "[object Object]"
Object.prototype.toString.call([1,2,3]) // "[object Array]"
Object.prototype.toString.call(() => {console.log(1)}) // "[object Function]"
Object.prototype.toString.call(new Set()) // "[object Set]"
Object.prototype.toString.call(new Map()) // "[object Map]"
Object.prototype.toString.call(new Date()) // "[object Date]"
Object.prototype.toString.call(new RegExp()) // "[object RegExp]"

基本满足了所有要求,除了NaN

四、instanceof(弃用)
instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
例如:a.proto === b.prototype 即可用说a是b的实例, a instanceof b返回true
这里需要注意的是: instanceof检测的是 原型链 .
例如:

[] instanceof Array // true
Array instanceof Object // true
[] instanceof Object // true

so,instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型

五、constructor(弃用)
所有对象都会从它的原型上继承一个 constructor 属性。

''.constructor === String // true
new Number(1).constructor === Number // true

...
需要注意的是:
1、null 和 undefined不是有效的对象,没有constructor存在
2、依赖一个对象的 constructor 属性并不安全, 因为constructor是可以改变的

ios微信浏览器动态更换页面title

今天突然遇到一个需求,是让我根据从后台拿到的数据,来给页面设置title.因为我现在做的是h5移动端开发,用的vue,所以拿到这个需求后,我陷入了深深的思考……
想了半天没用的,因为我开发用vue,所以一开始我想着created的时候用ajax获取数据,然后把数据传给vue实例,然后直接赋值给title标签内。

<title>{{title}}</title>

后来一想,这样还得进行父子组件的通信,有点啰嗦,要不用vuex?不行,没什么卵用。此路不通,又陷入了思考中……
要不,一开始不给页面设置title,然后等接受了数据之后,新建一个title元素,然后再插到head里面去?

var title = document.createElement("title")
title.innerText = data.title
document.head.appendChild(title)

或者直接

document.getElementsByTagName('title')[0].innerHTML = data.title

倒是可行,可是感觉有点low,有没有更好的办法呢?

然后,然后……
我突然灵光一闪,直接这样

document.title = data.title

可行,done……

But,世事总是无常的,根据微信iOS开发适配指南,
iOS适配指南截图

WTF?意思就是老版本的iOS微信浏览器修改页面标题无法正常显示呗……虽然2017年三月份过去半年了,但保不定还有一部分不升级微信,算了,还得做个兼容,来,开始吧……

首先,分析这个bug出现的原因,是因为微信之前使用的内核是UIWebView,并没有类似titleChange事件,虽然document.title已经修改了,但无法做出反应。

有没有什么hack方案?

谷歌了一番,还真有……代码如下:

function changeTitle(title) {
  document.title = title
  $('<iframe src="/title.html" style="display: none"></iframe>').one('load', function() {
    setTimeout(() => {
      $(this).remove()
    }, 0)
  })
}
// when route change use this update your page title
changeTitle(title)

优点就是适配通用,比较完美的实现了功能
缺点也很明显,多发送了一个请求……

等等,为什么加了一个iframe再拿掉就可以了呢?

原因:在native里打开网页,一般是通过webview搭配naviBar实现的。webview负责显示网页,naviBar负责显示标题、后退和关闭等信息。naviBar上显示的标题即是通过webview获取到的,所以关键问题是webview如何获取页面标题?

webview获取页面标题常用以下几种方案:

  • 初始化时传参
  • HTML加载完成后,获取HTML里title标签内容

所以页面在加载完成后,触发了webview的pageload事件,在这个事件监听函数里获取页面title并更新显示到naviBar上。在单页应用生命周期中,因为页面切换并不像传统网页那样跳转->加载的模式,后续的页面切换就都没法再触发pageload事件,导致naviBar上显示的标题一直未更新。

还是那个问题,为什么通过iframe就可以?

其实答案很简单:我们知道iframe也是一种文档对象,所以iframe加载完成也能触发webview的pageload事件,让webview “认为” 页面更新了,所以就重新获取title并更新naviBar的显示。

done……

有兴趣的可以看看知乎上的回答:如何通过 js 修改微信浏览器的title?

参考资料:

论二分查找算法

最近看到一个面试题,手写一个二分查找,撸了一下,没跑起来,特来重新学习。。。

  • 定义:二分查找算法,binary search algorithm,也叫折半搜索算法,对半搜索算法,是一种在有序数组中查找某一特定元素的搜索算法。
  • 由定义可知,二分查找算法的前提是:
    • 有序
    • 数组
  • 二分算法的逻辑比较简单:
    • 取数组的中间数,与目标数进行比较,如果相等,返回中间数的下标
    • 目标数大于中间数,则在中间数右边继续查找
    • 目标数小于中间数,在中间数左边继续查找
    • 某一步骤数组为空,表示找不到
  • 时间复杂度:总共有n个元素,渐渐跟下去就是n,n/2,n/4,....n/2^k,其中k就是循环的次数,由于你n/2^k取整后>=1,即令n/2^k=1,可得k=log2n, O(log2n) => O(logn)。
  • 空间复杂度:O(1)。虽以递归形式定义,但是尾递归,可改写为循环。

代码实现

  • 递归版
function binarySearch(target, arr, low, high) {
	let start = low === undefined ? 0 : low
	let end = high === undefined ? arr.length - 1 : high
	if (start > end) {
		return -1
	}
	let mid = parseInt(start + (end - start) / 2)
	if (arr[mid] === target) {
		return mid
	} else if (arr[mid] > target) {
		return binarySearch(target, arr, start, mid - 1)
	} else {
		return binarySearch(target, arr, mid + 1, end)
	}
}
  • while循环版
function binarySearch(target, arr) {
	let start = 0
	let end = arr.length - 1
	while(start <= end) {
		mid = parseInt(start + (end - start) / 2)
		if (target == arr[mid]) {
			return mid
		} else if (target > arr[mid]) {
			start = mid + 1
		} else if (target < arr[mid]) {
			end = mid - 1
		}
	}
	return -1
}

done!

移动端微信支付的采坑指南--前端

这几天搞定了移动端的微信的支付,现在来简要记录一下采坑心得

首先,移动端的微信支付主要有两种情况:微信内置浏览器采用公众号支付,微信外浏览器采用h5支付,官方文档详见https://pay.weixin.qq.com/wiki/doc/api/index.html

先来说说公众号支付流程

  • 首先需要设置公众号的相关配置,主要是支付目录和授权域名,详见文档,不多说。
  • 进行微信网页授权,文档https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842。有两种形式,静默授权,即用户无感知,只能获取用户的openid。另一种就是需要用户点击确认授权的,可以获取用户的userinfo,包括了手机号码、位置信息等。从前端来说,需要配置一个跳转,如:
const APP_ID = 'xxxx' // 这里写公众号的appid
const tempUrl = 'http://entpre2.slsai.com/home' // 这里写回调url,code会挂载在url上
const SCOPE = 'snsapi_base' // 授权形式,这里我采用静默授权
window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' + APP_ID +
  '&redirect_uri=' + encodeURIComponent(tempUrl) +
  '&response_type=code&scope=' + SCOPE + '&state=STATE#wechat_redirect'

然后
页面自动跳转,url会长成这个样子:http://entpre2.slsai.com/home?code=xxxxxx&state=STATE
解释一下,这个state就是你之前要传的回调参数
然后你把code取出来,传给后端,后端一般会发起统一下单流程,并给你返回一些数据:如
未完待续……

文本溢出显示省略号的具体实现

项目中,经常会遇到实现文本溢出呈省略号,有的要求单行文本 ,有的要求多行文本,具体代码如下:

1、单行文本

div {
  width: 100px;
  overflow: hidden;
  text-overflow:ellipsis;
  white-space: nowrap;
}

2、多行文本

div {
  width: 100px;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

(因使用了WebKit的CSS扩展属性,该方法适用于WebKit浏览器及移动端)

注:
-webkit-line-clamp用来限制在一个块元素显示的文本的行数。 为了实现该效果,它需要组合其他的WebKit属性。常见结合属性:
display: -webkit-box; 必须结合的属性 ,将对象作为弹性伸缩盒子模型显示 。
-webkit-box-orient 必须结合的属性 ,设置或检索伸缩盒对象的子元素的排列方式 。

js中相等性判断探索

js中相等性判断探索

最近发现一个比较神奇的问题如下:

  • [] == false // true
  • !![] == true // true

一脸懵逼中。。。
那么,现在就来从开始探索一下这道题的原理

js比较操作

js提供了三中不同的比较操作,分别是:

  • 严格相等 ("triple equals" 或 "identity"),使用 ===
  • 宽松相等 ("double equals") ,使用 ==
  • ES6新出的Object.is()

说一下几种操作的特点与不同。首先,使用=== 与Ojbect.is()进行比较时,是不会进行类型转换的,先判断两边的数据类型,如要两边的数据类型不同,就会直接返回false,再判断数据的值是否相同,这里有个坑点就是对于Number,=== 与Ojbect.is()在处理+0, -0, 与NaN方面有一些不同之处,如下

+0 === -0  // true
Object.is(+0, -0) // false

NaN === NaN // false
Object.is(NaN, NaN) // true

再来说一下==,这个因为涉及到了类型的转换,比较复杂一些。

  • 相等操作符比较两个值是否相等,在比较前将两个被比较的值转换为相同类型。在转换后(等式的一边或两边都可能被转换),最终的比较方式等同于全等操作符 === 的比较方式。
  • 理解一下两个方法,ToNumber(A),是指在比较前将A转为数字,与 +A(单目运算符+)的效果相同。ToPrimitive(A), 是指通过调用A.toString()或者A.valueOf()方法将A转为原始值(primitive)

然后来看一下表格表格

可知:
1、Undefined 和 Null:一般情况下,他们只和自身及对方相等,其他情况都会返回false。有个IsFalsy()看起来很奇怪,对象不都应该是返回true么,有啥好判断的?还真不是……有些浏览器会允许一些特殊的对象,如:document.all 等,在某些情况下(相等操作符等)充当undefined的角色,所以,他就会返回false。都是坑爹呢这是……
2、Number: 想与Numbr进行对比,二话不说,你先转成number再BB
3、Boolean:刚想与Boolean做比较,额,他倒是先转成Number了,得嘞,走上了与Number比较的老路
4、Object:可能因为Object比较杂,所以需要先通过ToPrimitive()去找到他的真身以后再进行比较
5、String:优先级最低,因为留给他的对手不多了,一般都是被迫转型,除非对手是String或者ToPrimitive()是String

看起来,类型转换机制还挺复杂的,所以,一般情况下,能用 === 的就尽量不要用 == 了,免得掉进了自己的坑里。什么,非要用==不可?好吧,来看看一下几个案例:

"0" == false; // true
false == 0;  // true 
false == "";  // true
false == [];  // true 
"" == 0;  // true 
"" == [];  // true 
0 == [];  // true 

如果以上这些情况你都能理解,嗯,你可以放心的用==了。
最后,祝大家好运~

Recommend Projects

  • React photo React

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

  • Vue.js photo Vue.js

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

  • Typescript photo Typescript

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

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

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

Recommend Topics

  • javascript

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

  • web

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

  • server

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

  • Machine learning

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

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

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

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.