2019-09-09 JS面试题(持续更新中)
1、JS 中的 MUL 函数
1 2 3 4 5 6 7 8 9 | function mul(x){ return function (y){ return function (z){ return x*y*z } } } var result = mul(2)(3)(4); console.log(result, 'result' ); //24 |
2、JS 中 slice 和 splice 的区别?
var arr = [0,1,2,3,4,5,6,7,8,9]; console.log(arr.slice(2,9));//[2,3,4,5,6,7,8],如果此时打印arr,显示的数据结构并未发生改变,和之前的值是一样的。 console.log(arr.splice(2,9));//[2,3,4,5,6,7,8,9]; 如果此时打印arr,的数据,显示的结果是 [0,1]。数据结构已经发生改变了。
总结一下:
slice(start,end) 方法,返回一个新数组,包含从 start 到end(不包含该元素) 的数组元素。(索引从0开始且 不会改变原数组,而是返回一个子数组)。
start 参数:必须,规定从哪个位置开始选取,如果为负数,规定从数组的尾部开始选取,-1是指最后一个元素。
end 参数:可选。如果没有这个参数的话,那么指的是从start开始到数组结束的所有元素,如果这个参数为负数,那么规定是从数组尾部开始算起的元素。
splice():该方法向或者从数组中添加或者删除,返回被删除的数据。(该方法会改变原数组)
splice(index,howmany,item1,...itemx):
index 参数:必须,整数,规定添加或删除的位置,使用负数的话,则表示从数组的尾部开始。
howmany 参数:必须,要删除的数量,如果为0,则不删除数据。
item1...itemx 参数:可选,向数组添加的新数据。
例如:
var testArray = [0,1,2,3,4,5]; console.log(testArray.splice(2,2,'hello','world'));//[2,3] console.log(testArray);//[0,1,'hello','world',4,5]
3、JS 中的展开运算符操作?
var mids = ['hello','world']; var newMids = [1,2,3,..mids,4,5]; console.log(newMids);//[1,2,3,'hello','world',4,5]
4、JS 中的函数提升?
JS 中创建函数有2中方法, 函数声明 和 函数表达式。
函数声明:
fn();//这个是函数声明 function fn(){ console.log('这个是函数声明') }
函数表达式:
fns();// 报错, fns is not a function var fns = function(){ console.log('这个是函数表达式') }
由此说明:函数声明的优先级要高于函数表达式。
5、JS 中的 typeof 返回哪些数据类型?
string , boolean , number, undefined , null, object ,symbol (es6)。
6、强制类型转换 和 隐式类型转换?
parseInt parseFloat number 和 == ===
7、添加,删除,替换,插入 到某个节点的方法
1)创建新节点 createElement()//创建一个具体的元素 createTextNode()//创建一个文本节点 例如: 元素获取body: var body = document.getElementsByTagName("body")[0]; (或者 var body = document.body;) var div = document.createElement('div'); div.className='create_div'; body.appendChild(div); //创建完毕。 var texts = document.createTextNode('hello world'); div.appendChild(texts); //创建完毕。 2)添加,移出,替换,插入 appendChild()//添加 removeChild()//移除 replaceChild()//替换 insertBefore()//插入 3)查找 getElementsByTagName()//标签名称 getElementsByName()//元素的Name 属性的值 getElementById()//元素id ,唯一性。
8、看下面代码输出结果?
for(var i =0;i<5;i++){ setTimeout(function(){ console.log(i) },1000) } //5,5,5,5,5
解决办法:(8.1)将 var 变为 let 即可。
(8.2)使用闭包
for(var i=0;i<=5;i++){ setTimeout(function(){ console.log(i) }(i),1000) } //0,1,2,3,4,5
(8.3)
9、数组去重方法:
1 | 双层 for 循环,Array.sort()加一行遍历冒泡,Array.filter() 加indexOf,ES6中的set去重,object键值对去重。 |
function duplicateRemoval(arr){ //双层for循环 for(let i=0;i<arr.length;i++){ for(let j=i+1;j<arr.length;j++){ if(arr[i]==arr[j]){ arr.splice(j,1); j--; } } } return arr; } // es6 中的 set去重 function unique(arr){ return Array.from(new Set(arr)) //或者 return [...new Set(arr)] } //Array.from()方法:就是一个类数组对象或者可遍历对象转换成一个真正的数组。(最基本要求是具有length属性的对象。) //利用indexOf 去重 以及 includes (异曲同工之妙),如果有的话,则返回true,反之为false表示没有。 function unique(arr){ if(!Array.isArray(arr)){ //用来判断是否是数组 return } let array = []; for(let i=0;i<arr.length;i++){ if(array.indexOf(arr[i]===-1)){ //这里表示没有==> 也可以换成 这样子表示: if(!array.includes(arr[i])){//表示没有} arrar.push(arr[i]) } } return array; } //Array.filter() 过滤数组,方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,且不会改变原始数组。 function unique(arr){ return arr.filter(function(item,index,arr){ return arr.indexOf(item,0)===index; //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素。 }) }
10、数组的方法
push() 向数组的末尾添加一个或多个元素,并返回新的长度。
pop() 删除向数组末尾的一个元素,并把他作为返回值返回。
unshift() 向数组的头添加一个元素或多个元素,并返回新的长度,向前边插入元素后,其他元素依次调整。
shift() 删除数组头的一个元素,并把他作为返回值返回。
slice(截取开始的索引,截取结束的索引) 向数组中提取指定元素。截取结束的索引可以不写,如果索引为负值的话,代表从后边开始计算。
splice(开始的索引,删除的数量) 删除数组中的指定元素,会影响原数组,并把删除的元素作为返回值返回。
concat() 可以连接两个或多个数组,并返回新数组,不会影响原数组。
join() 可以将数组转换为字符串。
reverse() 用来反转数组,会影响原数组。
sort() 用来对数组中的元素进行排序,影响原数组。
11、测试 numbers 数组的内容是什么?
const length = 4;
const numbers = [];
for(var i=0;i<length;i++);{ //注意看这里哦,多了一个分号
numbers.push(i+1);
}
numbers;//
for() 在空语句上进行4次迭代(不执行任何操作),而忽略实际将项目推入数组的块:{numbers.push(i+1);}
//上面代码等同于:
var i;
for(i=0;i<length;i++){
// do nothing;
}{
numbers.push(i+1);
}
numbers;//[5]
12、意外的全局变量
function foo(){
let a = b = 0;
a++;
return a;
}
foo();
typeof a;//'undefined'
typeof b;//'number'
//上面代码等效于:
function foo(){
let a;
window.b = 0;
a++;
return a;
}
foo();
typeof a;//undefined
typeof b;//number
13、http 协议与 https协议的区别:
超文本传输协议HTTP协议被用于在web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了web浏览器和网站服务器之间的传输报文,
就可以直接读懂其中的信息,因此,HTTP协议不适合传输一些敏感信息,比如:信用卡号,密码等支付信息。
为了解决HTTP协议这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS,为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和
服务器之间的通信加密。
HTTP和HTTPS的基本概念:
HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准TCP,用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。
HTTP和HTTPS区别:
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(secure sockets layer)协议用于对HTTP
协议传输的数据进行加密,从而就有了HTTPS。简单来说,HTTPS协议是SSL+HTTP协议构建的可进行加密传输,身份认证的网络协议,要比HTTP协议安全。
两者之间的主要区别是:
HTTPS协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL加密传输协议。
HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443.
HTTP的连接很简单,是无状态的,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输,身份认证的网络协议,比HTTP协议安全。
链接:https://www.cnblogs.com/sueyyyy/p/12012570.html
14、如何产生闭包? 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。【函数嵌套,内部函数引用了外部函数的数据(变量/函数)】
闭包是什么?
闭包是嵌套的内部函数。包含被引用变量(函数)的对象。【闭包存在于嵌套的内部函数中】
例子:
<---html代码 -->
<body onload="init()">
<p>one</p>
<p>two</p>
<p>three</p>
<p>four</p>
<p>five</p>
</body>
//js 代码
function init(){ //将变量i保存在每个段落对象上
var ps = document.getElementsByTagName('p');
for(var i=0;i<ps.length;i++){
ps[i].i = i;
ps[i].onclick = function(){
console.log(this.i)
}
}
}
function init(){ //加一层闭包,i已函数参数形式传递给内层函数
var ps = document.getElementsByTagName('p');
for(var i=0;i<ps.length;i++){
(function(arg){
ps[i].onclick = function(){
console.log(arg);
}
})(i)
}
}
function init(){ //加一层闭包,i以局部变量形式传递给内层函数
var ps = document.getElementsByTagName('p');
for(var i=0;i<ps.length;i++){
(function(){
var temp = i; //调用局部变量
ps[i].onclick = function(){
console.log(temp);
}
})()
}
}
function init(){ //加一层闭包,返回一个函数作为响应事件
var ps = document.getElementsByTagName('p');
for(var i=0;i<ps.length;i++){
ps[i].onclick = function(arg){
return function(){
console.log(arg)
}
}(i)
}
}
function init(){
var ps = document.getElementsByTagName('p');
for(var i=0;i<ps.length;i++){
ps[i].onclick = new Function("console.log("+i+")")
}
}
//错误的写法:
function init(){
var ps = document.getElementsByTagName('p');
for(var i=0;i<ps.length;i++){
ps[i].onclick = function(){
console.log(i);//这里会输出5,不管点击那个都会输出5.
}
}
}
15、js 基本数据类型和引用数据类型
基本数据类型 和 引用数据类型:
ECMAScript 包括两个不同类型的值:基本数据类型和引用数据类型。
常见的基本数据类型:
number ,string ,boolean ,undefined,null。基本数据类型是按值访问的,因为可可以直接操作保存在变量中的实际值。
引用数据类型:存放在堆内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置,如对象,数组,函数等。
传值与传址:基本类型与引用类型最大的区别实际就是传值与传址的区别。
例如:var a = [1,2,3,4];
var b = a;//这个是传址,对象中传给变量的数据是引用类型,会存储在堆中。
var c = a[0];//这个是传值,把对象中的属性/数组中的数组项赋值给变量,这时变量c是基本数据类型,存储在栈内存中,改变栈中的数据不会影响堆中的数据。
浅拷贝:在定义一个对象或数组时,变量存放的往往只是一个地址。当我们使用对象拷贝时,如果属性是对象或数组时,这时候我们传递的也只是一个地址。因此子对象在访问属性时,会根据地址回溯到父对象
指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。
深拷贝:不希望父子对象之间产生关联,那么可以使用深拷贝。
16、JS原型链
创建对象的方法:
1. var obj1 = {name:'one'}; 字面量
var obj11 = new Object({name:'one11'});
2. var M = function(name){ this.name = 'two';} 构造函数
var obj22 = new M('obj22');
3. var P = {name:'three'}; Object.create
var obj33 = Object.create(P);
原型与原型链

什么原型对象?实例?构造函数?
var M = function(name){ this.name = name;}
var o3 = new M('o3');
实例就是对象,在上面这例子中,o3就是实例,M就是构造函数。实例通过new一个构造函数生成的。从上图中可以知道,实例的__protpo__指向的是原型对象。实例的构造函数的prototype也是指向的原型对象。原型对象的constructor指向的是构造函数。
原型链:就是原型组成的链,对象的__proto__它的是原型,而原型也是一个对象,也有__proto__属性,原型的__proto__又是原型的原型,这样可以一直通过__proto__向上找,这就是原型链,找到最大的object,就终止。
原型对象和实例之间有什么作用?
var M = function(name){ this.name = name;}
var o3 = new M('0s');
var o5 = new M();
o3.__proto__.say = function(){
console.log('hello world')
}
o3.say();//hello world
o5.say();//hello world
只有函数有prototype,对象是没有的。但是函数也是有__proto__的,因为函数也是对象。函数的__proto__指向的是Function.prototype。

instanceof 是判断实例对象的__proto__和生成该实例的构造函数的prototype是不是引用的同一个地址。是返回true,否则返回false。实例额原型构造函数,obj.__proto__.constructor
new 运算符原理: 一个新对象被创建,他继承自foo.prototype.构造函数返回一个对象。在执行的时候,相应的传参会被传入,同时上下文this会被指定为这个新的实例。
new foo 等同于new foo(),只能用在不传递任何参数的情况。
17、js冒泡排序
function sort (temp){
for(var i=0;i<temp.length;i++){
for(var j=0;j<temp.length-i-1;j++){
if(temp[j]>temp[i]){
var result = temp[j];
temp[j] = temp[j+1];
temp[j+1] = result;
}
}
console.log(temp)
}
}
var tempArray = [1,6,2,65,9,4,3,8,6];
sort(tempArray); // 比较相邻的元素,如果后一个比前一个大,则交换位置。
//快速排序:
function quickSort(temp){
if(temp.length<=1){
return temp;
}
var index = Math.floor(temp.length/2);
var pivot = temp.splice(index,1)[0];//获取删除的数字
var arrleft = [];
var arrright = [];
for(var i=0;i<temp.length;i++){
if(temp[i]<privot){
arrleft.push(temp[i]);
}else{
arrright.push(temp[i]);
}
}
return quickSort(arrleft).concat([pivot],quickSort(arrright));
}
18、es6函数新增
函数形参的默认值:
function foo(x,y='tom'){
console.log(x,y)
}
foo('hello');//hello tom
foo('hello','');//hello
默认值与结垢赋值结合:
function foo({x,y=3}){
console.log(x,y);
}
foo({});//undefined 3;
foo({x:1,y=2});//1,2
foo();//报错
参数默认值的位置:
函数的length属性和作用域:
length:指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。
作用域:一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域。
rest参数: rest参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function foo(...values){
var sum = 0;
for(var val of values){
sum+=val;
}
}
foo(1,2,3);//6
箭头函数: =>定义函数 (函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。不可以当作构造函数,也就是说不可以使用new命令。不可以使用arguments对象。不可以使用yield命令。)
var f = () =>5;
var sum = (nums,num2)=>num1+num2;
19、同步异步
同步异步定义:
同步和异步关注的是消息通信机制。同步,就是调用某个东西时,调用方得等待这个调用返回结果才能继续往后执行。异步,和同步相反,调用方不会立即得到结果,而是在调用发出后调用者可以继续执行后续操作,被
调用者通过状态来通知调用者,或者通过回调函数来处理这个调用。
同步操作好比排队。
20、发送http请求发生了什么?
当我们在浏览器的地址输入 www.baidu.com 然后回车,回车这一瞬间发生了什么呢?

域名解析---发起TCP 3次握手--建立TCP连接后发起http请求--服务器端响应http请求,浏览器得到html代码--浏览器解析html代码,并请求html代码中的资源--浏览器对页面进行渲染呈现给用户。
21、JS 小题
1、 console.log( [] == ![] );
//打印为 true。
关系运算符,把其他数据类型转换成 number,然后比较
逻辑运算符,把数据转换成 Boolean
复杂数据类型在隐式转换时会先转成string
[].valueOf().toString() =>''
![] => false
Number('') === Number(false)
2、 console.log( 'b'+'a'++'a'+'a');
//打印为 baNaNa
字符串连接符,把其他数据类型转换成 string,然后拼接
算符运算符,把其他数据类型转换成 number,然后做加法计算
'b'+'a'+'NaN'+'a' => baNaNa
3、 console.log( NaN === NaN );
//打印为 false
NaN 不等于任何值。
4、 console.log([1,2,3] + [4,5,6]);
// 1,2,34,5,6
字符串连接符,把其他数据类型转换成string,然后拼接
[1,2,3].valueOf().toString() => 1,2,3
[4,5,6].valueOf().toString() => 4,5,6
所以结果就是: '1,2,3'+'4,5,6' => 1,2,34,5,6
5、 console.log(Math.min() > Math.max());
//true
Math.min() => Infinity
Math.max() => -Infinity
22、文本超出部分显示省略号
/*css 单行*/ .类名 { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } /*多行*/ .类名 { display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3;/*这里显示几行文字*/ overflow:hidden }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构