JavaScript是每一位前端开发者都必备的技能,接下来会分章节文章阐述介绍每一部分的内容。
JavaScript基础整理①
- 1、JavaScript类型
- 2、原始(primitive)类型
- 3、对象(Object)类型
- 4、类型判定
- 4.1、typeof
- 4.2、instanceof
- 4.3、Object.prototype.toString.call
- 5、类型转换
- 5.1、对象转原始类型
- 6、四则运算符
- 6.1、比较运算符
- 7、this
1、JavaScript类型
JavaScript数据类型分为两大类:
原始类型:number、string、nul、undefined、boolean、symbol;对象类型:三大类,分别是内部对象(本地对象和内置对象)、宿主对象和自定义对象;-
- 本地对象:ES提供的需要实例化
new才能够使用的对象:Object、Function、Array、String、Boolean、Number、Date、Error、EvalError、RangeError、ReferenceError、syntaxError、TypeError、URIError;
- 本地对象:ES提供的需要实例化
-
- 内置对象,也就是
Global和Math,其中JavaScript的全局属性有infinity-代表正的无穷大的数值、NaN-指示某一个数值不是数字值、undefined-指示未定义的数值。
- 内置对象,也就是
-
- 宿主对象:执行JavaScript脚本的环境所提供的对象,是浏览器提供的对象,所有的BOM和DOM都是宿主对象。
-
- 自定义对象:包括对象字面量方式(通过JSON创建对象)、工厂模式、构造函数模式、原型模式、组合使用构造函数模式和原型模式、其他模式(
动态原型模式、寄生构造函数模式、稳妥构造函数模式)。
- 自定义对象:包括对象字面量方式(通过JSON创建对象)、工厂模式、构造函数模式、原型模式、组合使用构造函数模式和原型模式、其他模式(
2、原始(primitive)类型
在JavaScript中,存在着7种原始数值,分别是:
booleannullundefinednumberstringsymbobigint
原始类型存储的都是数值,没有函数可以调用,不会存在如下:undefined.toString();

注意:如果采用
'1'.toString()是可以正常使用,这是由于'1'已经被强制转换为了String类型也就是对象类型,可以调用toString方法。
JavaScript的number类型是浮点类型,使用会遇到bug,例如0.1 + 0.2 !== 0.3;
string类型数值不可以改变,无论使用任何方法;
3、对象(Object)类型
对象类型和原始类型不一样,原始类型存储的是数值,一般保存在栈上,对象类型存储的是地址-指针,数据保存在堆上;
创建一个对象类型时候,计算机会在堆内存种开辟空间保存数值,开发者需要找到该内存空间,这个空间会拥有一个指针(地址)。
const a = []
对于常量a而言,设定内存地址#001,那么地址#001的位置保存了数值[],常量a存放了地址#001,给出如下示例代码:
const a = []
const b = a
b.push(1)
当我们将变量赋值给另外一个变量时,复制的是原本变量的地址(指针),也就是说当前变量 b 存放的地址(指针)也是 #001。因此当我们对任一变量进行数据修改的时候,等同于修改存放在地址(指针) #001 上的值,所以就导致了两个变量的值都发生了改变。
若函数参数是对象的时候:
function test(person) {
person.age = 26
person = {
name: 'yyy',
age: 30
}
return person
}
const p1 = {
name: 'yck',
age: 25
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?
- 首先,函数传参是传递对象指针的副本;
- 达到函数内部修改参数的属性这一步,
p1的数值修改,则age从25 -> 26; - 若重新给
person分配一个对象就出现了问题,如下图:

所以最后 person 拥有了一个新的地址(指针),也就和 p1 没有任何关系了,导致了最终两个变量的值是不相同的。
4、类型判定
4.1、typeof
typeof对于原始类型来说,除了null都可以显示正确的类型,若想判定null的话可以使用过xxx === null;
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
typeof 1n // bigint
typeof对于对象来说,除了函数都会显示object,所以说typeof并不能准确判定变量到底是什么类型。
typeof [] // 'object'
typeof {} // 'object'
typeof console.log // 'function'
4.2、instanceof
instanceof通过原型链的方式来判定是否构建函数的实例,一般用来判定具体的对象类型:
const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true
var str = 'hello world'
str instanceof String // false
var str1 = new String('hello world')
str1 instanceof String // true
对于原始类型而言,instanceof直接判定类型不可以, 不过可以利用如下方法:
class PrimitiveString {
static [Symbol.hasInstance](x) {
return typeof x === 'string'
}
}
console.log('hello world' instanceof PrimitiveString) // true
我们可能不知道 Symbol.hasInstance 是什么东西,其实就是一个能让我们自定义 instanceof 行为的东西,以上代码等同于 typeof 'hello world' === 'string',所以结果自然是 true 了。
这其实也侧面反映了一个问题:instanceof 并不是百分之百可信的。
// true
[].constructor === Array
4.3、Object.prototype.toString.call
Object.prototype.toString.call 综合来看是最佳选择,能判断的类型最完整,基本上是开源库选择最多的方式。

5、类型转换
JavaScript中存在三种情况:
- 转换为bool
- 转换为数字
- 转换为字符串

注意:图中有一个错误,
Boolean 转字符串这行结果我指的是 true 转字符串的例子,不是说 Boolean、函数、Symblo 转字符串都是 true
转Boolean:在条件判断时,除了
undefined, null, false, NaN, '', 0, -0,其他所有值都转为 true,包括所有对象。
5.1、对象转原始类型
对象在转换类型的时候,会调用内置的[[ToPrimitive]]函数,算法逻辑如下:
- 若已经是原始类型,则不需要转换了;
- 若需要转换的字符串就调用
x.toString(),转换为基础类型的话就返回转换的数值,不是字符串类型的话就先调用valueOf,结果不是基础类型的话就调用toString; - 调用
x.valuof,若转换为了基础类型,就返回转换的数值; - 若都没有返回原始类型,则报错。
当然,可以选择重写
Symbol.toPrimitive,这个方法可以在转原始类型的时候优先级最高。
let a = {
valueOf() {
return 0
},
toString() {
return '1'
},
[Symbol.toPrimitive]() {
return 2
}
}
1 + a // => 3
6、四则运算符
加法运算符不同于其他几个运算符,它有以下几个特点:
- 运算中其中一方为字符串,那么就会把另一方也转换为字符串
- 如果一方不是字符串或者数字,那么会将它转换为数字或者字符串
1 + '1' // '11'
true + true // 2
4 + [1,2,3] // "41,2,3"
- 对于第一行代码来说,触发特点一,所以将数字 1 转换为字符串,得到结果
'11' - 对于第二行代码来说,触发特点二,所以将
true转为数字1 - 对于第三行代码来说,触发特点二,所以将数组通过
toString 转为字符串 1,2,3,得到结果41,2,3
6.1、比较运算符
- 若是对象,则通过
toPrimitive转换对象 - 若是字符串,则通过
unicode字符索引来比较
let a = {
valueOf() {
return 0
},
toString() {
return '1'
}
}
a > -1 // true
在上述的代码中,a是对象,所以通过valueOf转换为原始类型在比较数值。
7、this
首先给出常见的this调用场景:
function foo() {
console.log(this.a)
}
var a = 1
foo()
const obj = {
a: 2,
foo
}
obj.foo()
const c = new foo()
- 对于直接调用
foo而言,不管foo函数放在何处,this一定是window; - 对于
obj.foo而言,只要记住,谁调用了函数,谁就是this,所以这时候foo函数中的this指的就是obj对象; - 对于
new的方式而言,this被永远绑定在了c上面,不会被任何方式改变this;
给出JavaScript的箭头函数中的this:
function a() {
return () => {
return () => {
console.log(this)
}
}
}
console.log(a()()())
- 首先箭头函数没有
this,箭头函数的this只是取决于包裹箭头函数的第一个普通函数的this,在这个例子中,因为包裹箭头函数的第一个普通函数就是a,所以这时的this就是window。另外箭头函数使用bind方法是无效的。 - 另外一种情况就是
bind这些改变上下文的API,this取决于第一个参数,若第一个参数空,则就是window;
注意:若对一个函数进行多次
bind,上下文表现如何?
let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)() // => ?
也可以写作另一种形式:
let a = { name: 'yck' }
function foo() {
console.log(this.name)
}
foo.bind(a)() // => 'yck'
以上就是bind的所有规则了,不同的规则之间根据优先级的高低来决定this的最终指向。
首先,new 的方式优先级最高,接下来是bind这些函数,然后是obj.foo()这种调用方式,最后是 foo 这种调用方式,同时,箭头函数的 this 一旦被绑定,就不会再被任何方式所改变。
总结如下图所示:







![[java/初学者]类的继承](https://img-blog.csdnimg.cn/33e4026791414af3a19fb854600468b5.png)












