js引用类型引起的问题

举报
小妖现世 发表于 2021/07/27 00:14:49 2021/07/27
【摘要】 6种基本类型stringnumberboolnullundefinedsymbol通俗易懂的话来讲,js的基本类型使用用来存储值得,它们分配大小是有限度 在定义基本类型变量的时候它们的内存都被分配完成,数字有最大值和最小值null undefined的是固定的值bool 值为 true和falsestring 、number 、boolean 和 symbol 这四种类型统称为原始类型(Pr...

6种基本类型

  • string
  • number
  • bool
  • null
  • undefined
  • symbol

通俗易懂的话来讲,js的基本类型使用用来存储值得,它们分配大小是有限度 在定义基本类型变量的时候它们的内存都被分配完成,

  • 数字有最大值和最小值
  • null undefined的是固定的值
  • bool 值为 true和false

stringnumberbooleansymbol 这四种类型统称为原始类型(Primitive) ,表示不能再细分下去的基本类型;symbol 表示独一无二的值,通过 Symbol 函数调用生成,由于生成的 symbol 值为原始类型,所以 Symbol 函数不能使用 new 调用;nullundefined 通常被认为是特殊值,这两种类型的值唯一,就是其本身。

引用类型

  • 对象

  • 数组

  • 函数

和基本类型区分开来。对象在逻辑上是属性的无序集合或者有序集合,是存放各种值的容器。对象值存储的是引用地址,所以和基本类型值不可变的特性不同,对象值是可变的。

包装对象

我们知道对象拥有属性和方法。但比如字符串这种基本类型值不属于对象为什么还拥有属性和方法呢?

实际上在引用字符串的属性或方法时,会通过调用 new String() 的方式转换成对象,该对象继承了字符串的方法来处理属性的引用,一旦引用结束,便会销毁这个临时对象,这就是包装对象的概念。

不仅仅只是字符串有包装对象的概念,数字和布尔值也有相对应的 new Number()new Boolean() 包装对象。nullundefined 没有包装对象,访问它们的属性会报类型错误。

字符串、数字和布尔值通过构造函数显式生成的包装对象,既然属于对象,和基本类型的值必然是有区别的,这点可以通过 typeof 检测出来。

typeof 'seymoe'                 // 'string'
typeof new String('seymoe')     // 'object'

复制代码

数据类型的判断

  • typeof

  • instanceof

  • Object.prototype.toString()

    typeof
    `typeof` 操作符来判断一个值属于哪种基本类型,返回值是一个string,对null判断有误,认为null是个空指针
    typeof 'seymoe' // 'string' 
    typeof true // 'boolean' 
    typeof 10 // 'number' 
    typeof Symbol() // 'symbol' 
    typeof null // 'object' 
    无法判定是否为 null 
    typeof undefined // 'undefined'
    复制代码

    如果使用 typeof 操作符对对象类型及其子类型,譬如函数(可调用对象)、数组(有序索引对象)等进行判定,则除了函数都会得到 object 的结果。

    typeof {} // 'object'
    typeof [] // 'object'
    typeof(() => {})// 'function'
    复制代码

    由于无法得知一个值到底是数组还是普通对象,显然通过 typeof 判断具体的对象子类型远远不够。

    instanceof
    通过 `instanceof` 操作符也可以对对象类型链上的构造函数进行判定,其原理就是测试构造函数的 `prototype` 是否出现在被检测对象的原型链上。 ``` [] instanceof Array // true ({}) instanceof Object // true (()=>{}) instanceof Function // true ```

    注意:instanceof 也不是万能的。其原理就是测试构造函数

     var a={}
     a.__proto__=[]
     a instanceof Array //true
     a instanceof Object //true
    
    复制代码
    Object.prototype.toString()
    `Object.prototype.toString()` 可以说是判定 JavaScript 中数据类型的终极解决方法了,具体用法请看以下代码:
     Object.prototype.toString.call({})            // '[object Object]'
     Object.prototype.toString.call([])              // '[object Array]'
     Object.prototype.toString.call(() => {})        // '[object Function]'
     Object.prototype.toString.call('seymoe')        // '[object String]'
     Object.prototype.toString.call(1)               // '[object Number]'
     Object.prototype.toString.call(true)            // '[object Boolean]'
     Object.prototype.toString.call(Symbol())        // '[object Symbol]'
     Object.prototype.toString.call(null)            // '[object Null]'
     Object.prototype.toString.call(undefined)       // '[object Undefined]'
    
     Object.prototype.toString.call(new Date())      // '[object Date]'
     Object.prototype.toString.call(Math)            // '[object Math]'
     Object.prototype.toString.call(new Set())       // '[object Set]'
     Object.prototype.toString.call(new WeakSet())   // '[object WeakSet]'
     Object.prototype.toString.call(new Map())       // '[object Map]'
     Object.prototype.toString.call(new WeakMap())   // '[object WeakMap]'
    复制代码

数据类型转换

ToPrimitive
JavaScript 对象转换到基本类型值时,会使用 ToPrimitive 算法,这是一个内部算法,是编程语言在内部执行时遵循的一套规则。

ToPrimitive 算法在执行时,会被传递一个参数 hint,表示这是一个什么类型的运算(也可以叫运算的期望值),根据这个 hint 参数,ToPrimitive 算法来决定内部的执行逻辑。

hint 参数的取值只能是下列 3 者之一:

  • string
  • number
  • default
转换算法
当对象与到基本类型值发生转换时,会按照下面的逻辑调用对象上的方法: **为了进行转换,JavaScript 会尝试查找并调用三个对象方法:**
  1. 调用obj[Symbol.toPrimitive](hint)- 带有符号键Symbol.toPrimitive(系统符号)的方法,如果存在这样的方法,

  2. 否则如果提示是 "string"

    • 尝试obj.toString()obj.valueOf(),无论存在什么。
  3. 否则,如果提示是"number""default"

    • 尝试obj.valueOf()obj.toString(),无论存在什么。
确定 hint

我们提到了 ToPrimitive 算法中用到的 hint 参数,那怎样确定一次运算场景下的 hint 取值是什么呢?很简单----新建一个对象,打印各个运算场景下的 hint 值:

let obj = {
  name: "John",
  money: 1000,

  [Symbol.toPrimitive](hint) {
    console.log(`hint: ${hint}`);
  }
};

alert(obj) // hint: string 
+obj // hint: number
obj + 500 // hint: default



// 一个没有提供 Symbol.toPrimitive 属性的对象,参与运算时的输出结果
var obj1 = {};
console.log(+obj1);     // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ""); // "[object Object]"

// 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性,再来查看输出结果
var obj2 = {
  [Symbol.toPrimitive](hint) {
    if (hint == "number") {
      return 10;
    }
    if (hint == "string") {
      return "hello";
    }
    return true;
  }
};
console.log(+obj2);     // 10      -- hint 参数值是 "number"
console.log(`${obj2}`); // "hello" -- hint 参数值是 "string"
console.log(obj2 + ""); // "true"  -- hint 参数值是 "default"
复制代码
## Symbol.toPrimitive 和 toString/valueOf 方法
并不要求 `Symbol.toPrimitive` 和 `toString/valueOf` 方法必须返回 `hint` 参数值所暗示的类型值。

但要注意下面两点:

  1. Symbol.toPrimitivetoString 方法的返回值必须是基本类型值。
  2. valueOf 方法除了可以返回基本类型值,也可以返回其他类型值。

当我们创建一个普通对象时({}new Object() 的方式等),对象上是不具备 [Symbol.toPrimitive] (方法)属性的。所以,对于普通对象的到基本类型值的运算,一般按照具体场景:

  1. hint 值为 "string" 时,先调用 toStringtoString 如果返回一个基本类型值了,则返回、终止运算;否则接着调用 valueOf 方法。
  2. 否则,先调用 valueOfvalueOf 如果返回一个基本类型值了,则返回、终止运算;否则接着调用 toString 方法。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。