JS基础面试题:变量类型和计算、原型和原型链

109 阅读4分钟
  1. JS预编译

变量类型和计算

JS中有哪些数据类型?

JavaScript共有八种数据类型,分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。

其中 Symbol 和 BigInt 是ES6 中新增的数据类型:

  • Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。
  • BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。

值类型和引用类型的区别

这些数据可以分为原始数据类型或值类型(primitive data type)和引用数据类型(index data type):

  • 栈:原始数据类型(undefined、boolean、number、string、symbol)
  • 堆:引用数据类型(对象、数组、Null和函数)

null为特殊引用类型,指针指向为空地址;函数也是特殊引用类型,但不用于存储数据,所以没有“拷贝、复制函数”的说法。

两种类型的区别在于存储位置的不同:

  • 原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;
let a = 100
let b = a
a = 200
console.log(b) // 100

image.png

  • 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
let a = { age: 20}
let b = a
b.age = 21
console.log(a.age) //21

image.png

堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:

  • 在数据结构中,栈中数据的存取方式为先进后出。
  • 堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。

在操作系统中,内存被分为栈区和堆区:

  • 栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
  • 堆区内存一般由开发者分配释放,若开发者不释放,程序结束时可能由垃圾回收机制回收。

typeof能判断哪些类型

  • 识别所有值类型
  • 识别函数
  • 判断是否是引用类型(不可再细分)

image.png

image.png

null和undefined的不同点

当检测 null 或 undefined 时,注意相等(==)与全等(===)两个操作符的区别 (en-US) ,前者会执行类型转换:

typeof null        // "object" (因为一些以前的原因而不是'null')
typeof undefined   // "undefined"
null === undefined // false
null  == undefined // true
null === null // true
null == null // true
!null //true
isNaN(1 + null) // false
isNaN(1 + undefined) // true

字符串拼接

image.png

何时使用===,何时使用==

==会自动类型转换让两者尽量相等,如0和''都可以转换成false

image.png

image.png

truly变量:!!a === true 的变量 falsely变量:!!a === false 的变量

image.png

JS中逻辑判断和if语句判断的不是true和false,而是truly变量和falsely变量。

手写深拷贝

  • 注意判断值类型和引用类型
  • 注意判断是数组还是对象
  • 递归

原型和原型链

JS是一门基于原型继承的语言,不能和Java一样使用class继承。当然ES6推出了class语法,但只是一个语法糖,其本质还是原型继承。

class

  • constructor
  • 属性
  • 方法
class Student {
    constructor(name, number) {
        this.name = name
        this.number = number
    }
    sayHi() {
        console.log(
            `姓名 ${this.name},学号 ${this.number}`
        )
    }
}

// 通过类声明对象/实例
// new的时候会走constructor,把名字和学号赋给实例对象。
const xialuo = new Student('夏洛', 001)

继承

  • extends
  • super
  • 扩展或重写方法
// 父类
class People {
    constructor(name) {
        this.name = name
    }
    eat() {
        console.log(`${this.name} eat something`)
    }
}

// 子类
class Student extends People {
    constructor(name, number) {
        super(name) // 不用自己处理,传给父类按父类处理
        this.number = number
    }
    sayHi() {
        console.log(
            `姓名 ${this.name},学号 ${this.number}`
        )
    }
}

// 子类
class Teacher extends People {
    constructor(name, number) {
        super(name)
        this.major = major
    }
    teach() {
        console.log(`${this.name} 教授 ${this.major}`)
    }
}

instanceof

image.png

原型

image.png

image.png

image.png

原型关系

  • 每个class都有显示原型prototype
  • 每个实例都有隐式原型__proto__
  • 实例的__proto__指向对应class的prototype

基于原型的执行规则

  • 获取属性xialuo.name或执行方法xialuo.sayhi()时
  • 先在自身属性和方法寻找
  • 如果找不到,则自动去__proto__中寻找

原型链

image.png

image.png

如何准确判断一个变量是不是数组?

手写一个简易的jQuery,考虑插件和扩展性

class的原型本质,怎么理解?