js基础习题及答案(一)

this的不同应用场景,如何取值

  • 当做普通函数被调用
    this=window
  • 使用call apply bind
    this=call | apply | bing 中绑定的this
  • 作为对象方法调用
    this=对象本身
  • 在class中的方法中调用
    this=当前实例
  • 箭头函数
    this=上一个作用域的this

手写bind函数

 Function.prototype.myBind = function(context) {
    //返回一个绑定this的函数,我们需要在此保存this
    let self = this
        // 可以支持柯里化传参,保存参数
    let arg = [...arguments].slice(1)
        // 返回一个函数
    return function() {
        //同样因为支持柯里化形式传参我们需要再次获取存储参数
        let newArg = [...arguments]
        console.log(newArg)
            // 返回函数绑定this,传入两次保存的参数
            //考虑返回函数有返回值做了return
        return self.apply(context, arg.concat(newArg))
    }
  }

实际中闭包的应用

  • 隐藏数据
  • 制作一个缓存的应用
function createCache(){
	const data={}//需要缓存的数据
	return {
		set:(key,val)=>{
			data[key]=val
		},
		get:key=>data[key]
	}
}
const c=createCache()
c.set('name','张三')//设置缓存的数据
const name=c.get('name')//获取缓存中的数据
console.log(name)

同步和异步的区别

  • 基于js是单线程语言
  • 同步会阻塞代码执行
  • 异步不会阻塞代码执行

property和attribute的区别

  • property:修改对象属性, 不会体现html结构中
  • attribute:修改html属性,会改变html结构
  • 两者都有可能引起DOM重新渲染

DOM是哪种数据结构

树(DOM结构)

DOM性能

  • DOM操作非常昂贵,避免频繁操作DOM
  • 对DOM查询做缓存
  • 将频繁操作改为一次性操作

DOM查询做缓存

//不缓存DOM查询结果
for(let i=0;i<document.getElementByTagName('p').length;i++){
	//每次循环,都会计算length,频繁进行DOM查询
}
//缓存DOM查询结果
const pList=document.getElementByTagName('p')
const pLength=pList.length
for(let i=0;i<length;i++){
	//缓存length,只进行一次DOM查询
}

将频繁操作改为一次性操作

  • 一次插入多个节点,考虑性能
const listNode =document.getElementById('list')
//创建一个文档片段,此时还没有插入到DOM树中
const frag=document.createDocumentFragment()

//执行插入
for(let i=0;i<10;i++){
	const li=document.createElement('li')
	li.innerHTML=`第${i}个li`
	frag.appendChild(li)
}
//循环结束后再插入到dom树中
listNode.appendChild(flag)

编写一个通用的事件绑定函数

function bindEvent(elem,type,selector,fn){
	if(fn==null){//说明不需要代理
		fn=selector
		selector=null
	}
	elem.addEventListener(type,e=>{
		let target
		if(selector){
			//需要代理
			target=e.target
			//元素被指定的选择器字符串选择,Element.matches()  方法返回true; 否则返回false
			if(target.mathes(selector)){
				fn.call(target,e)
			}else{
			//不需要代理
				fn(e)
			}
		}
	})
}

描述事件冒泡

  • 基于DOM树形结构
  • 事件会顺着触发元素往上冒泡,一层一层往上冒
  • 应用场景:事件代理

Cookie的缺点

  • 存储大小,最大4KB
  • http 请求时需要发送到服务端,增加请求数据量
  • 只能用document.cookie=’…’ 来修改,太简陋

localStorage和sessionStorage

  • html5专门为存储而设计,最大可存5M
  • API简单好用
  • 不会随着http请求发送出去
  • localStorage数据会永久存储,除非代码或手动删除
  • sessionStorage数据只存在当前会话,浏览器关闭则清空

描述cookie localStorage sessionStorage的区别

  • cookie容量为4KB,localStorage和sessionStorage容量为5M
  • localStorage数据会永久存储,除非代码或手动删除
  • sessionStorage数据只存在当前会话,浏览器关闭则清空
  • cookie会请求服务端,localStorage、sessionStorage不会请求服务端

window.onload 和 DOMContentLoaded区别

  • window.onload 在页面全部资源加载完成后才能执行,包括图片视频
  • DOMContentLoaded 在DOM加载完成即可,图片可能尚未加载

页面渲染过程

  • 根据HTML代码生成DOM Tree
  • 根据CSS代码生成CSSOM
  • 将DOM Tree 和CSSOM结合形成Rander Tree
  • 根据Rander Tree 渲染页面
  • 遇到script则暂停渲染,优先加载并执行js代码,完成后再继续
  • 直至把Rander Tree 渲染完成

编写防抖函数

防止频繁向服务器发送请求

 // 防抖
  debounce(fn, delay = 500) {
    // timer是闭包中的
    let timer = null
    return function () {
      if (timer) {
        clearTimeout(timer)
      }
      timer = setTimeout(() => {
        fn.apply(this, arguments)
        timer = null
      }, delay)
    }
  }

编写节流函数

每隔一段时间触发一次

 // 节流
  throttle(fn, delay = 100) {
    // timer是闭包中的
    let timer = null
    return function () {
      if (timer) {
        return
      }
      timer = setTimeout(() => {
        fn.apply(this, arguments)
        timer = null
      }, delay)
    }
  }

let、const、var的区别

  • 使用var声明的变量,其作用域为该语句所在的函数内,并且存在变量提升
  • 使用let声明的变量,其作用域为该语句所在的代码块内,不存在变量提升
  • 使用const声明的是常量,不能修改这个常量,且需要赋初始值

typeof能判断哪些类型

  • undefined 、string、number、boolean、symbol
  • object(注意typeof null===‘object’)
  • function

列举强制类型转换和隐式类型转换

  • 强制:parseInt、parseFloat、toString等
  • 隐式:if、逻辑运算、==、+拼接字符串

手写深度比较,模拟lodash.isEqual

//判断是否为对象类型
function isObject(obj){
	return typeof obj==='object'&&obj!==null
}
//深度比较两个值
function isEqual(obj1,obj2){
	//判断是否为对象
	if(!isObject(obj1)||!isObject(obj2)){
		//说明是值类型
		return obj1===obj2 //直接比较
	}
	
	//说明传入的两个元素为同一个元素直接返回true
	if(obj1===obj2){
		return true
	}
	
	//判断两个对象属性个数是否相等
	//如果不相等则返回false
	const obj1Keys=Object.keys(obj1)
	const obj2Keys=Object.keys(obj2)
	if(obj1Keys.length!==obj2Keys.length){
		return false
	}
	
	//使用递归进行深度比较,以obj1为基准作比较
	for(let key in obj1){
		const res=isEqual(obj1[key],obj2[key])
		//判断res结果,如果为false说明不相等,直接返回false
		if(!res){
			return false
		}
	}
	return true
}

split()和join()的区别

例如:

'1-2-3'.split('-')//[1,2,3]
[1,2,3].join('-')//'1-2-3'
  • split是将字符串按指定字符分割成数组
  • join是将数组按指定字符并结成字符串

数组的pop push shift unshift分别做什么

pop

弹出数组最后一个元素,返回结果为弹出的元素

const arr=[10,20,30,40]
const arrRes=arr.pop()
console.log(arrRes)//40

push

添加元素到数组最后一个,返回结果为数组长度

const arr=[10,20,30,40]
const arrRes=arr.push(50)
console.log(arrRes)//返回为数组长度

unshift

添加元素到数组第一个,返回结果为数组长度

const arr=[10,20,30,40]
const arrRes=arr.unshift(50)
console.log(arrRes)//返回为数组长度

shift

移除数组第一个元素,返回结果为数组长度

const arr=[10,20,30,40]
const arrRes=arr.unshift(50)
console.log(arrRes)//返回为数组长度

这些函数都会改变原数组

补充:
纯函数

  • 不改变原数组(没有副作用)
  • 返回一个数组

常见纯函数有

  • concat()
  • map()
  • filter()
  • slice()

非纯函数有

  • push()
  • pop()
  • shift()
  • unfhift()
  • foreach()
  • some()
  • every()
  • reduce()

数组slice()和splice区别

slice()

  • 截取数组元素
  • 参数1:开始截取的索引位置
  • 参数2(可选):结束截取的索引位置
  • 不写参数2将截取参数1后所有元素
  • 返回一个新的数组
const arr=[10,20,30,40]
const arrRes=arr.slice(1,3)
console.log(arrRes)//[20,30,40]

splice()

  • 截取数组元素
  • 参数1:开始剪贴的索引位置
  • 参数2:剪贴的个数
  • 参数n(可选):在剪贴的位置插入的元素
  • 会改变原数组
const arr=[10,20,30,40]
const arrRes=arr.splice(1,2,'a','b')
console.log(arrRes)//[10,'a','b',40]

get和post的区别

  • get一般用于查询操作,post一般用于用户提交操作
  • get参数拼接在url上,post参数放在请求体内(数据体积更大)
  • post易于防止CSRF

函数call和apply的区别

fn.call(this,p1,p2,p3)
fn.apply(this,arguments)

传参形式不同,call以依次传入方式传参,而apply以数组形式传参

闭包是什么?有何特性?有何影响?

闭包

  • 闭包是指有权访问另一个函数作用域中的变量的函数

特性

  • 外界无法访问闭包内部的数据
  • 一般的函数,调用完毕之后,系统自动注销函数,而对于闭包来说,在外部函数被调用之后,闭包结构依然保存在

影响

  • 使用闭包会占有内存资源,过多的使用闭包会导致内存泄露等。

如何阻止事件冒泡和默认行为

  • event.stopPropagation() 阻止事件冒泡
  • event.preventDefault() 阻止默认行为

函数声明和函数表达式的区别

  • 函数声明function fn(){…}
  • 函数表达式 const fn=function(){…}
  • 函数声明在代码执行前会预加载,而函数表达式不会

new Object() 和 Object.create()的区别

  • {} 等同于 new Object(),原型Object.prototype
  • Object.create(null)没有原型
  • Object.create({…})可以指定原型

判断字符串以字母开头,后面是字母数字下划线,长度6-30

  • const reg=/^ [a-zA-z]\w{5,29}/

手写字符串trim(),保证浏览器兼容性

String.prototype.trim=function(){
	return this.replace(/^\s+/,'').replace(/\s+$/,'')
}

什么是JSON

  • JSON是一种数据格式,本质是一段字符串
  • JSON格式和js对象结构一致,对js语言更友好
  • window.JSON是全局对象:JSON.stringify() JSON.parse()

获取当前页面url参数

function getUrl(url) {
        var urlObj={}
        var arr=url.split("?")[1].split('&')
       for(var i=0;i<arr.length;i++){
           var newArr=arr[i].split('=')
           urlObj[newArr[0]]=newArr[1]
        }
        return urlObj
    }

手写flatern 考虑多层级

let arr=[1,2,3,[4,5,[6,7]]]
//方式一:递归
function flat(arr){
	//验证arr是否存在深层嵌套
	const isDeep=arr.some(item=>item instanceof Array)
	if(!isDeep){//说明不存在深层嵌套
		return arr
	}
	const res=[].concat(...arr)
	return flat(res)//使用递归
}
//方式二:字符串tostring
function flat(arr){
	return arr.toString().split(',').map(item=>{
		return Number(item)
	})
}
//方式三:json.stringify(更麻烦)
//JSON.stringify(arr):"[1,2,3,[4,5,[6,7]]]"
//replace(/\[|\]/ig,"") '"1","2","3","4","5","6""7"'
//最后使用map将字符串类型转换成数字类型
function flat(arr){
	return JSON.stringify(arr).replace(/\[|\]/g,"").split(',').map(item=>{
		return Number(item)
	})
}

数组去重

方式一 (传统方式)

function unique(arr){
	const res=[]
	arr.foreach(item=>{
		if(res.indexOf(item)<0){//说明不存在
			res.push(item)
		}
	})
	return res
}

方式二 (set)

function unique(arr){
	const set=new Set(arr)
	return [...set]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值