本系列将从以下专题去总结:
- JS基础知识深入总结
- 对象高级
- 函数高级
- 事件对象与事件机制
暂时会对以上四个专题去总结,现在开始JS之旅的第一部分:JS基础知识深入总结。下图是我这篇的大纲。
话在前面:我一直都认为,在互联网学习的大环境下,网路学习资源很多,但这些博客等仅仅是一个向导,一个跳板,真正学习知识还是需要线下,需要书籍。
另外,如有错误,请留言或私信。一起成长,谢谢。
1.1 数据类型的分类和判断
1.1.1 数据类型的分类
- 基本(值)类型 [primitive values]
基本类型 | 类型的值 | 检测方法 |
---|---|---|
Number | 可以任意数值 | 用typeof检测结果为number |
String | 可以任意字符串 | 用typeof检测结果为string |
Boolean | 只有true/false | 用typeof检测结果为boolean |
undefined | 只有undefined | 用typeof检测数据类型和‘===’(全等符号) |
null | 只有null | ‘===’(全等符号) |
Symbol | 通过Symbol()得到,值可任意 | 用typeof可检测结果为symbol |
- 对象(引用)类型 [reference values]
对象类型 | 描述 | 检测方法 |
---|---|---|
Object | 可以任意对象 | 可以用typeof/instanceof检测数据类型 |
Array | 一种特别的对象(有数值下标,而且内部数据是有序的。一般的对象内部的数据是无序的,比如你一个对象中有name和age,他们是无序的。) | instanceof |
Function | 一种特别的对象(可以去执行的对象,内部包含可运行的代码。一个普通的对象可以执行吗?不能。)另外,对象是存储一些数据的,当然函数也是存储一些代码数据。 | typeof |
Date | 时间对象 | instanceof |
RegExp | 正则对象 | instanceof |
1.1.2 基本/对象数据类型特点比较
基本数据类型 | 对象类型 |
---|---|
基本类型的值是不可变的 | 引用类型的值是可变的 |
基本类型的比较是它们的值的比较 | 引用类型的比较是引用(指针指向)的比较 |
基本类型的变量是存放在栈内存(Stack)里的 | 引用类型的值是保存在堆内存(Heap)中的对象(Object) |
1.1.3 数据类型的判断
-
typeof
注1:用typeof
判断返回数据类型的字符串(小写)表达。比如:typeof ‘hello’
结果是string
。
注2:用typeof
来测试有以下七种输出结果:number
string
boolean
object
function
symol
undefined
。 因此typeof不能去判断出null
与object
,因为用typeof
去判断null
会输出object
。
注3:所有的任何对象,用typeof
测试数据类型都是object
。因此,typeof
不能去判断出object
与array
。 -
===(全等符号)
注1:只可以判断undefined 和 null 因为这两种基本类型的值是唯一的,即可用全等符比较。 -
instanceof
注1:A instanceof B
翻译就是B的实例对象是A 吗? 判断对象的具体类型(到底是对象类型中的Object
Array
Function
Date
RegExp
的哪一个具体的类型),返回一个Boolean值。 -
借调法:
Object.prototype.toString.call()
注1:这种方法只可以检测出内置类型(引擎定义好的,自定义的不行),这种方法是相对而言更加安全。Object
Date
String
Number
RegExp
Boolean
Array
Math
Window
等这些内置类型。
以上说明都有案例在面试题里
1.1.3 四个常见问题
- 问题1:undefined与报错(not defined)的区别?
对象.属性:属性不存在则返回undefined
访问变量:变量不存在则报错,xx is not defined
从这个点再去看一个简单的例子:var obj={ name:'lvya' }; console.log(obj.age); //undefined console.log(age); //报错,age is not defined
根据上面这个例子,问个问题。请问访问function Person(name,age,price) { this.name = name this.age = age this.price=price setName=function (name) { this.name=name; } } var p1 = new Person('LV',18,'10w') console.log(p1.price); // 10w
p1.price
先找啥?后找啥?通过啥来找?(问题问的不好,直接看答案吧)
An:p1.price
先找p1
后找price
。 p1是一个全局变量哦,这个全局变量本身存在栈内存中,它的值是一个地址值,指向new Person
出来的对象。怎么找呢?先找p1是沿着作用域找的,后找price是沿着原型链找的。这就是联系,从另外一个方面细看问题。可能这样看问题,你就可以把原型链和作用域可以联系起来思考其他问题。
串联知识点:请你讲讲什么是原型链和作用域链?
我们从a.b这个简单的表达式就可以看出原型链和作用域链。(a正如上例的p1)第一步先找a!a是一个变量,通过作用域链去查找,一层一层往外找,一直找到最外层的window,还没找到那就会报错,a is not defined
。 找到a这个变量,它的值有两种情况:基本数据类型和对象类型。
如果是基本数据类型(除了undefined和null)会使用包装类,生成属性。如果是undefined和null就会报错,显示不能读一个undefined或null的属性。
如果是对象类型,这就是对象.属性的方式,开始在对象自身查找,找不到沿着原型链去找。原型链也找不到的时候,那么就会输出undefined
。
-
问题2:undefined与null的区别?
undefined
代表定义未赋值
nulll
定义并赋值了, 只是值为null
var a; console.log(a); // undefined a = null; console.log(a); // null
使用
Object.prototype.toString.call()
形式可以具体打印类型来区别undefined和null。
如果值是undefined
,返回“[object Undefined]”。
如果这个值为null
,则返回“[object Null]”。 -
问题3:什么时候给变量赋值为null 呢?
初始赋值, 表明这个变量我将要去赋值为对象
结束前, 这个对象不再使用时,让对象成为垃圾对象(被垃圾回收器回收)//起始 var b = null // 初始赋值为null, 表明变量b将要赋值为对象类型 //确定赋值为对象 b = ['lvya', 12] //结束,当这个变量用不到时 b = null // 让b指向的对象成为垃圾对象(被垃圾回收器回收) // b = 2 //当然让b=2也可以,但不常使用
-
问题4:变量类型与数据类型一样吗?
数据的类型:包含基本数据类型和对象类型
变量的类型(实则是变量内存值的类型) JS弱类型语言,变量本身是无类型的。包含基本类型
: 保存的就是基本类型的数据(比如:数字1,字符串‘hello lvya’,布尔值false等)和引用类型
: 保存的是地址值,这个地址值去指向某个对象。
1.1.4 一张图看懂JavaScript各类型的关系
1.1.5 谈谈valueOf( ) 与 toString( )
toString()
和valueOf()
都是在Object.prototype里面定义.
-
toString()
表示的含义是把这个对象表示成字符串形式, 并且返回这个字符串形式.
首先,在Object.prototype
中它对toString()方法的默认实现是"[object Object]"。
验证一下:var p={ }; console.log(p.toString()); //[object Object] 去Object.prototype的去找(输出他的默认实现) function Person(){ } var p1=new Person(); console.log(p1.toString()); //[object Object] 去Object.prototype的去找(输出他的默认实现)
再看一下可以在自己的对象或者原型上对 toString() 进行覆写(重写, override)。这时访问这个对象的toString()方法时,就会沿着原型链上查找,刚好在自身对象上就找到了toString(),这个时候就不再去找原型链上的顶端
Object.prototype
的默认的toString()啦,便实现了对象的toString()的重写。
验证一下:var p = { toString: function (){ return "100"; } }; //100 这个时候就会在首先在P对象上找toString()方法,这个时候就是对toString方法的重写 console.log(p.toString());
再举一个重写的栗子:
var date = new Date(); console.log(date.toString()); //Fri Jan 18 2019 21:13:44 GMT+0800 (中国标准时间) /*从输出结果可知,Date这个构造函数的原型其实是有toString()方法的, 说明JS引擎已经在Date原型对象中重写了toString()方法, 故不会在Object.prototype中找*/ console.log(Date.prototype); //发现确实有toString()方法 var n = new Number(1); console.log(n.toString()); //1(字符串) /* 同理:这就是说明他们在js引擎内置的包装对象,说白了,就是内部已经给Number对象上重写了 toString()方法。这个方法刚好就是将数字转为字符串*/
-
valueOf()
应该返回这个对象表示的基本类型的值!在Object.prototype.valueOf
中找到, 默认返回的是this。当需要在对象上重写valueOf()
时,应该是返回一个基本数据类型的值。
先看一个默认返回的值的情况。(也就是说它是去这个对象的原型链的顶端Object.prototype.valueOf
找的valueOf
方法 )function Person(){ } var p1 = new Person(); console.log(p1.valueOf() == p1); //true
对返回结果的说明:这个时候
p1.valueOf
是在Object.prototype.valueOf
找到的,返回值默认this。此时this就是p1的这个对象。故结果返回true
。
现在看一下重写valueOf后的情况var p = { toString: function (){ return "100"; }, valueOf : function (){ return 1; } }; console.log(p.toString()); //100(字符串) //还来不及去Object.prototype.valueOf 其本身就有了toString方法 故当然读本身对象的toString()方法 console.log(p.valueOf()); //1(number数据类型) //同理,没去Object.prototype.valueOf找 而是找其本身的valueOf方法
我们再来验证JS引擎对那些内置对象有去重写
toString()
和valueOf()
呢?var n = new Number(100); console.log(n.valueOf()); //100 (number类型) var s = new String("abc"); console.log(s.valueOf()); //abc (string类型) var regExp = /abc/gi; console.log(regExp.valueOf() === regExp); //true //说明这个时候正则对象上没有valueOf,是在Object.prototype.valueOf找的,返回this,this指的就是regExp正则对象。
结论:在JS中, 只有基本类型中那几个包装类型进行了重写, 返回的是具体的基本类型的值, 其他的类型都没有重写,是去对象原型链的顶层
Object.prototype.valueOf
去找的。
1.1.6 数据类型间的比较
了解完valueOf()
和toSting()
方法后,其实他们就是对象与基本数据类型的比较的基础。我们数据类型,分为基本数据类型和对象类型两种,故在数据类型比较中,只会有三种情况:
- 基本数据类型间的比较
- 对象类型间的比较
- 基本数据类型与对象类型间的比较
基本数据类型间的比较
规则:如果类型相同,则直接比较; 如果类型不同, 都去转成
number
类型再去比较
三个特殊点:1.undefined
==null
2.0
和undefined
,0
和null
都不等 3. 如果有两个NaN
参与比较,则总是不等的。
总结:都是基本数据类型,但当类型不同时,转为number类型的规律如下:
基本类型中非number类型 | 转为number类型 |
---|---|
undefined ‘12a’ ‘abc’ ‘\’ |
Nan |
'' ' ' '\t' '0' null false |
0 |
true ‘1’ |
1 |
‘12’ |
12 |
我们来看看ECMA官方文档对转number类型的说明:
另外 再补充一点,在JS世界里,只有五种转Boolean类型是false
的:0
Nan
undefined
null
""
false
。其他的转Boolean值都是true
。
我们再来看看ECMA官方文档对转Boolean类型的说明:
所以,从这里我们就可以发现其实原文的ECMA官方文档就是很棒的学习资料,已经帮你整理的很完备了。多去翻翻这些官方文档的资料很有帮助。
例子1:属于基本类型间的比较,而且都是基本类型中的number类型,相同类型直接比较。
var a=1;
var b=1;
console.log(a == b); //true
console.log("0" == ""); //false
//都是相同的string类型,不用转,直接用字符串比较
例子2:属于基本类型间的比较,但是其具体的类型不同,需要转为number
类型再去比较。
console.log(true == "true"); //false 相应转为number类型去比较:1与Nan比较
console.log(0 == "0"); //true 相应转为number类型去比较:0与0比较
console.log(0 == ""); //true 相应转为number类型去比较:0与0比较
console.log(undefined == null); //true Nan与0比较??特殊
例子3:属于三大特殊点
console.log(undefined == null); //true
console.