JavaScript类型

2023-05-16

  • 为什么有的编程规范要求用 void 0 代替 undefined?
  • 字符串有最大长度吗?
  • 0.1 + 0.2 不是等于 0.3 么?为什么 JavaScript 里不是这样的?
  • ES6 新加入的 Symbol 是个什么东西?
  • 为什么给对象添加的方法能用在基本类型上?

基本类型

基本类型(基本数值、基本数据类型)是一种既非对象也无方法的数据。在 JavaScript 中,共有7种基本类型:string,number,bigint,boolean,null,undefined,symbol (ECMAScript 2016新增)。

  • 多数情况下,基本类型直接代表了最底层的语言实现。
  • 所有基本类型的值都是不可改变的。但需要注意的是,基本类型本身和一个赋值为基本类型的变量的区别。变量会被赋予一个新值,而原值不能像数组、对象以及函数那样被改变。
// 使用字符串方法不会改变一个字符串
var bar = "baz";
console.log(bar);               // baz
bar.toUpperCase();
console.log(bar);               // baz

// 使用数组方法可以改变一个数组
var foo = [];
console.log(foo);               // []
foo.push("plugh");
console.log(foo);               // ["plugh"]

// 赋值行为可以给基本类型一个新值,而不是改变它
bar = bar.toUpperCase();       // BAZ

基本类型值可以被替换,但不能被改变。

JavaScript是如何处理基本类型的

// 基本类型
let foo = 5;

// 定义一个貌似可以改变基本类型值的函数
function addTwo(num) {
   num += 2;
}
// 和前面的函数一样
function addTwo_v2(foo) {
   foo += 2;
}

// 调用第一个函数,并传入基本类型值作为参数
addTwo(foo);
// Getting the current Primitive value
console.log(foo);   // 5

// 尝试调用第二个函数...
addTwo_v2(foo);
console.log(foo);   // 5
  • addTwoaddTwo_v2函数调用时,JavaScript会检查标识符foo的值,从而准确无误的找到第一行实例化变量的声明语句。
  • 找到以后,JavaScript将其作为参数传递给函数的形参。
  • 在执行函数体内语句之前,JavaScript会将传递进来的参数(基本类型的值)复制一份,创建一个本地副本。这个副本只存在于该函数的作用域中,我们能够通过指定在函数中的标识符访问到它(addTwo中的numaddTwo_v2中的foo)。
  • 接下来,函数体中的语句开始执行:
    • 第一个函数中,创建了本地num参数,num的值加2,但这个值并不是原来的foo的值。
    • 第二个函数中,创建了本地参数foo,并将它的值加2,这个值不是外部foo的值。在这种情况下,外部的foo变量不能以任何方式被访问到。这是因为JavaScript的词法作用域(lexical scoping)所导致的变量覆盖,本地的变量foo覆盖了外部的变量foo。欲知详情,请参阅闭包。
  • 综上所述,函数中的任何操作都不会影响到最初的foo,我们操作的只不过是它的副本

我的理解

let foo = 5; 
function addTwo_v2(foo) {
   foo += 2;
}
addTwo(foo); 函数的调用执行过程如下
{ 
let foo = foo  //创建一个名为形参的本地变量,只不过这个形参的变量名与外部的foo变量名相同
因为JavaScript的词法作用域(lexical scoping)所导致的变量覆盖
本地的变量foo覆盖了外部的变量foo,在这种情况下,外部的foo变量不能以任何方式被访问到
且在foo = foo的过程中,将参数foo(外部变量)的值的值复制了一份给本地的foo
foo += 2 //的过程中,使用的都是内部变量foo+运算执行的过程中,是重新创建一个number基本类型,它的值为2
然后计算5+2 = 7,然后将7这个number基本类型的值重新赋值给了foo,
而不是把5这个number基本类型的值改为7
}
console.log(foo) //这时访问的是外部变量的foo,第一行的foo,所以打印的是5

也就是说在let a = 5之后,除非重新给a赋值,否则a的值一直都是5,不会改变

这就是为什么说所有基本类型的值都是无法改变的

Undefined、Null

undefined全局对象的一个属性。也就是说,它是全局作用域的一个变量。undefined的最初值就是原始数据类型undefined。

A variable that has not been assigned a value is of type undefined. A method or statement also returns undefined if the variable that is being evaluated does not have an assigned value. A function returns undefined if a value was not returned.

  • 一个还没有被赋值的变量的类型是undefined。如果一个正在被评估的方法或者声明(函数声明,变量声明)没有被赋值,那么也会返回undefined。一个函数如果没有返回值,也会返回undefined。

我的理解

  • 和Number一样,Undefined的是一种数据类型,可以理解为一种type,比如5的type是Number,而undefined的type是Undefined。也就是说Number可以有很多种值,而Undefined只有一种值。
  • 上面所说的返回undefined,返回的就是一个值。这个值也可以被赋值给别的变量。
  • 同时undefined也是全局作用域中的一个变量,这个变量的值就是undefined。类似于const undefined = undefined

为什么有的编程规范要求用 void 0 代替 undefined?

因为 JavaScript 的代码 undefined 是一个变量,而并非是一个关键字,而变量是可以被重新赋值的,所以undefined就有可能在非全局作用域中被改写。

let a = 1
{
  let undefined = a
  let b = undefined
  let c = void 0
  console.log(b)  //  1
  console.log(c)  //undefined
}
undefined = a
a = undefined
console.log(a)  // undefined

由此可以看出,在非全局作用域中undefined变量有可能被改写。void 0运算后返回的是undefined这个值,而不是变量,所以我们为了避免无意中被篡改,建议使用 void 0 来获取 undefined 值。

Null

null 特指对象的值未设置。它是 JavaScript 基本类型 之一,在布尔运算中被认为是falsy。
null 是一个字面量,不像 undefined,它不是全局对象的一个属性。null 是表示缺少的标识,指示变量未指向任何对象。把 null 作为尚未创建的对象,也许更好理解。在 API 中,null 常在返回类型应是一个对象,但没有关联的值的地方使用。

Undefined 跟 Null 有一定的表意差别,Null 表示的是:“定义了但是为空”。所以,在实际编程时,我们一般不会把变量赋值为 undefined,这样可以保证所有值为 undefined 的变量,都是从未赋值的自然状态。
Null 类型也只有一个值,就是 null,它的语义表示空值,与 undefined 不同,null 是 JavaScript 关键字,所以在任何代码中,你都可以放心用 null 关键字来获取 null 值。

我的理解

null可以理解为一个空指针指向的地方,在对象地址赋值给变量之前的变量值
let a = {}
执行后,a 的值是指向这个空对象的地址
这个过程可以理解为:

  1. let a = null 先给a赋值为null
  2. 执行new Object,返回一个object对象,放在内存中
  3. 将内存中对象的地址赋值给a

所以let a = null是已经定义了a变量,但a的值为null,也就是空。

字符串有最大长度吗?

String 用于表示文本数据。String 有最大长度是 2^53 - 1,这个所谓最大长度,并不完全是常说的字符数。
因为 String 的意义并非“字符串”,而是字符串的 UTF16 编码,我们字符串的操作 charAt、charCodeAt、length 等方法针对的都是 UTF16 编码。所以,字符串的最大长度,实际上是受字符串的编码长度影响的。

JavaScript 中的字符串是永远无法变更的,一旦字符串构造出来,无法用任何方式改变字符串的内容,所以字符串具有值类型的特征。

JavaScript 字符串把每个 UTF16 单元当作一个字符来处理。

Number

JavaScript 中的 Number 类型基本符合 IEEE 754-2008 规定的双精度浮点数规则,但是 JavaScript 为了表达几个额外的语言场景(比如不让除以 0 出错,而引入了无穷大的概念),规定了几个例外情况:

  • NaN,Not a Number;
  • Infinity,无穷大;
  • -Infinity,负无穷大。
let a = NaN
console.log(a) //NaN
let x = 0
console.log(1/x) //Infinity
console.log(-1/x) //- Infinity

值得注意的是,JavaScript 中有 +0 和 -0,在加法类运算中它们没有区别,但是除法的场合则需要特别留意区分,“忘记检测除以 -0,而得到负无穷大”的情况经常会导致错误,而区分 +0 和 -0 的方式,正是检测 1/x 是 Infinity 还是 -Infinity。

let x = +0
let y = -0
console.log(1/x) //Infinity
console.log(1/y) //-Infinity
console.log(x == y) //true
console.log(x === y) //true

NaN

NaN 是一个全局对象的属性。

NaN 属性的初始值就是 NaN,和 Number.NaN 的值一样。在现代浏览器中(ES5中), NaN 属性是一个不可配置(non-configurable),不可写(non-writable)的属性。

NaN如果通过 ==!==== 、以及 !==与其他任何值比较都将不相等 – 包括与其他 NAN值进行比较。必须使用 Number.isNaN() 或 isNaN() 函数。在执行自比较之中:NaN,也只有NaN,比较之中不等于它自己。

let a = NaN
console.log(a == NaN)   //false
console.log(a === NaN)  //false
console.log(a != NaN)  //true
console.log(a !== NaN) //true
console.log(NaN == NaN)  //false
console.log(NaN === NaN)  //false
console.log(NaN != NaN) //true
console.log(NaN !== NaN) //true

Number.NaN === NaN; // false
isNaN(NaN);         // true
isNaN(Number.NaN);  // true

Number.NaN !== NaN //true

我的理解

每个NaN都不一样,但是可以通过isNaN()函数判断它是不是NaN

  • 请注意isNaN()和Number.isNaN()之间的区别:如果当前值是NaN,或者将其强制转换为数字后将是NaN,则前者将返回true。而后者仅当值当前为NaN时才为true:
isNaN('hello world');        // true
Number.isNaN('hello world'); // false
Number.isNaN(NaN); // true

说明isNaN()会进行强制类型转换,而Number.isNaN()不会进行转换,只能判断NaN

0.1 + 0.2 不是等于 0.3 么?为什么 JavaScript 里不是这样的?

根据浮点数的定义,非整数的 Number 类型无法用 = 也不行) 来比较,浮点数运算的精度问题导致等式左右的结果并不是严格相等,而是相差了个微小的值。

0.1+0.2 == 0.3 //false
0.2+0.3 == 0.5 //true
  • 十进制小数会被转化为二进制浮点数进行计算,如果二进制表示超过52位,则会进行舍入,造成舍入误差。
  • 小数计算过程中,某些小数会因为舍入而不精确,同时计算出结果后,也有可能对结果进行舍入,从而导致两个数值结果不等。

正确的比较方法是使用 JavaScript 提供的最小精度值,绝对值小于e:

  console.log( Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON); //true

检查等式左右两边差的绝对值是否小于最小精度,才是正确的比较浮点数的方法。这段代码结果就是 true 了。

ES6 新加入的 Symbol 是个什么东西?

symbol 是一种基本数据类型 (primitive data type)。Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。

var sym1 = Symbol();
var sym2 = Symbol('foo');
var sym3 = Symbol('foo');
console.log(sym2 == sym3 ) //false

Symbol(“foo”) 不会强制将字符串 “foo” 转换成symbol类型。它每次都会创建一个新的 symbol类型。

众所周知的 symbols

除了自己创建的symbol,JavaScript还内建了一些在ECMAScript 5 之前没有暴露给开发者的symbol,它们代表了内部语言行为。

  • Symbol.iterator属性
    一个返回一个对象默认迭代器的方法。被 for…of 使用。
  • Symbol.for(key)方法
    使用给定的key搜索现有的symbol,如果找到则返回该symbol。否则将使用给定的key在全局symbol注册表中创建一个新的symbol。

Object

对象 指包含数据和用于处理数据的指令的数据结构.

为什么给对象添加的方法能用在基本类型上?

  • 在 JavaScript 中,对象的定义是“属性的集合”。属性分为数据属性和访问器属性,二者都是 key-value 结构,key 可以是字符串或者 Symbol 类型。

  • JavaScript 中的“类”仅仅是运行时对象的一个私有属性,而 JavaScript 中是无法自定义类型的

  • JavaScript 中的几个基本类型,都在对象类型中有一个内置对象
    Number;String;Boolean;Symbol。

  • 3 与 new Number(3) 是完全不同的值,它们一个是 Number 类型, 一个是对象类型。

3 == new Number(3) //true
3 === new Number(3) //false

Number、String 和 Boolean,三个构造器是两用的,当跟 new 搭配时,它们产生对象,当直接调用时,它们表示强制类型转换。

所以答案就是. 运算符提供了装箱操作,它会根据基础类型构造一个临时对象,使得我们能在基础类型上调用对应对象的方法。

类型转换

  • 很多实践中推荐禁止使用“ ==”,而要求程序员进行显式地类型转换后,用 === 比较。

StringToNumber

  • 字符串到数字的类型转换,存在一个语法结构,类型转换支持十进制、二进制、八进制和十六进制

  • JavaScript 支持的字符串语法还包括正负号科学计数法,可以使用大写或者小写的 e 来表示:1e3,-1e-2。

  • 多数情况下,Number() 是比 parseInt 和 parseFloat 更好的选择。

NumberToString

  • 在较小的范围内,数字到字符串的转换是完全符合你直觉的十进制表示。当 Number 绝对值较大或者较小时,字符串表示则是使用科学计数法表示的。

装箱转换

每一种基本类型 Number、String、Boolean、Symbol 在对象中都有对应的类,所谓装箱转换,正是把基本类型转换为对应的对象

    var symbolObject = (function(){ return this; }).call(Symbol("a"));

    console.log(typeof symbolObject); //object
    console.log(symbolObject instanceof Symbol); //true
    console.log(symbolObject.constructor == Symbol); //true

装箱机制会频繁产生临时对象,在一些对性能要求较高的场景下,我们应该尽量避免对基本类型做装箱转换。

  • 使用内置的 Object 函数,我们可以在 JavaScript 代码中显式调用装箱能力。
    var symbolObject = Object(Symbol("a"));

    console.log(typeof symbolObject); //object
    console.log(symbolObject instanceof Symbol); //true
    console.log(symbolObject.constructor == Symbol); //true

拆箱转换

  • 在 JavaScript 标准中,规定了 ToPrimitive 函数,它是对象类型到基本类型的转换(即,拆箱转换)。对象到 String 和 Number 的转换都遵循“先拆箱再转换”的规则。
  • 通过拆箱转换,把对象变成基本类型,再从基本类型转换为对应的 String 或者 Number。
  • 拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueOf 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。

    var o = {
        valueOf : () => {console.log("valueOf"); return {}},
        toString : () => {console.log("toString"); return {}}
    }

    o * 2
    // valueOf
    // toString
    // TypeError

    String(o)
    // toString
    // valueOf 
    // TypeError

number拆箱,先执行valueOf,再执行toString
string拆箱,先执行toString,再执行valueOf

类型对照

image

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript类型 的相关文章

  • React 组件渲染被调用两次而不改变状态

    我正在渲染一个简单的反应组件 其中没有设置状态和道具 我在渲染函数中仅将文本记录到控制台一次 但它被记录了两次 rendering counter rendering counter 下面是counter js组件的代码 import Re
  • 使用 jquery 淡入/淡出

    我正在研究我的学生项目 我是新的 jquery 对于该项目 我必须使用 jquery 来增强一些功能 并且我已经学到了很多来执行基本任务 但我陷入了一些非常令人困惑的事情 我的一个脚本实际上在鼠标悬停在功能上时更改了 div 容器的图像 功
  • 少吞咽然后缩小任务

    我必须在 gulp 中执行 2 个步骤 减少 css 文件格式 缩小生成的 css 文件 这是我的吞咽文件 var gulp require gulp watch require gulp watch less require gulp l
  • 使用智能菜单jquery打印json

    menu name Computers children name Notebook children name Apple name Windows name Tablets children name Apple name Androi
  • 重新排序 Magento JavaScript 包含 (addJs)

    我会保持简单 在我的产品页面上 我需要删除prototype js 文件并将其替换为最新版本的prototype 到目前为止 我已经使用 local xml 成功替换了它
  • Javascript 自时间戳以来经过的时间

    我试图通过将其存储在变量中来 缓存 一些信息 如果 2 分钟过去了 我想获取 实时 值 调用 url 如果 2 分钟还没有过去 我想从变量中获取数据 我基本上想要的是 if time passed is less than 2 minute
  • 使用 Charts.js 禁用动画

    我在使用 Charts js 关闭动画时遇到一些问题 这是我的代码 var pieData value 30 color F38630 value 50 color E0E4CC value 100 color 69D2E7 var myP
  • 如何在Javascript中声明静态变量[重复]

    这个问题在这里已经有答案了 在下面的代码中 我希望有一个计数器来跟踪创建的 Person 对象的数量 这段代码没有这样做 我该如何实现呢 function Person this name Peter this counter this c
  • Chart.js - 如何将数组集合推入数据集

    我一直在尝试多种方法将数组集合推送到数据集中 任何人都可以帮助我根据下面的代码将数组推入堆积图表中 这是例子 Codepen 堆叠栏 https codepen io narendrajadhav pen abzpWam JavaScrip
  • 强制执行 show.bind

    我有一个包含数据的表 当从另一个视图触发事件时 我希望视图检查 show bind 语句 问题是该事件没有更改当前视图中的任何数据 foo html tr p canBeRemoved p tr 我正在使用 EventAggregator
  • 尝试使用 CasperJS 跟踪 iframe 中的链接

    我正在尝试使用CasperJS http casperjs org index html跟踪 iframe 中的链接 但我似乎无法获取 iframe 的文档 这是使用我找到的 iframe 示例页面进行的测试 第三个 iframe 有一个名
  • jQuery 中如何判断 JSON 对象是否为空

    我有以下 JSON meta limit 20 next null offset 0 previous null total count 0 objects 我对对象感兴趣 我想知道对象是否为空并显示警报 像这样的东西 success fu
  • 使用 jQuery 更改 SVG 元素的“xlink:href”属性

    我正在尝试使用单击事件更改 xlink href 属性 到目前为止它部分有效 这就是我正在做的 HTML a href class ui btn ui corner all ui shadow editIcon style text ali
  • 单击时突出显示文本(javascript jquery html)

    当您在所有浏览器中双击某个单词时 它们会自动突出显示单击下的单词 但是否有可能找到一种方法exact单击一下就会发生同样的事情吗 我想这涉及到的事情可能是 TextRange 的东西 对所有段落 或整个正文或 div 的 onclick 做
  • 用于图形操作的 Javascript 库

    有没有建议的 javascript 替代 pythonpygraph http code google com p python graph or NetworkX http networkx lanl gov 应该注意的是 可视化不是必需
  • Bootstrap 3 - 模态背景不会根据模态对话框的高度调整大小?

    我将一个表单放入模式中 并尝试在用户触发单选按钮时显示表单的一些隐藏字段 显示隐藏字段后 模态自动重新缩放的高度 但模态背景的高度不能用作模态对话框 我该如何解决它 div class modal fade div class modal
  • 限制线的长度

    我正在尝试画一条代表 弹弓 的线 并且希望它具有最大拉伸长度 在 p5 中 我在位置和位置之间画了一条线 line posA x posA y posB x posB y posA 是鼠标 x 和 y posB 是画布上圆的位置 我想要做的
  • python 函数返回 javascript date.getTime()

    我正在尝试创建一个简单的 python 函数 它将返回与 javascript 相同的值new Date getTime 方法 如所写here http www w3schools com js js dates asp javascrip
  • 保留对 React 状态变量的“引用”

    据我所知 Javascript 中没有指针 我有以下问题 但我想知道是否有一个解决方案让我无法解决 解决方案可能是普通的 Javascript 或者像 Context API 这样的 React js 钩子 useContext 或者更多
  • 调试客户端时使用 Chrome/Firefox

    我正在使用带有 getUserMedia 的相机 但出现了一些需要修复的错误 问题是 Visual Studio 只允许我使用 IE 调试 JavaScript 我的意思是命中断点 而 IE 不支持 getUserMedia 如果您想在 I

随机推荐