toString & valueOf

这几天在看《深入理解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()));   //string
console.log(typeof(arr.toString()));   //string
console.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); //true

Object.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"));  //true
console.log(Object.prototype.hasOwnProperty("toString"));  //true

arr.__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"));  //true
console.log(num.valueOf());  //123

Object.prototype.valueOf = function(){
    console.log("Object.prototype.valueOf");
};
console.log(num.valueOf());  //123

num.__proto__.toString = function(){
    console.log("toString");
};
console.log(num.valueOf());  //123

num.__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")); //false
console.log(Object.prototype.hasOwnproperty("valueOf")); //true
console.log(arr.__proto__.hasOwnProperty("toString")); //true

console.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");  //true

arr.__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、undefined
console.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方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值