html
题目:HTML中默认的block、inline 、inline-block元素
block:<div>、<p>、<ul>、<h1>、<hr>
list-item:<li>
table:<table>
inline:<i>、<b>、<img/>、<a>、<span>
inline-block:<button>、<input>
css
题目:清除浮动
overflow:hidden
在父元素中添加一个属性:overflow:hidden 一般情况下也不会使用这种方式,因为overflow:hidden有一个特点,离开了这个元素所在的区域以后会被隐藏
clear:both
在浮动的盒子之下再放一个标签,在这个标签中使用clear:both,来清除浮动对页面的影响. 缺点:会有冗余的页面元素,影响代码维护
<div class="fahter">
<div class="big">big</div>
<div class="small">small</div>
<div class="clear">额外标签法</div>
</div>
<style>
.clear{
clear:both;
}
</style>
clearfix:after
使用父元素的after伪元素并添加clear:both (重点推荐)
<body>
<div class="father clearfix">
<div class="big">big</div>
<div class="small">small</div>
<!--<div class="clear">额外标签法</div>-->
</div>
<div class="footer"></div>
</body>
<style>
.clearfix:after{/*伪元素是行内元素 正常浏览器清除浮动方法*/
content: "";
display: block;
height: 0;
clear:both;
visibility: hidden;
}
.clearfix{
*zoom: 1;/*ie6清除浮动的方式 *号只有IE6-IE7执行,其他浏览器不执行*/
}
</style>
1题目:实现css四合院
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html * {
margin: 0;
padding: 0;
}
html{
height: 100%;
}
body{
height: 100%;
display: flex;
flex-direction: column;
}
#header{
height: 50px;
background-color: red;
}
#footer{
height: 50px;
background-color: blue;
}
#container{
display: flex;
flex: 1;
}
#container-left{
width: 100px;
background-color: green;
}
#container-right{
width: 200px;
background-color: yellow;
}
#container-center{
display: flex;
flex: 1;
}
</style>
</head>
<body>
<header id="header">header</header>
<div id="container">
<div id="container-left">left</div>
<div id="container-center">center</div>
<div id="container-right">right</div>
</div>
<footer id="footer">footer</footer>
</body>
</html>
注意:主要是需要将html和body设置height:100%,其次需要运用flex: 1;的方式去直接盛满整个剩余的屏幕
1题目:css实现主题的切换
注意:主要需要用到css变量的问题,var
<head>
<style>
html{
--color:green
}
#bg-color{
background: var(--color);
}
#title-color{
color: var(--color);
}
</style>
</head>
<body>
<div id="bg-color">11</div>
<div id="title-color">title</div>
</body>
题目:css什么属性能实现视频弹幕人物遮挡过滤
答:mask属性
1题目:CSS选择器优先级
结果:100
200
300
1题目:CSS实现抽屉效果
- 网页加载是,抽屉先隐藏
- 1s之后显示抽屉,3s之后隐藏抽屉
- 显示和隐藏要有transtion效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS抽屉</title>
<style>
#drawer-container{
position: fixed;
right: 0;
top: 0;
width: 0;
height: 100%;
background: green;
transition: width 0.3s;
}
</style>
</head>
<body>
<div id="drawer-container">抽屉</div>
<script>
let drawer = document.getElementById('drawer-container')
setTimeout(()=>{
drawer.style.width = "300px"
},1000)
setTimeout(()=>{
drawer.style.width = "0px"
},3000)
</script>
</body>
</html>
注意:直接用document+setTimeout改变宽度就可以了,用transition设置平滑变化的内容和时间。
1题目:CSS水平垂直居中、尺寸不确定的<div>、实现水平垂直居中。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.container {
height: 500px;
width: 500px;
background: green;
}
.box {
background: pink;
}
/* 方法1:定位 + transform */
#container {
position: relative;
}
#box {
position: absolute;
left: 50%;
top: 50%;
transform: (50%, 50%);
}
/* 方法2:定位 + margin:auto */
/* 注意:这种方法只能用于div宽高固定,不然box会撑满container */
#container {
position: relative;
}
#box {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
height: 200px;
width: 200px;
}
/* 方法3:使用flex布局 */
#container{
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<div id="container" class="container">
<div id="box" class="box">box</div>
</div>
<script>
</script>
</body>
</html>
HTTP
题目: screen
console.log(screen.width);
//屏幕宽度
console.log(screen.height);
//屏幕高度
题目:Http常见的状态码有哪些?
状态码分类:
1**服务器请求
2**请求成功,如:200
3**重定向,如:302
4**客户端错误,如:404
5**服务端错误,如:500
1题目:如何实现跨域?
答:<script><img><link>这三个标签可以无视同源策略!
1题目:URL有哪些组成部分,分别对应JS种location那些属性
location.href:网页的整个网址
location.protocol: 查看当前是什么协议,是http还是https
location.host:取域名
location.search:取出当前网页的传递参数
location.pathname:浏览器的路径
1题目:手写函数getParamValue(key)
注意:需要new一个URLSearchParams的构造函数
1题目:描述一下http缓存
答:http的缓存策略是(强制缓存 + 协商缓存)刷新操作对缓存有影响
1、什么是缓存?
答:将资源存储,可以把一些没有必要获取的东西不从新获取。
2、为什么需要缓存?
答:需要让页面加载的更快一些。 哪些资源可以被缓存?-静态资源(js css img)
3、强制缓存Cache-Control
答:Cache-Control是Response里的参数,
4、强制缓存cache-control的值
答:*max-age设置秒级别的时间,缓存的最大过期时间
*no-cache我们不用本地缓存,交给服务端处理
5、HTTP缓存-协商缓存
- 服务器端缓存策略:服务器端来判断一个资源是不是可以用缓存 服务端判断客户端资源,是否和服务端资源一样:访问服务器端时如果本地资源和服务器资源一样就没有必要再重新返回一份资源
- 一致就返回304,否则返回200和最新的资源
1题目:描述一下加密方式
答:加密方式有两种,(对称加密 + 非对称加密)
http是明文传输
1、对称加密
答:加密和解密都用同一个key,不安全,劫持了加密方式。
2、非对称加密
答:用一对key,用A来加密,用B来解密
3、https加密方式是(非对称加密 + 对称加密)
答:服务器先用非对称加密publicKey,加密后传递给客户端,客户端获取到publicKey,客户端生成一个随机数作为对称加密的Key传递给服务端,最后之后的传输都用生成的这个对称加密的Key来进行解密
4、为什么要用这种方式(非对称加密 + 对称加密)?
答:因为节约成本。非对称加密成本太高,做一次就好!对称加密成本低,需要加密的文件太过庞大。用这种方式安全,有节约成本。
javascript
1题目:JS值类型VS引用类型 ![](https://img-blog.csdnimg.cn/f3df6fc7e7404cc79802a2358758835d.png)
答案:a=100,obj1={x:1,y:20}
注意:引用类型从新赋值的对象和前一个引用赋值无关
1题目:ES6和TS带来了什么价值?
答:ES6有更加规范的语法,避免怪异问题,增加开发效率。
ES6有更快的执行效率,比如ascy/await比promise执行更快
TS静态类型,开发大型项目会更加稳定
题目:js实现递归深度拷贝引用类型
const obj = {
name: '江停',
age: 31,
arr: [1, 2, 3]
}
let obj1 = cloneObj(obj)
obj1.name = '严峫'
console.log(obj);
console.log(obj1);
function cloneObj(obj) {
if (typeof obj !== 'object' || typeof obj === null) {
return obj
}
let res
if (obj instanceof Array) {
res = []
} else {
res = {}
}
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
res[i] = cloneObj(obj[i])
}
}
return res
}
1题目:Map和Set和Object Array 有什么区别?
- Map是有序结构,key是任意类型。object无序结构,key两种类型
- Set可以自动去重
- Map和Set比Object Array执行速度要更快
1题目:以下代码输出什么?
输出结果是:10个10
注意:因为setTimeout是宏任务,它需要等待同步任务执行完成才能执行,所以会让循环完成后一起执行,而且每一次循环都会创建出一个setTimeout所以会执行10次。
1题目:如何让上题输出0-9
注意:将var改成let就可以了,让它形成块级作用域,也可以将setTimeout包裹成一个Promise在循环时进行调用,也可以实现0-9的结果
for (var i = 0; i < 10; i++) {
timeoutPromise(i);
}
function timeoutPromise(i) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(i);
resolve(true);
}, 1000);
});
}
1题目:现有三种菜单,button属性,select属性,model属性
class Mune{
constructor(title,icon){
this.title = title
this.icon = icon
}
isDisabled(){
return false
}
exec(){
}
}
class Button extends Mune{
constructor(title,icon){
super(title,icon)
}
exec(){
console.log('hello');
}
}
class Select extends Mune{
constructor(title,icon){
super(title,icon)
}
exec(){
return ['item1','item2','item3']
}
}
class Modal extends Mune{
constructor(title,icon){
super(title,icon)
}
exec(){
let div = document.createElement('div')
div.innerHTML('modal')
return div
}
}
1题目:普通函数和箭头函数的this指向
const obj = {
f1(){
const fn = () => {
console.log('this1',this);
}
fn()
//指向obj,因为箭头函数没有this指向,箭头函数this指向继承它的父集
fn.call(window)
//指向obj,因为箭头函数this指向固定,不能改
},
f2: ()=>{
function fn(){
console.log('this2',this);
}
fn()
//指向window,因为函数this指向window
fn.call(this)
//指向window,因为call(this),
},
f3(){
function fn(){
console.log('this3',this);
}
fn()//指向window,因为函数this指向window
}
}
obj.f1()
obj.f2()
obj.f3()
题目:calss相关的this指向
class Foo{
f1(){
console.log('this1',this);
}
f2 = () => {
console.log('this2',this);
}
f3 = () => {
console.log('this3',this);
}
static f4() {
console.log('this4',this);
}
}
const f = new Foo()
f.f1()
//指向实例
f.f2()
//指向实例
f.f3.call(this)
//指向实例,箭头函数,不能通过call来改变this的指向
Foo.f4()
//指向class,
题目:JS内存回收使用什么算法?
答:标记清除。JavaScript中的垃圾回收机制是一种内存管理技术,其主要目的是在不再使用的内存资源被回收前,使其成为可用的内存。标记清除是JavaScript中最常见的垃圾回收技术之一,它通过标记无用的内存资源并清除它们来释放内存。
题目:WeakMap和WeakSet有什么作用?
答:临时记录数据和关系,防止内存泄露。弱引用不会影响相互之间的内存清理。
Set:对象允许存储任何类型的唯一值,无论是原始值或者是对象引用
WeakSet:成员都是对象,成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏
Map:键值对的集合,类似集合;可以遍历,有很多方法,可以跟各种数据格式转换
WeakMap:只接受对象为键名(null)除外,不接受其他类型的值作为键名;键名是弱引用,键值可以任意;键名所指向的对象可以被垃圾回收机制回收,此时键名无效,不能遍历。
题目:“栈溢出”是什么?JS执行和栈溢出有什么关系?
答:递归的次数太多,导致stack overflow(栈溢出)
尾递归(就是把递归写在函数的末尾)--避免递归的栈溢出(爆栈)
题目:JS自由变量,最后结果输出什么?
结果: 12
题目:JS实现一个简单的节流
<body>
<div id="div" draggable="true" style="height: 100px;width: 100px;background: green;"></div>
<script>
const div = document.getElementById('div')
let timer = null
div.addEventListener('drag', function (e) {
if (timer) {
return
}
timer = setTimeout(() => {
console.log(e.offsetX, e.offsetY);
timer = null
}, 500)
})
</script>
</body>
题目:节流的封装
<div id="div1" draggable="true" style="height: 100px;width: 100px;background: green;"></div>
<script>
function throttle(fn, delay = 100) {
let timer = 0
return function(){
if(timer) return
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = 0
}, delay)
}
}
const div1 = document.getElementById('div1')
div1.addEventListener('drag',throttle((e) => {
console.log('鼠标位置', e.offsetX, e.offsetY);
}))
</script>
题目:实现一个简单的防抖
<input id="input1">
<script>
const input1 = document.getElementById('input1')
let timer = null
input1.addEventListener('keyup', function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
console.log(input1.value);
timer = null
}, 500)
})
</script>
题目:实现防抖封装
<input id="input" />
<script>
const input = document.getElementById('input')
function debounce(fn) {
let timer = null
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, 500)
}
}
input.addEventListener('keyup', debounce(() => {
console.log(input.value);
}))
</script>
Vue
1题目:vue3自定义v-model,手写代码
- 父组件:
<template>
<div>
<demo v-model="context"></demo>
<div>{{context}}</div>
</div>
</template>
<script>
import demo from './demo.vue'
export default {
data() {
return {
context: ''
}
},
components: {
demo
}
}
</script>
- 子组件:第一种写法
<template>
<p>
v-model rest
<input :value="modelValue" @input="inputHandler">
</p>
</template>
<script>
export default{
name:'demo',
props:{
modelValue:{
type:String,
default:''
}
},
setup(props, context){
function inputHandler(e){
context.emit('update:modelValue',e.target.value)
}
return {
inputHandler
}
}
}
</script>
- 子组件:第二种写法
<template>
<p>
<input :value="modelValue" @input="$emit('update:modelValue',$event.target.value)">
</p>
</template>
<script>
export default{
name:'demo',
props:{
modelValue:{
type:String,
default:''
}
},
emits:['update:modelValue']
}
</script>
1题目:vue封装一个心跳组件,组件每隔一秒打印一个"hello"
import {onBeforeUnmount,onMounted,ref} from 'vue'
export default {
setup() {
const timerRef = ref(0)
onMounted(() => {
function fn() {
console.log('hello');
timerRef.value = setTimeout(fn, 1000)
}
fn()
})
onBeforeUnmount(() => {
console.log('destroy');
clearTimeout(timerRef.value)
})
}
}
注意:心跳最好用setTimeout,需要用 setTimeout回调函数的方式去写,不然setTimeout就只会执行一次。最后需要在组件销毁的时候去执行clearTimeout清除定时器,不然会造成内存泄露的问题
1题目:封装useLocation
- useLocation
import { onMounted, reactive, toRefs } from "vue"
function getLocation(fail){
return new Promise((resolve,reject) => {
setTimeout(()=>{
if(fail === 404){
reject({ errno: 1, msg: fail })
}else if(fail === 200){
resolve({ errno: 0, data: { x: 100, y: 200 } })
}
},1000)
})
}
function useLocation() {
const info = reactive({
loading:true,
data:{},
err:''
})
onMounted(() => {
getLocation(200).then(res => {
console.log(res);
info.data = res.data
info.loading = false
}).catch(err => {
info.data = err.msg
info.loading = false
console.log(err);
})
})
return toRefs(info)
}
export default useLocation
- LocationTest.vue
<template>
<div>
<p>location test</p>
<div v-if="loading">loading</div>
<div v-else>
<div v-if="err">{{err}}</div>
<div v-else>{{JSON.stringify(data)}}</div>
</div>
</div>
</template>
<script>
import useLocation from '../hook/useLocation.js'
export default {
setup() {
const {loading, data, err} = useLocation()
return {
loading,
data,
err
}
}
}
</script>
题目:Vue的<slot>有哪些应用场景?
答:比如布局组件、弹框显示内容,所有需要手动传入子组件的地方。
1题目:Vue3封装useTitle
- 在Mounted时获取网页标题
- 基础版,只获取不修改
- 升级版,可获取可修改
引用组件:
<template>
<div>
{{ titleRef }}
<input v-model="titleRef"/>
</div>
</template>
<script>
import useTitle from '../hook/useTitle'
export default {
setup() {
let titleRef = useTitle()
return {
titleRef
}
}
}
</script>
封装的useTitle组件:
import { onMounted, ref, watch } from "vue"
function useTitle(){
const titleRef = ref('')
onMounted(() => {
titleRef.value = document.title
})
watch(titleRef, (oldValue, newValue) => {
if(oldValue !== newValue){
document.title= newValue
}
})
return titleRef
}
export default useTitle
题目:Vue3响应式原理
const fns = new Set()
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
const result = Reflect.get(target, key, receiver)
if (activeFn) fns.add(activeFn)
return result
},
set(target, key, val, receiver) {
const result = Reflect.set(target, key, val, receiver)
fns.forEach(fn => fn())
return result
},
deleteProperty(target, key) {
const result = Reflect.deleteProperty(target, key)
fns.forEach(fn => fn())
return result
}
})
}
let activeFn
function effect(fn) {
activeFn = fn
fn() // 执行一次,触发 proxy get
}
const user = reactive({ name: '江停'})
effect(() => {
console.log('name', user)
})
user.name = '严峫'
setTimeout(() => {
user.name = '吴雩'
delete user.name
}, 1000)
题目:Vue3的ref原理
注意:ref也是通过proxy来实现的,严格来说ref是通过react来实现的 ,ref是把一个之类型封装成一个对象,然后再把这个对象传递给reactive
题目:Vue3比Vue2的diff算法带来的变化
1、Proxy响应式
2、patchFlag
- 编译模板时动态节点做标记
- 标记分为不同类型,如:text,props
- diff算法时,可以区分静态节点,以及不同类型的动态节点
3、hoistStatic
- 静态节点的定义,提升到父作用域缓存起来
- 多个相邻的静态节点,会被缓存起来
- 典型的拿空间换时间的策略
4、cacheHandler
- 缓存事件
- 有缓存用缓存,没有缓存新建一个
5、SSR优化
- 静态节点直接输出,绕过vdom
- 动态节点,还是需要动态渲染
6、tree-shaking
- 编译时,根据不同的情况,引入不同的API
React
1题目:手写一个JSX的循环和判断?
import { useEffect, useState } from "react"
function UseInput() {
let [ flag, setFlag ] = useState(true)
let [ mun, setMun ] = useState([
{ name: '简隋英' },
{ name: '李玉' }
])
return <>
{
flag ? <ul>
{
mun.map(item => {
return <li key="index">{ item.name }</li>
})
}
</ul>:<div>landing...</div>
}
</>
}
export default UseInput
题目:React组件通讯方式?
第一种:属性props
父组件:
import { useEffect, useState } from "react"
import ChildrenCom from './ChildrenCom'
function FatherCom() {
const [name, setName] = useState("简隋英")
const [age, setAge] = useState(28)
const onPeople = (name, age) =>{
setName(name)
setAge(age)
}
return <>
<div>
<ChildrenCom name={name} onPeople={onPeople}></ChildrenCom>
<div>年龄:{age}</div>
</div>
</>
}
export default FatherCom
子组件:
function ChildrenProps(props) {
const changePeople = () => {
const { onPeople } = props
onPeople('李玉', 21)
}
return <div>
{props.name}
<button onClick={changePeople}>改变</button>
</div>
}
export default ChildrenProps
第二种:context
context.js
import React from "react"
const nameContext = React.createContext({
name: null,
age: null
})
// 可以导出多个
export {
nameContext
}
父组件:
import ChildrenProps from './ChildrenProps'
import { nameContext } from './Context'
function UseInput() {
let name = '江停1'
let age = 20
return <>
<nameContext.Provider value={{name,age}}>
<ChildrenProps></ChildrenProps>
</nameContext.Provider>
</>
}
export default UseInput
子组件:
import { useContext, useState } from "react"
import { nameContext } from './Context'
function ChildrenProps(props,context){
const dataValue = useContext(nameContext)
console.log(dataValue);
return <div>
{dataValue.name}
{dataValue.age}
</div>
}
export default ChildrenProps
第三种:Redux
redux的应用
https://blog.csdn.net/Seven_Ting/article/details/130377603?spm=1001.2014.3001.5502
题目:点击三次按钮后定时器会打印什么值?
答案:一直是0
注意:函数组件内容更新会全部更新一遍,而useEffect里没有配置value所以它每一次执行都会命中useState(0)所以无论加了多少次都会是0。如果想要打印出操作后的值就需要在useEffect里去配置打印的值。
题目:React手写一个受控组件
import { useState } from "react"
function UseInput() {
const [num, updateNum] = useState(0);
const onChange = (e) => {
updateNum(e.target.value)
}
return (
<div>
{num}
<input value={num} onChange={onChange} />
</div>
)
}
export default UseInput
题目:setState 可能会被合并,以下代码log出什么和最后value的值?
结果:console.log:100,100
value:103
注意:因为普通setState会被合并,setState传入函数不会被合并,log打印出100,是因为setState是异步的,所以执行setState后直接打印不会打印出执行后的结果。
题目:setState 可能会被合并,以下代码log出什么和最后value的值?
结果:console.log:100,100
value: 101
注意:value打印出101是因为普通 setState执行会合并,所以4个合并成一个。
为什么log打印出两个100而不是101呢!是因为,react18修复了这个工能,react17:setTimeout同步更新,react18:setTimeout异步更新
题目:React封装一个心跳组件,组件每隔一秒打印一个"hello"
import { useEffect, useState } from "react"
function UseInput() {
useEffect(()=>{
let timer = 0
function fn(){
console.log('hello');
timer = setTimeout(fn,1000)
}
timer = setTimeout(fn,1000)
return () => {
console.log('清除');
clearTimeout(timer)
}
})
return <>
ddd
</>
}
export default UseInput
题目:React事件和Dom事件的区别?
- React统一挂载到root节点,(React17之前是document)
- React是合成事件,来模拟dom事件
- 模拟事件可以跨平台,不仅仅用于Dom
题目:更具JSX写出render函数
{
tag:'div',
props:{
className:'container'
},
children:[
{
tag:'p',
props:{
data-name:'p1'
},
event:{
onClick:onClick
},
text:'hello',
children:[
{
tag:'p',
text:name
}
]
},
{
tag:'img',
props:{
src:imgSrc
}
},
{
tag:MyComonpent,
props:{
title:title
}
}
]
}
题目:Vue-router或是React-router如何定义和获取路由的动态参数?
题目:Vue和React函数组件的区别
答:Vue组件没有实例和生命周期,比较简单
React函数组件+Hook,可以实现完备的功能
Vue的Composition API不能用于函数组件
vue的函数组件:
export default {
functional: true,
render(createElement, { data, children }) {
return createElement( 'button', data, children );
}
};
数据结构与算法
1题目:用JS实现队列,实现入队,出队长度
注意:push的时间复杂度是O(1),for循环遍历数组时间复杂度O(n),unshift的时间复杂度数O(n)
利用链表的方式显示队列
class MyQueue {
constructor(){
this.head = null
this.tail = null
this.length = 0
}
add(value){
let node = {value}
if(this.length === 0){
this.head = node
this.tail = node
}else{
this.tail.next = node
this.tail = node
}
this.length++
}
delete(){
if(this.length <= 0) {return null}
let value = null
if(this.length === 1){
value = this.head.value
this.head = null
}else{
value = this.head.value
this.head = this.head.next
}
return value
this.length--
}
}
// 功能测试
const queue = new MyQueue()
queue.add(100)
console.log('length1', queue.length) // 1
queue.add(200)
console.log('length2', queue.length) // 2
console.log('delete1', queue.delete()) // 100
queue.add(300)
console.log('length3', queue.length) // 2
console.log('delete2', queue.delete()) // 200
console.log('length4', queue.length) // 1
console.log('delete3', queue.delete()) // 300
console.log('length5', queue.length) // 0
注意:链表实现队列就像是包菜一样,入队就像是从菜心开始长,出队就是从外皮开始剥。
add:
delete:
题目:用Js链表实现入栈和出栈
class MyNode{
//这个类似于一个替换值t
constructor(val){
this.value = val
this.next = null
}
}
class MyStack{
constructor(){
this.stack = null
}
add(val){
const node = new MyNode(val)
//首先将入栈的值给t.value里:{value:100,next:null}
node.next = this.stack
//其次将上一个入栈的值赋值给t.next
//形成新入的值在最外层,{value:200,next:{value:100,next:null}}
this.stack = node
//最后将t的整体包裹了新入栈的数据的对象赋值给当前的内容
console.log('this.next',this.stack);
}
delete(){
const t = this.stack
//当前栈已空,直接返回,t是 MyStack.stack
if(t === null){
return '当前栈已空'
}else{
//将内层的next的赋值给原本的值,实现出栈
this.stack = t.next
return t.value
}
}
}
const stack = new MyStack()
stack.add(100)
stack.add(200)
console.log('delete',stack.delete());
console.log('delete',stack.delete());
console.log('delete',stack.delete());
题目:手写一个快速排序
注意:快速排序的规则就是先找中间的数,比中间大的放在左边,小的放在右边,再去对两边的数进行同样的操作。
注意:循环一次是O(n),双层循环是O(n^2),二分查找O(logn),快速排序是O(n*logn)
function quickSort(arr) {
if(arr.length <= 0){
return arr
}
let midIndex = Math.floor(arr.length/2)
let midValue = arr[midIndex]
let left = []
let right = []
for(let i = 0; i < arr.length; i++){
if(i !== midIndex){
if(midValue > arr[i]){
left.push(arr[i])
}else{
right.push(arr[i])
}
}
}
return [...quickSort(left),midValue,...quickSort(right)]
}
let arr = [12, 45, 78, 25, 12, 3, 45, 74, 1, 14, 85]
console.log(quickSort(arr));
题目:合并两个递增数组
提示:利用一次循环,利用双指针去对比。
const arr1 = [1,3,5,7,9]
const arr2 = [2,4,6,8]
let arr = []
let i = 0
while (arr1[i] ||arr2[i]) {
if(arr1[i] > arr2[i]){
if(arr2[i]){
arr.push(arr2[i])
}
if(arr1[i]){
arr.push(arr1[i])
}
}else{
if(arr1[i]){
arr.push(arr1[i])
}
if(arr2[i]){
arr.push(arr2[i])
}
}
i++
}
console.log(arr);
题目:用栈来翻转字符串,只能用push和pop两个API
function onStack(val){
let arr1 = []
//入栈
for(let i of val){
arr1.push(i)
}
let arr2 = ''
let popValue = null
//出栈
while(arr1.length){
popValue = arr1.pop()
arr2 += popValue
}
return arr2
}
console.log(onStack('12345'));
题目:两个数组,求交集和并集
const arr1 = [1, 3, 4, 6, 7]
const arr2 = [2, 5, 3, 6, 1]
// 交集
function getIntersection(arr1, arr2) {
let arr = new Set()
let arr1Set = new Set(arr1)
for(let i of arr2){
if(arr1Set.has(i)){
arr.add(i)
}
}
return Array.from(arr)
}
// 并集
function getUnion(arr1, arr2) {
let arrSet = new Set(arr1)
for(let i of arr2){
arrSet.add(i)
}
return Array.from(arrSet)
}
// 测试
console.log('交集', getIntersection(arr1, arr2))
console.log('并集', getUnion(arr1, arr2))
注意:主要用set去重的方式去做,set的计算要比Array的快!
题目: 手写二分查找,说明时间复杂度。
注意:二分查找首先是查找的内容是已排序
时间复杂度是O(logn)
需要找的数先去找数组中中间的值,去作对比,大于就往右边找,小于就往左边找。下一次继续这个操作。
方法:递归或循环
求中间元素的值mid,即:mid = left + (right - left) / 2 得到中间下标
function find(arr, x) {
let left = 0
let right = arr.length - 1
let mid = 0
while (left <= right) {
mid = Math.floor(left + (right - left) / 2)
if (arr[mid] === x) {
return {
type: true,
position: mid
}
} else if (arr[mid] < x) {
left = mid + 1
} else {
right = mid - 1
}
}
return {
type: false,
position: null
}
}
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let x = 3
console.log(find(arr, x));
题目:树的深度优先、广度优先遍历结果。
一、树的深度优先结果:ABCGDEFHL
注意:树的深度优先是先遍历根节点,先左后右,深度遍历
注意:
- 访问根节点;
- 对根节点的
children
持续进行深度优先遍历(递归);
function dfs(root) {
console.log(root.value)
if(root.children){
root.children.forEach(dfs)
}
}
dfs(tree) // 这个tree就是前面定义的那个树
二、广度优先遍历结果:ABECGDFHL
注意:广度优先遍历是,先遍历根节点,再同层从左到右遍历
注意:
- 创建要给队列,把根节点入队;
- 把队头出队并访问;
- 把队头的
children
依次入队; - 重复执行2、3步,直到队列为空。
function deep(tree){
let queue = []
queue.push(tree)
while(queue.length > 0){
const node = queue.shift()
console.log(node.value);
if(node.children){
node.children.forEach(val => {
queue.push(val)
})
}
}
}
题目:用JS对象来表达这课树
const tree = {
value: 'A',
children: [
{
value: 'B',
children: [
{
value: 'C'
},
{
value: 'G'
},
{
value: 'D'
}
]
},
{
value: 'E',
children: [
{
value: 'F',
children: [
{
value: 'H'
},
{
value: 'L'
}
]
}
]
}
]
}
题目:二叉树的前、中、后序遍历结果
1、前序遍历:中、左、右
2、中序遍历:左、中、右
3、后序遍历:左、中、右
题目:用JS对象表示这个二叉树
let tree = {
value:'A',
left:{
value:'B',
left:{
value:'C',
left:null,
right:{
value:'G',
left:null,
right:null
}
},
right:{
value:'D',
left:null,
right:null
}
},
right:{
value:'E',
left:{
value:'F',
left:{
value:'H',
left:null,
right:null
},
right:{
value:'L',
left:null,
right:null
}
},
right:null
}
}