1.call()函数的实现步骤
- 判断调用对象是否为函数,即使是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
- 判断传入上下文对象是否存在,如果不存在,则设置为 window 。
- 处理传入的参数,截取第一个参数后的所有参数。
- 将函数作为上下文对象的一个属性。
- 使用上下文对象来调用这个方法,并保存返回结果。
- 删除刚才新增的属性。
- 返回结果。
代码实现:
let A = {
name: 'zs',
getName: function(msg){
return msg + this.name
}
}
let B = {
name: 'lis'
}
Function.prototype.myCall = function(context) {
//判断调用对象是否为函数
if(typeof this !== 'function') {
console.error('type err');
return;
}
//判断传入上下文对象是否存在,如果不存在,则设置为window
context = context || window;
//处理传入参数,截取第一个参数后的所有参数
let args = [...arguments].slice(1);
//将函数作为上下文对象的一个属性
context.fn = this;
//使用上下文对象调用这个方法,并保存返回结果。
const res = context.fn(...args);
//删除刚刚新增的属性
delete context.fn;
//返回结果
return res
}
console.log('封装',A.getName.myCall(B,'信息'));//封装 信息lis
console.log('原生',A.getName.call(B,'hello')); //原生 hello lis
2. apply()函数的实现步骤
- 判断调用对象是否为函数,即使是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。
- 判断传入上下文对象是否存在,如果不存在,则设置为 window 。
- 将函数作为上下文对象的一个属性。
- 判断参数值是否传入
- 使用上下文对象来调用这个方法,并保存返回结果。
- 删除刚才新增的属性
- 返回结果
代码实现:
let A = {
name: 'zs',
getName: function(msg){
return msg + this.name
}
}
let B = {
name: 'lis'
}
Function.prototype.myApply = function(context) {
//判断调用对象是否为函数
if(typeof this !== 'function') {
console.error('type err');
return;
}
//判断传入上下文对象是否存在,如果不存在,则设置为window
context = context || window;
//将函数作为上下文对象的一个属性
context.fn = this;
let res = null;
//判断参数是否传入
console.log(arguments)
if(arguments[1]) {
res = context.fn(...arguments[1]);
} else {
res = context.fn();
}
//删除刚刚新增的属性
delete context.fn;
//返回结果
return res
}
console.log('封装',A.getName.myApply(B,['信息']));//封装 信息lis
console.log('原生',A.getName.apply(B,['原生'])); //原生 原生 lis
3.bind()函数的实现步骤
- 接收的参数:第一个参数为this的指向,后面的参数就是函数接收的参数了。this是必需提供的参数,其他的参数都是非必需的;
- 返回值:返回值应该是一个函数;
代码实现
let A = {
name: 'zs',
getName: function(msg){
return msg + this.name
}
}
let B = {
name: 'lis'
}
//封装bind函数
Function.prototype.myBind = function(context,...args) {
return ()=> {
return this.apply(context,...args)
};
}
let res = A.getName.myBind(B,['封装']);
console.log(res());//封装lis
let result = A.getName.bind(B,['原生']);
console.log(result());//原生lis
4.防抖节流实现和应用场景
1.防抖实现
触发高频事件后,n秒内函数之后执行一次,如果n秒内再次触发,会重新计算事件。
- 应用场景(一般用于点击发送请求)
应用场景:一般是onrize,onscroll等这些频繁触发的函数,比如你想获取滚动条的位置,然后执行下一步动作;鼠标不断点击触发,mousedown(单位时间内只触发一次),input输入....
- 代码实现
function debounce(fn, wait) {
let timer=null;
let that = this;
let args = arguments;
return function () {
//每次点击清除当前定时器
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
//将传入函数this指向当前函数。
fn.apply(that,args);
}, wait)
}
}
<input type="text" placeholder="请输入">
let input = document.querySelector('input');
input.addEventListener('input',debounce(()=>{
console.log('input');
},1000))
// 输入完成之后一秒后才会执行回调
2.节流实现
触发高频事件后,n秒内函数函数只会执行一次。
- 1.应用场景
应用场景: search搜索联想,用户在不断输入值时
- 代码实现
function throttle(fn, wait) {
let timer=null;
let that = this;
let args = arguments;
return function () {
//wait秒之内用户点击只会执行一次定时器
if (timer) return
timer = setTimeout(() => {
timer = null;
clearTimeout(timer);
fn.apply(that,args);
}, wait)
}
}
<button>点击</button>
let button = document.querySelector('button')
button.addEventListener('click',throttle(()=>{
console.log('button');
},1000))
//一秒之内只会触发一次回调函数
5.浅拷贝和深拷贝实的现步骤
5.1浅拷贝的实现
只拷贝对象或数组第一层
更深层次对象级别的只拷贝引用(只将地址拷贝给了新对象)
注意:第一层不是对象时,浅拷贝也是深拷贝,修改值时不会影响新对象的值变化
代码实现:
const target = {
a: '1',
b: {c:'1'}
}
//只拷贝对象或数组第一层
function shallClone(target) {
if (typeof target === 'object' && target !== null) {
let cloneTarget = Array.isArray(target)?[]:{};
for(let p in target) {
cloneTarget[p] = target[p];
}
return cloneTarget;
} else {
return target
}
}
let cloneTarget = shallClone(target);
5.2深拷贝实现(举两个例子)
深拷贝(复杂):复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。
基本数据类型(undefined,null,number,String,boolean,bigInt):存放在栈中,
引用数据类型(Object,Array,Function): 存放在堆中。
-
1.第一种实现方法
JSON.parse(JSON.stringify())
function deepCopy(obj) {
return JSON.parse(JSON.stringify(obj))
}
var obj = {
age: 1,
name: undefined,
sex: null,
tel: /^1[34578]\d{9}$/,
say: () => {
console.log('hahha')
}
}
console.log(deepCopy(obj)); // { age: 1, sex: null, tel: {} }
注意:这种拷贝方法不可以拷贝一些特殊的属性(例如正则表达式,undefine,function)
- 第二种实现方法 用递归去复制所有层级属性
function deepCopyTwo(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj == 'object') {
for (const key in obj) {
//判断obj子元素是否为对象,如果是,递归复制
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepCopyTwo(obj[key]);
} else {
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
return objClone;
}
var obj = {
age: 1,
name: undefined,
sex: null,
tel: /^1[34578]\d{9}$/,
say: () => {
console.log('hahha')
}
}
console.log(deepCopy(obj)); // { age: 1, sex: null, tel: {} }
6.new 操作实现的步骤
new 操作过程具体做了什么
- 创建一个新的对象
obj
- 将对象与构建函数通过原型链连接起来
- 将构建函数中的
this
绑定到新建的对象obj
上 - 根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理。
function Person (name,age){
this.name= name;
this.age = age
}
function myNew(Fn,...args) {
let child = Object.create(Fn.prototype);
//相当于
// child.__proto__ == Fn.prototype
//将传入对象的this指向对象的原型链上
let result = Fn.apply(child,args);
return (typeof result === 'object' || typeof result === 'function') ? result : child;
}
let child = myNew(Person,'zs','22');
console.log(child.name,child.age); //zs,22
7.instanceof 的实现步骤
instanceof
运算符用于检测构造函数的prototype
属性是否出现在某个实例对象的原型链上。
//封装instanceof
//L: 实例对象 R: Function 原型链
function instance_of(L,R) {
R = R.prototype;
L = L.__proto__;
while(true) {
if(L === null) return false;
if(L === R) return true;
L = L.__proto__;
}
}
8.Object.create() 底层实现
Object.create()
方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
function myCreate(father){
const obj = {};
obj.__proto__ = father;
return obj;
}
//使用
let Person = {}
let child = myCreate(Person.prototype);
let child = Object.create(Person.prototype);
//相当于
child.__proto = Person.prototype;
9.原生js 封装ajax 实现的步骤
- 原生Ajax五个基本步骤
1、创建ajax对象
var xhr = new XMLHttpRequest();
2、用ajax对象的open方法设置连接服务器的参数
xhr.open( method,url,async);
method:请求类型,post或get
url:请求文件的具体地址
async::是否异步(true为异步,false为同步)
3、设置发送数据的头部,一般用来说明数据格式,如:
xhr.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded”);
注: multipart/form-data(一般在发送文件时使用,以二进制形式发送)和application/json(发送json格式数据)
4、发送请求,xhr.send(数据)
5、判断通讯状态或接收返回数据,这个是写在第三个步骤回调函数里边的
function ajax(url) {
let res =null;
//1.创建异步请求对象
let xhr = new XMLHttpRequest();
// 2.配置 ajax 请求地址
xhr.open('get',url,true);
// 3.设置响应头
xhr.setRequestHeader(
"Content-Type",
"application/x-www-form-urlencoded"
);
// 4.绑定监听事件
xhr.onreadystatechange=()=>{
// 当异步请求状态为4时,并且返回的状态码为200,接收响应成功。
if(xhr.readyState =='4' && xhr.status == '200') {
res = JSON.parse(xhr.responseText)
}
res.xhr.responseText;
}
// 5.发送请求
xhr.send(null);
}
10.使用promise封装ajax请求
function ajax(url) {
return new Promise((resolve, reject) => {
//1.创建异步请求对象
let xhr = new XMLHttpRequest();
// 2.配置 ajax 请求地址
xhr.open('get', url, true);
// 3.设置响应头
xhr.setRequestHeader(
"Content-Type",
"application/x-www-form-urlencoded"
);
// 4.绑定监听事件
//每当readyState改变时,都会调用这个函数。
xhr.onreadystatechange = () => {
// 当异步请求状态为4时,请求已完成,并且准备就绪
if (xhr.readyState == '4') {
//如果200,代表请求成功
if (xhr.status == '200')
resolve(xhr.responseText)
}else if(xhr.status == '404') {
reject(new Error('404 NOT FOUND'))
}
res.xhr.responseText;
}
// 5.发送请求
xhr.send(null);
})
}
//调用
ajax('/api/user')
.then(res=>{ })
.catch(err=>console.log(err))