前端编程题:
1.节流和防抖
函数节流:一个函数执行一次后,只有大于设定的执行周期后才会执行第二次
function throttle(fn, delay) {
// 记录上一次函数触发的时间
var lastTime = 0;
return function() {
// 记录当前函数触发的时间
var nowTime = Date.now();
if (nowTime - lastTime > delay) {
// 修正this指向问题
fn.call(this);
// 同步时间
lastTime = nowTime;
}
}
}
作者:浪里行舟
链接:https://juejin.im/post/6844903727900409870
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
function throttle(fn, interval = 300) {
let canRun = true;
return function () {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
canRun = true;
}, interval);
};
}
函数的节流就是通过闭包保存一个标记(canRun = true),在函数的开头判断这个标记是否为 true,如果为 true 的话就继续执行函数,否则则 return 掉,判断完标记后立即把这个标记设为 false,然后把外部传入的函数的执行包在一个 setTimeout 中,最后在 setTimeout 执行完毕后再把标记设置为 true(这里很关键),表示可以执行下一次的循环了。当 setTimeout 还未执行的时候,canRun 这个标记始终为 false,在开头的判断中被 return 掉
防抖函数:一个需要频繁触发的函数,在规定时间内,只让最后一次生效,前面的不生效。
function debounce(fn, delay) {
// 记录上一次的延时器
var timer = null;
return function() {
// 清除上一次延时器
clearTimeout(timer)
timer = setTimeout(function() {
fn.apply(this)
}, delay)
}
}
作者:浪里行舟
链接:https://juejin.im/post/6844903727900409870
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
function debounce(fn, interval = 300) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, interval);
};
}
其实函数防抖的原理也非常地简单,通过闭包保存一个标记来保存 setTimeout 返回的值,每当用户输入的时候把前一个 setTimeout clear 掉,然后又创建一个新的 setTimeout,这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数了。
2.深拷贝
热门的函数库lodash,也有提供_.cloneDeep用来做深拷贝
jquery 提供一个$.extend可以用来做深拷贝
JSON.parse(JSON.stringify()),不能拷贝函数类型
手写递归方法
//定义检测数据类型的功能函数
function checkedType(target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
//实现深度克隆---对象/数组
function clone(target) {
//判断拷贝的数据类型
//初始化变量result 成为最终克隆的数据
let result,
targetType = checkedType(target)
if (targetType === 'Object') {
result = {}
} else if (targetType === 'Array') {
result = []
} else {
return target
}
//遍历目标数据
for (let i in target) {
//获取遍历数据结构的每一项值。
let value = target[i]
//判断目标结构里的每一值是否存在对象/数组
if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
//对象/数组里嵌套了对象/数组
//继续遍历获取到value值
result[i] = clone(value)
} else {
//获取到value值是基本的数据类型或者是函数。
result[i] = value
}
}
return result
}
3.js的连续赋值
来自:https://www.cnblogs.com/Gavin257/p/9562214.html
首先来一个经典案例:
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x); //undefined
console.log(b.x); //{n: 2}
说明:
1)此处的a,b是引用类型
2)在javascript中字段访问操作符".“的优先级高于赋值操作符”="
3)出现多个赋值操作符"="时,运算顺序为从右向左
第一行和第二行执行后:a和b同时指向同一地址(存放对象{n:1}
第三行最先执行的是a.x
第三行然后执行:
a = {n: 2},使得a指向另一个地址,让a指向对象{n:2}的引用
第三行最后一步:就是最左边的赋值
(a.x )= (a = {n: 2});
实际上是给对象
{
n:1
x:undefined
}
中的x赋值,
{
n:1
x:{n:2}
}
总结:在运行完上述命令后,变量a指向了新对象{n : 2};变量b 的地址没有发生改变,因而仍指向修改后的对象{n: 1;x: {n : 2}}。
4.闭包和作用域
来自:https://www.baidu.com/link?url=EQyHYLvrPKsw4ekagYIjv7aThqd0zfpWN_ptzT0E-xnQCbvvi_tICqqg5R0GyiyAYmr-8rffvjgsn_bHd1OkSa&wd=&eqid=b6ccc59500199d1d000000065f425808
var a = 0,
b = 0;
function A(a) {
A = function (b) {
console.log(a + b++)
}
console.log(a++)
}
A(1)//1
A(2)//4
闭包机制:闭包创建后,可以保存创建时的活动对象。
自加操作符:++,当++作为后缀操作符时,调用++的表达式的值为自加前的自加对象的值。
此处说明 ++操作符的特性。
var i = 0;
var eg = i++
console.log(i, eg) // 1 0
第一次调用A时,执行到console.log(a++)时,a已经完成自加,此时a的值为2,a++的值为1。
A(1)=1
第二次调用A时,A已经被重新赋值,指向了一个新的函数引用;
由于标识符A是在全局作用域定义的,所以在函数内部被重新赋值,在全局作用域也可以访问到重新赋值后的函数。
此时,也创建了一个闭包,该闭包保存了创建环境的活动对象。
此时活动对象:{ a: 2 },同时,根据传入的数值2,确定 b = 2,b++值为3。
执行到 console.log(a + b++),故而输出4
4.实现new 操作符
来自:https://blog.csdn.net/q1424966670/article/details/92839918
要手动实现一个 new 操作符,首先要知道 new 操作符都做了什么事,即构造函数的内部原理:
前置知识:
作者:刘狗蛋
链接:https://www.zhihu.com/question/34183746/answer/124279182
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
__proto__ 、prototype傻傻分不清楚? 记住以下两点:1. __proto__是每个对象都有的一个属性,而prototype是函数才会有的属性。2. __proto__指向的是当前对象的原型对象,而prototype指向的,是以当前函数作为构造函数构造出来的对象的原型对象。看起来有点绕,我 show you the code,下面我们用右手作为原型来给自己构造一个女朋友://在JavaScript的世界中,所有的函数都能作为构造函数,构造出一个对象
//下面我给自己构造一个女朋友
function GirlFriend () {
this.name = "Alice";
}
//现在我设置GirlFriend()这个函数的prototype属性
//一般来说直接用匿名的对象就行,我这里是为了方便理解,
//先定义一个hand对象再把hand赋值给GirlFriend()的prototype
var hand = {
whichOne: "right hand",
someFunction: function(){
console.log("not safe for work.");
}
};
GirlFriend.prototype = hand;
//这个时候,我们可以用GirlFriend()作为构造函数,构造出myObject对象
var myObject = new GirlFriend();
console.log(myObject.__proto__ === GirlFriend.prototype) //true
好了,通过上面的代码,我们构建了一个女神对象myObject,而 myObject 的原型是 hand 对象,而刚好 myObject 的构造函数GirlFriend()的 prototype 属性也指向 hand 对象。现在我们知道,prototype 与__proto__ 的关系就是:你的__proto__来自你构造函数的prototype 还有,上面的例子中,myObject 是通过 new GirlFriend()创建的,而 hand 对象,则是赋值语句创建的,这有什么不同? 其实hand这种直接用赋值语句加花括号来创建的对象,叫做对象字面量,你可以想象JavaScript内置了一个叫Object()的构造函数,这个函数的prototype属性指向的是一个空对象:console.log(Object.prototype) //输出{}
而所有对象字面量都是通过Object()构造出来的,换言之,对象字面量__proto__ 属性都指向Object.prototype, which is 一个空对象。 所以我们可以知道, hand.__proto__ 指向的是Object.prototype 再附送你一个fun fact: Object.prototype这个对象,它的__proto__指向的是null,然后就没有然后了。console.log(Object.prototype.__proto__);//输出null
1.创建一个新对象;
2.链接到原型(将构造函数的 prototype 赋值给新对象的 __proto__);
3.绑定this(构造函数中的this指向新对象并且调用构造函数)
4.返回新对象
这样我们就可以手动实现一个 new 方法了
// 构造器函数
let Parent = function (name, age) {
this.name = name;
this.age = age;
};
Parent.prototype.sayName = function () {
console.log(this.name);
};
//自己定义的new方法
let newMethod = function (Parent, ...rest) {
// 1.以构造器的prototype属性为原型,创建新对象;
let child = Object.create(Parent.prototype);
// 2.将this和调用参数传给构造器执行
let result = Parent.apply(child, rest);
// 3.如果构造器没有手动返回对象,则返回第一步的对象
return typeof result === 'object' ? result : child;
};
//创建实例,将构造函数Parent与形参作为参数传入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo';
//最后检验,与使用new的效果相同
child instanceof Parent//true
child.hasOwnProperty('name')//true
child.hasOwnProperty('age')//true
child.hasOwnProperty('sayName')//false
我们实现的 realizeNew() 方法需要传入的参数是:构造函数 + 属性
1.首先我们创建一个新对象,
2.然后通过 arguments 类数组获取构造函数和其他参数
我们可以知道参数中包含了构造函数以及我们调用create时传入的其他参数,接下来就是要想如何得到其中这个构造函数和其他的参数,由于arguments是类数组,没有直接的方法可以供其使用,我们可以有以下两种方法:
1.Array.from(arguments).shift(); 转换成数组 使用数组的方法 shift 将第一项弹出
2.[].shift().call(arguments) ; 通过 call() 让arguments能够借用shift()方法
绑定this的时候需要注意:
1.给构造函数传入属性,注意构造函数的this属性
2.参数传进 Con 对 obj 的属性赋值,this要指向 obj 对象
3.在 Con 内部手动指定函数执行时的this 使用call、apply实现
4.最后我们需要返回一个对象
我们来测试一下:
function Person (name,age){
this.name = name;
this.age = age;
this.say = function () {
console.log("I am " + this.name)
}
}
//通过new创建构造实例
let person1 = new Person("Curry",18);
console.log(person1.name); //"Curry"
console.log(person1.age); //18
person1.say(); //"I am Curry'
//通过realize()方法创造实例
let person2 = realizeNew (Person,"Curry",18);
console.log(person2.name); //"Curry"
console.log(person2.age); //18
5.Js – 函数柯里化
(参考和推荐:https://segmentfault.com/a/1190000012769779)
通用实现
一个通用实现:
function currying(fn, ...rest1) {
return function(...rest2) {
return fn.apply(null, rest1.concat(rest2))
}
}
注意这里concat接受非数组元素参数将被当做调用者的一个元素传入
用它将一个sayHello函数柯里化试试:
function sayHello(name, age, fruit) {
console.log(console.log(`我叫 ${name},我 ${age} 岁了, 我喜欢吃 ${fruit}`))
}
const curryingShowMsg1 = currying(sayHello, '小明')
curryingShowMsg1(22, '苹果') // 我叫 小明,我 22 岁了, 我喜欢吃 苹果
const curryingShowMsg2 = currying(sayHello, '小衰', 20)
curryingShowMsg2('西瓜') // 我叫 小衰,我 20 岁了, 我喜欢吃 西瓜
嘻嘻,感觉还行~
高阶柯里化函数
以上柯里化函数已经能解决一般需求了,但是如果要多层的柯里化总不能不断地进行currying函数的嵌套吧,我们希望经过柯里化之后的函数每次只传递一个或者多个参数,那该怎么做呢:
function curryingHelper(fn, len) {
const length = len || fn.length // 第一遍运行length是函数fn一共需要的参数个数,以后是剩余所需要的参数个数
return function(...rest) {
return rest.length >= length // 检查是否传入了fn所需足够的参数
? fn.apply(this, rest)
: curryingHelper(currying.apply(this, [fn].concat(rest)), length - rest.length) // 在通用currying函数基础上
}
}
function sayHello(name, age, fruit) { console.log(`我叫 ${name},我 ${age} 岁了, 我喜欢吃 ${fruit}`) }
const betterShowMsg = curryingHelper(sayHello)
betterShowMsg('小衰', 20, '西瓜') // 我叫 小衰,我 20 岁了, 我喜欢吃 西瓜
betterShowMsg('小猪')(25, '南瓜') // 我叫 小猪,我 25 岁了, 我喜欢吃 南瓜
betterShowMsg('小明', 22)('倭瓜') // 我叫 小明,我 22 岁了, 我喜欢吃 倭瓜
betterShowMsg('小拽')(28)('冬瓜') // 我叫 小拽,我 28 岁了, 我喜欢吃 冬瓜
来自:https://blog.csdn.net/weixin_37680520/article/details/108371908
https://www.cnblogs.com/plBlog/p/12356042.html
维基百科上说道:柯里化,英语:Currying(果然是满满的英译中的既视
感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个
参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
看这个解释有一点抽象
1.我们就拿被做了无数次示例的add函数,来做一个简单的实现。
// 普通的add函数
复制代码
function add(x, y) {
return x + y
}
// Currying后
function curryingAdd(x) {
return function (y) {
return x + y
}
}
add(1, 2) // 3
curryingAdd(1)(2) // 3
实际上就是把add函数的x,y两个参数变成了先用一个函数接收x然后返回一个函数去处理y参数。现在思路应该就比较清晰了,就是只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
但是问题来了费这么大劲封装一层,到底有什么用处呢?没有好处想让我们程序员多干事情是不可能滴,这辈子都不可能.
来列一列Currying有哪些好处呢?
2. 参数复用
// 正常正则验证字符串 reg.test(txt)
// 函数封装后
function check(reg, txt) {
return reg.test(txt)
}
check(/\d+/g, 'test') //false
check(/[a-z]+/g, 'test') //true
// Currying后
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}
}
var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
上面的示例是一个正则的校验,正常来说直接调用check函数就可以了,但是如果我有很多地方都要校验是否有数字,其实就是需要将第一个参数reg进行复用,这样别的地方就能够直接调用hasNumber,hasLetter等函数,让参数能够复用,调用起来也更方便。
3.实现一个函数功能:sum(1,2,3,4…n)转化为 sum(1)(2)(3)(4)…(n)
// 使用柯里化 + 递归
注意:length 是js函数对象的一个属性值,该值是指 “该函数有多少个必须要传入的参数”,
function curry ( fn ) {
var c = (...arg) => (fn.length === arg.length) ?
fn (...arg) : (...arg1) => c(...arg, ...arg1)
return c
}
var a=function(a,b,c){return a+b+c}
undefined
function curry ( fn ) {
var c = (...arg) => (fn.length === arg.length) ?
fn (...arg) : (...arg1) => c(...arg, ...arg1)
return c
}
undefined
var b=curry(a)
undefined
a(1,2,3)
6
b(1)(2)(3)
6
a.length
3
curry(sum)
4.实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
// 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
var _args = Array.prototype.slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var _adder = function() {
_args.push(...arguments);
return _adder;
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
add(1)(2)(3) // 6
add(1, 2, 3)(4) // 10
add(1)(2)(3)(4)(5) // 15
add(2, 6)(1) // 9
6.实现一个Array.prototype.flat()函数
1.方式1:
Array.prototype.myFlat = function(arr=[]) {
if (Array.isArray(this)) {
this.forEach(item => {
if(Array.isArray(item)){
item.myFlat(arr)
} else {
arr.push(item)
}
});
return arr;
} else {
throw tihs + ".flat is not a function";
}
};
ƒ (arr1) {
if (Array.isArray(this)) {
let arr=arr1&&arr1.length>0?arr1:[];
this.forEach(item => {
if(Array.isArray(item)){
item.myFlat(arr)
} else {
arr.push…
array.myFlat()
(7) [1, 2, 3, 4, 5, 6, 7]
var array=[1,[2,3],4,[5,6,7],[8,9,10,11,[12,13,
array.myFlat()
(20) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
2.升级版本:可以控制解开多少层。
Array.prototype.myFlat = function(num = 1) {
if (Array.isArray(this)) {
let arr = [];
if (!Number(num) || Number(num) < 0) {
return this;
}
this.forEach(item => {
if(Array.isArray(item)){
let count = num
arr = arr.concat(item.myFlat(--count))
} else {
arr.push(item)
}
});
return arr;
} else {
throw tihs + ".flat is not a function";
}
};
链接:https://zhuanlan.zhihu.com/p/108289604?utm_source=qq&utm_medium=social&utm_oi=951563727821062144
3.简洁写法:
function flatDeep(arr) {
return arr.reduce((res, cur) => {
if(Array.isArray(cur)){
return [...res, ...flatDeep(cur)]
}else{
return [...res, cur]
}
},[])
}
var array=[1,[2,3],4,[5,6,7],[8,9,10,11,[12,13,[14,15,16],17,18],[19,20],]]
flatDeep(array)
(20) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
链接:https://juejin.im/post/6864398060702760968
7.数据双向绑定
来自:https://zhuanlan.zhihu.com/p/123012477
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<input type="text" id="input" />
<br>
<!-- span的值为:<span id="span"></span> -->
<script>
// 数据
data = {
text: 'defau44lt'
};
const input = document.getElementById('input');
// const span = document.getElementById('span');
// 数据劫持
Object.defineProperty(data, 'text', {
// 数据变化 —> 修改视图
set(newVal) {
input.value = newVal;
// span.innerHTML = newVal;
},
get: function () {
var value=document.getElementById('input').value
return value
},
});
// 视图更改 --> 数据变化
input.addEventListener('keyup', function (e) {
data.text = e.target.value;
console.log(data.text);
});
</script>
</body>
</html>
8.创建一个Event类,并创建on、off、trigger、once方法
来自:https://www.cnblogs.com/hyshi/p/10918500.html
版本1:
一、创建一个Event.js
复制代码
class Event {
constructor() {
this.handlers = { // 记录所有的事件和处理函数
}
}
/* *
* on 添加事件监听
* @param type 事件类型
* @param handler 事件回调
* on('click', ()=>{})
* */
on(type, handler, once=false) {
if (!this.handlers[type]) {
this.handlers[type] = [];
}
if (!this.handlers[type].includes(handler)) {
this.handlers[type].push(handler);
handler.once = once;
}
}
/* *
* off 取消事件监听
*
* */
off(type, handler) {
if (this.handlers[type]) {
if (handler === undefined) {
this.handlers[type] = []
} else {
this.handlers[type] = this.handlers[type].filter((f)=>{
return f!=handler
})
}
}
}
/* *
* @param type 要执行哪个类型的函数
* @param eventData事件对象
* @param point this指向
*
* */
trigger(type, eventData = {}, point=this) {
if (this.handlers[type]) {
this.handlers[type].forEach(f => {
f.call(point, eventData);
if (f.once) {
this.off(type, f)
}
});
}
}
/* *
* once 函数执行一次
* @param type 事件处理
* @param handle 事件处理函数
* */
once(type, handler) {
this.on(type, handler, true);
}
}
复制代码
二、使用Event.js
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#box {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background: red;
}
</style>
<script src="./event.js"></script>
</head>
<body>
<div id="box"></div>
<script>
/*
* 1.记录摁下时鼠标的位置和元素位置
* 鼠标位置-摁下时的鼠标位置 = 鼠标移动的位置
* 元素位置=鼠标移动距离+摁下时元素位置
**/
class Drag extends Event{
// 构造函数
constructor(el) {
super(); // 继承
this.el = el;
this.startOffset = null; // 鼠标摁下时元素的位置
this.startPoint = null; // 鼠标的坐标
let move = (e)=>{
this.move(e)
}
let end = (e)=>{
document.removeEventListener('mousemove', move);
document.removeEventListener('mouseup', end);
this.end(e)
}
el.addEventListener('mousedown', (e)=> {
this.start(e);
document.addEventListener('mousemove', move);
document.addEventListener('mouseup', end);
})
}
start(e) {
let {el} = this;
console.log(this)
console.log(el)
this.startOffset = {
x: el.offsetLeft,
y: el.offsetTop
}
this.startPoint = {
x: e.clientX,
y: e.clientY
}
this.trigger('dragstart', e, this.el)
}
end(e) {
this.trigger('dragend',e, this.el)
}
move(e) {
let {el, startOffset, startPoint} = this;
let nowPoint = {
x: e.clientX,
y: e.clientY
}
let dis = {
x: nowPoint.x - startPoint.x,
y: nowPoint.y - startPoint.y
}
el.style.left = dis.x + startOffset.x + 'px';
el.style.top = dis.y + startOffset.y + 'px';
this.trigger('dragmove', e, el)
}
}
(function() {
let box = document.querySelector('#box');
let dragBox = new Drag(box);
dragBox.on('dragstart', function(e) {
console.log(e);
console.log(this);
this.style.background = 'yellow';
})
dragBox.on('dragend', function(e) {
console.log('b')
this.style.background = 'blue';
})
dragBox.once('dragmove', function(e) {
console.log('c')
// this.style.background = 'blue';
})
console.log(dragBox)
})()
</script>
</body>
</html>
复制代码
参考第二个版本:https://juejin.im/post/6844903965646127117#heading-2
class EventEmitter {
constructor(){
this.events = {}
}
on(name,cb){
if(!this.events[name]){
this.events[name] = [cb];
}else{
this.events[name].push(cb)
}
}
emit(name,...arg){
if(this.events[name]){
this.events[name].forEach(fn => {
fn.call(this,...arg)
})
}
}
off(name,cb){
if(this.events[name]){
this.events[name] = this.events[name].filter(fn => {
return fn != cb
})
}
}
once(name,fn){
var onlyOnce = () => {
fn.apply(this,arguments);
this.off(name,onlyOnce)
}
this.on(name,onlyOnce);
return this;
}
}
9.实现call,apply,bind
作者:white_give
链接:https://juejin.cn/post/6874901113062031367
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1)call的实现:
Function.prototype.myCall = function (ctx, ...args) {
ctx = ctx || window;
ctx.fn = this;把调用call的方法作为指定的上下文对象的一个属性
const result = ctx.fn(...args);
delete ctx.fn;
return result;
}
2)apply的实现
Function.prototype.myApply = function (ctx, args = []) {
if (args && !Array.isArray(args)) {
throw ('Uncaught TypeError: CreateListFromArrayLike called on non-object');
}
ctx = ctx || window;
ctx.fn = this;
const result = ctx.fn(...args);
delete ctx.fn;
return result;
}
3)bind的实现
Function.prototype.myBind = function (ctx, ...args1) {
const that = this;
const o = function () {};
const newFn = function (...args2) {
const args = args1.concat(args2);
if (this instanceof o) {
that.apply(this, args);
} else {
that.apply(ctx, args);
}
}
o.prototype = that.prototype;
newFc.prototype = new o;
return newFn;
}
10.手写axios
a)先看看axios的使用:
getNewsList(){
this.axios.get('api/getNewsList').then((response)=>{
this.newsList=response.data.data;
}).catch((response)=>{
console.log(response);
})
}
b)实现
axios 原理还是属于 XMLHttpRequest, 因此需要实现一个ajax。
还需要但会一个promise对象来对结果进行处理。
以get请求为例,实现一个axios
在我看来,写一个ajax就需要5步,也就是5个单词,这就是一个ajax的流程。
这五个单词分别为:new open setRequestHeader onreadystatechange send。记住这五个单词你就有了ajax的整体的框架了。
首先:new字
let ajax = fucntion (){
let httpRequest;
// IE7以上,以及其他主流浏览器
if(window.XMLHttpRequest){
httpRequest = new XMLHttpRequest();
if(httpRequest.overrideMimiType){
httpRequest.overrideMimeType('text/xml')
}
} else if(window.ActiveXObject){
// 针对IE6,及IE5.5 ie5
httpRequest = new ActiveXObject();
let activeName = ['MSXML2.XMLHTTP', 'Microsoft.XMLHTTP']; //创建XMLHttpRequest对象控件
for(let i = 0; i < activeName.length; i++){
try {
httpRequest = new ActiveXObject(activeName[i]);
if(httpRequest){
break;
}
} catch(e){
console.log(e)
}
}
}
return httpRequest;
}
以上是最麻烦的一步,后面的步骤就比较简单了。
然后是open
ajax.open('GET', 'url', true);
ajax.open('POST', 'url', true);
如果是GET方式就不需要了,如果是POST方式就需要设置请求头
ajax.setRequestHeader('Content-Type: application/x-www-form-urlencoded; charset = utf8')
响应主体
ajax.onreadystatechange = function(){
if(ajax.readystate == 4){
if(ajax.status == 200){
alert(ajax.responseText)
}
}
}
send
send发送请求,如果是get就不用发送内容
ajax.send(null)
如果是post可以发送请求的条件
ajax.send('name=Wang')
介绍一下readystate的情况
0 (未初始化) 对象已建立,但是尚未初始化(尚未调用 open 方法)
1 (初始化) 对象已建立,尚未调用 send 方法
2 (发送数据) send方法已调用,但是当前的状态及http头未知
3 (数据传送中) 已接收部分数据,因为响应及http头不全,这时通过 responseBody 和 responseText 获取部分数据会出现错误,
4 (完成) 数据接收完毕,此时可以通过通过 responseXml 和 responseText 获取完整的回应数据
————————————————
版权声明:本文为CSDN博主「山上的仓鼠」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wangyoushuai/article/details/87973788
// 获取异步请求对象
function getXhr(){
var xhr=null;
if(window.XMLHttpRequest){
xhr=new XMLHttprequest();
}else{
// 如果浏览器版本是IE8以下浏览器
xhr=new ActiveXObject('Microsoft.XMLHttp');
}
return xhr;}
// url:"url路径" type:请求方式 data:请求参数类型 dataType:返回的字符串类型
function ajax({url,type,data,dataType}){
return new Promise(function(resolve,reject){
//1. 创建异步请求对象
var xhr=getXhr();
// 备注:无需通过上面的方式,简单的创建异步请求对象的简化代码如下:
// var xhr = window.XMLHttpRequest ? new XMLHttprequest() : new ActiveXObject('Microsoft.XMLHttp');
//2.绑定监听事件
xhr.onreadystatechange=function(){
// 当异步请求状态变为4时,并且返回的状态码为200,接收响应成功
if(xhr.readyState==4&&xhr.status==200){
// 当返回接收的字符串类型为json串时,自动转换json串
if(dataType!==undefined
&&dataType.toLowerCase()==="json")
var res=JSON.parse(xhr.responseText)
else
// 否则直接获取返回的响应文本中的内容
var res=xhr.responseText
// 通过Promise,将返回的数据向后传递,相当于获取到请求数据将数据return出来
resolve(res);
}
}
// 如果请求方式为get请求,则将请求参数拼接在url后
if(type.toLowerCase()==="get"&&data!==undefined){
url+="?"+data;
}
//3.打开连接
xhr.open(type,url,true);
// 如果请求方式为post请求,则修改请求消息头
if(type.toLowerCase()==="post")
//增加:设置请求消息头
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
//4.发送请求
if(type.toLowerCase()==="post"&&data!==undefined)
xhr.send(data);
else
xhr.send(null);})
实现ajax的get请求
var Ajax={
get: function(url, fn) {
// XMLHttpRequest对象用于在后台与服务器交换数据
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
// readyState == 4说明请求已完成
if (xhr.readyState == 4 && xhr.status == 200) {
// 从服务器获得数据
fn.call(this, xhr.responseText);
}
};
xhr.send();
}
}
封装Ajax,实现Axios进行回调
var Axios = {
get: function(url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
// readyState == 4说明请求已完成
if (xhr.readyState == 4 && xhr.status == 200) {
// 从服务器获得数据
resolve(xhr.responseText)
}
};
xhr.send();
})
},
}
axios的使用如下:
11.简单实现promise:(https://zhuanlan.zhihu.com/p/84757243)
class myPromise {
constructor(callback) {
this.msg = ""
this.success = null
this.fail = null
//这个callback也就是我们传递的函数
callback(
success => {
// 成功 success对应res()传递的参数
this.msg = "SUCCESS"
this.success = success
},
fail => {
// 失败 success对应rej()传递的参数
this.msg = "FAIL"
this.fail = fail
}
)
}
then(success, fail) {
if (this.msg === "SUCCESS") {
success(this.success)
}
if (this.msg === "FAIL") {
fail(this.fail)
}
}
}