🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 一、数据类型 JS分两种数据类型: > **基本数据类型**:**Number、String、Boolean、Null、 Undefined、Symbol(ES6),**这些类型可以直接操作保存在变量中的实际值。 > >**引用数据类型**:**Object(在JS中除了基**本**数据类型以外的都是对象,数据是对象,函数是对象,正则表达式是对象)** ## 1、基本数据类型(六种)存放在栈中 >基本数据类型是指存放在**栈**中的**简单数据段,数据大小确定,内存空间大小可以分配,**它们是直接按值存放的,所以可以直接**按值访问** * 1、`Number`数值类型 `Number`类型包含整数和浮点数(浮点数数值必须包含一个小数点,且小数点后面至少有一个数字)两种值 >注意:在js中浮点数的精度是17位,计算是二进制的计算数 据,所以得到的不是整数 ``` var num1 = 0.1; var num2 = 0.2; console.log(num1 + num2); //0.30000000000000004 ``` `NaN`:非数字类型,属于数值型基本数据类型 >特点: 1):设计任何的NaN操纵都会返回NaN `console.log('ab'/10); ` // NaN 2) NaN不等于自身。 `console.log(NaN == NaN);// false;` 判断是否是`Number`类型 >1、`isNaN`:判断是否是一个非数字类型,传入的非数字类型,返回`true`,否则返回`false` >注意:传入的参数首先会被转化为数值,如果参数类型为对象类型,先调用`valueOf()`方法,再确定该方法返回的值是否可以转换为数值类型,如果不能,再调用`toString()`方法,再确定返回值 2、`typeof` `console.log(typeof 12)` //Number 数值类型的转换: `Number()`:可以用于任何的数据类型 `parseInt`:提取 整数数值 `paseFloat`:提取浮点数值 * 2、`String` 字符串类型 >特点: 1、字符串的单引号和双引号作用效果一样 2、字符串有`length`属性,可以取得字符串的长度 3、字符串的值是不可变的,所以很多的字符串的`api`不会改变原字符串值 >字符串转换: `String()`:适用于任何的数据类型(null -> null undefined -> undefined) `toString()`:`null`和`undefined`没有这个功能 `console.log(null.toString()); //error 报错` * 3、`Boolean` 布尔类型 该类型只有两个值:`true`、`false` ``` 转换为`boolean`: `Boolean()` Boolean(undefined):false Boolean(null):false Boolean(非空对象包括空数组[]和空对象{}):true Boolean(非0): true || Boolean(0和NaN):false Boolean(非空包括空格字符串):true || Boolean(''):false [注意]true不一定等于1,false也不一定等于0 ``` 出现场景: (1)条件语句导致执行的隐士类转换 (2)字面量或变量定义 ``` 类型转换: Number(true): 1     ||   Number(false) : 0 String(true):'true'      ||   String(false):'false' ``` * 4、`Null` 空对象指针类型 如果定了一个对象,初始化可以为null,因为null的基本类型是`Null`,在`if`语句中默认转化为`false`,在和数值计算默认为`0` 出现场景:对象不存在 ``` 类型转换: Booleam(null) false Number(num) 0 String(null) 'null' ``` `Number(null) // 0` * 5、`Undefined` 申明了变量但是没有初始化,默认为`undefined`,在`if`语句中默认转化为`false`, >`undefined`:表示‘缺少值’,就是应该有一个值,但是没有定义,以下用法是典型的出现`undefined`情况 (1)变量被申明,等于`undefined` (2)调用函数时,应该提供的参数没有提供,该参数等于`undefined` (3)对象没有赋值的属性,该属性值为`undefined` (4)函数没有返回值,默认返回`undefined` ``` 类型转换: Boolean(undefined):  false Number(undefined):  NaN String(undefined):  'undefined' ``` * 6、`Symbol` `ES6`新增的一个基本数据类型,表示唯一性 ``` let id1 = Symbol('id'); let id2 = Symbol('id'); console.log(id1 == id2); //false ``` `Symbol`属性类型不支持`for...in `和`Object.keys()` ``` let id = Symbol("id"); let obj = { [id]:'symbol' }; for(let option in obj){ console.log(obj[option]); //空 } ``` 但是也能有方法去访问:`Object.getOwnPropertySymbols` 方法会返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。 ``` console.log(Object.getOwnPropertySymbols(obj)) // [ Symbol(c) ] ``` * 介绍两个`Symbol.for` 和 `Symbol.keyFor` (1)、`Symbol.for` :方法根据给到的键`key`来运行时的`symbol`注册表中找到对应的`symbol`,如果找到了,则返回它,否则新增一个与该键关联的`symbol`,并放入全局的`symbol`,这个Symbol值可以是被二次用到的 返回值: 返回由给定的 key 找到的 symbol,否则就是返回新创建的 symbol。 ``` Symbol.for("foo"); // 创建一个 symbol 并放入 symbol 注册表中,键为 "foo" Symbol.for("foo"); // 从 symbol 注册表中读取键为"foo"的 symbol Symbol.for("bar") === Symbol.for("bar"); // true,证明了上面说的 Symbol("bar") === Symbol("bar"); // false,Symbol() 函数每次都会返回新的一个 symbol 为了防止冲突,最好给你要放入 symbol 注册表中的 symbol 带上键前缀。 Symbol.for("mdn.foo"); Symbol.for("mdn.bar"); ``` (2)、`Symbol.keyFor` 方法用来获取 symbol 注册表中与某个 symbol 关联的键。 ``` var globalSym = Symbol.for("foo"); Symbol.keyFor(globalSym); // "foo" ``` ## 2、引用数据类型 引用数据类型也叫对象数据类型,包括`function`,`object`,`array`,`date`,`RegExp`等可以可以使用new创建的数据,又叫对象类型,他们是存放在**堆**(heap)**内存**中的数据 特点: > * 引用类型的值可以改变 > * 引用数据类型可以添加属性和方法 > * 引用数据类型的赋值是对象引用 > * 引用类型的比较是引用的比较 > * 引用类型是同时保存在栈区中和堆区中的,引用类型的存储需要在内存的栈区和堆区中共同完成,栈区保存变量标识符和指向堆内存的地址 注意:在引用类型赋值时对象的引用,所以从一个变量向另一个变量赋值引用类型的值时,同样会将存在在变量中的对象的值赋值一份到新的变量分配的空间,引用类型保存在变量中的时对象在堆存在的地址,所以与基本数据类型的简单赋值不同,这个值的副本实际上时一个指针,而这个指针指向储存在堆内存的一个对象,那么赋值操作后,两个变量都保存了同一个对象的地址,而这个地址都指向同一个对象,因此改变其中任何一个变量,都会影响 ![](https://img.kancloud.cn/94/9b/949bd62bd70cb9e38b83d5e7aeeb390e_774x312.png) 在ECMAScript中,Object类型是所有它的实例的基础 `Object`的每个实例都具有下列的属性和方法: * constructor:构造函数 * hasOwnProperty(proertyName) 用于检查给定的属性在当前对象实例(而不是实例的原型)中是否存在。 * isPropertyOf(Object) 用于检查其原型链的对象是否存在于指定对象的实例中,是则返回true,否则返回false。 ``` var a = {} function Person() {} var p1 = new Person() // 继承自原来的原型,但现在已经无法访问 var Person.prototype = a var p2 = new Person() // 继承a console.log(a.isPrototypeOf(p1)) // false a是不是p1的原型 console.log(a.isPrototypeOf(p2)) // true a是不是p2的原型 console.log(Object.prototype.isPrototypeOf(p1)) // true console.log(Object.prototype.isPrototypeOf(p2)) // true ``` * propertyIsEnumerable(propertyName) 用于检查给定的属性是否可以用 for-in 语句进行枚举。 * toLocaleString() 返回对象的字符串表示,该字符串与执行环境的地区对应。 * toString() 返回对象的字符串表示。 * valueOf() 返回对象的字符串、数值、布尔值表示。通常与toString()方法的返回值相同。 ![](https://img.kancloud.cn/84/ca/84ca4284a2076a450c57bc9e40328a5e_442x397.png) 拓展:声明对象的几种方式 ``` <script> //内置函数 var obj1=new Object(); obj1.name="zhuyu"; obj1.age=21; obj1.uni=function(){ console.log(this.name+this.age); } console.log(obj1); obj1.uni(); //字面量 var obj2={ name:"zhuyu2", age:21, uni2:function(){ console.log(this.name+this.age) } } console.log(obj2); obj2.uni2(); // 自定义函数 function Obj(name,age){ this.name=name; this.age=age; this.uni3=function(){ console.log(this.name+this.age) } } var obj3=new Obj("zhuyu3",21); console.log(obj3); obj3.uni3(); // Es6类 class Obj2{ constructor(name,age){ this.name=name; this.age=age; } uni4(){ console.log(this.name+this.age) } } var obj4=new Obj2("zhuyu4",21); console.log(obj4); obj4.uni4(); //使用Object.create() var person={ image:"true", uni5:function(){ console.log(`名字是${this.name},年龄是${this.age}`); } } var obj5=Object.create(person); obj5.name="zhuyu5"; obj5.age=21; obj5.image=false; obj5.uni5(); console.log(obj5) </script> ``` ## 3、基本数据类型和引用数据类型的区别 **总结基本数据类型和引用数据类型区别** **1、声明变量时内存分配不同**  *原始类型:在栈中,因为占据空间是固定的,可以将他们存在较小的内存中-栈中,这样便于迅速查询变量的值 ![](https://img.kancloud.cn/46/e4/46e42d38099492fdbbd8ff61e3c52399_544x222.png)  *引用类型:存在堆中,栈中存储的变量,只是用来查找堆中的引用地址。 ![](https://img.kancloud.cn/13/d9/13d9c998c52de5f3410f1ba5186bd5f9_577x513.png) ![](https://img.kancloud.cn/d8/cb/d8cb74eeab07843c29ffb6f0ecd5ef8d_800x643.png)     这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响 **2、不同的内存分配带来不同的访问机制**     在javascript中是不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值,这就是传说中的**按引用访问**。     而原始类型的值则是可以直接访问到的。 **3、复制变量时的不同**  1)原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量,**此后这两个变量是完全独立的,他们只是拥有相同的value而已。** 2)引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,     也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。     (这里要理解的一点就是,复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)。**多了一个指针** 浅拷贝: ![](https://img.kancloud.cn/ac/df/acdf1e6b5fba88c52d6abc11bc303782_440x290.png) 深拷贝: ![](https://img.kancloud.cn/73/1c/731c901e7c5fe3ddc1531d41774ae695_440x290.png) **4、**参数传递的不同(**把实参复制给形参的过程**)**** 首先我们应该明确一点:ECMAScript中所有函数的参数都是按值来传递的。   但是为什么涉及到原始类型与引用类型的值时仍然有区别呢?还不就是因为内存分配时的差别。     1)原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。   2)引用值:对象变量它里面的值是这个对象在堆内存中的内存地址,这一点你要时刻铭记在心!     因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因了,因为它们都指向同一个对象。 ## 4、检测类型 * 法一:`typeof` >最基本的判断方式,该操作符返回一个表示数据类型的字符串,`number`、`string`、`boolean`、`object`、`function`、`undefined`、`symbol` 1. 'undefined'              --未定义的变量或值 2. 'boolean'                 --布尔类型的变量或值 3. 'string'                     --字符串类型的变量或值 4. 'number'                  --数字类型的变量或值 5. 'object'                    --对象类型的变量或值,或者null(这个是js历史遗留问题,将null作为object类型处理) 6. 'function'                 --函数类型的变量或值         ``` console.log(typeof a);    //'undefined'     console.log(typeof(true));  //'boolean'     console.log(typeof '123');  //'string'     console.log(typeof 123);   //'number'     console.log(typeof NaN);   //'number'     console.log(typeof null);  //'object'         var obj = new String();     console.log(typeof(obj));    //'object'     var  fn = function(){};     console.log(typeof(fn));  //'function'     console.log(typeof(class c{}));  //'function' ``` * 法二:`instanceof` >**运算符用来测试一个对象在其原型链中是否存在一个构造函数的 `prototype` 属性** 语法:`object instanceof constructor` (1)基础类型 ``` let num = 1 num instanceof Number // false num = new Number(1) num instanceof Number // true ``` 这两个都是一样的数值,为什么不一样? 因为instanceof 检测的是:检测目标的`__proto__`与构造函数`prototype`,相同返回true,不同返回false,对于`string`、`boolean`是一样的 注意: ``` new String(1) // String {"1"} String(1) // "1" ``` (2) 继承关系的用法 ``` // 判断 foo 是否是 Foo 类的实例 , 并且是否是其父类型的实例 function Aoo(){} function Foo(){} Foo.prototype = new Aoo();//JavaScript 原型继承 var foo = new Foo(); console.log(foo instanceof Foo)//true console.log(foo instanceof Aoo)//true ``` (3) 复杂类型 ``` let arr = [] arr instanceof Array // true arr instanceof Object // true Array.isArray(arr) // true ``` 注意: `(new Number(1)) instanceof Object // true` (4) 其他类型 ``` let reg = new RegExp(//) reg instanceof RegExp // true reg instanceof Object // true let date = new Date() date instanceof Date // true date instanceof Object // true ``` 但是`Fuction`不一样 ``` function A() {} let a = new A() a instanceof Function // false a instanceof Object // true A instanceof Function // true ``` -->分析`a`为什么不是? >* a是new出来,所以是经过构造,因此已经是对象,不再是函数,所以false。 >* a是经过构造的对象,返回ture没问题。 > * 如上所述,A是个函数,因此没什么概念上的问题。但是要知道`A.__proto__`即`Function.prototype`是`ƒ () { [native code] }`,这是与object以后处于原型链上层的存在,而且与object平级: ``` let obj = {} obj.__proto__ // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …} obj.__proto__.prototype // undefined let A = function() {} A.__proto__ // ƒ () { [native code] } A.__proto__.prototype // undefined ``` ![](https://img.kancloud.cn/93/43/9343106669debaf28f0dbf11206c40b6_1080x417.png)