JS面试题
1、javascript 的 typeof 返回哪些数据类型
number、boolean、undefined、string、object、function
2、javascript 有哪些数据类型
基本数据类型:number、boolean、undefined、string、null、
引用数据类型:object、RegExp、array、Date、Math
3、例举 3 种强制类型转换和 2 种隐式类型转换?
强制转换:parseInt,parseFloat,Number()
隐式转换:==
4、split() 和 join()的区别
前者是切割数组的形式,后者是将数组转换成字符串
5、数组方法pop() push() unshift() shift()
pop() 尾部删除 shift()头部删除
push() 尾部添加 unshift() 头部添加
6、数组方法
every() filter() forEach() isArray() map() reduce() indexOf()
7、深拷贝和浅拷贝
浅拷贝:赋值符号 =
深拷贝:
1、var arr2 = arr1.slice(0),(多层数组有问题,如[ 1 ,2,[0,1] ,3])
2、var arr2 = arr1.concat();(多层数组有问题)
3、for循环重新压进新的空数组 (多层数组需要多层循环压进去,否则有问题)
4、ES6的扩展运算符 var [...arr2] = arr1,(多层数组和多层对象有问题,如[ 1 ,2,[0,1] ,3])
let oldObj = {
a: 1, b: 2
}
let newObj = {...oldObj}
newObj.a = 2
console.log(oldObj) // {a: 1, b: 2}
let outObj = {
inObj: {a: 1, b: 2}
}
let newObj1 = {...outObj}
newObj1.inObj.a = 2
console.log(outObj) // {inObj: {a: 2, b: 2}}
let outArr = [1,2,[4,5],3]
let newArr = [...outArr]
newArr[0] = 2;
newArr[2][1] = 6;
console.log(outArr)//[1,2,[4,6],3]
5、JSON.parse(JSON.stringify(arr1)) ,若为正则、函数、undefined、NaN等值有坑(实际工作中常用的方法)
8、字符串里面的字符数最多次数以及该字符是什么?
var str = "asdkfhsodjcpeksoxshieorjsa";
var obj = {};var maxNum = 0; var letter = '';
for(var i=0;i<str.length;i++){
if(!obj[str.charAt(i)]){
obj[str.charAt(i)] = 1;
}else{
obj[str.charAt(i)]++;
}
};
for(var item in obj){
if(obj[item ]>maxNum){
maxNum=obj[item];
letter = item
}
}
console.log('出现次数最多的是:'+letter+'出现'+maxNum+'次')
9、99乘法表制作
<script>
for (var i = 1;i <=9;i++) {
for(var j = 1;j <=i;j++) {
document.write(i+"*"+j+"=" + i*j +" ")
}
document.write("<br/>")
}
</script>
10、js将数字转换为汉字
function convertToChinaNum ( num ) {
var arr1 =newArray('零','一','二','三','四','五','六','七','八','九');
var arr2 =newArray('','十','百','千','万','十','百','千','亿','十','百','千','万','十','百','千','亿');//可继续追加更高位转换值
if(!num ||isNaN(num)){
return"零";
}
var english = num.toString().split("")
var result ="";
for(var i =0; i < english.length; i++) {
var des_i = english.length -1- i;//倒序排列设值
result = arr2[i] + result;
var arr1_index = english[des_i];
result = arr1[arr1_index] + result;
}
//将【零千、零百】换成【零】 【十零】换成【十】
result = result.replace(/零(千|百|十)/g,'零').replace(/十零/g,'十');
//合并中间多个零为一个零
result = result.replace(/零+/g,'零');
//将【零亿】换成【亿】【零万】换成【万】
result = result.replace(/零亿/g,'亿').replace(/零万/g,'万');
//将【亿万】换成【亿】
result = result.replace(/亿万/g,'亿');
//移除末尾的零
result = result.replace(/零+$/,'')
//将【零一十】换成【零十】
//result = result.replace(/零一十/g, '零十');//貌似正规读法是零一十
//将【一十】换成【十】
result = result.replace(/^一十/g,'十');
return result;
}
11、事件绑定和普通事件有什么区别?
事件绑定是指把事件注册到具体的元素之上,普通事件指的是可以用来注册的事件
普通事件会覆盖掉,只执行后者方法,不支持DOM事件流
dom.onclick = function(){}
事件绑定不会覆盖掉,会依次执行,支持DOM事件流
addEventListener('click',function(){},true)
12、跨域问题?
概念:只要协议、域名、端口有任何一个不同,都被当作是不同的域
请求接口的时候有时候会出现子域名等,导致跨域问题
解决方案:JSONP、websocket、CORS、PostMessage
注意JSONP只支持GET方法
JSONP原理及实现
什么是跨域?javascript跨域的四种方式介绍-js教程-PHP中文网
13、call和apply的区别?
都是改变this的指向,两个方法属于Function.prototype原型上的方法
call传参一个一个的传,apply传参是个数组
14、继承的方法?
总共有5种
对象冒充、call、apply、原型继承、混合继承(call和原型继承混合)
JavaScript中B继承A的方法 - Me丶微笑 - 博客园
15、JavaScript指针、闭包、作用域?
this指向调用上下文
闭包:内作用域调用外作用域的变量,滥用闭包函数会造成内存泄露,因为闭包中引用到的包裹函数中定义的变量都
永远不会被释放,所以我们应该在必要的时候,及时释放这个闭包函数
作用域:定义一个函数就开辟了一个局部作用域,整个JS执行环境有一个全局作用域
16、事件委托是什么?
符合 W3C 标准的事件绑定 addEventLisntener /attachEvent
利用事件冒泡的原理,把自己的所触发的事件,让他的父元素代替执行!
17、如何阻止事件冒泡和默认事件?
阻止冒泡:e.stopPropagation()
阻止默认事件:e.preventDefault()
JS阻止冒泡和取消默认事件(默认行为)-前端开发博客
18、添加 删除 替换 插入到某个节点的方法 ?
obj.appendChild()
obj.insertBefore() //原生的 js 中不提供 insertAfter();
obj.replaceChild()//替换
obj.removeChild()//删除
19、document load 和 document ready 的区别?
Document.onload 是在结构和样式加载完才执行 js
window.onload:不仅仅要在结构和样式加载完,还要执行完所有的样式、图片这些资源文
件,全部加载完才会触发 window.onload 事件
Document.ready 原生种没有这个方法,jquery 中有 $().ready(function)
20、和=的区别?
前者只是判断值是否相等,后者会判断数据类型和值是否都相等
21、javascript 的同源策略?
一段脚本只能读取来自于同一来源的窗口和文档的属性,这里的同一来源指的是主机名(域名指向的是ip地址,即主机地址)、协
议和端口号的组合
http,ftp:协议
主机名;localhost
端口名:80:http 协议的默认端口
https:默认端口是 8083
同源策略带来的麻烦:ajax 在不同域名下的请求无法实现,
如果说想要请求其他来源的 js 文件,或者 json 数据,那么可以通过 jsonp 来解决
23、JavaScript 是一门什么样的语言,它有哪些特点?
js是面向对象的弱类型语言
语言特性:面向对象(要掌握创建对象的多种方式,继承的多种方式、原型链),动态/弱类型语言
24、已知 ID 的 Input 输入框,希望获取这个输入框的输入值,怎么做?(不使用第三方框架)
document.getElementById('ID').value
25、希望获取到页面中所有的 checkbox 怎么做?(不使用第三方框架)
var domList = document.getElementsByTagName(‘input’)
var checkBoxList = []; //返回的所有的 checkbox
var len = domList.length; //缓存到局部变量
while (len--) { //使用 while 的效率会比 for 循环更高
if (domList[len].type == 'checkbox') {
checkBoxList.push(domList[len]);
}
}
//获取已经选中的多选项 CheckBox
26、箭头函数和普通函数的区别是什么?
答:普通函数this总是指向他的调用者,默认指向window,在严格模式下,没有直接调用者的函数中this是undefined,使用call、apply、bind绑定,this指的是绑定的对象
箭头函数的this,指向定义时所在的对象,而不是调用时所在的对象;箭头函数不能构造函数(使用new 命令),否则抛出错误;不能使用arguments对象;不能使用yield命令
27、讲一下let 、var、const的区别?
var 没有块级作用域,支持变量提升
let 有块级作用域,不支持变量提升,不允许重复声明同名变量,暂存性死区,不能通过window进行访问
const 有块级作用域,不允许变量提升,不允许重复声明同名变量,暂存性死区,不能改变变量的值
28、实现一个new的伪代码?
创建一个对象;链接原型;绑定this;返回该对象
function _new( ) {
let obj = new Object();
let Con = [].shift.call(arguments);
obj.__proto__ = Con.prototype;
let result = Con.apply(obj,arguments);
return typeof result ==='object' ? result :obj
}
function create(Con, ...args) {
this.obj = {};
//创建一个空的对象//将空对象指向构造函数的原型链
Object.setPrototypeOf(obj, Con.prototype);
//obj绑定到构造函数上,便可以访问构造函数中的属性
let result = Con.apply(obj, args);
//如果返回的result是一个对象则返回该对象,new方法失效,否则返回obj
return result instanceof Object ? result : obj;
}
29、原型、原型链?
每一个JS函数中都有一个prototype
(原型)属性,指向这个函数的原型对象,通过这个函数产生的实例对象都有一个__proto__
(隐式原型)属性,这个属性也是指向同一个原型对象,所有的实例对象的属性都会继承这个原型对象的属性,原型对象上也有一个__proto__
属性,指向的objec
原型对象,所有的对象属性都会继承objec
原型对象属性。而objec
t原型对象的__proto__
指向的是null
。当我们访问对象的某个属性时,就会从实例对象,原型对象,object
原型对象上层层寻找,由此形成原型链。而原型就是原型对象上的属性。
30、垃圾回收机制(闭包的延伸)?
分为以下两个阶段:
1、标记阶段:垃圾回收器,从根对象开始遍历,访问到的每一个对象都会被标示为可到达对象。
2、清除阶段:垃圾回收器在对内存当中进行线性遍历,如果发现该对象没有被标记为可到达对象,那么就会被垃圾回收机制回收。这里面牵扯到了引用计数法,每次引用都被会‘➕1’ 如果标记清零,那么就会被回收掉。
31、函数的节流和防抖动?
防抖动函数:将多次触发变成最后一次触发;
function debounce(fn,wait){
let timer = null;
return function (){
let arg = arguments;
if(timer){
clearTimeout(timer);
timer = null;
}
timer = setTimeout(()=>{
fn.apply(this,arg)
},wait)
}
}
function clg(){
console.log('clg')
}
window.addEventListener('resize',debounce(clg,1000))
节流函数:将多次执行变成每隔一个时间节点去执行的函数
function throttle(fn,time){
let lastTime = null;
return function(){
let nowTime = Date.now();
if(nowTime - lastTime > time || !lastTime){
fn();
last = nowTime
}
}
}
function sayHi(){
console.log('hi')
}
setInterval(throttle(sayHi,1000),500)
32、简单介绍下event loop?
js作为单线程语言。在执行过程中,会产生执行环境。这些执行环境中的代码被顺序的加入到执行栈中,如果遇到异步代码,会被挂起并加入到任务队列当中,等到主线程任务执行完毕,event loop就会从任务队列取出需要执行的代码放入到执行栈中执行。所以本质上来讲,js中的异步还是同步的行为。
任务队列有分为宏任务和微任务队列。
一次正确的event loop执行顺序如下:
1、执行所有同步代码
2、执行栈为空,查询是否有需要执行的微任务。
3、微任务(有:则执行,无:则跳出)
4、必要的话开始渲染UI
5、开始下一轮的任务队列执行宏任务中的异步代码。
33、数组去重?
//扩展运算符只对单层数据深拷贝
functin uniqueStr ( arr ) {
var newArr = new Set( arr );
return [...newArr]
}
functin uniqueStr ( arr ) {
var newArr = [],obj = {};
for (var = i;i < arr.length;i++) {
if(!obj[arr[i]]){
newArr.push(arr[i])
obj[arr[i]] = 1
}
}
return newArr
}
function uniqueArr(arr) {
const res = new Map()
return arr.filter((a)=>{
return !res.has(a) && res.set(a,1)
})
}
https://www.cnblogs.com/zhishaofei/p/9036943.html
34、创建对象的5中方法?
1、new 操作符+Object创建对象
var person = new Object();
person.name = "lisi";
person.age = 21;
person.family = ["lida","lier","wangwu"];
person.say = function(){
alert(this.name);
}
2、字面式创建对象
var person ={
name: "lisi",
age: 21,
family: ["lida","lier","wangwu"],
say: function(){
alert(this.name);
}
};
3、工厂模式
funtion createPerson(name,age,family) {
var o = new Object();
o.name = name;
o.age = age;
o.family = family;
o.say = function(){
alert(this.name)
}
}
var person = createPerson('lisi','12',['wangwu','liumazi']);
console.log(person1 instanceof Object); //true
console.log(person1 instanceof Person); //true
console.log(person1.constructor); //constructor 属性返回对创建此对象的数组、函数的引用
//K可以重复调动,工厂式的创造对象
4、构造函数模式
function Person(name,age,family) {
this.name = name;
this.age = age;
this.family = family;
this.say = function(){
alert(this.name);
}
return o;
}
var person1 = new Person("lisi",21,["lida","lier","wangwu"]);
console.log(person1 instanceof Object); //true
console.log(person1 instanceof Person); //true
console.log(person1.constructor); //constructor 属性返回对创建此对象的数组、函数的引用
构造函数模式每个实例包含不同的Function实例,占用内存
5、原型模式
function Person() {
}
Person.prototype.name = "lisi";
Person.prototype.age = 21;
Person.prototype.family = ["lida","lier","wangwu"];
Person.prototype.say = function(){
alert(this.name);
};
console.log(Person.prototype); //Object{name: 'lisi', age: 21, family: Array[3]}
var person1 = new Person(); //创建一个实例person1
console.log(person1.name); //lisi
var person2 = new Person(); //创建实例person2
person2.name = "wangwu";
person2.family = ["lida","lier","lisi"];
console.log(person2); //Person {name: "wangwu", family: Array[3]}
// console.log(person2.prototype.name); //报错
console.log(person2.age); //21
6、混合模式(构造函数+原型模式)
function Person(name,age,family){
this.name = name;
this.age = age;
this.family = family;
}
Person.prototype = {
constructor: Person,
//每个函数都有prototype属性,指向该函数原型对象,
//原型对象都有constructor属性,这是一个指向prototype属性所在函数的指针
say: function(){
alert(this.name);
}
}
var person1 = new Person("lisi",21,["lida","lier","wangwu"]);
console.log(person1);
var person2 = new Person("wangwu",21,["lida","lier","lisi"]);
console.log(person2);
还有动态原型模式、寄生构造函数模式、稳妥构造函数模式
35、哪些方法改变原数组,哪些方法不改变原数组
改变原数组的:
1、shift:将第一个元素删除并且返回删除元素
2、unshift:在原数组的最前端依次添加,并且返回新数组的长度
3、push:在原数组的最后依次添加项,并返回新数组的长度
4、pop:将最后一个元素移除并返回移除的项
5、reverse:反转数组的顺序
6、sort:对数组进行依次排序
7、splice:三个参数。第一个代表开始的下标,第二个代表 要删除的个数,第三个代表要替换的东西 返回被删除的数组
不改变原数组的:
1、concat:拼接,连接多个数组
2、slice:提取,返回被提取的字符
3:join:将数组中所有元素以参数作为分隔符放入一个字符
4、map,filter,some,every,foreach等不改变原数组
拓展
toString(),数组转字符串
36、js数组迭代方法与归并方法
ES5为数组定义了5个迭代的方法:
运行的函数会接受三个参数:1)数组项的值 2)该项在数组中的位置 3)数组对象本身。
.every()
对数组的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。
例:
var arr = [1,2,3,4,5,4,3,2,1];
var num = arr.every(function(item,index,array){
return (item > 2);
})
console.log(num); //false
.some()
对数组的每一项运行给定函数,如果该函数对任一项返回true,则返回true。
例:
var arr = [1,2,3,4,5,6,7,8];
var num = arr.some(function(item,index,array){
return (item > 2);
})
console.log(num); //true
.filter()
对数组的每一项运行给定函数,返回该函数会返回true的项组成的数组。
例:
var arr= [1,2,3,4,5,4,3,6,1];
var num= arr.filter(function(item,index,array){
return (item > 3);
})
console.log(filterResult); //[4,5,4,6]
·map()
对数组的每一项运行给定函数,返回每次函数调用的结果组成的数组。
例:
var arr = [1,2,3,4,5,6,7,8];
var num = arr.map(function(item,index,array){
return item*2;
})
console.log(num); //[2,4,6,8,10,12,14,16]
·forEach()
对数组的每一项运行给定函数。该方法没有返回值。
var arr = [1,2,3,4,5,6,7,8];
var num = arr.forEach(function(item,index,array){
console.log(item*2); //[2,4,6,8,10,12,14,16]
})
console.log(num); //undefined
归并方法
·reduce() 是从数组的第一项开始,逐个遍历到最后。
·reduceRight() 是从数组的最后一项开始,逐个遍历到最前。
这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。
传给这两个方法的函数接收4个参数:1)前一个值 2)当前值 3)项的索引 4)数组对象
例:
var values = [3,6,5,2,4,2];
var sum = values.reduce(function(prev,cur,index,array){
return prev + cur; //3+6=9+5=14+2=16+4=20+2=22
});
console.log(sum); //22