数据类型

2018-08-25  本文已影响0人  hhooke

ps : 本人学习笔记

类型

注意null

typeof null === "object"; // true

检测null类型

var a = null;

(!a && typeof a === "object"); // true

function

很多人搞不清函数和Object的关系,查阅规范可以知道,它实际上是 object 的一个 “ 子类型 ” 。具体来说,函数是 “ 可调用对象 ” ,它有一个内部属性 [[Call]] ,该属性使其可以被调用。函数不仅是对象,还可以拥有属性。

function fn(a,b){}

console.log(fn.length)// 2

函数对象的 length 属性是其声明的参数的个数

数组也属于object的一个子类型

数组

数组通过数字进行索引,但有趣的是它们也是对象,所以也可以包含字符串键值和属性(但这些并不计算在数组长度内)

var a = []

a[0] = 1;
a['asda'] = 'asd'

console.log(a.length) //1

如果字符串键值能够被强制类型转换为十进制数字的话,它就会被当作数字索引来处理

var a = [ ];
a["13"] = 42;
a.length; // 14

类数组

一些DOM操作返回的集合 就是类数组

通过arguments 对象(类数组)将函数的参数当作列表来访问(从 ES6 开始已废止)。

function foo() {
    var arr = Array.prototype.slice.call(arguments);
    arr.push("bam");
    console.log(arr);
}
foo("bar", "baz"); // ["bar","baz","bam"]

ES6 中的内置工具函数 Array.from(..) 也能实现同样的功能

var arr = Array.from( arguments );

字符串

字符串和数组的确很相似,它们都是类数组,都有 length 属性以及 indexOf(..) (从 ES5 开始数组支持此方法)和concat(..) 方法:

var a = "foo";
var b = ["f","o","o"];

a.length; // 3
b.length; // 3

a.indexOf( "o" ); // 1
b.indexOf( "o" ); // 1

var c = a.concat( "bar" ); // "foobar"
var d = b.concat( ["b","a","r"] ); // ["f","o","o","b","a","r"]

a === c; // false
b === d; // false

a; // "foo"
b; // ["f","o","o"]

a[1] = "O";
b[1] = "O";

a; // "foo"
b; // ["f","O","o"]

JavaScript 中字符串是不可变的,而数组是可变的。并且 a[1] 在 JavaScript 中并非总是合法语法,在老版本的 IE 中就不被允许(现在可以了)。 正确 的方法应该是 a.charAt(1) 。

var a = 'foo'
a[0] = 'h'
console.log(a[0]) // f

许多数组函数用来处理字符串很方便。虽然字符串没有这些函数,但可以通过 “ 借用 ” 数组的非变更方法来处理字符串

数字

特别大和特别小的数字默认用指数格式显示,与 toExponential() 函数的输出结果相同

var a = 5E10;
a; // 50000000000
a.toExponential(); // "5e+10"
var b = a * a;
b; // 2.5e+21
var c = 1 / a;
c; // 2e-11

由于数字值可以使用 Number 对象进行封装(参见第 3 章),因此数字值可以调用 Number.prototype 中的方法。例如, 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"
a.toPrecision( 4 ); // "42.59"
a.toPrecision( 5 ); // "42.590"
a.toPrecision( 6 ); // "42.5900"

数字常量还可以用其他格式来表示,如二进制、八进制和十六进制。
当前的 JavaScript 版本都支持这些格式:

0xf3; // 243 的十六进制
0Xf3; //  同上
0363; // 243 的八进制

计算问题

二进制浮点数中的 0.1 和 0.2 并不是十分精确,它们相加的结果并非刚好等于 0.3 ,而是一个比较接近的数字0.30000000000000004

整数检测

要检测一个值是否是整数,可以使用 ES6 中的 Number.isInteger(..) 方法

要检测一个值是否是 安全的整数 ,可以使用 ES6 中的 Number.isSafeInteger(..) 方法

void 运算符

undefined 是一个内置标识符(除非被重新定义,见前面的介绍),它的值为 undefined ,通过 void 运算符即可得到该值。

var a = 42;
console.log( void a, a ); // undefined 42

特殊的数字

不是数字的数字 NAN

NAN : 不是数字 将它理解为 “ 无效数值 ”“ 失败数值 ” 或者 “ 坏数值 ” 可能更准确些

var a = 2 / "foo";
isNaN( a ); // true

很明显 "foo" 不是一个数字 ,但是它也不是 NaN 。这个 bug 自 JavaScript 问世以来就一直存在,至今已超过 19 年

从 ES6 开始我们可以使用工具函数 Number.isNaN(..)

无穷数

var a = 1 / 0; // Infinity
var b = -1 / 0; // -Infinity

特殊等式

如前所述, NaN 和 -0 在相等比较时的表现有些特别。由于 NaN 和自身不相等,所以必须使用 ES6 中的Number.isNaN(..) (或者 polyfill )。而 -0 等于 0 (对于 === 也是如此),因此我们必须使用isNegZero(..) 这样的工具函数。

ES6 中新加入了一个工具方法 Object.is(..) 来判断两个值是否绝对相等,可以用来处理上述所有的特殊情况

var a = 2 / "foo";
var b = -3 * 0;
Object.is( a, NaN ); // true  NaN 只和自身不相等
Object.is( b, -0 ); // true
Object.is( b, 0 ); // false

基本类型和引用类型

基本类型 : 总是 通过值复制的方式来赋值 / 传递,包括 null 、 undefined 、字符串、数字、布尔和 ES6 中的 symbol 。

引用类型 : 当复制保存着对象的某个变量时,操作的是对象的引用,但在为对象添加属性时,操作的是实际的对象。

在复制变量值时,基本类型会在变量对象上创建一个新值,再复制给新变量。此后,两个变量的任何操作都不会影响到对方;而引用类型是将存储在变量对象的值复制一份给新变量,但是两个变量的值都指向存储在堆中的一个对象,也就是说,其实他们引用了同一个对象,改变其中一个变量就会影响到另一个变量。

//基本类型值
var a = 'a';
var b = a;
a = 'b';
console.log(b); //a
//引用类型值,以数组为例

//1.对其中一个变量直接赋值不会影响到另一个变量(并未操作引用的对象)
var a = [1,2,2,6,4]
var b = a;
a = [4,5,6,7]
console.log(b) //[ 1, 2, 2, 6, 4 ]

//2.使用push(操作了引用的对象)
var a = [1,2,3];
var b = a;
a.push(4);
console.log(a);//1,2,3,4
console.log(b); //1,2,3,4

对象参数传递

var a = [1,2,3,4]

function pusha(arr) {
    arr.push(5)
}

pusha(a)

console.log(a)

a的值会被传递到pusha里,此时传的是a的地址,pusha执行的时候其实是在操作a。

内部属性 [[class]]

所有 typeof 返回值为 "object" 的对象(如数组)都包含一个内部属性 [[Class]]

一般通过 Object.prototype.toString(..) 来查看

Object.prototype.toString.call( [1,2,3] ); // "[object Array]"

Object.prototype.toString.call( /regex-literal/i ); // "[object RegExp]"

封装对象包装

封装对象( object wrapper )扮演着十分重要的角色。由于基本类型值没有 .length 和 .toString() 这样的属性和方法,需要通过封装对象才能访问,此时 JavaScript 会自动为基本类型值 包装一个封装对象

var a = "abc";
a.length; // 3
a.toUpperCase(); // "ABC"
Object.prototype.toString.call( a ); // "[object String]"

js引擎会自动帮我们封装,在我们使用这些属性的时候,所以我们不必提前的将他们对象化,否则会降低执行效率。

封装对象释疑

使用封装对象时有些地方需要特别注意。比如 Boolean :

var a = new Boolean(false);
if (!a) {
    console.log("Oops"); //  执行不到这里
}

我们为 false 创建了一个封装对象,然而该对象是真值( “truthy” ,即总是返回 true),所以这里使用封装对象得到的结果和使用 false 截然相反。如果想要自行封装基本类型值,可以使用 Object(..) 函数(不带 new 关键字):

var a = "abc";
var b = new String( a );
var c = Object( a );

typeof a; // "string"
typeof b; // "object"
typeof c; // "object"

b instanceof String; // true
c instanceof String; // true

Object.prototype.toString.call( b ); // "[object String]"
Object.prototype.toString.call( c ); // "[object String]"

拆封

如果想要得到封装对象中的基本类型值,可以使用 valueOf() 函数

var a = new String( "abc" );
var b = new Number( 42 );
var c = new Boolean( true );

a.valueOf(); // "abc"
b.valueOf(); // 42
c.valueOf(); // true
上一篇下一篇

猜你喜欢

热点阅读