变量篇
数据类型
Js数据类型分成基础类型和引用类型两大类。
下图说明了基础类型和引用类型在堆栈中的情况:
基础类型
基础类型包括:
- undefined
- null
- string
- number
- boolean
- symbol
undefined
是表示变量未赋值时,此时该变量的值默认是 undefined
null
在类型判断中是 Object
,但不是对象,没有属性和方法symbol
是 ES6
新增类型,可以创建一个独一无二的值且不是字符串。具有全局的唯一性、隐藏性。
基础类型是存于栈中。且值不可改变,因为栈中存储的是本身的值,当改变时,就是新的变量,之前将被回收。
引用类型
引用类型能指对象类型,如object,function等等。
引用类型在栈中存放的是引向堆中的内容指针。当引用类型a赋值给其它变量b时,a和b中存储的都是同一引用指针,所以变量b是跟a是指向的同一内容。
对象又分成 原生对象
、宿主对象
。
原生对象包括:
- Number
- Boolean
- String
- Object
- Function
- Array
- RegExp
- Error
- Date
- Math
- JSON
- Global
- Arguments
宿主对象:一般指宿主如浏览器,由宿主框架通过某种机制注册到JavaScript引擎中的对象。
- Window
- Image
类型判断
方法一:typeof
typeof
一般只能判断基础类型(null除外)。
typeof 1 // "number"
typeof '1' // "string"
typeof true // "boolean"
typeof false // "boolean"
typeof Symbol('hello') // "symbol"
typeof undefined // "undefined"
typeof null // "object"
typeof {} // "object"
typeof [] // "object"
typeof NaN // "number"
方法二:instanceof
instanceof
是通过原型链来判断类型的。通过原型一层一层的往上找,直到找到为止。如果到上顶层都未找到都表示对象类型不同。
instanceof
一般用来判断对象具体是什么类型,如 object/function/array
等
对于基础类型而言,如果不是
new
的那么是等于Flase
的。
// 基础类型必须是new才会相同
String('1') instanceof String // false
new String('1') instanceof String // true
Number(1) instanceof Number // false
new Number(1) instanceof Number // true
// 引用类型
function Person() {}
let student = new Person()
student instanceof Person // true
// 引用类型二
function Person() {}
function Student() {}
Student.prototype = new Person()
let xiaowang = new Student()
xiaowang instanceof Student // true
xiaowang instanceof Person // true
Student instanceof Person // false
方法三: constructor
可对基础类型和引用类型使用,但不能对 undefined
、null
使用,报错 TypeError: Cannot read property 'constructor' of undefined
('1').constructor === String // true
(1).constructor === Number // true
(true).constructor === Boolean // true
(0).constructor === Boolean // false
({}).constructor === Object // true
([]).constructor === Array // true
(new Date()).constructor === Date // true
方法四: Object.prototype.toString.call
它不能检测非原生构造函数的构造函数名,也就是自定义函数之类只能检测出是object,而不能检测出具体的的是什么对象。
Object.prototype.toString.call('1') // "[object String]"
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call(0) // "[object Number]"
Object.prototype.toString.call(false) // "[object Boolean]"
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call([]) // "[object Array]"
变量提升
在编译阶段前,会对 var
声明的变量进行提升到当前作用域的最顶端。且 var
的可声明多次,后面会覆盖前面的。
console.log(a) // undefined
var a = 'hello'
// 上面代码等价于下面这种
var a
console.log(a)
a = 'hello'
let
和 const
不会有变量的提升,且只能声明一次,如多次声明,将会报错。
暂时性死区:let
/ const
会使区块形成封闭的作用域。若在声明之前使用变量,就会报错。
const
在声明是必须同时赋值,不赋值报错。声明的是基础类型时,不可改变值内容,当声明的是引用类型时,可改变属性值。
let a = 1
let a = 2 // Error Uncaught SyntaxError: Identifier 'a' has already been declared
const b; // Error: Missing initializer in const declaration
const a = 3; // Error
let/const/var 区别
var
会进行声明提升到执行上下文作用域顶端,let/const
则不会。var
可重复声明,后声明的覆盖前面的。let/const
重复声明会报错。var/let
声明是可不赋值,默认是undefined
,const
则必须赋值,不赋值报错。- 作用域区别,
var
是当前执行上下文之内都可访问,但let/const
拥有块级作用域,一般是当前的{}
内的范围。