最近啊,有同学来问,数据类型转换咱们都学过,可是在面试题中遇到了咱们怎么就老是不会用啊。

讲到数据类型转换,咱们基本上都是讲到隐式数据类型转换和显式转换。讲到数据类型转换,咱们就要来聊聊,valueOf 和 toString 这两个办法了。

基本上,所有JS数据类型都领有这两个办法,null除外。

这两个办法都是在原型链上的办法,为了解决javascript值运算与显示的问题。

valueOf 和 toString 简直都是在呈现操作符(+-*/==><)时被调用(隐式转换)。

toString

 toString返回一个示意该对象的字符串,当对象示意为文本值或以冀望的字符串形式被援用时,toString办法被主动调用。

  1. 手动调用的成果

运行的成果,全副都转成了字符串。

比拟非凡的中央就是,在示意对象的时候,就会打印[object Object],示意数组的时候,就打印数组内容以逗号连贯的字符串,相当于数组办法Array.join(',')办法。

let a = {} let b = [1, 2, 3] let c = '123' let d = function(){ console.log('fn') } console.log(b.toString())   // '1,2,3' console.log(a.toString())   // '[object Object]'console.log(c.toString())   // '123' console.log(d.toString())   // 'function(){ console.log('fn') }'

2. 最精准的类型判断

 toString 有时候在某种场合会比应用 typeof & instanceof 判断更高效和精确些,属于更准确的判断形式

toString.call(()=>{})       // [object Function] toString.call([])           // [object Array] toString.call('')           // [object String] toString.call({})           // [object Object] toString.call(undefined)    // [object undefined] toString.call(null)         // [object null] toString.call(22)           // [object Number] toString.call(new Date)     // [object Date] toString.call(Math)         // [object Math] toString.call(window)       // [object Window]

3. 什么时候会主动调用

很多同学会问了,这个toString办法,什么时候会主动调用呢?应用操作符的时候,如果其中一边为对象,则会先调用toSting办法,也就是隐式转换,而后再进行操作。

let c = [1, 2, 3] let d = {a:2} Object.prototype.toString = function(){     console.log('Object') } Array.prototype.toString = function(){     console.log('Array')     return this.join(',')   // 返回toString的默认值(上面测试) } Number.prototype.toString = function(){     console.log('Number') } String.prototype.toString = function(){     console.log('String') } console.log(2 + 1)  // 3 console.log('s')    // 's' console.log('s'+2)  // 's2' console.log(c < 2)  // false        (一次 => 'Array') console.log(c + c)  // "1,2,31,2,3" (两次 => 'Array') console.log(d > d)  // false        (两次 => 'Object')

4. 重写toString办法

既然晓得了有 toString 这个默认办法,那咱们也能够来重写这个办法

class A {    constructor(count) {        this.count = count         }    toString() {                 return '我有这么多钱:' + this.count         } } let a = new A(100) console.log(a)              // A {count: 100} console.log(a.toString())   // 我有这么多钱:100 console.log(a + 1)          // 我有这么多钱:1001

valueOf

valueOF这个办法,执行的时候返回以后对象的原始值。具体性能与toString大同小异,同样具备以上的主动调用和重写办法。

在上面的内容里,就不解说其余的内容了。次要讲一下这两者的区别吧

let c = [1, 2, 3] let d = {a:2} console.log(c.valueOf())    // [1, 2, 3] console.log(d.valueOf())    // {a:2}

两者区别

共同点:在输入对象时会主动调用。

不同点:默认返回值不同,且存在优先级关系。

二者并存的状况下,在数值运算中,优先调用了valueOf,字符串运算中,优先调用了toString。

上面通过代码演示给大家看一下执行的原理成果

class A {    valueOf() {        return 2         }         toString() {      return '哈哈哈'         } } let a = new A() console.log(String(a))  // '哈哈哈'   => (toString)console.log(Number(a))  // 2         => (valueOf) console.log(a + '22')   // '222'     => (valueOf) console.log(a == 2)     // true      => (valueOf) console.log(a === 2)    // false     => (严格等于不会触发隐式转换)

执行的最初后果能够看出,如果转换为字符串时调用toString办法,如果是转换为数值时则调用valueOf办法。

但其中的 a + '22' 很不谐和,字符串合拼不是应该是调用toString办法吗?

为了验证这个问题,咱们须要做一次更加谨严的演示试验看看:

暂且先把 valueOf 办法去掉

class A {    toString() {    return '哈哈哈'         } } let a = new A()console.log(String(a))  // '哈哈哈'     => (toString) console.log(Number(a))  // NaN         => (toString)console.log(a + '22')   // '哈哈哈22'   => (toString) console.log(a == 2)     // false       => (toString)

去掉 toString 办法看看

class A {    valueOf() {        return 2         } } let a = new A() console.log(String(a))  // '[object Object]'    => (toString) console.log(Number(a))  // 2                    => (valueOf) console.log(a + '22')   // '222'                => (valueOf) console.log(a == 2)     // true                 => (valueOf)

比照之后发现有很大不同吧。它没有像下面 toString 那样对立规整。

对于那个 [object Object],应该是从 Object 那里继承过去的,所以咱们再去掉它看看

class A {    valueOf() {        return 2         } }let a = new A() Object.prototype.toString = null;console.log(String(a))  // 2        => (valueOf) console.log(Number(a))  // 2        => (valueOf) console.log(a + '22')   // '222'    => (valueOf) console.log(a == 2)     // true     => (valueOf)

总结:valueOf偏差于运算,toString偏差于显示。

  1. 在进行对象转换时,将优先调用toString办法,如若没有重写 toString,将调用 valueOf 办法;如果两个办法都没有重写,则按Object的toString输入。
  2. 在进行强转字符串类型时,将优先调用 toString 办法,强转为数字时优先调用 valueOf。
  3. 应用运算操作符的状况下,valueOf的优先级高于toString。

[Symbol.toPrimitive]

MDN:Symbol.toPrimitive 是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数。

看这个解释基本上很多同学都是看不懂的,不论你有没有学过JS。

其实不必想的太简单,就把他看成是一个函数就能够了。

  1. 作用:同valueOf()和toString()一样,然而优先级要高于这两者;

2.该函数被调用时,会被传递一个字符串参数hint,示意以后运算的模式

一共有三种模式:

string:字符串类型

number:数字类型

default:默认

上面来看看Symbol.toPrimitive在代码中的实现吧:

class A {        constructor(count) {             this.count = count             }        valueOf() {            return 2             }         toString() {          return '哈哈哈'             }             // 我在这里             [Symbol.toPrimitive](hint) {        if (hint == "number") {            return 10;        }                if (hint == "string") {             return "Hello Libai";                }                    return true;            } } const a = new A(10) console.log(`${a}`)     // 'Hello Libai' => (hint == "string") console.log(String(a))  // 'Hello Libai' => (hint == "string") console.log(+a)         // 10            => (hint == "number") console.log(a * 20)     // 200           => (hint == "number") console.log(a / 20)     // 0.5           => (hint == "number") console.log(Number(a))  // 10            => (hint == "number") console.log(a + '22')   // 'true22'      => (hint == "default") console.log(a == 10)     // false        => (hint == "default")

比拟非凡的是(+)拼接符,这个属于default的模式。

划重点:此办法不兼容IE,这个重点我说进去都感觉难堪

面试题剖析

讲完下面的原理比拟,上面我来分享几道往年大厂的面试,题目是我学员给我提供的。

以下几道大厂必考的面试题,能够说十分完满的体现出 toString 与 valueOf 的代码作用了

1.如何让(a===1&&a===2&&a===3)的值为true?

双等号(==):会触发隐式类型转换,所以能够应用 valueOf 或者 toString 来实现。

每次判断都会触发valueOf办法,同时让value+1,能力使得下次判断成立。

class A {    constructor(value) {        this.value = value;         }         valueOf() {         return this.value++;        } } const a = new A(1);if (a == 1 && a == 2 && a == 3) {    console.log("Hi Libai!");}

全等(===):严格等于不会进行隐式转换,这里应用 Object.defineProperty 数据劫持的办法来实现

let value = 1; Object.defineProperty(window, 'a', { get() {    return value++     } }) if (a === 1 && a === 2 && a === 3) {     console.log("Hi Libai!")}

下面咱们就是劫持全局window下面的a,当a每一次做判断的时候都会触发get属性获取值,并且每一次获取值都会触发一次函数履行一次自增,判断三次就自增三次,所以最初会让公式成立。

  1. 如何实现一个有限累加函数

问题:用 JS 实现一个有限累加的函数 add,示例如下:

add(1); // 1add(1)(2);  // 3add(1)(2)(3); // 6add(1)(2)(3)(4); // 10// 以此类推 function add(a) {    function sum(b) { // 应用闭包        a = b ? a + b : a; // 累加        return sum;         }     sum.toString = function() { // 只在最初一次调用            return a;     }     return sum; // 返回一个函数 } add(1)              // 1add(1)(2)           // 3add(1)(2)(3)        // 6add(1)(2)(3)(4)     // 10 
  1. add函数外部定义sum函数并返回,实现间断调用
  2. sum函数造成了一个闭包,每次调用进行累加值,再返回以后函数sum
  3. add()每次都会返回一个函数sum,直到最初一个没被调用,默认会触发toString办法,所以咱们这里重写toString办法,并返回累计的最终值a

这样说比拟好了解:

add(10): 执行函数add(10),返回了sum函数,留神这一次没有调用sum,默认执行sum.toString办法。所以输入10;

add(10)(20): 执行函数add(10),返回sum(此时a为10),再执行sum(20),此时a为30,返回sum,最初调用sum.toString()输入30。add(10)(20)...(n)顺次类推。

3. 柯里化实现多参累加

这里是下面累加的升级版,实现多参数传递累加。

add(1)(3,4)(3,5)    // 16add(2)(2)(3,5)      // 12function add(){             // 1 把所有参数转换成数组         let args = Array.prototype.slice.call(arguments)         // 2 再次调用add函数,传递合并以后与之前的参数            let fn = function() {            let arg_fn = Array.prototype.slice.call(arguments)                     return add.apply(null, args.concat(arg_fn))         }             // 3 最初默认调用,返回合并的值         fn.toString = function() {            return args.reduce(function(a, b) {             return a + b                 })             }                 return fn     }             // ES6写法     function add () {    let args = [...arguments];     let fn = function(){         return add.apply(null, args.concat([...arguments]))     }    fn.toString = () => args.reduce((a, b) => a + b)     return fn;     }

好了,明天的内容就分享那么多,不晓得对于 valueOf 和 toString办法你有没有更加粗浅的了解了呢?
如果明天的分享教程你学到了货色,别忘了关注公众号【前端研究所】哦!平时也欢送把你不懂的问题分享到公众号文章的评论注意,说不定下一次就来解说你的疑难了呢!