1. 原始数据类型
原始数据类型 | 说明 | 默认值 |
---|---|---|
Number | 数字型,包含 整型值和浮点型值,如21、0.21 | 0 |
Boolean | 布尔值类型,如true、false,等价于1和0 | false |
String | 字符串类型,如“张三” 注意咱们JS里面,字符串都带引号 | “” |
Undifined | var a;声明了变量a但是没有给值,此时a=undefined | undefined |
Null | var a = null;声明了变量a为空值 | null |
BigInt | 可以用任意精度表示整数 | const y = x + 1n; 9007199254740993n |
Symbol(符号类型) | let s = Symbol();ES6中新增 |
1.1 布尔值
下列运算符会返回布尔值:
- 前置逻辑运算符:
!(Not)
- 相等运算符:
===
,!==
,==
,!=
- 比较运算符:
>
,>=
,<
,<=
如果 JavaScript 预期某个位置应该是布尔值,会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为false,其他值都视为true。
undefined
null
false
0
NaN
(非数字值)""或''
(空字符串)
注意: 空数组([]
)和空对象({}
)对应的布尔值,都是true。
if ([]) {
console.log('true');
}
// true
if ({}) {
console.log('true');
}
// true
1.2 null 和 undefined
null
与undefined
都可以表示“没有”,含义非常相似。将一个变量赋值为undefined
或null
,老实说,语法效果几乎没区别。
var a = undefined;
// 或者
var a = null;
上面代码中,变量a分别被赋值为undefined
和null
,这两种写法的效果几乎等价。
在if
语句中,它们都会被自动转为false
,相等运算符(==
)甚至直接报告两者相等。
if (!undefined) {
console.log('undefined is false');
}
// undefined is false
if (!null) {
console.log('null is false');
}
// null is false
undefined == null
// true
null
是一个表示“空”的对象,转为数值时为0;
undefined
是一个表示"此处无定义"的原始值,转为数值时为NaN
。
1.3 字符串(string
)
① length
返回字符串的长度
所谓字符串的长度是指其包含的字符的个数。
var len = a.length();
//len = 5
② 转成小写(toLowerCase
)/大写(toUpperCase
)字母
将整个字符串转成小写/大写字母。
var lower_string = a.toLowerCase();
//lower_string = "hello"
var upper_string = a.toUpperCase();
//upper_string = "HELLO"
③ concat
字符串拼接
作用:将两个或多个字符的文本组合起来,返回一个新的字符串。
效果:该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
语法:arrayObject.concat(arrayX,arrayX,......,arrayX)
var a = "hello";
var b = ",world";
var c = a.concat(b);
alert(c);
//c = "hello,world"
④ indexOf
/lastIndexOf
返回索引
indexOf
>> 返回字符串中一个子串第一处出现的索引(从左到右搜索)。如果没有匹配项,返回 -1 。
var a = "hello";
var index1 = a.indexOf("l");
//index1 = 2
var index2 = a.indexOf("l",3);
//index2 = 3
lastIndexOf
>> 返回字符串中一个子串最后一处出现的索引(从右到左搜索),如果没有匹配项,返回 -1 。
var a = "hello";
var index1 = lastIndexOf('l');
//index1 = 3
var index2 = lastIndexOf('l',2)
//index2 = 2
⑦ charAt
返回指定位置的字符
var a = "hello";
var get_char = a.charAt(0);
//get_char = "h"
⑧ 匹配一个正则表达式 match
、 replace
、search
match
>> 检查一个字符串匹配一个正则表达式内容,如果没有匹配返回 null。
var a = "hello";
var b = ",world";
var re = new RegExp(/^\w+$/);
var is_alpha1 = a.match(re);
//is_alpha1 = "hello"
var is_alpha2 = b.match(re);
//is_alpha2 = null
replace
>> 用来查找匹配一个正则表达式的字符串,然后使用新字符串代替匹配的字符串。
var result1 = a.replace(re,"Hello");
//result1 = "Hello"
var result2 = b.replace(re,"Hello");
//result2 = ",world"
var str="Visit Microsoft!"
str.replace(/Microsoft/, "W3School")
// Visit W3School!
search
>> 执行一个正则表达式匹配查找。如果查找成功,返回字符串中匹配的索引值。否则返回 -1 。
var index1 = a.search(re);
//index1 = 0
var index2 = b.search(re);
//index2 = -1
⑨ 提取字符串
substring
>> 返回字符串的一个子串,传入参数是起始位置和结束位置。
var sub_string1 = a.substring(1);
//sub_string1 = "ello"
var sub_string2 = a.substring(1,4);
//sub_string2 = "ell"
substr
>> 返回字符串的一个子串,传入参数是起始位置和长度
var sub_string1 = a.substr(1);
//sub_string1 = "ello"
var sub_string2 = a.substr(1,4);
//sub_string2 = "ello"
slice
>> 提取字符串的一部分,并返回一个新字符串(与 substring 相同)。
var sub_string1 = a.slice(1);
//sub_string1 = "ello"
var sub_string2 = a.slice(1,4);
//sub_string2 = "ell"
⑩ split
字符串变成数组
通过将字符串划分成子串,将一个字符串做成一个字符串数组。
var arr1 = a.split("");
//arr1 = [h,e,l,l,o]
栈内存
原始数据类型是按值访问的,因为可以直接操作保存在变量中的实际值
var a = 10;
var b = a;// b获取的是a值得一份拷贝,虽然,两个变量的值相等,但是两个变量保存了两个不同的基本数据类型值
b = 20;
console.log(a); // 10
b只是保存了a复制的一个副本。所以,b的改变,对a没有影响。
下图演示了这种基本数据类型赋值的过程:
栈(stack): 栈会自动分配内存空间,会自动释放,存放基本类型,简单的数据段,占据固定大小的空间。
所有在方法中定义的变量都是放在栈内存中,随着方法的执行结束,这个方法的内存栈也自然销毁。
优点: 存取速度比堆快,仅次于直接位于CPU中的寄存器,数据可以共享;
缺点: 存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
栈: 可以递归调用方法,这样随着栈深度的增加,JVM维持着一条长长的方法调用轨迹,直到内存不够分配,产生栈溢出。
2.引用数据类型(object)
引用数据类型:Object 、Array 、Function 、Date等
引用数据类型是保存在堆内存中的对象。
与其他语言的不同是,你不可以直接访问堆内存空间中的位置和操作堆内存空间。只能操作对象在栈内存中的引用地址。
所以,引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象。
var obj1 = new Object();
var obj2 = obj1;
obj2.name = "mirror";
console.log(obj1.name); //mirror
说明这两个引用数据类型指向了同一个堆内存对象。obj1赋值给obj2,实际上这个堆内存对象在栈内存的引用地址复制了一份给了obj2,但是实际上他们共同指向了同一个堆内存对象。实际上改变的是堆内存对象。
下面我们来演示这个引用数据类型赋值过程:
转载于: 深浅拷贝转载
2.1 深拷贝&浅拷贝&赋值
三者关系
赋值就是把地址直接赋值到一个新的变量中去了
// 对象赋值
var obj1 = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1,[2,3],[4,5]],
};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1',obj1)
console.log('obj2',obj2)
浅拷贝只拷贝了第一层,其余的仅为赋值
// 浅拷贝
var obj1 = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1,[2,3],[4,5]],
};
var obj3 = shallowCopy(obj1);
obj3.name = "lisi";
obj3.language[1] = ["二","三"];
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
console.log('obj1',obj1)
console.log('obj3',obj3)
① 深拷贝与浅拷贝
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
深拷贝和浅拷贝的示意图大致如下:
② 浅拷贝的实现方式
1.Object.assign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
var obj = { a: {a: "kobe", b: 39} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "wade";
console.log(obj.a.a); //wade
注意:当object只有一层的时候,是深拷贝
let obj = {
username: 'kobe'
};
let obj2 = Object.assign({},obj);
obj2.username = 'wade';
console.log(obj);//{username: "kobe"}
2.Array.prototype.concat()
let arr = [1, 3, {
username: 'kobe'
}];
let arr2=arr.concat();
arr2[2].username = 'wade';
console.log(arr);
修改新对象会改到原对象:
3.Array.prototype.slice()
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);
同样修改新对象会改到原对象:
关于Array的slice和concat方法的补充说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。
原数组的元素会按照下述规则拷贝:
如果该元素是个对象引用(不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。
可能这段话晦涩难懂,我们举个例子,将上面的例子小作修改:
let arr = [1, 3, {
username: ' kobe'
}];
let arr3 = arr.slice();
arr3[1] = 2
console.log(arr,arr3);
③ 深拷贝的实现方式
let arr = [1, 3, {
username: ' kobe'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan';
console.log(arr, arr4)
原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
这种方法虽然可以实现数组或对象深拷贝,但不能处理函数
let arr = [1, 3, {
username: ' kobe'
},function(){}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan';
console.log(arr, arr4)
这是因为JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数
3.类型判断
此部分内容部分摘自:https://juejin.cn/post/6844903623231537159
3.1 typeof
typeof运算符可以返回一个值的数据类型。
数值、字符串、布尔值分别返回number、string、boolean。
typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"
function f() {}
typeof f // "function"
typeof undefined // "undefined"
// object
typeof window // "object"
typeof {} // "object"
typeof [] // "object"
typeof null // "object"
上面代码中,空数组([]
)的类型也是object
,这表示在 JavaScript
内部,数组本质上只是一种特殊的对象。
null的类型是object,这是由于历史原因造成的。
3.2 instanceof
此部分内容最好先了解一下原型链♥-♥
instanceof
操作符判断左操作数对象的原型链上是否有右侧这个构造函数的prototype
属性,也就是说指定对象是否是某个构造函数的实例,最后返回布尔值。
[] instanceof Array; //true
[] instanceof Object; //true
new Date() instanceof Date;//true
new Date() instanceof Object;//true
function Person(){};
new Person() instanceof Person;//true
new Person() instanceof Object;//true
虽然
instanceof
能够判断出 [] 是Array的实例,但它认为 [] 也是Object的实例,为什么呢?
[]、Array、Object
三者之间的关系: 从instanceof
能够判断出[].__proto__
指向Array.prototype
, 而Array.prototype.__proto__
又指向了Object.prototype,Object.prototype.__proto__
指向了null,标志着原型链的结束。
[]、Array、Object
就形成了如下图所示的一条原型链:
注意:instanceof
运算符只能用于对象,不适用原始类型的值。?
'hello' instanceof String // false
null instanceof Object // false
undefined instanceof Object // false
复制代码字符串、null和undefined不是对象,所以返回false。
3.3 constructor
3.4 Object.prototype.toString
3.5 Object.prototype.toString.call
toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,… 基本上所有对象的类型都可以通过这个方法获取到。
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object Window]
复制代码需要注意的是,必须通过Object.prototype.toString.call来获取,而不能直接 new Date().toString(), 从原型链的角度讲,所有对象的原型链最终都指向了Object, 按照JS变量查找规则,其他对象应该也可以直接访问到Object的toString方法,而事实上,大部分的对象都实现了自身的toString方法,这样就可能会导致Object的toString被终止查找,因此要用call来强制执行Object的toString方法。
3.6 isNaN()
3.7 Array.isArray( ) 检验值是否为数组
3.x 总结
typeof可以准确地判断出基本类型,但是对于引用类型除function之外返回的都是object;
已知是引用类型的情况可以选用instanceof或constructor方法进行具体类型的判断:
instanceof是基于原型链的;
constructor 属性易变,不可信赖,为了规范,在重写对象原型时一般都需要重新给constructor赋值,以保证实例对象的类型不被改写;
Object.prototype.toString.call() 通用但很繁琐。