变量篇

数据类型

Js数据类型分成基础类型和引用类型两大类。

下图说明了基础类型和引用类型在堆栈中的情况:

163f6b03478ae38a.jpg

基础类型

基础类型包括:

  • undefined
  • null
  • string
  • number
  • boolean
  • symbol

undefined 是表示变量未赋值时,此时该变量的值默认是 undefined
null 在类型判断中是 Object ,但不是对象,没有属性和方法
symbolES6 新增类型,可以创建一个独一无二的值且不是字符串。具有全局的唯一性、隐藏性。

基础类型是存于栈中。且值不可改变,因为栈中存储的是本身的值,当改变时,就是新的变量,之前将被回收。

引用类型

引用类型能指对象类型,如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除外)。

  1. typeof 1 // "number"
  2. typeof '1' // "string"
  3. typeof true // "boolean"
  4. typeof false // "boolean"
  5. typeof Symbol('hello') // "symbol"
  6. typeof undefined // "undefined"
  7. typeof null // "object"
  8. typeof {} // "object"
  9. typeof [] // "object"
  10. typeof NaN // "number"

方法二:instanceof

instanceof 是通过原型链来判断类型的。通过原型一层一层的往上找,直到找到为止。如果到上顶层都未找到都表示对象类型不同。

instanceof 一般用来判断对象具体是什么类型,如 object/function/array

对于基础类型而言,如果不是 new 的那么是等于 Flase的。

  1. // 基础类型必须是new才会相同
  2. String('1') instanceof String // false
  3. new String('1') instanceof String // true
  4. Number(1) instanceof Number // false
  5. new Number(1) instanceof Number // true
  6. // 引用类型
  7. function Person() {}
  8. let student = new Person()
  9. student instanceof Person // true
  10. // 引用类型二
  11. function Person() {}
  12. function Student() {}
  13. Student.prototype = new Person()
  14. let xiaowang = new Student()
  15. xiaowang instanceof Student // true
  16. xiaowang instanceof Person // true
  17. Student instanceof Person // false

方法三: constructor

可对基础类型和引用类型使用,但不能对 undefinednull 使用,报错 TypeError: Cannot read property 'constructor' of undefined

  1. ('1').constructor === String // true
  2. (1).constructor === Number // true
  3. (true).constructor === Boolean // true
  4. (0).constructor === Boolean // false
  5. ({}).constructor === Object // true
  6. ([]).constructor === Array // true
  7. (new Date()).constructor === Date // true

方法四: Object.prototype.toString.call

它不能检测非原生构造函数的构造函数名,也就是自定义函数之类只能检测出是object,而不能检测出具体的的是什么对象。

  1. Object.prototype.toString.call('1') // "[object String]"
  2. Object.prototype.toString.call(1) // "[object Number]"
  3. Object.prototype.toString.call(0) // "[object Number]"
  4. Object.prototype.toString.call(false) // "[object Boolean]"
  5. Object.prototype.toString.call({}) // "[object Object]"
  6. Object.prototype.toString.call([]) // "[object Array]"

变量提升

在编译阶段前,会对 var 声明的变量进行提升到当前作用域的最顶端。且 var 的可声明多次,后面会覆盖前面的。

  1. console.log(a) // undefined
  2. var a = 'hello'
  3. // 上面代码等价于下面这种
  4. var a
  5. console.log(a)
  6. a = 'hello'

letconst 不会有变量的提升,且只能声明一次,如多次声明,将会报错。

暂时性死区:let / const 会使区块形成封闭的作用域。若在声明之前使用变量,就会报错。

const 在声明是必须同时赋值,不赋值报错。声明的是基础类型时,不可改变值内容,当声明的是引用类型时,可改变属性值。

  1. let a = 1
  2. let a = 2 // Error Uncaught SyntaxError: Identifier 'a' has already been declared
  3. const b; // Error: Missing initializer in const declaration
  4. const a = 3; // Error

let/const/var 区别

  1. var 会进行声明提升到执行上下文作用域顶端,let/const 则不会。
  2. var 可重复声明,后声明的覆盖前面的。let/const 重复声明会报错。
  3. var/let 声明是可不赋值,默认是 undefinedconst 则必须赋值,不赋值报错。
  4. 作用域区别,var 是当前执行上下文之内都可访问,但 let/const 拥有块级作用域,一般是当前的{}内的范围。