JavaScript基础知识(1)- 数据类型

JavaScript规定了几种语言类型

最新的 ECMAScript 标准定义了 7 种数据类型:

6 种原始类型:

Object

JavaScript中的变量在内存中的具体存储形式

在JS中,每一个数据都需要一个内存空间。内存空间又被分为两种,栈内存(stock)与堆内存(heap)。

基础数据类型与栈内存

JS中的基础数据类型,这些值都有固定的大小,往往都保存在栈内存中,由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按值访问。

基础数据类型: Number,String,Null,Undefined,Boolean

引用数据类型与堆内存

JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JavaScript不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗浅地理解为保存在栈内存中的一个地址,该地址与堆内存的实际值相关联。

理解值类型和引用类型

JavaScript值类型和引用类型有哪些

(1)值类型(基本类型):数值(number)、布尔值(boolean)、null、undefined、string(在赋值传递中会以引用类型的方式来处理)。

(2)引用类型:对象、数组、函数。

用“连锁店”和“连锁店钥匙”来理解。
(1)值类型:变量的交换等于在一个新的地方按照连锁店的规范标准(统一店面理解为相同的变量内容)新开一个分店,这样新开的店与其它旧店互不相关、各自运营。把一个值类型(也叫基本类型)str2赋值给另一个变量时,其实是分配了一块新的内存空间,因此改变str1的值对str2没有任何影响,因为它不同于引用类型(变量的交换其实是交换了指像同一个内容的地址)。
(2)引用类型:变量的交换等同于把现有一间店的钥匙(变量引用地址)复制一把给了另外一个老板,此时两个老板同时管理一间店,两个老板的行为都有可能对一间店的运营造成影响。str2只进行了一次赋值,理论上它的值已定,但后面通过改写str1的值,发现str2的值也发生了改变,这正是引用类型的特点。

值类型和引用类型的区别

(1)基本类型的值是一经确定就不可变的
(2)基本类型的比较是值的比较
只有在它们的值相等的时候它们才相等。
当比较的两个值的类型不同的时候运算符会进行类型转换,但是当两个值的类型相同的时候,即使是也相当于是===。

var a = 1;
var b = true;
console.log(a == b);//true

在用==比较两个不同类型的变量时会进行一些类型转换。如上的比较先会把true转换为数字1再和数字1进行比较,结果就是true了。

var a = 'coco';
var b = 'coco';
console.log(a === b);

(3)基本类型的变量是存放在栈区的(栈区指内存里的栈内存)
(4)引用类型的值是可变的,可以为引用类型添加属性和方法,也可以删除其属性和方法。引用类型可以拥有属性和方法,并且是可以动态改变的。
(5)引用类型的值是同时保存在栈内存和堆内存中的对象,js不同于其他语言,其不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间,实际上,是操作对象的引用,所以引用类型的值是按引用访问的。
准确地说,引用类型的存储需要内存的栈区和堆区(堆区是指内存里的堆内存)共同完成,栈区内存保存变量标识符和指向堆内存中该对象的指针,也可以说是该对象在堆内存的地址。
(6)引用类型的比较是引用的比较

var person1 = '{}';
var person2 = '{}';
console.log(person1 == person2); // true复制代码

基本类型的比较--当两个比较值的类型相同的时候,相当于是用 === ,所以输出是true。

var person1 = {};
var person2 = {};
console.log(person1 == person2); // false

null和undefined的区别

首先,null像在Java里一样,被当成一个对象。但是,JavaScript的数据类型分成原始类型(primitive)和合成类型(complex)两大类,Brendan Eich觉得表示"无"的值最好不是对象。其次,JavaScript的最初版本没有包括错误处理机制,发生数据类型不匹配时,往往是自动转换类型或者默默地失败。Brendan Eich觉得,如果null自动转为0,很不容易发现错误。

因此,Brendan Eich又设计了一个undefined。

undefined

undefined 的字面意思就是:未定义的值 。这个值的语义是,希望表示一个变量最原始的状态,而非人为操作的结果 。 这种原始状态会在以下 4 种场景中出现:

【1】声明了一个变量,但没有赋值

var foo;console.log(foo);// undefined

访问 foo,返回了 undefined,表示这个变量自从声明了以后,就从来没有使用过,也没有定义过任何有效的值,即处于一种原始而不可用的状态。

【2】访问对象上不存在的属性

console.log(Object.foo);// undefined

访问 Object 对象上的 foo 属性,返回 undefined , 表示Object 上不存在或者没有定义名为 foo 的属性。数组中的元素在内部也属于对象属性,访问下标就等于访问这个属性,返回 undefined ,就表示数组中不存在这个元素。

【3】函数定义了形参,但没有传递实参

//函数定义了形参 a
function fn(a) {
    console.log(a); // undefined
}
fn(); //未传递实参

函数 fn 定义了形参 a, 但 fn 被调用时没有传递参数,因此,fn 运行时的参数 a 就是一个原始的、未被赋值的变量。

【4】使用 void 对表达式求值

void 0 ;// undefined
void false;// undefined
void [];// undefined

null

null 的字面意思是:空值  。这个值的语义是,希望表示 一个对象被人为的重置为空对象,而非一个变量最原始的状态 。 在内存里的表示就是,栈中的变量没有指向堆中的内存对象,当一个对象被赋值了null 以后,原来的对象在内存中就处于游离状态。

null 有属于自己的类型 Null,而不属于Object类型,typeof 之所以会判定为 Object 类型,是因为JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。

undefined 表示一个变量自然的、最原始的状态值,而 null 则表示一个变量被人为的设置为空对象,而不是原始状态。所以,在实际使用过程中,为了保证变量所代表的语义,不要对一个变量显式的赋值 undefined,当需要释放一个对象时,直接赋值为 null 即可。

判断JS数据类型的四种方法

  • typeof
typeof ''; // string 有效
typeof 1; // number 有效

typeof 操作符会返回一些令人迷惑但技术上却正确的值:
对于基本类型,除 null 以外,均可以返回正确的结果。
对于引用类型,除 function 以外,一律返回 object 类型。
对于 null ,返回 object 类型。
对于 function 返回  function 类型。
  • instanceof
模拟实现
instanceof (A,B) = {
    var L = A.__proto__;
    var R = B.prototype;
    if(L === R) {
        // A的内部属性 __proto__ 指向 B 的原型对象
        return true;
    }
    return false;
}
[] instanceof Array; // true
{} instanceof Object;// true
[] instanceof Object; // true

[] 的 __proto__  直接指向Array.prototype,间接指向 Object.prototype,所以按照 instanceof 的判断规则,[] 就是Object的实例。依次类推,类似的 new Date()、new Person() 也会形成一条对应的原型链 。因此,instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。

instanceof 操作符的问题在于,它假定只有一个全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。

针对数组的这个问题,ES5 提供了 Array.isArray() 方法 。该方法用以确认某个对象本身是否为 Array 类型

  • constructor

当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。

当执行 var f = new F() 时,F 被当成了构造函数,f 是F的实例对象,此时 F 原型上的 constructor 传递到了 f 上,因此 f.constructor == F。

同样,JavaScript 中的内置对象在内部构建时也是这样做的:

''.constructor === String;//true
false.constructor === Boolean;//true
new Date().constructor === Date;//true
new Number(9527) === Number;//true
  • toString(正确的判断数组类型)

toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。对于 Object 对象,直接调用 toString()  就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。

Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call([]) ;   // [object Object]
Object.prototype.toString.call({}) ;    // [object Array]

所以如何准确的判断数组类型?

1. Object.prototype.toString.call({}) ;    // [object Array]
2. Array.isArray([]);//true

运算中存在的隐式类型转换

场景

  1. 运算中存在的隐式类型转换 (+,-,*,/,==)
  2. 语句中存在的隐式类型转换 (if...else...)
  3. alert时存在的隐式类型转换

隐式转换中主要涉及到三种转换:

  1. 将值转为原始值,ToPrimitive()。
  2. 将值转为数字,ToNumber()。
  3. 将值转为字符串,ToString()。

常见的转换规则(原文https://segmentfault.com/a/1190000012780467

原始值

类型 Undefined Null String Boolean Number
undefined null 所有字符串 true false 所有数字/NaN

引用类型

Object的成员叫对象,包括Array,Function,Math,Date,JSON,RegExp等除了原始值之外的所有的类型的成员。

valueOf / toString转换

转换方法 Object String Array Number Date Boolean Function
valueOf() 原始值 原始值 原始值 原始值 毫秒时间戳 原始值 string
toString() [Object Object] 原始值 join方法返回的结果 数字字符串 本地时间字符串 'true'/'false' string

Boolean(n)转换

转换后的值 Undefined Null String Boolean Number Object
true - - 非空字符串 true 非0 非NaN 所有
true undefined null 空字符串 false 0 NaN -

Number(n)转换

转换后的值 Undefined Null String Boolean Number Array Object
Number类型值 - - '12345' - 原样返回 ??? -
0 - null '0' false 0 ??? -
1 - - '1' true 1 ??? -
NaN undefined - 包含除数字外字符串 - NaN ??? 全部

String(n)转换

转换的值 Undefined Null String Boolean Number Array Object
结果 'undefined' 'null' 原样返回 'true'/'false' toString结果 先转为原始值,然后调用toString

Object(n)转换

转换的值 Undefined Null String Boolean Number Array Object
结果 {} {} 原始值为X的Boolean对象 原始值为X的Number对象 原始值为X的String对象 原值输出