JavaScript面试题整理(仅供参考)

1. 原生js基础?

2. js的数据类型有几种?

答:8种:Number,String,Boolean,Null,Undefined,Object,Symbel,Bigint

3. 闭包及实际案例?

答:变量的作用域无非就是两种:全局变量和局部变量。

"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

闭包就是能够读取其他函数内部变量的函数。

闭包简单理解成"定义在一个函数内部的函数"。

在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

闭包的用途:闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

// 例:手写一个1到100的和的闭包

function a() {

var i = 1;

var result = 0;

return function b() {

result += i++

return i > 100 ? result : b()

}()

}

var sum = a()

console.log(sum)

4. 深拷贝和浅拷贝的区别?

答:浅拷贝只复制指向某个对象的指针,而不是复制对象的本身,新旧对象还是共享同一块内存;

深拷贝会例外创造一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对象。

实现深拷贝的方法:

(1)JSON方法:var obj1 =JSON.parse(JSON.stringify(obj))

(2)函数库lodash的_.cloneDeep方法:var _ = require('lodash') var obj1 = _.cloneDeep(obj)

(3)递归实现深拷贝:

function copy(object) {

// 判断传入的参数是数组还是对象

let target = object instanceof Array ? [] : {}

for (const [k ,v] of Object.entries(object)) {

target[k] = typeof v == 'object' ? copy(v) : v

}

return target

}

var obj1 = copy(obj)

console.log(obj.a.d === obj1.a.d);//false

ES5:

1. 原型和原型链?

答:Javascript对象都有一个叫做原型的公共属性,属性名是__proto__。这个原型属性是对另一个对象的引用,通过这个原型属性我们就可以访问另一个对象所有的属性和方法。

每一个实例对象都有一个私有属性__proto__,指向它的构造函数的原型对象(prototype)。原型对象也有自己的__proto__,层层向上直到一个对象的原型对象为null。这一层层原型就是原型链。

2. 事件冒泡、事件捕获、事件委托?

答:事件委托原理:事件在被触发之后,会逐级冒泡并能被父级元素捕捉到。这样就只需要在父级元素上绑定一份处理器,就可以处理所有子元素触发的事件。典型例子就是每个li中的事件就可以绑定在ul上,这样避免了重复绑定。

事件委托依靠的就是事件冒泡和捕获的机制。事件委托又称之为事件代理。

事件冒泡会从当前触发的事件目标一级一级往上传递,依次触发,直到document为止。

事件捕获会从document开始触发,一级一级往下传递,依次触发,直到真正事件目标为止。

document.getElementById('box3').addEventListener('click', sayBox3, false);

removeEventListener()

// 阻止冒泡 event.stopPropagation();

IE: // 添加事件,事件流固定为冒泡 attachEvent(事件名,事件处理函数)

// 删除事件 detachEvent(事件名,事件处理函数)

// IE里阻止冒泡 window.event.cancelBubble = true;

3. 数组的一些方法?

答:https://www.cnblogs.com/sqh17/p/8529401.html

4. 变量提升,作用域?

答:https://www.cnblogs.com/dennisj/p/12900097.html

变量提升指的是,无论是哪里的变量在一个范围内声明的,那么JavaScript引擎会将这个声明移到范围的顶部。

(1)作用域其实就是一个变量绑定的有效范围。

(2)JS使用的是静态作用域,即一个函数使用的变量如果没在自己里面,会去定义的地方查找,而不是去调用的地方查找。去调用的地方找到的是动态作用域。

(3)var变量会进行申明提前,在赋值前可以访问到这个变量,值是undefined。

(4)函数申明也会被提前,而且优先级比var高。

(5)使用var的函数表达式其实就是一个var变量,在赋值前调用相当于undefined(),会直接报错。

(6)let和const是块级作用域,有效范围是一对{}。

(7)同一个块级作用域里面不能重复申明,会报错。

(8)块级作用域也有“变量提升”,但是行为跟var不一样,块级作用域里面的“变量提升”会形成“暂时性死区”,在申明前访问会直接报错。

(9)使用let和const可以很方便的解决循环中异步调用参数不对的问题。

(10)let和const在全局作用域申明的变量不会成为全局对象的属性,var会。

(11)访问变量时,如果当前作用域没有,会一级一级往上找,一直到全局作用域,这就是作用域链。

(12)try...catch的catch块会延长作用域链,往最前面添加一个错误对象。

(13)with语句可以手动往作用域链最前面添加一个对象,但是严格模式下不可用。

(14)如果开发环境支持ES6,就应该使用let和const,不要用var。

5. 创建对象的5种方法?

答:https://www.cnblogs.com/Joans/p/4754346.html

6. eventloop时间循环机制?

答:同步就是代码主体一行一行的去执行;异步则是在回调用去等待他的执行结果返回给我。

eventloop 分为两个线程 一个是主线程一个是Event Table 线程。主线程则先执行同步函数执行完以后再等待EventTable线程执行异步函数的回调。

eventloop就是出现了先执行 同步 再等待异步的情况。

事件循环机制的核心是事件触发线程,由于执行栈产生异步任务,异步任务完成后事件触发线程将其回调函数传入到任务队列中,当执行栈为空,任务队列将队列头的回调函数入执行栈,从而新的一轮循环开始。这就是称为循环的原因。

ES6:

1. 更新了哪些语法?项目中实际使用?

答:(1)数组的方法

fill() : 数组填充

Array.fill(value) : 会对数组填充数组成员, 填充长度等于数组长度

Array.fill(value, start, end) : 可以使用指定的元素填充数组

includes():数组包含

Array.includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。对NaN一样有效。

from():数组转换

将类数组对象转换为数组

所谓类数组对象,即必须包含length属性,且元素属性名必须是数值或者可以转换成数值的字符。

将字符串转换成数组

将map结构转换成数组,推荐使用扩运算符(...)。

将set对象的元素转换成数组

copyWithin():数组覆盖

Array.prototype.copyWithin(target, start = 0, end = this.length)

它接受三个参数:

target (必需):从该位置开始替换数据。

start (可选):从该位置开始读取数据,默认为 0 。如果是负数,start 将从末尾开始计算。

end (可选):到该位置前停止读取数据,默认等于数组长度。copyWithin 将会拷贝到该位置,但不包括 end 这个位置的元素。如果是负数, end 将从末尾开始计算。

(2)字符串方法

includes():返回布尔值,表示是否找到了参数字符串。

startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。

endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。

第二个参数,表示开始搜索的位置。(起始值为0)

const s = 'zfpx';

console.log(s.startsWith('p',2)); // true

console.log(s.endsWith('f',2)); // true

console.log(s.includes('f',2)); // false

注意:endsWith的行为与其他两个方法有所不同,它针对前n个字符。而其他两个方法针对从第n个位置直到字符串结束。

repeat():返回一个新字符串,表示将原字符串重复n次。

(3)模板字符串

模版字符串,用`(反引号)标识,用${}将变量括起来。

(4)箭头函数

箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。 正是因为它没有this,从而避免了this指向的问题。

(5)解构赋值

解构赋值的作用是可以快速取得数组或对象当中的元素或属性,而无需使用arr[x]或者obj[key]等传统方式进行赋值。

数组

const color = ['red', 'blue'];

//传统赋值方式

var first = color[0];

var second = color[1];

//解构赋值方式

const [first, second] = color;

console.log(first); //'red'

console.log(second); //'blue'

对象

const people = {

name: 'lux',

age: 20,

education: {

degree: 'Masters'

}

}

const { name} = people;

console.log(name); //lux

const { name, age } = people;

console.log(`${name}--${age}`) ;//lux--20

const {education: {degree}} = user;

console.log(degree); // Masters

(6)扩展运算符

对象的赋值:(如果有重复的属性名,等号右边会覆盖等号左边)

const obj = { a: 1, b: 2}

const obj2 = { ...obj } // => { a: 1, b: 2 }

const obj3 = { ...obj, b: 3, c: 4} // => { a: 1, b: 3, c: 4 },覆盖了 b,新增了 c

数组的深拷贝:

const arr2 = arr;

const arr3 = [...arr];

console.log(arr===arr2); //true, 说明arr和arr2指向同一个数组

console.log(arr===arr3); //false, 说明arr3和arr指向不同数组

合并数组:

const arr1 = [1,2];

const arr2 = [3,4];

const arr3 = [5,6];

console.log([...arr1, ...arr2, ...arr3]); //[1, 2, 3, 4, 5, 6]

将一个数组变成参数序列:(代替了apply)

console.log(Math.max(...[654, 233, 727]))//相当于console.log(Math.max(654, 233, 727))

将字符串转换为数组:

[...'hello'] // [ "h", "e", "l", "l", "o" ]

map结构:

const map = new Map();

map.set('k1', 1);

console.log(map) // Map(1) {"k1" => 1}

console.log(...map) //["k1", 1]

let map = new Map([

[1, 'one'],

[2, 'two'],

[3, 'three'],

]);

let arr = [...map];

console.log(...map.keys()); // [1, 2, 3]

2. 箭头函数和普通函数的区别?this的指向?

答:箭头函数是匿名函数不能作为构造函数,不能使用new;

箭头函数的this永远指向的是上下文的this,没有办法改变this指向,普通函数的this 指向调用它的对象。

3. promise和async/await的区别?解决了哪些问题?优缺点?还有哪些解决异步的方式?

答:https://blog.csdn.net/qq_37617413/article/details/90637694

https://www.jianshu.com/p/3a37272de675

promise的用法:

Promise,简单来说就是一个容器,里面保存着某个未来才会结束的时间(通常是一个异步操作的结果)

基本语法:

let p = new Promise((resolve,reject) => {

//...

resolve('success')

});

p.then(result => {

console.log(result);//success

});

promise共有三个状态: pending(执行中)、success(成功)、rejected(失败)

链式调用

错误捕获:

Promise.prototype.catch用于指定Promise状态变为rejected时的回调函数,可以认为是.then的简写形势,返回值跟.then一样

`let p = new Promise((resolve,reject) => {

reject('error');

});

p.catch(result => {

console.log(result);

})`

async、await

简介:异步编程的最高境界就是不关心它是否是异步。async、await很好的解决了这一点,将异步强行转换为同步处理。

async/await与promise不存在谁代替谁的说法,因为async/await是寄生于Promise,Generater的语法糖。

用法:async用于申明一个function是异步的,而await可以认为是async wait的简写,等待一个异步方法执行完成。

规则:

1 async和await是配对使用的,await存在于async的内部。否则会报错

2 await表示在这里等待一个promise返回,再接下来执行

3 await后面跟着的应该是一个promise对象,(也可以不是,如果不是接下来也没什么意义了…)

写法:

`async function demo() {

let result01 = await sleep(100);

//上一个await执行之后才会执行下一句

let result02 = await sleep(result01 + 100);

let result03 = await sleep(result02 + 100);

// console.log(result03);

return result03;

}

demo().then(result => {

console.log(result);

});`

错误捕获:

如果是reject状态,可以用try-catch捕捉

let p = new Promise((resolve,reject) => {

setTimeout(() => {

reject('error');

},1000);

});

async function demo(params) {

try {

let result = await p;

}catch(e) {

console.log(e);

}

}

demo();

区别:

1 promise是ES6,async/await是ES7

2 async/await相对于promise来讲,写法更加优雅

3 reject状态:

1)promise错误可以通过catch来捕捉,建议尾部捕获错误,

2)async/await既可以用.then又可以用try-catch捕捉

async/await优点一:它做到了真正的串行的同步写法,代码阅读相对容易

async/await优点二:对于条件语句和其他流程语句比较友好,可以直接写到判断条件里面

async/await优点三:同样的,处理复杂流程时,在代码清晰度方面有优势

欢迎继续补充哦~~~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值