【你不知道的javaScript 中卷 笔记1】javaScript中的类型与值
一、类型与值
1.0 javaScript 七种内置类型:
- 空值(null)
- 未定义(undefined)
- 布尔值( boolean)
- 数字(number)
- 字符串(string)
- 对象(object)
- 符号(symbol,ES6 中新增)
1.1 typeof 运算符查看值的类型
typeof
对null
的处理显示“object”,并非显示“null”,typeof null === "object"; // true
这是javaScript
语言的一个bug
,需要使用复合条件来检测 null
值的类型
var a = null;
(!a && typeof a === "object"); // true
function
不是 JavaScript
的一个内置类型,它是 object
的一个“子类型”,但是typeof
处理函数显示"function"
typeof function a(){ /* .. */ } === "function"; // true
对于数组类型,也是 object 的一个“子类型”typeof [1,2,3] === "object"; // true
1.2 undefined 和 undeclared
var a;
typeof a; // "undefined"
typeof b; // "undefined"
b 是 一个undeclared变量,但typeof b并没有报错,因为typeof有一个特殊的安全防范机制。利用typeof的安全防范机制,可以安全的判断,即使变量不存在也不会报错阻断程序如:
// 如果DEBUG不存在js引擎就会报错
if (DEBUG) {
console.log( "Debugging is starting" );
}
// 这样是安全的
if (typeof DEBUG !== "undefined") {
console.log( "Debugging is starting" );
}
1.3 数组
在 JavaScript 中,数组可以容纳任何类型的值,可以是字符串、 数字、对象(object),甚至是其他数组
var a = [ 1, "2", [3] ];
a.length; // 3
a[0] === 1; // true
a[2][0] === 3; // true
数组声明后即可向其中加入值,不需要预先设定大小
var a = [ ];
a.length; // 0
a[0] = 1;
a[1] = "2";
a[2] = [ 3 ];
a.length; // 3
含有空白或空缺单元的数组,可以正常运行
var a = [ ];
a[0] = 1;
// 此处没有设置a[1]单元
a[2] = [ 3 ];
a[1]; // undefined
a.length; // 3
可以包含字符串键值和属性,但这些并不计算在数组长度内
var a = [ ];
a[0] = 1;
a["foobar"] = 2;
a.length; // 1
a["foobar"]; // 2
a.foobar; // 2
如果字符串键值能够被强制类型转换为十进制数字的话,就会被当作数字索引来处理。
var a = [ ];
a["13"] = 42;
a.length; // 14
类数组转换成数组,ES5中可以通过数组的工具函数如Array.prototype.slice.call()
ES6工具函数 Array.from(..)
1.4 字符串
字符串和数组很类似,有length 属性以及 indexOf(..)和 concat(..) 方法,但javaScript 中字符串是不可变的,字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串。可以通过“借用”数组的非变更方法来处理字符串:
var a = "foo"
var c = Array.prototype.join.call(a,"-")
var d = Array.prototype.map.call(a,(v)=>{
return v.toUpperCase() + '.'
}).join("")
join
数组原型对象上的方法,可以将数组转换成字符串,
var a = "foo"
Array.prototype.reverse.call(a)
console.log(a)
在字符串上使用数组的反转函数会报错,因为字符串一旦初始化就不会改变TypeError: Cannot assign to read only property '0' of object '[object String]'
1.5 数字
tofixed(..)
方法可指定小数部分的显示位数
var a = 42.59;
a.toFixed( 0 ); // "43"
a.toFixed( 1 ); // "42.6"
a.toFixed( 2 ); // "42.59"
a.toFixed( 3 ); // "42.590"
a.toFixed( 4 ); // "42.5900"
toPrecision(..)
方法用来指定有效数位的显示位数:
var a = 42.59;
a.toPrecision(1 ); // "4e+1"
a.toPrecision( 2 ); // "43"
a.toPrecision( 3 ); // "42.6"
上面的方法不仅适用于数字变量,也适用于数字常量
// 无效语法,. 被视为常量 42. 的一部分
42.toFixed( 3 ); // SyntaxError
// 下面的语法都有效:
(42).toFixed( 3 ); // "42.000"
0.42.toFixed( 3 ); // "0.420"
42..toFixed( 3 ); // "42.000"
//42..tofixed(3) 则没有问题,因为第一个 . 被视为 number 的一部分,第二个 . 是属性访问 运算符
42 .toFixed(3); // "42.000"
从 ES6 开始,严格模式(strict mode)不再支持 0363 八进制格式(新格式如 下)。0363 格式在非严格模式(non-strict mode)中仍然受支持
ES6 支持以下新格式:
0o363; // 243的八进制
0O363; // 同上
0b11110011; // 243的二进制
0B11110011; // 同上
0.1 + 0.2 === 0.3; // false
二进制浮点数中的 0.1 和 0.2 并不是十分精确,它们相加的结果并非刚好等于 0.3,而是一个比较接近的数字 0.30000000000000004,所以条件判断结果为 false。
怎样来判断 0.1 + 0.2 和 0.3 是否相等呢?
设置一个误差范围值,通常称为“机器精度”(machine epsilon),对javaScript
的数字来说,这个值通常是 2^-52 (2.220446049250313e-16)
。
从 ES6 开始,该值定义在 Number.EPSILON
中
if (!Number.EPSILON) {
Number.EPSILON = Math.pow(2,-52);
}
使用 Number.EPSILON 来比较两个数字是否相等
function numbersCloseEnoughToEqual(n1,n2) {
return Math.abs( n1 - n2 ) < Number.EPSILON;
}
var a = 0.1 + 0.2;
var b = 0.3;
numbersCloseEnoughToEqual( a, b ); numbersCloseEnoughToEqual( 0.0000001, 0.0000002 ); // false
Number. MAX_VALUE
:最大浮点数大约是 1.798e+308
Number.MIN_VALUE
:最小浮点数大约是 5e-324,无限接近于 0
整数的安全范围
ES6: Number.MAX_SAFE_INTEGER:
“安全”呈现的最大整数是2^53 - 1,即9007199254740991
ES6: Number. MIN_SAFE_INTEGER
:最 小 整 数 是 -9007199254740991
由于 JavaScript 的数字类型无法精确呈现 64 位数值,所以必须将它们保存(转换)为字符串
整数检测
ES6 中的 Number.isInteger(..)
:检测一个值是否是整数
polyfill Number.isInteger(..) 方法
if (!Number.isInteger) {
Number.isInteger = function(num) {
return typeof num == "number" && num % 1 == 0;
};
}
ES6 中的 Number.isSafeInteger(..) 方法:检测一个值是否是安全的整数
polyfill Number.isSafeInteger(..) 方法:
if (!Number.isSafeInteger) {
Number.isSafeInteger = function(num) {
return Number.isInteger( num ) &&
Math.abs( num ) <= Number.MAX_SAFE_INTEGER;
};
}
32 位有符号整数
a | 0可以将变量a中的数值转换为32位有符号整数,因为数位运算符|只适用于32位 整数,因此与 0 进行操作即可截取 a 中 的 32 位数位。
1.6 特殊数值
undefined
- 指从未赋值
- 是一个标识符,可以被当作变量来使用和赋值
- void 运算符,undefined 是一个内置标识符(除非被重新定义,见前面的介绍),它的值为 undefined, 通过 void 运算符即可得到该值。
var a = 42;
console.log( void a, a ); // undefined 42
//void并不改变表达式的结果, 只是让表达式不返回值:
null
- null 指空值(empty value)
- 曾赋过值,但是目前没有值
- null 是一个特殊关键字,不是标识符
NaN
- 不是数字的数字
- 是一个“警戒值”(sentinel value,有特殊用途的常规值),用于指出数字类型中的错误 情况,即“执行数学运算没有成功,这是失败后返回的结果”。
- NaN是一个特殊值,它和自身不相等,是唯一一个非自反
var a = 2 / "foo";
var b = "foo";
a; // NaN b; "foo"
window.isNaN( a ); // true
window.isNaN( b );// true
isNaN(..) 检查参数是否不是 NaN,也不是数字,"foo" 不是一个数字,但是它也不是 NaN
ES6工具函数 Number.isNaN(..)
if (!Number.isNaN) {
Number.isNaN = function(n) {
return (
typeof n === "number" && window.isNaN( n )
}
var a = 2 / "foo"
var b ="foo"
); };
Number.isNaN( a ); // true
Number.isNaN( b );// false
Infinity
var a = 1 / 0; // Infinity
var b = -1 / 0; // -Infinity
零值
为什么会有 -0 ?
有些应用程序中的数据需要以级数形式来表示(比如动画帧的移动速度),数字的符号位 (sign)用来代表其他信息(比如移动的方向)。此时如果一个值为 0 的变量失去了它的符号位,它的方向信息就会丢失。所以保留 0 值的符号位可以防止这类情况发生。
区分 -0 和 0方法:
function isNegZero(n) {
n = Number( n );
return (n === 0) && (1 / n === -Infinity);
}
isNegZero( -0 ); // true
isNegZero( 0 / -3 ); // true
isNegZero( 0 );// false
特殊等式
Es6工具方法 Object.is(..)
var a = 2 / "foo";
var b = -3 * 0;
Object.is( a, NaN );
Object.is( b, -0 );
Object.is( b, 0 );
if (!Object.is) {
Object.is = function(v1, v2) {
// 判断是否是-0
if (v1 === 0 && v2 === 0) {
return 1 / v1 === 1 / v2;
}
// 判断是否是NaN
if (v1 !== v1) {
return v2 !== v2;
}
// 其他情况
return v1 === v2;
};
}
1.7 值和引用
var a = 2;
var b = a; // b是a的值的一个副本
b++;
a; // 2
b; // 3
var c = [1,2,3];
var d = c; // d是[1,2,3]的一个引用
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]
简单值(即标量基本类型值,scalar primitive)总是通过值复制的方式来赋值 / 传递,包括 null、undefined、字符串、数字、布尔和 ES6 中的 symbol。
复合值(compound value)——对象和函数,则总 是通过引用复制的方式来赋值 / 传递。
引用指向的是值本身而非变量,所以一个引用无法更改另一个引用的指向
var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]
// 然后
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]
b=[4,5,6] 并不影响 a 指向值 [1,2,3],除非 b 不是指向数组的引用,而是指向 a 的指针,在 javaScript 中不存在指针
function foo(x) {
x.push( 4 );
x; // [1,2,3,4]
// 然后
x = [4,5,6];
x.push( 7 );
x; // [4,5,6,7]
}
var a = [1,2,3];
foo( a );
a; // 是[1,2,3,4],不是[4,5,6,7]
向函数传递 a 的时候,实际是将引用 a 的一个复本赋值给 x,而 a 仍然指向 [1,2,3]。 在函数中我们可以通过引用x来更改数组的值(push(4)之后变为[1,2,3,4])。但x = [4,5,6] 并不影响 a 的指向,所以 a 仍然指向 [1,2,3,4]。不能通过引用 x 来更改引用 a 的指向,只能更改 a 和 x 共同指向的值。
foo( a.slice() );
slice(..) 不带参数会返回当前数组的一个浅复本(shallow copy)。由于传递给函数的是指向该复本的引用,所以 foo(..) 中的操作不会影响 a 指向的数组。
如果要将标量基本类型值传递到函数内并进行更改,就需要将该值封装到一个复合值(对象、数组等)中,然后通过引用复制的方式传递。
function foo(wrapper) {
wrapper.a = 42;
}
var obj = {
a: 2
};
foo( obj );
obj.a; // 42
总结
- 赋值 / 参数传递是通过引用复制还是值复制完全由值的类型来决定,所以使用哪种类型也间接决定了赋值 / 参数传递的方式。
- null 类型只有一个值 null,undefined 类型也只有一个值 undefined。所有变量在赋值之 前默认值都是 undefined。void 运算符返回 undefined。
- 数字类型有几个特殊值,包括 NaN(意指“not a number”,更确切地说是“invalid number”)、+Infinity、-Infinity 和 -0。
【你不知道的javaScript 中卷 笔记1】javaScript中的类型与值的更多相关文章
- JavaScript进阶内容笔记1:各种对象类型判断
该文章主要用来介绍JavaScript中常用的一些对象检测判断方法,整理资源来自书本和网络,如有错误或说明不详之处,望评论提出,本菜定提名感谢……(本文章知识比较基础,大牛请提些意见再绕道,三克油^_ ...
- 【你不知道的javaScript 中卷 笔记2】javaScript中的类型转换
1.1 对象内部属性 [[Class]] 常见的原生函数: String() Number() Boolean() Array() Object() Function() RegExp() Date( ...
- 《高性能JavaScript》学习笔记——日更中
------------------2016-7-20更------------------ 最近在看<高性能JavaScript>一书,里面当中,有讲很多提高js性能的书,正在看的过程中 ...
- EF中System.Data.Entity.Internal.AppConfig的类型初始值设定项引发异常
使用Entity的时候遇到的一个错 问题出在项目的App.config中 解决: 1.configSections要写在最顶端 2. 当中的incariantName会变成incariantNodeN ...
- 《你不知道的JavaScript(中卷)》读书笔记
中卷有点无聊,不过也是有干货的,但是好像要背一下的样子.不过作者大大都夸我是“优秀的开发人员”了,肯定要看完呀~~ 开发人员觉得它们太晦涩,很难掌握和运用,弊(导致bug)大于利(提高代码可读性).这 ...
- 从头开始学JavaScript 笔记(一)——基础中的基础
原文:从头开始学JavaScript 笔记(一)--基础中的基础 概要:javascript的组成. 各个组成部分的作用 . 一.javascript的组成 javascript ECMASc ...
- 《编写可维护的javascript》读书笔记(中)——编程实践
上篇读书笔记系列之:<编写可维护的javascript>读书笔记(上) 上篇说的是编程风格,记录的都是最重要的点,不讲废话,写的比较简洁,而本篇将加入一些实例,因为那样比较容易说明问题. ...
- 学习笔记:JavaScript传参方式———ECMAScript中所有函数的参数都是按值传递
我们把命名参数(arguments)视为局部变量,在向参数传递基本类型值时,如同基本类型变量的复制一样,传递一个副本,参数在函数内部的改变不会影响外部的基本类型值.如: function add10( ...
- 编程笔记:JavaScript 中的类型检查
在Badoo的时候我们写了大量的JS脚本,光是在我们的移动web客户端上面就有大概60000行,可想而知,维护这么多JS可是相当具有挑战性的.在写如上规模js脚本客户端应用的时候我们必须对一件事保持警 ...
随机推荐
- Mac安装Mysql-python遇到的坑,被这俩报错反复摩擦:'my_config.h' file not found 和 IndexError: string index out of range
最后Stackoverflow上面的大神解决了问题: Link brew install mysql brew unlink mysql brew install mysql-connector-c ...
- MySQL读写分离---Mycat
一.什么是读写分离 在数据库集群架构中,让主库负责处理事务性查询,而从库只负责处理select查询,让两者分工明确达到提高数据库整体读写性能.当然,主数据库另外一个功能就是负责将事务性查询导致的数据变 ...
- Java类的加载过程与ClassLoader的理解及测试
当程序准备运行某个类,但该类还未被加载到内存中时,会经过以下三个步骤进行类的加载: 类的加载(Load)→类的连接(Link)→类的初始化(Initialize) 加载:类经过javac.exe编译的 ...
- pip安装了包但pycharm里找不到
使用pip install scipy安装了scipy1.2.3,但是打开pycharm里import还是报错,打开pycharm设置,里面的解释器里也找不到所需的包. 原因:安装了anaconda, ...
- k8s系列---stateful(有状态应用副本集)控制器
http://blog.itpub.net/28916011/viewspace-2215046/ 在应用程序中,可以分为有状态应用和无状态应用. 无状态的应用更关注于群体,任何一个成员都可以被取代. ...
- 关于Java/Kotlin下载图片,图片打开不能显示问题探究
图片下载其实是个很简单的功能,通过IO流从在线地址获取流,之后将流输出到文件即可完成下载功能,但是,最近我发现某个网站中的图片下载成功,但是打开却是无法打开,这让我迷惑,百度上根本就没有人说清楚 今天 ...
- Jenkins自动化构建vue项目然后发布到远程Linux服务器
部署Jenkins参照另一篇博客: centos7安装Jenkins及其卸载 一.jenkins相关插件的安装 1.安装Publish Over SSH插件用于SSH连接远程的服务器. 登录 jenk ...
- 在Django中连接MySQL数据库(Python3)
我的环境: python3.6, Django2.1.5, MySQL8.0.15, win10, PyCharm, 要求:已经安装了MySQL数据库 ...
- Matplotlib数据可视化(2):三大容器对象与常用设置
上一篇博客中说到,matplotlib中所有画图元素(artist)分为两类:基本型和容器型.容器型元素包括三种:figure.axes.axis.一次画图的必经流程就是先创建好figure实例, ...
- Executor 任务执行器
Executor: 是一个接口 用于执行提交的任务 解耦任务提交和执行(线程的创建及调度) Executor的实现可以根据实际需求延展不同的逻辑:1. 对于提交的任务同步或者异步执行,如下同步执行: ...