之前看到过一道面试题,如何让a==1&& a==2 && a==3 等于true,开始一看,一脸懵逼,后来搜索了下,看到了不同的解决方法.

访问修饰符能够解决这个问题,毕竟每访问一次值加1就好啦.


var value = 0;
Object.defineProperty(window,'a',{
    get:function(){
    return this.value += 1;
    }
})
console.log(a== 1 && a==2 && a==3);//true

不过,貌似对象还有一些方法能够改变访问到的值;比如:

  • Object.prototype.valueOf()
  • Object.prototype.toString()
  • [Symbol.toPrimitive(hint)

Object.prototype.valueOf()



对象的`valueOf`旨在返回对象的原始值,会在需要将对象转换成原始值的地方`自动执行`.内置的一些对象已经定义`valueOf`方法,而对象默认则返回本身作为`valueOf()`的返回值。

Object.prototype.toString()



同理,`toString()`方法会返回表示该对象的字符串,会在对象预期要被转换成字符串的地方自动执行.对象默认的`toString()`方法会返回`[object type]`,这个`type`就是`对象构造函数的名称`

Symbol.toPrimitive



`[Symbol.toPrimitive](hint)`方法作用同`valueOf()`一样,但是优先级要高于`valueOf()`;而且该方法还会接受一个参数`hint`,这个参数用来表示期望转换的原始值的**具体类型**,有以下几种:
  • number:数字类型
  • string:字符串类型
  • default 默认

对象转换成原始值



很明显,以上三种方法都是在对象被预期转换成某种原始值时触发,那么触发的时机是什么,需要`何种类型`的原始值就要弄清楚了.

预期被转换成字符串类型

对应hint类型为string

  1. 进行输出的地方,如alert().
  2. String(obj):
    let a = {
        toString(){
            return '2'
        }
    }
    console.log(String(a));

3.字符串链接(+)操作:

let a = {
    toString(){
        return '2'
    }
}   
console.log(a+'vv');
  1. 模板字符串:

let a = {
  [Symbol.toPrimitive] (hint) {
    console.log(hint) // string
    return 2
  }
}

console.log(`你是老${a}?`) // 你是老2?

预期被转换成数字类型

对应的hint类型为number

(1).除法:

let a = {
    valueOf(){
        return 2;
    }
}
console.log(2/a,a/2);

可以使用[Symbol.toPrimitive]()方法来验证一下:

    let a = {
        [Symbol.toPrimitive](hint){
            console.log(hint);
            return 2
        }
    }
    console.log(2/a);

(2). Number(obj):

    let a = {
        [Symbol.toPrimitive](hint){
            console.log(hint);
            return 2
        }
    }
    console.log(Number(a));

(3). 正负号 (注意不是加减运算)

    let a = {
        [Symbol.toPrimitive](hint){
            console.log(hint);
            return 2
        }
    }
    console.log(+a);
    console.log(-a);

预期被转换成默认类型(其他)

对应的hint类型为default

(1).数字加法 (即与对象相加的一方为数字类型)

let a = {
    [Symbol.toPrimitive](hint){
        console.log(hint);
        return 2;
    }
}
console.log(1+a); // 3

这一点有点意外,原以为像这种情况预期转换的类型应该是数字类型,但事实上却是default

(2).布尔运算:所有对象都被转换成true:

let a = {
    [Symbol.toPrimitive](hint){
        console.log(hint); // 没有触发
        return false;
    }
}
console.log(Boolean(a),a && 123); // true  123

布尔运算包括==;

三种方法触发的顺序



(1).**首先** 判断对象是否有`[Symbol.toPrimitive](hint)`方法,若有,则执行该方法:
let a = {
        [Symbol.toPrimitive](hint){
            console.log(hint);
            return 'right your mother right';
        },

        toString(){
            return 'f**k';
        },
        valueOf(){
            return 'sb';
        }
    }

    console.log(String(a));

(2).如果预期被转换成字符串类型时,则优先执行toString()方法:

let a = {
    toString(){
        return 'f**k'
    },
    valueOf() {
        return 'sb'
    }
}
console.log(String(a));

(3).如果预期被转换成默认类型或者数字类型时,则优先执行valueOf()方法:

let a = {
    toString(){
        return 123
    },
    valueOf() {
        return 456
    }
}
console.log(Number(a));

注:若没有valueOf()方法,但是定义了toString()方法,则会执行toString()方法;

let a = {
    toString(){
        return 123;
    }
}
console.log(Number(a))