toString & valueOf

来源:互联网 发布:fedora dnf和yum 编辑:程序博客网 时间:2024/05/01 13:42

这几天在看《深入理解ES6》,这本书的作者是Nicholas C. Zakas,也就是我最爱的《高程》作者~~不过还是得把目标中的文章部分写一下。

今天我要讲的是toString()和valueOf()。

toString()

相比于toString(),我更愿意叫它实例对象.proto.toString()。

顾名思义:toString()就是将任何数据类型转换为字符串类型(Null、undefined以及[object Object]除外)。

那么number类型和boolean也会转换吗?答案是当然~

let obj = {    "name" : "suoz",    "age" : 20};let num = 123;let arr = ["1","red",3];let flag = true;console.log(obj.toString());    //[object Object]console.log(typeof(num.toString()));   //stringconsole.log(typeof(arr.toString()));   //stringconsole.log(typeof(flag.toString()));   //string

我觉得这里会疑惑两点
第一点:为什么number和boolean类型的变量可以调用toString()方法。

在另外一篇文章中我说过,number、boolean、string类型可以是属于一种特殊的引用类型(基本包装类型)。

let num = 123;num.toString();  //读取模式

当创建其中一种类型的变量时,在访问该变量的时候(”读取”模式),后台会自动帮助我们完成以下操作。

let num = new Number(123);  //创建一个Number类型的对象num.toString(); //使用该对象的方法num = null; //使用后 立即将该对象销毁

这就在不知不觉中,用户调用了number类型的方法,但是没有任何感觉。

第二点:Object类型调用toString()方法返回的是[object Object]??

其实这就为我们下面的Object.prototype.toString()的介绍作了铺垫。

这里先粗略解释,因为使用toString()方法调用的是该对象[[proto]]指向的原型对象内部的toString(),而不是Object.prototype.toString(),但由于Object类型的对象[[proto]]指向的原型对象还是Object.prototype.toString(),所以它调用了这个方法。

let obj = {"name":"suoz"};console.log(obj.toString());  //[object Object]console.log(obj.__proto__ === Object.prototype); //trueObject.prototype.toString = function(){    console.log("hello world!");};console.log(obj.toString());  //hello world!

其实它内部实现机制是这样的:

实例对象.__proto__.toString = function(){    var str = "";      for(var i=0; i<this.length-1; i++) {          str += this[i] + ",";      }      str += this[this.length-1];      return str;  };


Object.prototype.toString()

通常我们可以使用它来进行引用数据类型的判断

let num = new Number(123);let str = new String("hello");let flag = new Boolean(true);let arr = ["red","pink"];let obj = {"name":"suoz"};let a = null;let b = undefined;console.log(Object.prototype.toString.call(num));  //[object Number]console.log(Object.prototype.toString.call(str));  //[Object String]console.log(Object.prototype.toString.call(flag));  //[Object Boolean]console.log(Object.prototype.toString.call(arr));  //[Object Array]console.log(Object.prototype.toString.call(obj));  //[Object Object]console.log(Object.prototype.toString.call(a));  //[Object NULL]console.log(Object.prototype.toString.call(b));  //[Object Undefined]

每次创建一个对象,都会为该对象的原型对象内部增加toString方法,所以直接调用对象的toString方法时,根据原型链机制,会调用对象[[proto]]指向的原型对象内部toString,而不是Object.prototype内部的toString

let arr = ["red","color"];console.log(arr.toString());    //"red,color"console.log(Object.prototype.toString.call(arr)); //[object Array]console.log(arr.__proto__.hasOwnProperty("toString"));  //trueconsole.log(Object.prototype.hasOwnProperty("toString"));  //truearr.__proto__.toString = function(){    console.log("hello world!");};console.log(arr.toString());  //hello world!arr.__proto__.toString = null;console.log(arr.toString()); //[object Array]

valueOf()

第一种情况:基本数据类型

let num = 123;let str = "hello";let flag = true;
console.log(num.__proto__.hasOwnProperty("valueOf"));  //trueconsole.log(num.valueOf());  //123Object.prototype.valueOf = function(){    console.log("Object.prototype.valueOf");};console.log(num.valueOf());  //123num.__proto__.toString = function(){    console.log("toString");};console.log(num.valueOf());  //123num.__proto__valueOf = function(){    console.log("num.__proto__.valueOf");};console.log(num.valueOf()); //num.__proto__.valueOf

创建一个基本数据类型的值时,对象[[proto]]指向原型对象内部有valueOf,所以会覆盖Object.prototype.valueOf方法。(并且内部的valueOf和Object.prototype.valueOf方法内部实现机制不一样,前者不会调用toString,而后者会调用原型对象内部的toString方法,下面会仔细讲解)

第二种情况分析在下面。


Object.prototype.valueOf()

定义:将对象转换为原始值(number、string、boolean类型)。你很少需要自己调用此函数,当遇到一种需要转换为原始值的情况时,系统会自动调用。

默认情况下, valueOf() 会被每个对象Object继承。每一个内置对象都会覆盖这个方法为了返回一个合理的值,如果对象没有原始值,valueOf() 就会返回对象自身。

第二种情况:引用数据类型

let obj = {"name":"suoz"};let arr = ["red","pink"];
console.log(arr.__proto__.hasOwnProperty("valueOf")); //falseconsole.log(Object.prototype.hasOwnproperty("valueOf")); //trueconsole.log(arr.__proto__.hasOwnProperty("toString")); //trueconsole.log(arr.valueOf()); //["red","color"]Object.prototype.valueOf = function(){    console.log("valueof");};console.log(arr.valueOf()); //valueOf、undefined

验证创建一个引用类型对象(除了Object类型),该实例对象[[proto]]指向的原型对象内部没有valueOf方法,只有toString方法。

Object.prototype.toString = function(){    console.log("Object toString");};console.log(arr.valueOf()); //["red","pink"]console.log(arr == "red,pink");  //truearr.__proto__.toString = function(){    console.log("__proto__ toString");    return "red,pink";};console.log(arr.valueOf()); //__proto__ toString、["red","pink"]console.log(arr == "red,pink");  //__proto__ toString、true

验证了Object类型和Array类型都是用的Object.prototype.valueOf(),而Object.prototype.valueOf内部实现机制中包括调用了实例对象原型对象的toString方法

这里我疑惑的是为什么”==”返回”red,pink”,而调用valueOf()返回的是[“red”,”pink”],它们不都是直接使用了Object.prototype.valueOf吗(并且该方法内部又调用了实例对象[[proto]]指向原型对象的toString方法吗)?

Object.prototype.valueOf = function(){    console.log("Object valueOf");};console.log(arr.valueOf()); //Object valueOf、undefinedconsole.log(arr == "red,pink");  //Object valueOf、false

我觉得这里疑惑点还是得考了解一下Object.prototype.valueOf内部实现机制(源代码)去解决。(个人认为可以暂时性按下面的代码理解内部,后续补充)

arr.__proto__.toString = function(){    return "red,pink";};Object.prototype.valueOf = function(){    var str = arr.__proto__.toString.call(this);    if(//如果这里是"=="){        //return str;  //"red,pink"    }else if(//如果这里是直接调用valueOf){        //return this;  //返回对象本身 ["red","pink"]    }};

什么时候调用toString或valueOf(后续补充)

总结:

  1. 使用Object.prototype.toString().call()方法,可以判断任何数据类型。
  2. 创建一个对象,该对象内部的方法toString会覆盖Object.prototype.toString方法,因此直接调用toString的时候,根据原型链搜索属性机制,会搜索该对象[[proto]]指向的原型对象内部的toString方法。
  3. [[proto]]指向的原型对象内部的toString方法 与 Object.prototype.toString方法内部实现机制是不一样的。前者是返回字符串类型值,后者是返回数据的类型[object XXX]
  4. 对于基本数据类型来说,创建一个变量或者对象,内部都会有valueOf方法覆盖Object.prototoye.valueOf方法。
  5. 对于引用类型来说,创建一个对象,内部不会覆盖方法,所以调用valueOf还是使用的Object.prototype.valueOf方法。
  6. 对于基本类型来说,覆盖的valueOf方法不会隐式调用toString方法
  7. 对于引用类型来说,Object.prototype.valueOf方法内部会隐式调用toString方法。
原创粉丝点击