【面试】前端基础(三)

JavaScript


数据类型

基本数据类型

Number String Boolean Undefined Symbol

引用数据类型

Object Array Function Map Set Null

如何判断数据类型

typeof 能判断所有的值类型,能识别引用类型

let a =>undefined
let b = 1 =>number
let c = 'c' =>string
let d = true => boolean
let e = Symbol('e') =>symbol

typeof console.log =>function
typeof null =>object
typeof [] =>object

instanceof能判断引用类型

[] instanceof Array
{} instanceof Object
如何判断两个数据是否相等
使用==/===判断

==对于基础类型,是判断值是否相等,并且有一定的隐式转换。如

4 == '4' //true
0 == '' //true
0 == false //true
null == undefined //true

除了判断 是否等于null其他判断都应该使用 ===

==对于引用类型,是判断址是否相等

变量提升

在声明变量前就使用该变量。

var存在变量提升,输出的值是undefined

let、const不允许变量提升,会报错。

实现浅拷贝和深拷贝

浅拷贝:这个一般用来克隆一维数组

let a = [1,2,3]
let b = []
for(let i=0;i<a.length;i++){
	b.push(a[i])
}

深拷贝:使用递归。

首先考虑一下克隆的对象会是什么:基本类型、Array、Object。

基本类型:使用=赋值

Array:使用push

Object:使用obj[k]=…

function deepClone(o){
	if(Array.isArray(o)){
		var result = []
		for(let i=0;i<o.length;i++){
			result.push(deepClone(o[i]))
		}
	}else if(typeof o == 'object'){
		var result = {}
		for(let k in o){
			result[k] = deepClone(o[k])
		}
	}else{
		var result = o
	}
	return result
}

稍微检查一下,如果

var t = deepClone(null)

期望的结果是null,而不是{}

修改下

else if(typeof o == 'object' && o != null)

再考虑到原型链,Array不好使用Push的方法了,可以这么改

function deepClone(o){
	if(typeof o!='object'|| o==null){
		return o
	}
	let result
	if(Array.isArray(o)){
		result = []
	}else{
		result = {}
	}
	for(let k in o){
		if(o.hasOwnProperty(k)){
			result[k] = deepClone(o[k])
		}
		
	}
	return result
}
变量计算

小心隐式转换:+号

let a = 100 +'10'=>'10010'

作用域

全局、函数、块级

自由变量

一个变量被使用了,但是在当前作用域内没有找到该变量的定义,便会向外层的作用域寻找,直到找到全局作用域都没有,便会报错。

闭包

当函数执行完毕后,函数作用域销毁,在函数内定义的变量也应该销毁。

如果函数的返回值不是数值而是一个函数,则形成闭包。

函数作为参数,也同理。

function f(){
	let a = 200
	return function(){
		console.log(a)
	}
}
let a = 100
const fn = f()
fn() //200
function fn(){
	console.log(a)
}
function f(fn){
	let a = 200
	fn()
}
let a = 100
f(fn) //100

自由变量会向外层查找变量定义,而且会从函数定义的地方向外查找而不是函数执行的地方查找。

第一个例子中,函数的外层是f,有变量a的定义,所以输出200

第二个例子中,函数的外层是全局作用域,有变量a的定义,输出100

使用闭包封装数据

function f(){
	const data={}
	return {
		set:(k,v)=>{
			data[k] = v
		},
		get:(k)=>{
			return data[k]
		}
	}
}
原型链

再提一下instanceof

class People{}
class Student extend People{}
const xiaomin = new Student()
xiaomin instanceof Student //true
xiaomin instanceof People //true
//people继承于object

继承
每个class都有原型,即

People.prototype

所有class都继承于Object,即

People.prototype.__proto__ === Object.prototype
//没继承之前
Student.prototype.__proto__=== Object.prototype

原型链的终点

Object.prototype.__proto__ =>null

不使用class怎么继承?改变Student的原型指向

function People(){}
function Student(){}
Student.prototype.__proto__=People.prototype
Student.prototype = new Person()
上下文

this的值是在执行的时候决定的。

obj.fn() //this === obj
const fn = obj.fn
fn() //this === window 严格模式下是undefined

箭头函数里的this取其上级作用域的this

fn(){
	setTimeout(function(){
		//this === window
	})
}
fn(){
	setTimeout(()=>{
		//this === fn()
	})
}
事件委托
function handleClick(e){
	if(e.target.tagName.toLowerCase() === 'a'){
		console.log(e.target.innerHTML)
	}
}
for(let i = 0;i<10;i++){
	const dom = document.createElement('a')
	dom.innerHTML = i
	document.body.appendChild(dom)
}
document.body.addEventListener('click',handleClick)

通用事件委托函数

			function bindEvent(elem, type, selector, fn) {
				if (fn == null) {
					fn = selector
					selector = null
				}
				let target
				elem.addEventListener(type, (e) => {
					if (selector) {
						target = e.target
						if (target.matches(selector)) {
							fn.call(target, e)
						}
					} else {
						fn(e)
					}
				})
			}

异步

通过下面三种方法可以实现异步

定时器
Promise

状态

pending

resolve->resolved(fulfilled)

reject->(reject)

单向不可逆。

首先,声明promise时,会进入pending状态。使用该实例时,会根据实例的函数确认promise状态。

const p = new Promise(fn) //pending
p() //根据fn确认promise状态

返回值

resolve()或reject()里的参数会传到then/catch里

默认返回resolved状态的promise,不使用默认值的话,需要在return处返回自己的promise实例

p.then(()=>{return 123})
//相当于
p.then(()=>{
	return new Promise((resolve,reject)=>{
		resolve(123)
	})
})

then和catch

它们本质上也是一个promise实例。如果上一个promise实例是resolved状态,则会执行then(),是rejected状态则执行catch()

举一个回调地狱的例子

			const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
				el.style.transform = `translate3d(${x}px,${y}px,0)`
				el.addEventListener("transitionend", () => {
					end()
				})
			}
			const box = document.getElementById("box")
				box.addEventListener("click", () => {
				move(box, { x: 150 }, () => {
					move(box, { x: 150, y: 150 }, () => {
						move(box, { x: 0, y: 150 }, () => {
							move(box)
						})
					})
				})
			})

用Promise解决回调地狱

			const movePromise = (el, point) => {
				return new Promise((resolve) => {
					move(el, point, () => {
						resolve()
					})
				})
			}
			box.addEventListener("click", () => {
				movePromise(box, { x: 150 })
					.then(() => {
						return movePromise(box, { x: 150, y: 150 })
					})
					.then(() => {
						return movePromise(box, { x: 0, y: 150 })
					})
					.then(() => {
						return movePromise(box)
					})
			})

为什么能解决

回调地狱的本质是函数返回时再次调用该函数,且环环相扣,在回调中添加代码或更改回调的顺序很麻烦。

promise使每一次回调后都返回一个promise实例,修改它不需要考虑上一个回调和下一个回调,它们之间也互不影响。

使用Promise加载图片
let src = "xxx.jpg"
const loadImg = (src)=>{
	new Promise((resolve,reject)=>{
		const img = document.createElement('img')
		img.src = src
		img.onload = ()=>{
			resolve(img)
		}
		img.onerror = ()=>{
			reject('图片加载失败')
		}
	})
}
loadImg(src).then((img)=>{document.body.appendChild(img)}).catch(err=>{ console.log(err)})
Ajax

怎么实现

通过XMLHttpRequest实现

	const url = "xxx"
			const xhr = new XMLHttpRequest()
			xhr.onreadystatechange = () => {
				if (xhr.readyState !== 4) return
				if (
					(xhr.status >= 200 && xhr.status < 300) ||
					xhr.status === 304
				) {
					console.log(xhr.responseText)
				}
			}
			xhr.open("GET", url, true)
			xhr.send(null)

可以使用promise

	const url = "xxx"
		function getAjaxResponse(url){
			const p = new Promise((resolve,reject)=>{
				const xhr = new XMLHttpRequest()
				xhr.onreadystatechange = () => {
					if (xhr.readyState !== 4) reject(err)
					if (
						(xhr.status >= 200 && xhr.status < 300) ||
						xhr.status === 304
						) {
							resolve(xhr.responseText)
					}else{
						reject(err)
					}
			}
			xhr.open("GET", url, true)
			xhr.send(null)
			})
		}
			

状态码

xhr.readystate === 4
xhr.status >=200&& xhr.status<300 || xhr.status === 304

JSON数据转换

JSON->js

JSON.parse(xxx)

js->JSON

JSON.stringify(xxx)
跨域问题

https(协议)😕/www.xxx.com(域名):8800(端口号)

三个有一个不一样就是跨域

由于同源策略,跨域请求会被阻止。

解决:

​ CORS,服务器端在响应头允许跨域(IE10)

​ JSONP,script标签的跨域请求不会被阻止,可以使用它加载跨域文件。

优化

节流

每N秒执行一次

一直按着键盘,也会每N秒触发一次事件

function throttle(fn, delay = 500, lock) {
				return function () {
					if (!lock) return
					lock = false
					setTimeout(() => {
						fn.apply(this, arguments)
						lock = true
					}, delay)
				}
			}
			input.addEventListener(
				"keyup",
				throttle(
					function () {
						console.log(this.value)
					},
					100,
					true
				)
			)
防抖

在N秒内,该事件只执行一次,如果被重复触发,就重新计时。

下例中,如果一直按着键盘,就永远不会执行该事件

	const input = document.getElementById("input")
			function debounce(fn, delay = 500) {
				let timer = null
				return function () {
					if (timer) {
						clearTimeout(timer)
					}
					timer = setTimeout(() => {
						/* console.log(this)
						fn.apply(this, arguments)
						console.log(this) */
						fn()
						timer = null
					}, delay)
				}
			}
			input.addEventListener(
				"keyup",
				debounce(function () {
					/* console.log(this.value) */
					console.log(input.value)
				})
			)

fn.apply(this, arguments)将this指向调用该函数的实例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值