zl程序教程

您现在的位置是:首页 >  Javascript

当前栏目

js面试题

2023-03-07 09:15:00 时间

扩展运算符与rest运算符的区别?

扩展运算符用三个点表示,把数组或对象展开成一系列用逗号隔开的值

rest运算符也是三个点号,不过其功能与扩展运算符恰好相反,把逗号隔开的值序列组合成一个数组

解决异步的方式有哪些?

promise,Async await,Generator

promise:基于他在进行封装比如axios,fetch等,一般用于请求数据或交互。

Async await:一般用于变量的赋值,比如我必须等到这个变量附上值了,在往下操作

event loop简述原理

js执行时,会进入一个执行栈,执行栈里面放的是同步任务,执行代码的时候会遇到异步代码,像promise.then方法,setTimeout这两个异步任务就会放到任务队列中,等执行栈里面的同步代码执行完毕,去执行任务队列里面的异步任务,异步任务又分为宏任务和微任务,.then方法是微任务,setTimeout是宏任务,先执行微任务,在执行宏任务,执行setTimeout的时候,又会进入执行栈,里面又是同步代码,一直这样循环

ajax原理

1.创建ajax对象

2.如果有数据的话准备数据(可选)

2.设置请求的方法和接口地址

3.设置请求的编码

4.通过onreadystatechange事件去监听ajax对象请求过程

5.发送请求

ajax到async的发展过程

1.原生 ajax –》开发。 可读性,可维护性差—代码冗余。

2.jQuery ajax -》开发 可读性 可维护性差。

3.ES6 Promise 对jQuery封装的ajax改造成Prmoise封装。

4.ES8 async await 进一步进行改写--开发方便 可读性强 可维护性强。

this

代表当前上下文环境对象

1.this没有指向,指向全局对象window

2.new this更改为新创建的对象

3.函数对象.call(指定的对象,实参,实参,...)方法

--调用函数

--改变函数内部this指向为指定的对象

4.函数对象.apply(指定的对象,[实参,实参,...])方法

--调用函数

--改变函数内部this指向为指定的对象

5.事件处理函数中的this,当触发事件的时候,this更改为触发事件的DOM对象

6.事件对象.bind(指定对象);

--改变函数调用时的this指向

注意:bind不会调用函数

什么是同源策略?

所谓的同源,指的是协议,域名,端口相同。浏览器处于安全方面的考虑,只允许本域名下的接口交互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。

跨域的方式及原理?

1.jsonp js脚本标签script 上有一个src的属性 这个属性无视浏览器的限制 可以直接去访问外部资源

他的与缺点:

JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击

2.cors 后台添加一个白名单直接可以进行访问外部资源

header("Access-Control-Allow-Qrigin: *");

3. postMessage

iframe代理

通过iframe标签的src,加上属性onload=要执行的函数调用,在函数中,写contentWindow.postMessage,通过这个方法来发送数据。并通过window.onmessage来接收数据

4.niginx反向代理 由你自己写的接口在后端调用要访问的接口地址并拿回返回值,然后返回给index.html

5.proxy

Webpack中devServer的proxy也可以通过changeOrigin开区跨域

Axios的特点

同时支持浏览器端和服务端的请求

支持 Promise API

拦截请求和响应

转换请求和响应数据

取消请求

自动转换JSON数据

客户端支持防止 CSRF/XSRF(跨站请求伪造)

es5与es6继承方式?

es5:

1. 使用对象冒充实现继承

比如fn2类想继承fn1的属性与方法,首先在fn2中把fn1赋值给fn2的一个属性,比如名为parent,然后在fn2中调用this.parent(),这样就把fn1里的函数都在fn2中执行了,就实现了继承属性与方法,然后在通过delete this.parent,把parent删除就可以了。

2.通过call改变继承上下文

简单来说,就是通过call来改变要继承函数的this指向

3. 通过apply改变继承上下文

4. 采用原型链方式实现继承

在prototype上添加一个属性为想继承类,new的实例,这样就在原型上继承了想要继承的函数的属性和方法。

5. 采用混合模式实现继承

通过call来继承属性,prototype通过forin遍历想继承类的prototype上的方法,来继承方法。

es6:

通过extends来继承方法,super来继承属性。

处理数组用过哪些方法?

--concat( ) 连接数组

--join( ) 将数组元素按指定分隔符连接起来,返回一个字符串

--pop( ) 删除并返回数组的最后一个元素 (出栈) 常用

--push( ) 给数组添加元素 (入栈) 常用

--reverse( ) 颠倒数组中元素的顺序

--shift( ) 将元素移出数组 常用

--unshift( ) 在数组头部插入一个元素 常用

--slice( ) 返回数组的一部分 常用

--sort( ) 对数组元素进行排序 常用

--splice( ) 插入、删除或替换数组的元素 常用

--toString( ) 将数组转换成一个字符串

数组对象,变异和非变异方法?

1.数组的变异方法:push() pop() shift() unshift() splice() sort() reverse()

2.数组非变异方法:filter() concat() slice() forEatch(),map()

js判断是不是数组的方法?

方法一: 使用instanceof方法;

方法二: 使用constructor方法

在W3C定义中的定义:constructor 属性返回对创建此对象的构造器函数

方法三:ES5定义了Array.isArray

方法四:Array.prototype.isPrototypeOf(variable)

数组去重有哪些方法?

1.利用set的唯一性来达到去重效果(es6中最常用)

function unique(arr) {

return Array.from(new Set(arr))

}

var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];

console.log(unique(arr))

//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

这个无法去重空对象,后面会解决。

简化版:[...new Set(arr)],没看错,arr就是要去重的数组

2.利用双重for循环,然后splice去重(es5中最常用)

function unique(arr) {

for (var i = 0; i < arr.length; i++) {

// 从下标0开始和下标1逐个对比,如果有重复的,就通过splice删除

// j所在重复的元素,因为新的元素替换上来,所以需要j--来从新判断下

// 新替换上来的元素

for (var j = i + 1; j < arr.length; j++) {

if (arr[i] === arr[j]) {

arr.splice(j, 1);

j--;

}

}

}

return arr;

}

var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];

console.log(unique(arr))

//[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}] //NaN和{}没有去重,两个null直接消失了

3. 利用indexOf去重

function unique(arr) {

// 先创建一个空数组

var array = [];

for (var i = 0; i < arr.length; i++) {

// 如果indexOf返回-1,就证明没有重复的元素,就push到新数组中

if (array.indexOf(arr[i]) === -1) {

array.push(arr[i])

}

}

// 循环完毕后,返回这个新数组

return array;

}

var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];

console.log(unique(arr))

// [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}] //NaN、{}没有去重

4.利用sort()排序方法来实现

function unique(arr) {

// 先通过sort进行排序,把相同的元素先放到一起

arr = arr.sort()

// 然后把原数组的第一个元素存到arrry里

var arrry = [arr[0]];

for (var i = 1; i < arr.length; i++) {

// 如果当前的和上一个不相等,就添加进数组,如果重复就直接跳过

if (arr[i] !== arr[i - 1]) {

arrry.push(arr[i]);

}

}

return arrry;

}

var arr = [1, 'true',1, 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];

console.log(unique(arr))

// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined] //NaN、{}没有去重

5.利用es7中新增的includes方法

function unique(arr) {

var array = [];

for (var i = 0; i < arr.length; i++) {

//includes 检测数组是否有某个值,如果包含则返回 true,否则返回false

if (!array.includes(arr[i])) {

array.push(arr[i]);

}

}

return array

}

var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];

console.log(unique(arr))

//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}] //{}没有去重

6.利用hasOwnProperty()方法和filter

// 对象方法hasOwnProperty()方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。

function unique(arr) {

// 创建一个空对象

var obj = {};

// 通过filter过滤

return arr.filter(function (item, index, arr) {

// 如果里面的元素相等,那么就返回false把他过滤掉,如果返回true,就留下

return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)

})

}

var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];

console.log(unique(arr))

//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}] //所有的都去重了

处理字符串用过哪些方法?

--charAt( ) 返回字符串中的第n个字符 –例1 例2

--charCodeAt( ) 返回字符串中的第n个字符的Unicode编码

--concat( ) 连接字符串

--fromCharCode( ) 从字符编码创建—个字符串

--indexOf( ) 检索字符串 常用

--lastIndexOf( ) 从后向前检索一个字符串

--localeCompare( ) 用本地特定的顺序来比较两个字符串 是否相等

--match( ) 找到一个或多个正则表达式的匹配

--replace( ) 替换一个与正则表达式匹配的字符串

--search( ) 检索与正则表达式相匹配的字符串

--slice( ) 截取取一个字符串串

--split( ) 将字符串分割成字符串数组

--substr( ) 截取字符串 常用

--substring( ) 返回字符串的一个子串 常用

--toLowerCase( ) 将字符串转换成小写

--toUpperCase( ) 将字符串转换成大写

--valueOf( ) 返回对象的原始值

--toString( ) 返回字符串

处理对象用过哪些方法?

Object.assign()通过复制一个或多个对象来创建一个新的对象。

Object.create()使用指定的原型对象和属性创建一个新对象。

Object.defineProperty()给对象添加一个属性并指定该属性的配置。

Object.defineProperties()给对象添加多个属性并分别指定它们的配置。

Object.entries()返回给定对象自身可枚举属性的[key, value]数组。

Object.freeze()冻结对象:其他代码不能删除或更改任何属性。

Object.is()比较两个值是否相同。所有 NaN 值都相等(这与==和===不同)。

Object.isExtensible()判断对象是否可扩展。

Object.isFrozen()判断对象是否已经冻结。

Object.isSealed()判断对象是否已经密封。

Object.keys()返回一个包含所有给定对象自身可枚举属性名称的数组。

Object.values()返回给定对象自身可枚举值的数组。

Math对象常用的方法?

--floor(x)对数进行下舍入

--random()返回 0 ~ 1 之间的随机数

-- ceil(x)对数进行上舍入

js的数据类型有哪些?

值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。

引用数据类型:对象(Object)【数组(Array)、函数(Function)】。

substr与substring的区别?

substr( ) 截取字符串 。(返回从start位置开始length长度的子串)

substring( )返回字符串的一个子串 (返回从start位置开始到end位置的子串(不包含end))

数组的map,filter,forEach的区别?

共同点:

都有三个参数,第一个是数组中每个元素的值,第二个是下标,第三个是数组本身。

不同点:

map:他有return,可以返回更改后新的数组。

forEach:他的return返回undefined,他一般在函数中直接进行操作,直接操作,会改变原数组。

filter:他也有return,可以按照指定的条件来过滤数组,也会返回一个新的顾虑后的数组。

闭包?

有权访问函数内,私有变量的函数,也因为他是一个私有变量的函数,所以垃圾回收机制无法回收,就造成了内存泄漏。

闭包的适用场景?

1.选项卡和焦点轮播图

2.模块化开发 每一个组件就是一个闭包函数

什么是内存泄漏?

内存泄漏指因为疏忽或错误造成程序未能释放已经不在使用的内存的情况。

内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,而造成内存的浪费。

如何解决内存泄漏?

(1)良好的编码习惯,尽量在设计内存的程序段,检测出内存泄漏。

(2)使用了内存分配的函数或变量,使用完毕后,及时清除

什么是垃圾回收机制?

会定期对那些我们不再使用的变量、对象所占用的内存进行释放

Javascript 的垃圾回收机制:变量生命周期结束,内存会被回收

垃圾回收机制的算法有两点:

1.标记清除:标记清除和引用计数

2.引用计数:统计引用类型变量声明后被引用的次数,当次数为 0 时,该变量将被回收

全局变量:生命周期会一直持续,直到页面卸载

局部变量:函数调用结束,局部变量也不再被使用,它们所占用的空间也就被释放

闭包:由于闭包的原因,局部变量依然在被使用,所以也就不能够被回收

如何解决: 手动清除

说下原型链?

由实例对象的__proto__串起来到object.prototype.__proto__为空的链,就叫原型链

当js引擎查找对象的属性时,如果当前对象没有 就会去原型链上去找

js事件流?

1.冒泡型事件流:事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点。

2.捕获型事件流:事件开始时由最不具体的元素接收,然后逐级向下传播到较为具体的节点。

深拷贝和浅拷贝?

浅拷贝:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。

深拷贝:在内存中开一块新的内存地址,用于存放复制的新对象。

深拷贝方法:

1.通过递归与for..in

var obj = {

a: 1,

arr: [1, 2],

nation: '中国',

birthplaces: ['北京', '上海', '广州'],

fn1: function() {alert('拷贝我呀')}

};

var obj2 = { name: '杨' };

obj2 = deepCopy(obj, obj2);

console.log(obj2);

//深复制,要想达到深复制就需要用递归

function deepCopy(a, b) {

// 如果b传值了,就是b,如果没有就是空对象

var b = b || {};

// 遍历a的健名为i

for (var i in a) {

// 如果a对象当前的健名类型是object

if (typeof a[i] === 'object') {

// 如果a对象当前的元素的构造器是Array

if (a[i].constructor === Array) {

//那么b对象里的i属性就是个数组

b[i] = [];

} else {

//否则b对象里的i属性就是个对象

b[i] = {};

}

// 如果符合上面的条件之一,那我就在调用自己,把当前的对象或者数组和

// 跟他对应新创建的对象或数组一起传进去,在判断是不是,直到判断不是以后

deepCopy(a[i], b[i]);

} else {

// 就把a对象的值,在赋值给b对象

b[i] = a[i];

}

}

// 通过这样递归,就可以把原对象中的属性逐个复制出来。

// 最后把复制好的breturn出来就可以了

return b;

}

递归的另一种写法:

function deepClone(obj) {

var newObj = obj instanceof Array ? [] : {};

if (typeof obj !== 'object') {

return obj;

} else {

for (var i in obj) {

newObj[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i];

}

}

return newObj;

}

var a = [1, 2, 4, 6, "a", "12", [1, 2]];

var b = deepClone(a);

a[3] = 7;

console.log(a);

console.log(b);

2.通过

创建一个方法,传入一个obj对象,借用JSON.stringify和parse,stringify把传入的对象转成json字符串,然后再把json字符串通过parse,转换成原生js对象,在返回一个新的对象(函数不行,有缺陷)

var a = {

a: 1,

arr: [1, 2],

nation: '中国',

birthplaces: ['北京', '上海', '广州']

}

var b = JSON.parse(JSON.stringify(a));

console.log(a);

console.log(b);

3.通过对象合并方法Object.assign,前面加个空对象,来实现深拷贝

var a = {

a: 1,

arr: [1, 2],

nation: '中国',

birthplaces: [{name: '北京'}, '上海', '广州'],

fn1: function() {console.log('我被拷贝了')}

}

var b = Object.assign({}, a);

console.log(b);

DOM操纵 增删改查?

1.创建节点

document.createEelement 创建元素节点

document.createTextNode 创建文本节点

doucment.createAttribute 属性节点

2.添加节点

appenChild(node)

--添加节点到当前节点内部的后面(新创建的节点)

--移动节点到当前节点内部的后面(已有节点)

insertBefore(要添加或移动的节点,参考节点)

--添加节点到当前节点内部的前面(新创建的节点)

--移动节点到当前节点内部的前面(已有节点)

3.删除节点

remove()删除当前节点

removeChild(node)删除节点

4.复制节点

cloneNode(true)深拷贝

cloneNode(false)浅拷贝

5.替换节点

repalceChild(新节点,被替换的节点)

如何做防止重复提交?

1.react中通过redux-saga的附件effects里的takeList来进行阻止重复提交

2.防抖和节流

什么是防抖?

所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

let input = document.getElementById('user_input');

// 存储timeout的id

let timeID

// 抬起事件

input.onkeyup = () => {

// 点完后,如果有定时器正在执行,那么就去除

if (timeID) {

clearTimeout(timeID)

}

// console.log('我是id', timeID)

// 只执行,0.3秒后获取输入的值,在发送请求

timeID = setTimeout(() => {

// 1.获取用户输入

let { value } = input

// 2.模拟发送ajax请求联系服务器

sendAjax(value)

}, 300)

}

function sendAjax(data) {

console.log('点击的数据', data)

}

什么是节流?

设定一个特定的事件,让函数在特定时间内只执行一次,不会频繁触发。

// 一个标识

let isCanlog = true;

// 鼠标滚轮事件

document.onscroll = () => {

// 如果标识为true就打印

if (isCanlog) {

console.log(1);

// 打印完就改成false

isCanlog = false;

// 一面以后在变成true

setTimeout(()=>{

isCanlog = true

}, 1000)

}

}