JS基础面试题

1.JS 数据类型

①基本类型:数字(Number)、布尔值(Boolean)、字符串(String)、null、undefined、对象(Object)、symbol(ES6 新增的),BigInt(ES2020) 。

②对象类型包括:数组(Array)、函数(Function)、还有两个特殊的对象:正则(RegExp)和日期(Date)

// Number 
typeof 25 === 'number'

// String 
typeof 'cg' === 'string'

//Boolean 
typeof true === 'boolean'

//Symbol
typeof symbol() === 'symbol'

// undefined
typeof undefined === 'undefined'

//object
typeof {a: 1} === 'object'
typeof [1, 2, 4] === 'object'

//function
typeof function() {} === 'function'

//判断是数组还是对象
let a = {a: 1}
①a.constructor === Object
②a instanceof Object
③Object.prototype.toString.call(a)

let b = [1,2,3]
①b.constructor === Array
②b instanceof Array
③Array.isArray(b)
④Object.prototype.toString.call(b)

2.基本数据类型与引用数据类型的区别

①声明变量时不同的内存分配

         原始值:存储在栈(stack)中的简单数据段,也就是说,它们的值直接存储在变量访问的位置。这是因为这些原始类型占据的空间是固定的,所以可将他们存储在较小的内存区域 – 栈中。这样存储便于迅速查寻变量的值。

  引用值:存储在堆(heap)中的对象,也就是说,存储在变量处的值是一个指针(point),指向存储对象的内存地址。这是因为:引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。

②不同的内存分配机制也带来了不同的访问机制

        在javascript中是不允许直接访问保存在堆内存中的对象的,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值,这就是传说中的按引用访问。而原始类型的值则是可以直接访问到的。

③复制变量时的不同

  原始值:在将一个保存着原始值的变量复制给另一个变量时,会将原始值的副本赋值给新变量,此后这两个变量是完全独立的,他们只是拥有相同的value而已。

  引用值:在将一个保存着对象内存地址的变量复制给另一个变量时,会把这个内存地址赋值给新变量,也就是说这两个变量都指向了堆内存中的同一个对象,他们中任何一个作出的改变都会反映在另一个身上。(这里要理解的一点就是,复制对象时并不会在堆内存中新生成一个一模一样的对象,只是多了一个保存指向这个对象指针的变量罢了)。多了一个指针指向同一个内存。

④参数传递的不同(把实参复制给形参的过程)

  首先我们应该明确一点:ECMAScript中所有函数的参数都是按值来传递的。但是为什么涉及到原始类型与引用类型的值时仍然有区别呢?还不就是因为内存分配时的差别。  

  原始值:只是把变量里的值传递给参数,之后参数和这个变量互不影响。

  引用值:对象变量它里面的值是这个对象在堆内存中的内存地址,这一点你要时刻铭记在心!因此它传递的值也就是这个内存地址,这也就是为什么函数内部对这个参数的修改会体现在外部的原因了,因为它们都指向同一个对象。

3.var、let、const的区别

①var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。

②let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。

③const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。

4.常用的本地存储

主要以:sessionStorage、localStorage、cookie

相同点:都保存在浏览器端,同源的

不同点:

①传递方式不同

        cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。

        sessionStorage和loaclStorage不会自动把数据发给服务器,仅在本地保存。

②数据大小不同

        cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。存储大小限制也不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。

        sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或者更大。

③数据有效期不同

        sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;

        localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;

        cookie只在设置cookie过期时间之前一直有效,即使窗口或浏览器关闭。

④作用域不同

        sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;

        localStorage在所有同源窗口中都是共享的;

        cookie也是在所有同源窗口中都是共享的。

        Web Storage支持事件通知机制,可以将数据更新的通知发送给监听者。

        Web Storage的api接口使用更方便。

5.闭包的理解

闭包是在另一个函数(称为父函数)中定义的函数,并且可以访问在父函数作用域中声明和定义的变量。

闭包可以访问三个作用域中的变量

①在自己作用域中声明的变量

②在父函数中声明的变量

③在全局作用域中声明的变量

6.undefined,null 和 undeclared 有什么区别?

①null表示"没有对象",即该处不应该有值,转为数值时为0。典型用法是:

(1) 作为函数的参数,表示该函数的参数不是对象。

(2) 作为对象原型链的终点。

②undefined表示"缺少值",就是此处应该有一个值,但是还没有定义,转为数值时为NaN。典型用法是:

(1)变量被声明了,但没有赋值时,就等于undefined。

(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。

(3)对象没有赋值的属性,该属性的值为undefined。

(4)函数没有返回值时,默认返回undefined。

③undeclared:js语法错误,没有申明直接使用,js无法找到对应的上下文。

7.JS中的深拷贝与浅拷贝的区别?

①深拷贝递归地复制新对象中的所有值或属性,而拷贝只复制引用。

②在深拷贝中,新对象中的更改不会影响原始对象,而在浅拷贝中,新对象中的更改,原始对象中也会跟着改。

③在深拷贝中,原始对象不与新对象共享相同的属性,而在浅拷贝中,它们具有相同的属性。

④浅拷贝的实现方式

        1)Object.assign()
        Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

const obj = { a: { name: 'cg', age: 25}}
const newObj = Object.assign({}, obj)
newObj.a.name = 'root'
console.log(obj.a.name) // root

        注意:当object只有一层的时候,是深拷贝

const obj = {name: 'cg', age: 25}
const newObj = Object.assign({}, obj)
newObj.name = 'root'
console.log(obj) //{name: 'cg', age: 25}
console.log(newObj) // {name: 'root', age: 25}

        2)Array.prototype.concat()

const arr = [1, 2, {name: 'cg', age: 25}]
const arr1 = arr.concat()
arr1[2].name = 'root'
console.log(arr[2].name) // root

        3)Array.prototype.slice()

const arr = [1, 2, {name: 'cg', age: 25}]
const arr1 = arr.slice()
arr1[2].name = 'root'
console.log(arr[2].name) // root

⑤深拷贝的实现方式

        1)JSON.parse(JSON.stringify())

        原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

注意:这种方法虽然可以实现数组或对象深拷贝,但不能处理函数

const arr = [1, 2, {name: 'cg', age: 25}]
const arr1 = JSON.parse(JSON.stringify(arr))
arr1[2].name = 'root'
console.log(arr) // [1, 2, {name: 'cg', age: 25}]
console.log(arr1) // [1, 2, {name: 'root', age: 25}]

        2)手写递归方法

递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。

//定义检测数据类型的功能函数
function checkedType(target) {
    return object.prototype.toString.call(target).slice(8, -1)
}

//实现深度克隆---对象/数组
function clone(target) {

//判断拷贝的数据类型
//初始化变量result成为最终克隆的数据
    let result, targetType = checkedType (target)

    if (targetType ===' object') {
        result = {}
    } else if (targetType === ' Array') {
        result = [ ]
    }else
        return target
    }

    //遍历目标数据
    for (let i in target) {
        //获取遍历数据结构的每一项值。
        Let value = target[i]

        //判断目标结构里的每值是否存在对 象/数组 
        if (checkedType(value) === 'Object’|| checkedType(value) === 'Array') { 
            //对象/数组里嵌套了对象/数组//继续逼历获取到value值
            result[i] = clone(value )
        else { //获取到value值是基本的数据类型或者是函数。   
            result[i] = value;
        }
    return result
}

        3)函数库lodash

该函数库也有提供_.cloneDeep用来做 Deep Copy

const objects = [{ 'a': 1 }, { 'b': 2 }]
const deep = _.cloneDeep(objects)
console.log(deep[0] === objects[0])  // => false

8.JS 中 == 和 === 区别是什么?

①对于string,number等基础类型,==和===有区别

1)不同类型间比较,==之比较“转化成同一类型后的值”看“值”是否相等,===如果类型不同,其结果就是不等。

2)同类型比较,直接进行“值”比较,两者结果一样。

②对于Array,Object等高级类型,==和===没有区别

进行“指针地址”比较。

③基础类型与高级类型,==和===有区别

1)对于==,将高级转化为基础类型,进行“值”比较。

2)因为类型不同,===结果为false。

9.解释JS中的事件冒泡和事件捕获

①事件捕获和冒泡: 在html DOM API中,有两种事件传播方法,它们决定了接收事件的顺序。两种方法是事件冒泡和事件捕获。第一个方法事件冒泡将事件指向其预期的目标,第二个方法称为事件捕获,其中事件向下到达元素。

②事件捕获:捕获过程很少被使用,但是当它被使用时,它被证明是非常有用的。这个过程也称为滴流模式。在这个过程中,事件首先由最外层的元素捕获,然后传播到最内部的元素。

③事件冒泡:冒泡的工作原理与冒泡类似,事件由最内部的元素处理,然后传播到外部元素。

两种方式来阻止事件冒泡。

方式一:event.stopPropagation();

方式二:return false;

但是这两种方式是有区别的。return false 不仅阻止了事件往上冒泡,而且阻止了事件本身。event.stopPropagation() 则只阻止事件往上冒泡,不阻止事件本身。

10.解释一下什么是箭头函数?

箭头函数是在es6或更高版本中编写函数表达式的简明方法。箭头函数不能用作构造函数,也不支持this,arguments,super或new.target关键字,它最适合非方法函数。 通常,箭头函数看起来像 const function_name =()=> {}。

箭头函数有几个使用注意点。

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象,箭头函数继承而来的this指向永远不变。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作Generator函数。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Root1216

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值