js 各种逻辑坑

1. [1<2<3,3<2<1]:

  返回的是[true,true].原因是1<2<3等同于(1<2)<3就是 true<3,在比较的时候true或者false就么默认转化为数字1或0,最后就是[1<3,0<1],故是[true,true]

2.var a=1.0-0.9;  if(a==0.1){ console.log(true)}else{console.log(false)}  var b=0.8-0.7;if(a==b){console.log(true)}else{console.log(false)}

    返回的是  false false  ,这是由于 js小数运算会丢失精度,

3.var x=30; function test(){console.log(x); var x=10;console.log(x);x=20;function x(){} console.log(x)} test()

  返回的是ƒ x(){}  10   20

4. var a={n:1};var b=a; a.x=a={n:2}; console.log(a.x);console.log(b.x)

  返回的是 undefined   {n: 2};原因是  js的赋值运算顺序永远都是从右往左的,不过由于“.”是优先级最高的运算符,所以这行代码先“计算”了a.x,这时候发生了这个事情——a指向的对象{n:1}新增了属性x(虽然这个x是undefined的),由于b跟a一样是指向对象A的,要表示A的x属性除了用a.x,自然也可以使用b.x来表示了,接着,依循“从右往左”的赋值运算顺序先执行 a={n:2} ,这时候,a指向的对象发生了改变,变成了新对象{n:2}(我们称为对象B),

接着继续执行 a.x=a,很多人会认为这里是“对象B也新增了一个属性x,并指向对象B自己”但实际上并非如此,由于( .  运算符最先计算)一开始js已经先计算了a.x,便已经解析了这个a.x是对象A的x,所以在同一条公式的情况下再回来给a.x赋值,也不会说重新解析这个a.x为对象B的x。所以 a.x=a 应理解为对象A的属性x指向了对象B:

那么这时候结果就显而易见了。当console.log(a.x)的时候,a是指向对象B的,但对象B没有属性x。没关系,当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。但当查找到达原型链的顶部 - 也就是 Object.prototype - 仍然没有找到指定的属性B.prototype.x,自然也就输出undefined;

5.a可以同时 ==1&&==2&==3吗?

  

此题目的答案可以分为三大类:

1. 类型转换时的劫持

  首先我们要知道,在 JS 中类型转换只有三种情况,分别是:

  • 转换为布尔值

  • 转换为数字

  • 转换为字符串

 转换为原始类型

对象在转换类型的时候,会执行原生方法ToPrimitive

其算法如下:

  1. 如果已经是 原始类型,则返回当前值;

  2. 如果需要转 字符串 则先调用 toSting方法,如果此时是 原始类型 则直接返回,否则再调用 valueOf方法并返回结果;

  3. 如果不是 字符串,则先调用 valueOf方法,如果此时是 原始类型 则直接返回,否则再调用 toString方法并返回结果;

  4. 如果都没有 原始类型 返回,则抛出 TypeError类型错误。

当然,我们可以通过重写 Symbol.toPrimitive来制定转换规则,此方法在转原始类型时调用优先级最高。

所以以此定义我们可以有以下四种答案:

 

var a = {
    arr: [3, 2, 1],
    valueOf () {
        console.group('valueOf')
        console.log(this.arr)
        console.groupEnd('valueOf')       
  return this.arr.pop()
  }
}
if(a == 1&& a == 2&& a == 3) {
    console.log('biu')
}

var b = {
    arr: [3, 2, 1],
    toString () {
        console.group('toString')
        console.log(this.arr)
        console.groupEnd('toString')
        
  return this.arr.pop()
  }
}
if(b == 1&& b == 2&& b == 3) {
    console.log('biu')
}

var c = {
    arr: [3, 2, 1],
    [Symbol.toPrimitive] () {
        console.group('Symbol.toPrimitive')
        console.log(this.arr)
        console.groupEnd('Symbol.toPrimitive')        
  return this.arr.pop()
  }
}
if(c == 1&& c == 2&& c == 3) {
    console.log('biu')
}

var d = [1, 2, 3]
d.join = d.shift
if (d == 1 && d == 2 && d == 3) {
    console.log('biu')
}

 

注:事实上,这四种可以算是同一种。关于最后一种,我们可以来看看ECMA中的 Array.prototype.toString() 定义:

  1. 定义 array 为 ToObject(thisvalue)(原生方法,将当前数组转换成对象);

  2. 定义 func 为 Get(array,'join')(原生方法,在这一步调用 join 方法);

  3. 如果 IsCallble(func) (原生方法,判断是否有内部可调用的函数)为 false,则 设置 func 原生函数 %ObjProto_toString%(原生函数, toString 的具体实现);

  4. 返回 Call(func,array)

2. 对 getter 的劫持

所谓的 getter 就是对象属性在进行查询时会被调用的方法 get,利用此函数也可以实现题目功能。

代码如下:

window.val = 0
Object.defineProperty(window, 'd', { 
    get () {
        return ++this.val
    }
})
if (d == 1 && d == 2 && d == 3) {
    console.log('biu')
}

const e = new Proxy({}, {
  val: 1,
  get ()  {
    return () => this.val++;
  }
});
if (e == 1 && e == 2 && e == 3) {
    console.log('biu')
}

3. 正则表达式

JS 中的 RegExp.prototype.exec() 作用是在一个指定字符串中执行一个搜索匹配,返回一个结果数组或 null

当正则表达式使用 " g" 标志时,可以多次执行 exec 方法来查找同一个字符串中的成功匹配。当你这样做时,查找将从正则表达式的 lastIndex 属性指定的位置开始。(test() 也会更新 lastIndex 属性)。

lastIndex 是正则表达式的一个可读可写的整型属性,用来指定下一次匹配的起始索引。只有正则表达式使用了表示全局检索的 " g" 标志时,该属性才会起作用。

注:只有正则表达式使用了表示全局检索的 " g" 标志时,该属性才会起作用。

综上所述,我们可以有方案如下:

var f = {
    reg: /\d/g,
    valueOf () {
        return this.reg.exec(123)[0]
    }
}
if (f == 1 && f == 2 && f == 3) {
    console.log('biu')
}

注:上述方法其实也利用了类型转换的特点。

posted @ 2019-06-24 14:53  红尘紫陌  阅读(617)  评论(0编辑  收藏  举报