基本数据类型:
- `Undefined`,`Null`,`Boolean`,`Number`,`String`5种类型。在`ES6`中新增了一种基本的数据类型`Symbol`
引用数据类型:
- `Object`,`Function`,`Array`,`Date`等
Symbol:
表示独一无二的特性
两种类型有什么区别
区别 | 基本数据类型 | 引用数据类型 |
存储位置 | 栈(stack) | 堆 |
占据空间 | 小,大小固定 | 大,大小不固定 |
- 引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体
编辑
Undefined类型
- `Undefined`类型只有一个唯一的字面值`undefined`,表示的含义是一个变量不存在
哪些场景中会出现`undefined`
第一:使用只声明而未初始化的变量时,会返回`undefined`
var a
console.log(a) //undefined
第二:获取一个对象的某个不存在的属性时,会返回`undefined`
var obj={
userName:'zhangsan'
}
console.log(obj.age)//undefined
第三:函数没有明确的返回值,却对函数的调用结果进行打印
function fn(){}
console.log(fn()) //undefined
第四:函数定义的时候,使用了多个形参,但是在调用的时候传递的参数的数量少于形参数量,那么没有匹配上的参数就为`undefined`
function fn(p1,p2,p3){
console.log(p3) //undefined
}
fn(1,2)
Null类型
- `Null`类型只有一个唯一的字面值`null`,表示一个空指针的对象,这也是在使用`typeof`运行符检测`null`值时会返回`object`的原因
哪些场景中会出现`null`
第一:一般情况下,如果声明的变量是为了以后保存某个值,则应该在声明时就将其赋值为`null`
var obj=null
function foo(){
return {
userName:'zhangsan'
}
}
obj=foo();
第二:`JavaScript`在获取`DOM`元素时,如果没有获取到指定的元素对象,就会返回`null`
document.querySelector('#id') //null
在使用正则表达式进行匹配的时候,如果没有匹配的结果,就会返回`null`
'test'.match(/a/);// null
Undefined与null比较
相同点
- `Undefined`和`Null`类型在转换为`Boolean`类型的值时,都会转换为`false`
- `Undefined`类型派生自`Null`类型,所以在非严格相等的比较下,两者是相等的
不同点
- `null`是`JavaScript`的关键字,而`undefined`是`JavaScript`的一个全局变量,也就是挂载在`window`对象上的一个变量,并不是关键字
- 在使用`typeof`运算符进行检测时,`Undefined`类型的值会返回`undefined`.而`Null`类型的值返回为`object`
- 在进行数值类型的转换时,`undefined`会转换为`NaN`,无法参与计算,而`null`会转换为`0`,可以参与计算
Boolean类型
- `String`类型转换为`Boolean`类型:空字符都会转换成`false`,而任何非空字符串都会转换为`true`
- `Number`类型转换为`Boolean`类型:`0`和`NaN`都会转换为`false`.而除了`0`和`NaN`以外都会转换`true`
- `Object`类型转换`Boolean`类型:如果`object`为`null`时,会转换为`false`,如果`object`不为`null`,则都会转换成`true`
var obj={}
Boolean(obj) //true
var obj=null
Boolean(obj)//false
- `Function`类型转换`Boolean`类型:任何`Function`类型都会转换为`true`
var fn=function(){
}
Boolean(fn)//true
- `Null`类型转换为`Boolean`类型:我们知道`Null`类型只有一个`null`值,会转换为`false`
- `Undefined`类型转换`Boolean`类型:我们知道`Undefined`类型只有一个`undefined`值,会转换为`false`
Number类型
整型可以是十进制,也可以通过八进制或者是十六进制来表示
- 八进制:如果想要用八进制来表示一个数值,那么首位必须是0,其它位必须是0--7的数字,如果后面的数字大于7,则破坏了八进制的规则,这时会被当作十进制数来处理
var num1=024
console.log(num1) //20
var num2=079
console.log(num2) //79
- 十六进制:如果想用十六进制表示一个数值,那么前面两位必须是`0x`,其它的位必须是(0--9,`a--f`或者`A--F`).如果超出了这个范围,则会抛出异常
var num1=0x5f //95
var num2=Ox5h //Uncaught SyntaxError: Invalid or unexpected token
Number类型转换
在实际开发中,我们经常会遇到将其他类型的值转换为`Number`类型的情况。在`JavaScript`中,一共有3个函数可以完成这种转换,分别是`Number()`函数,`parseInt( )`函数,`parseFloat( )`函数
**Number( )函数**
可以用于将任何类型转换为`Number`类型
- 如果是数字,会按照对应的进制数据格式,统一转换为十进制返回
Number(10) //10
Number(010) // 8, 010是八进制的数据,转换成十进制是8
Number(0x10) // 16,0x10是十六进制的数据,转换成十进制是16
- 如果是`Boolean`类型的值,`true`返回1,`false`返回是的0
Number(true) //1
Number(false) //0
- 如果值为`null`,则返回0
Number(null) //0
- 如果值为`undefined`,则返回`NaN`
Number(undefined) //NaN
- 如果值为字符串类型,需要遵循如下规则
(1)如果该字符串只包含了数字,则会直接转换成十进制数;如果数字前面有0,则会直接忽略掉这个0
Number('21') //21
Number('012') //12
(2) 如果字符串是有效的浮点数形式,则会直接转成对应的浮点数,前置的多个重复的0会被删除,只保留一个
Number('0.12') //0.12
Number('00.12') //0.12
(3)如果字符串是有效的十六进制形式,则会转换为对应的十进制数值
Number('0x12') //18
(4) 如果字符串是有效的八进制,则不会按照八进制转换,而是直接按照十进制转换并输出,因为前置的0会被直接忽略掉。
Number('010') //10
Number('0020') //20
(5)如果字符串为空,即字符串不包含任何字符,或为连续多个空格,则会转换为0.
Number('') //0
Number(' ')//0
(6)如果字符串中包含了任何不适以上5种情况的其它格式内容,则会返回`NaN`
Number('123a') //NaN
Number('abc') //NaN
- 如果是对象类型,则会调用对象的`valueOf( )`函数获取返回值,并且判断返回值能否转换为`Number`类型,如果不能,会调用对象的`toString( )`函数获取返回值,并且判断是否能够转换为`Number`类型。如果也不满足,则返回`NaN`
//通过`valueOf( )`函数将对象转换成`Number`类型
var obj={
age:'12',
valueOf:function(){
return this.age
},
}
Number(obj) //12
//通过`toString( )`函数将对象转换成`Number`类型
var obj={
age:'21',
toString:function(){
return this.age
}
}
Number(obj)
**parseInt( )函数**
`parseInt()`函数用于解析一个字符串,并返回指定的基数对应的整数值,如果该字符串无法转换成`Number`类型,则会返回`NaN`
语法格式:parseInt(string,radix)
`string`参数表示要被解析的值,如果该参数不是一个字符串,那么会使用`toString( )`函数将其转换成字符串。并且字符串前面的空白符会被忽略。
`radix`表示的是进制转换的基数,可以是二进制,十进制,八进制和十六进制。默认值为10
- 如果遇到传入的参数是非字符串类型的情况,则需要将其优先转换成字符串类型。即使传入的是整型数据
- `parseInt( )`函数在做转换时,对于传入的字符串会采用前置匹配的原则
parseInt("fg123",16)
对于字符串`fg123`,首先从第一个字符开始,`f`是满足十六进制的数据的,因为十六进制数据的范围是`0--9`,`a--f`,所以保留`f`,然后是第二个字符`g`,它不满足十六进制数据范围,因此从第二个字符都最后一个字符全部舍弃,最终字符串只保留了字符`f`,然后将字符`f`转换成十六进制的数据,为15,因此最终返回的结果为`15`.
- 如果传入的值是浮点数,则会忽略小数点以及后面的数,直接取整
parseInt(12.98) //12
- `map( )`函数与`parseInt( )`函数的问题
我们这里假设有一个场景,存在一个数组,数组中的每个元素都是数字字符串,['1','2','3','4'],如果将这个数组中的元素全部转换成整数,应该怎样处理呢?
var arr = ["1", "2", "3", "4"];
var result = arr.map(function (val) {
return parseInt(val, 10);
});
console.log(result);
**parseFloat( )函数**
`parseFloat`函数用于解析一个字符串,返回对应的浮点数,如果给定值不能转换为数值,则返回`NaN`,与`parseInt( )`函数相比,`parseFloat( )`函数没有进制的概念
- 如果字符串前面有空白符,则会直接忽略掉,如果第一个字符就无法解析,则会直接返回`NaN`
parseFloat(' 2.6')// 2.6
parseFloat('f2.6') //NaN
- 对于小数点,只能正确匹配第一个,第二个小数点是无效的,它后面的字符也都将被忽略
parseFloat('12.23')// 12.23
parseFloat('12.23.39')//12.23
**总结:**
`Number( )` 函数转换的是传入的整个值,并不是像`parseInt( )`函数和`parseFloat( )`函数一样会从首位开始匹配符合条件的值。如果整个值不能被完整转换,则会返回`NaN`
isNaN( )函数与Number.isNaN( )函数对比
`Number`类型数据中存在一个比较特殊的值`NaN`,`NaN`存在的目的是在某些异常情况下保证程序的正常执行。例如`0/0`,在其他的语言中,程序会直接抛出异常,而在`JavaScript`中会返回`NaN`,程序可以正常运行,`NaN`有两个很明显的特点:
- 第一个是任何涉及`NaN`的操作都会返回`NaN`
- 第二个是`NaN`与任何值都不相等,即使是与`NaN`本身相比
在判断`NaN`时,`ES5`提供了`isNaN`函数,`ES6`为`Number`类型增加了静态函数`isNaN( ).`
//全局的`isNaN`函数本身存在误导性,而`ES6`中的`Number.isNaN( )`函数会在真正意义上去判断变量是否为`NaN`,不会做数据类型转换
Number.isNaN(NaN)// true
Number.isNaN(1) //false
Number.isNaN(null) //false
Number.isNaN(undefined) //false
String类型
`JavaScript`中的`String`类型可以通过双引号表示,也可以通过单引号表示,并且这两种方式是完全等效的,在`JavaScript`中有3种方式来创建字符串,分别是字符串字面量,直接调用`String( )`函数,还有就是通过`new String( )`构造函数的方式。
**字面量**
var str='hello'
var str2="JavaScript"
**直接调用`String( )`函数**
String(123) // '123'
String(123.56) // "123.56"
String(null) // "null"
String(undefined) //"undefined"
**new String( )构造函数**
new String(678)
//返回的是一个`String`类型的对象实例,当中有length属性,并且可以通过下标获取对应的值
**三种创建方式的区别**
使用字符串字面量方式和直接调用`String( )`函数的方式得到的字符串都是基本字符串,而通过`new String( )`方式生成的字符串是字符串对象。基本字符串在比较的时候,只需要比较字符串的值即可,而在比较字符串对象时,比较的是对象所在的地址。
var str='hello'
var str2=String('hello')
str===str2 //true
var str3=new String('hello')
var str4=new String('hello')
str3===str4 //false
**采用字面量方式定义的字符串也能够直接调用原型链上的这些函数**
实际上基本字符串本身是没有字符串对象上的这些函数的,而在基本字符串调用字符串对象才有的函数时,`JavaScript`会自动将基本字符串转换为字符串对象,形成一种包装的类型,这样基本字符串就可以正常调用字符串对象的方法了
字符串常见算法
**字符串逆序输出**
字符串逆序输出就是将一个字符串以相反的顺序进行输出。
- 第一种算法,我们是借助与数组的`reverse()`函数来实现
function reverseString(str) {
return str.split("").reverse().join("");
}
console.log(reverseString("abcdef"));
- 第二种算法
var arr=Array.from('abcdef') //转换成数组,这里比第一种方式简单
console.log(arr.reverse().join(""))
- 第三种算法,可以通过字符串本身提供的`chartAt`函数来完成
function reverseString2(str) {
var result = "";
for (var i = str.length - 1; i >= 0; i--) {
result += str.charAt(i);
}
return result;
}
console.log(reverseString2("abcdef"));
**统计字符串中出现次数最多的字符及出现的次数**
- 算法1,通过`key-value`形式的对象存储字符串以及字符串出现的次数,然后逐个判断出现次数最大的值,同时获取对应的字符
function getMaxCount(str) {
var json = {}; //表示key-value结构的对象
//遍历str的每一个字符得到key-value形式的对象
for (var i = 0; i < str.length; i++) {
//判断json对象中是否有当前从str字符串中取出来的某个字符。
if (!json[str.charAt(i)]) {
//如果不存在,把当前字符作为key添加到json对象中,值为1
json[str.charAt(i)] = 1;
} else {
//如果存在,则让value值加1
json[str.charAt(i)]++;
}
}
//存储出现次数最多的字符
var maxCountChar = "";
//存储出现最多的次数
var maxCount = 0;
//遍历json对象,找出出现次数最大的值
for (var key in json) {
if (json[key] > maxCount) {
maxCount = json[key];
maxCountChar = key;
}
}
return (
"出现最多的字符是" + maxCountChar + ",共出现了" + maxCount + "次"
);
}
var str = "javascriptjavaabc";
console.log(getMaxCount(str));
- 算法2,对字符串进行排序,然后通过`lastIndexOf()`函数获取索引值后,判断索引值的大小以获取出现的最大次数
function getMaxCount(str) {
//定义两个变量,分别表示出现最大次数和对应的字符。
var maxCount = 0,
maxCountChar = "";
//处理成数组,调用sort()函数排序,再处理成字符串
str = str.split("").sort().join("");
for (var i = 0, j = str.length; i < j; i++) {
var char = str[i];
//计算每个字符出现的次数
var charCount = str.lastIndexOf(char) - i + 1;
//与次数最大值进行比较
if (charCount > maxCount) {
//更新maxCount与maxCountChar的值
maxCount = charCount;
maxCountChar = char;
}
//变更索引为字符出现的最后位置
i = str.lastIndexOf(char);
}
return "出现最多的字符是" + maxCountChar + ",出现次数为" + maxCount;
}
console.log(getMaxCount("caa"));
**去除字符串中重复的字符**
- 算法1
function removeStringChar(str) {
//结果数组
var result = [];
//key-value形式的对象
var json = {};
for (var i = 0; i < str.length; i++) {
//当前处理的字符
var char = str[i];
//判断是否在对象中
if (!json[char]) {
//将value值设置为true
json[char] = true;
//添加到结果数组中
result.push(char);
}
}
return result.join("");
}
var str = "javascriptjavaabc";
console.log(removeStringChar(str));
- 算法2,使用`ES6`中的`Set`数据结构,可以结构具有自动去重的特性,可以直接将数组元素去重
const set = new Set([1,2,3,4,4,]);
//console.log(set) // Set(4) {1, 2, 3, 4}
[...set] // [1, 2, 3, 4] 通过扩展运算符将set中的内容转换成数组,同时可以看到已经去重。
运算符
等于运算符
三等于运算符
- 如果比较的值类型不相同,则直接返回`false`
1==='1' //false
true==='true' //false
- 如果比较的值都是数值类型,则直接比较值的大小
26===26 //true
34===NaN //false
- 如果比较的值是字符串类型,则判断每个字符是否相等
'abc'==='abc' //true
'abc'==='abd' //false
- 关于`null`与`undefined`比较
null===null //true
undefined===undefined //true
undefined===null //false
- 如果比较的值都是引用类型,则比较的是引用类型的地址
var a=[]
var b=a
var c=[]
console.log(a===b) //true
console.log(a===c) //false
new String('hello')===new String('hello')//false 两个不同对象,地址不相同
双等于运算符
相比于三等于运算符,双等于运算符在进行相等比较的时候,要复杂一点。因为它不区分数据类型,而且会做隐式类型的转换
- 比较的一方是`null`或者是`undefined`,只有在另一方是`null`或者是`undefined`的情况下才返回`true`,否则返回`false`
null==undefined //true
null==1 //false
undefined==2 //false
- 如果比较的是字符串和数值类型数据,则会将字符串转换为数值后再进行比较,如果转换后的数值是相等的则返回`true`,否则返回`false`
1=='1' //true
'222'==222 //true
- 如果比较的时候,有一方的类型是`boolean`类型,会将`boolean`类型进行转换,`true`转换为1,`false`转换0
'1'==true
'2'==true //false
'0'==false //true
typeof运算符
`typeof`运算符用于返回对应的数据类型
typeof operator
typeof (operator)
//`operator`表示要返回类型的操作数,可以是引用类型,也可以是基本数据类型。括号有时候是必须的,如果不加上括号将会因为优先级的问题,而得不到我们想要的结果
var num=123
typeof (num + 'hello')// string
typeof num + " hello" //"number hello"
- 处理`Undefined`类型
typeof undefined //"undefined"
typeof abc //"undefined" ,未声明的变量abc,通过typeof返回的是undefined
var sum
typeof sum //undefined 已经声明但是没有初始化的变量
- 处理`Boolean`类型的值
var b=true
typeof b //"boolean"
- 处理`Number`类型的值
typeof 666 //number
typeof 66.66 //number
- 处理`String`类型的值
typeof 'aaa' //string
typeof '' //string
- 处理`Function`类型的值
function fun(){}
typeof fun // "function"
var fun2=function(){}
typeof fun2 // "function"
通过`class`关键字定义的类,通过`typoef`计算返回的值也是`function`,`class`是在`ES6`中新增的一个关键字,原理依旧是原型继承,也就是说本质上仍然是一个`Function`
- 处理`Object`类型的值
var obj={userName:'zhangsan'}
typeof obj //"object"
//数组,通过`typeof`计算返回的值是`object`
var arr=[1,2,3]
typeof arr // "object"
var arr2=new Array()
typeof arr2 //"object"
- `typeof`运算符对`null`的处理
typeof null //objec
常用的判空方法
在`JavaScript`中判断一个变量是否为空,我们往往会想到对变量取反,然后判断是否为`true`,这里我们就分情况来看一下
**判断变量为`null`或者为`undefined`**
if(obj==null) //可以判断null或者是undefined的情况
if(obj===undefined) //只能判断undefined的情况
**(1)判断变量为空对象`{ }`**
function isEmpty(obj) {
for (let key in obj) {
//`hasOwnProperty( )`函数,判断是否有‘自身’存在的属性
if (obj.hasOwnProperty(key)) {
return false;
}
}
return true;
}
var obj = {
username: "zhangsan",
};
console.log(isEmpty(obj))
**(2)判断变量为空数组**
//首先要判断变量是否为数组,然后通过数组的`length`属性确定
//`instanceof` 用于判断一个变量是否某个对象的实例
var arr=new Array()
arr instanceof Array && arr.length===0
**(3)判断变量为空字符串**
//可以直接将其与空字符串进行比较,或者调用`trim()`函数去掉前后的空格以后,在去判断字符串的长度
str==''||str.trim().length==0
流程控制
在`JavaScript`中的关于`case`的比较是采用严格相等的方式(===)
function getStringValue(str) {
switch (str) {
case "1":
console.log("a");
break;
case "2":
console.log("b");
break;
case "3":
console.log("c");
break;
default:
console.log("d");
}
}
getStringValue("2"); //b
getStringValue(String("3")); //c
getStringValue(new String("3")); //d
引用数据类型
引用类型与基本数据类型的区别:
- (1)将引用数据类型赋值给变量,实际上赋值的是内存地址
- (2)引用数据类型的比较是对内存地址的比较,而基本数据类型的比较是对值的比较。
Object类型
new 操作符的作用
function Person(userName, age) {
console.log(this);//输出的是Person{ }对象
this.userName = userName;
this.age = age;
}
new Person("zhangsan", 20);
原型对象理解
函数对象的 prototype 属性
我们创建的每一个函数都有一个 `prototype` 属性,这个属性是一个指针,指向一个对象。这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法,简单来说,该函数实例化的所有对象的`__proto__`的属性指向这个对象,它是该函数所有实例化对象的原型
function Person(){
}
// 为原型对象添加方法
Person.prototype.sayName = function(){
alert(this.name);
}
编辑
constructor 属性
当函数创建,`prototype `属性指向一个原型对象时,在默认情况下,这个原型对象将会获得一个 constructor 属性,这个属性是一个指针,指向 `prototype` 所在的函数对象
Person.prototype.constructor == Person //true
编辑
对象的 `__proto__ `属性
var student = new Person();
console.log(student.__proto__ === Person.prototype); // true
编辑
//`isPrototypeOf()` 方法用于测试一个对象是否存在于另一个对象的原型链上
console.log(Person.prototype.isPrototypeOf(student)); // true
原型属性
属性访问
每当代码读取对象的某个属性时,首先会在对象本身搜索这个属性,如果找到该属性就返回该属性的值,如果没有找到,则继续搜索该对象对应的原型对象,以此类推下去
属性判断
使用 `hasOwnProperty() `方法来判断一个属性是存在与实例中,还是存在于原型中
function Person() {};
Person.prototype.name = "laker" ;
var student = new Person();
console.log(student.name); // laker
console.log(student.hasOwnProperty("name")); // false
student.name = "xiaoming";
console.log(student.name); //xiaoming 屏蔽了原型对象中的 name 属性
console.log(student.hasOwnProperty("name")); // true
`Object.create( )`方法
该函数的主要作用是创建并返回一个指定原型和指定属性的新对象
const person = {
userName: "zhangsan",
sayHello: function () {
console.log("hello " + this.userName);
},
};
const stu = Object.create(person);
stu.userName = "lisi";
stu.sayHello(); //hello lisi 覆盖了person中的userName属性原有的值
`Object.defineProperties( )`方法的基本使用
该方法的主要作用就是添加或修改对象的属性
var person = {};
Object.defineProperties(person, {
userName: {
value: "张三",
enumerable: true,
},
age: {
value: 12,
enumerable: true,
},
});
for (var p in person) {
console.log(p);
}
person.age = 20;
console.log(person.age);