面试必备小demo

前端面试手写大全(一)

1.排序

冒泡排序
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<script>
			var arr = [9, 6, 8, 5, 7, 4, 3, 1, 2]
			// 排序:冒泡排序 那相邻的两个数进行比较,得到最大/小的一个数
			function bubbleSort(arr) {
				for (var i = 0; i < arr.length; i++) { // 比较length轮
					for (var j = i; j < arr.length; j++) { // 确定最小的位就不再比较
						if (arr[i] > arr[j]) {
							var temp = arr[i] // 交换两个数
							arr[i] = arr[j]
							arr[j] = temp
							console.log("交换")
						}	`
					}
					console.log(arr)
				}
				return arr
			}
			bubbleSort(arr)
		</script>
	</body>
</html>
快速排序
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<script>
			var arr = [9, 6, 8, 5, 7, 4, 3, 1, 2]
			// 快速排序:
			// 找到中间位,比中间位小的放左边,比中间大的放右边
			function quickSort(arr){
				if(arr.length<1){
					return arr
				}
				var index = Math.floor(arr.length/2)
				var center = arr.splice(index,1)[0]
				var left = []
				var right = []
				for(var i = 0;i<arr.length;i++){
					if(arr[i]<center){
						left.push(arr[i])
					}else{
						right.push(arr[i])
					}
					
				}
				console.log(left,center,right)
				return quickSort(left).concat(center,quickSort(right))
			}
			var re = quickSort(arr)
			console.log("结果:",re)
		</script>
	</body>
</html>

2.ajax

ajax是什么
  1. 即异步的JavaScript 和XML,是一种创建交互式网页应用的网页开发技术,可以在不重新加载整个网页的情况下,与服务器交换数据,并且更新部分网页
实现过程
  1. 实现 Ajax异步交互需要服务器逻辑进行配合,需要完成以下步骤
  2. 创建 Ajax的核心对象 XMLHttpRequest对象

通过 XMLHttpRequest 对象的 open() 方法与服务端建立连接
构建请求所需的数据内容,并通过XMLHttpRequest 对象的 send() 方法发送给服务器端
通过 XMLHttpRequest 对象提供的 onreadystatechange 事件监听服务器端你的通信状态
接受并处理服务端向客户端响应的数据结果
将处理结果更新到 HTML页面中

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		
		<script>
			// 同步,异步  post与get  restFul  http请求过程   http响应码
			// 01:同步:按顺序从上至下执行阻塞式代码   异步:先执行主线程代码,再去执行非主线程的代码
			// 实现异步:回调函数,事件响应函数,订阅与发布模式,promsie  sync与await
			// ajax默认是异步的,可以
			function ajax(url,method,data,config){
				return new Promise((resovle,reject)=>{
					var xhr = new XMLHttpRequest()
					// 打开
					xhr.open(method,url)
					if(config&&config.headers){
						// 设置请求头
						for(var k in config.headers){
							xhr.setRequestHeader(k,config.headers[k])
						}
					}
					// 等待状态发生变化
					xhr.onreadystatechange = function(){
						if(xhr.readyState===4){
							// 成功
							if(xhr.status===200){
								resovle(JSON.parse(xhr.responseText))
							}else{
								// 失败
								reject(xhr)
							}
						}
					}
					xhr.send(data)
				})
			}
			ajax(url)
			.then(res=>{
				console.log(res)
			})
			.catch(err=>{
				console.log(err)
			})
			
			
			
			// 02:post与get 
			// get:数据大小  post没有限制
			// get2k  post不缓存  
			//post通常用来修改,新增,删除   get通常用来获取
			
			//restFul是一种程序设计规范,通常每一个url都是一个资源,可以通过get方法来获取post新增,put修改,delect删除
			
			
			// http请求过程:在
			// 01 浏览器通过域名找到ip
			// 02 建立tcp链接,发送请求体与请求头
			// 03 服务响应响应体,响应头,
			// 04 浏览器解析html 生成dom树(下载css与js)
			// 05 解析css 生成css树
			// 06 css树与dom树合成为渲染树进行渲染(遇到js先执行js)
			
			
			// 响应码
			// 1开头准备
			// 2开头成功 200成功  201创建成功  307重定向  404找不到  401没有权限  500服务器错误
			// 3开头重定向
			// 4开头客户端错误
			// 5开头 服务器错误
			
		</script>
	</body>
</html>

3.jsonp

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<script>
			
			// 什么是同源  请求的地址与当前页面地址,域名,子域名,端口号一致
			// 怎么跨域  jsonp,代理,cors响应头允许,
			// jsonp的原理, src没有同源策略
			// 统计功能用图片的src没有同源限制
			function jsonp(url){
				return new Promise((resolve,reject)=>{
					// 创建一个src标签
					var script = document.createElement("script")
					// 定义回调函数 
					var fun = "callback"+Date.now()
					// 拼接url
					var src = ""
					if(url.includes("?")){
						src = url+"&callback="+fun
					}else{
						src = url+"?callback="+fun
					}
					// 插入到body
					script.src = src
					document.body.appendChild(script)
					// 当执行回调函数返回数据
					window[fun]=function(data){
						resolve(data)
						document.body.removeChild(script)
					}
					script.onerror = function(err){
						reject(err)
						document.body.removeChild(script)
					}
				})
			}
			jsonp("url")
			.then(res=>{
				console.log(res)
			})
			.catch(err=>console.log(err))
		</script>
	</body>
</html>

4.防抖节流

  1. 防抖节流是优化高频率执行代码的一种手段,如:浏览器的 resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能
  2. 为了优化体验,需要对这类事件进行调用次数的限制,对此我们就可以采用 防抖(debounce) 和 节流(throttle) 的方式来减少调用频率
  3. 一个经典的比喻:想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应,假设电梯有两种运行策略 debounce 和 throttle,
  4. 超时设定为15秒,不考虑容量限制,电梯第一个人进来后,15秒后准时运送一次,这是节流;
  5. 电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖
    代码实现:
节流
// 节流
// 完成节流可以使用时间戳与定时器的写法 
//使用时间戳写法,事件会立即执行,停止触发后没有办法再次执行
function throttled1(fn, delay = 500) {
    let oldtime = Date.now()
    return function (...args) {
        let newtime = Date.now()
        if (newtime - oldtime >= delay) {
            fn.apply(null, args)
            oldtime = Date.now()
        }
    }
}
//使用定时器写法,delay毫秒后第一次执行,第二次事件停止触发后依然会再一次执行
function throttled2(fn, delay = 500) {
    let timer = null
    return function (...args) {
        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(this, args)
                timer = null
            }, delay);
        }
    }
}
//可以将时间戳写法的特性与定时器写法的特性相结合,实现一个更加精确的节流。实现如下
function throttled(fn, delay) {
    let timer = null
    let starttime = Date.now()
    return function () {
        let curTime = Date.now() // 当前时间
        let remaining = delay - (curTime - starttime)  // 从上一次到现在,还剩下多少多余时间
        let context = this
        let args = arguments
        clearTimeout(timer)
        if (remaining <= 0) {
            fn.apply(context, args)
            starttime = Date.now()
        } else {
            timer = setTimeout(fn, remaining);
        }
    }
}
防抖
//防抖
// 简单版本
function debounce(func, wait) {
    let timeout;

    return function () {
        let context = this; // 保存this指向
        let args = arguments; // 拿到event对象

        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}
// 防抖如果需要立即执行,可加入第三个参数用于判断,实现如下:
function debounce(func, wait, immediate) {

    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout); // timeout 不为null
        if (immediate) {
            let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
            timeout = setTimeout(function () {
                timeout = null;
            }, wait)
            if (callNow) {
                func.apply(context, args)
            }
        }
        else {
            timeout = setTimeout(function () {
                func.apply(context, args)
            }, wait);
        }
    }
}
防抖和节流的区别
  1. 相同点
    都可以通过使用 setTimeout 实现
    目的都是,降低回调执行频率。节省计算资源
  2. 不同点
    函数防抖,在一段连续操作结束后,处理回调,利用clearTimeout和 setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能
    函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次
应用场景
  1. 防抖在连续的事件,只需触发一次回调的场景有:
    搜索框搜索输入。只需用户最后一次输入完,再发送请求
    手机号、邮箱验证输入检测
    窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
  2. 节流在间隔一段时间执行一次回调的场景有:
    滚动加载,加载更多或滚到底部监听
    搜索框,搜索联想功能

5.bind

  1. bind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向
  2. 那么什么情况下需要改变this的指向呢?下面举个例子
var name = "lucy";
var obj = {
    name: "martin",
    say: function () {
        console.log(this.name);
    }
};
obj.say(); // martin,this 指向 obj 对象
setTimeout(obj.say,0); // lucy,this 指向 window 对象
  1. 从上面可以看到,正常情况say方法输出martin,但是我们把say放在setTimeout方法中,在定时器中是作为回调函数来执行的,因此回到主栈执行时是在全局执行上下文的环境中执行的,这时候this指向window,所以输出lucy.我们实际需要的是this指向obj对象,这时候就需要该改变this指向了
setTimeout(obj.say.bind(obj),0); //martin,this指向obj对象
  1. bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入),改变this指向后不会立即执行,而是返回一个永久改变this指向的函数
Function.prototype.myBind = function(context,...args){
				// 获取上下文,第一个参数
				context = context?context:window
				// 定义一个符号
				var sy = Symbol
				// 把this挂载在context 上下文
				context[sy] = this
				
				return function(...args){
					var list = [...args,...arguments]
					// 返回函数,函数执行 执行context[sy]方法
					
					context[sy](list)                   
					delete context[sy]
				}
			}
			var f = fun.myBind({
				myname:"张三"
			})
			f(4,6)

6.call

  1. call方法的第一个参数也是this的指向,后面传入的是一个参数列表
  2. 跟apply一样,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
function fn(...args){
    console.log(this,args);
}
let obj = {
    myname:"张三"
}

fn.call(obj,1,2); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window
  1. 同样的,当第一个参数为null、undefined的时候,默认指向window(在浏览器中)
			// call  apply  bind 区别
			// call与apply都是执行一个函数,第一个参数冒充this,bind冒充this 并产生一个新的函数

			Function.prototype.myCall = function(context, ...args) {

				var obj = context || window
				// obj.fn = this
				var sy = Symbol("特殊符号唯一");// 符号是唯一得,也可以用作对象得键名
				// console.log(this) // 缓存this
				// 执行this(执行函数)
				// obj.fn(...args)
				// delete obj.fn // 删除给obj新增得fn属性
				obj[sy] = this
				obj[sy](...args)
				delete obj[sy]
			}

			function fun(a, b) {
				console.log(this, a, b)
			}
			// 在原型挂载,所有函数都拥有myCall方法
			// fun.myCall({
			// 	name: "zzz",
			// 	age: 18
			// }, 5, 3)

7.new操作符

一、是什么

在JavaScript中,new操作符用于创建一个给定构造函数的实例对象

例子

function Person(name, age){
    this.name = name;
    this.age = age;
}
Person.prototype.sayName = function () {
    console.log(this.name)
}
const person1 = new Person('Tom', 20)
console.log(person1)  // Person {name: "Tom", age: 20}
t.sayName() // 'Tom'

从上面可以看到:

  1. new 通过构造函数 Person 创建出来的实例可以访问到构造函数中的属性
  2. new 通过构造函数 Person 创建出来的实例可以访问到构造函数原型链中的属性(即实例与构造函数通过原型链连接了起来)
  3. 现在在构建函数中显式加上返回值,并且这个返回值是一个原始类型
function Test(name) {
  this.name = name
  return 1
}
const t = new Test('xxx')
console.log(t.name) // 'xxx'

可以发现,构造函数中返回一个原始值,然而这个返回值并没有作用

下面在构造函数中返回一个对象

function Test(name) {
  this.name = name
  console.log(this) // Test { name: 'xxx' }
  return { age: 26 }
}
const t = new Test('xxx')
console.log(t) // { age: 26 }
console.log(t.name) // 'undefined'

从上面可以发现,构造函数如果返回值为一个对象,那么这个返回值会被正常使用

二、流程

从上面介绍中,我们可以看到new关键字主要做了以下的工作:

创建一个新的对象obj

将对象与构建函数通过原型链连接起来

将构建函数中的this绑定到新建的对象obj上

根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理

举个例子:

function Person(name, age){
    this.name = name;
    this.age = age;
}
const person1 = new Person('Tom', 20)
console.log(person1)  // Person {name: "Tom", age: 20}
t.sayName() // 'Tom'

流程图如下:
new操作符

三、手写new操作符
// new 关键字
			// 01 创建一个对象
			// 02 把this传入执行构造函数
			// 03 修改对象
			function mynew(fun,...args){
				let obj = {}
				fun.call(obj,...args)
				obj.__proto__.constructor = fun
				
				
				return obj
			}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值