1.call()函數(shù)的實現(xiàn)步驟
- 判斷調(diào)用對象是否為函數(shù),即使是定義在函數(shù)的原型上的,但是可能出現(xiàn)使用 call 等方式調(diào)用的情況。
- 判斷傳入上下文對象是否存在,如果不存在,則設(shè)置為 window 。
- 處理傳入的參數(shù),截取第一個參數(shù)后的所有參數(shù)。
- 將函數(shù)作為上下文對象的一個屬性。
- 使用上下文對象來調(diào)用這個方法,并保存返回結(jié)果。
- 刪除剛才新增的屬性。
- 返回結(jié)果。
代碼實現(xiàn):
let A = {
name: 'zs',
getName: function(msg){
return msg + this.name
}
}
let B = {
name: 'lis'
}
Function.prototype.myCall = function(context) {
//判斷調(diào)用對象是否為函數(shù)
if(typeof this !== 'function') {
console.error('type err');
return;
}
//判斷傳入上下文對象是否存在,如果不存在,則設(shè)置為window
context = context || window;
//處理傳入?yún)?shù),截取第一個參數(shù)后的所有參數(shù)
let args = [...arguments].slice(1);
//將函數(shù)作為上下文對象的一個屬性
context.fn = this;
//使用上下文對象調(diào)用這個方法,并保存返回結(jié)果。
const res = context.fn(...args);
//刪除剛剛新增的屬性
delete context.fn;
//返回結(jié)果
return res
}
console.log('封裝',A.getName.myCall(B,'信息'));//封裝 信息lis
console.log('原生',A.getName.call(B,'hello')); //原生 hello lis
2. apply()函數(shù)的實現(xiàn)步驟
- 判斷調(diào)用對象是否為函數(shù),即使是定義在函數(shù)的原型上的,但是可能出現(xiàn)使用 call 等方式調(diào)用的情況。
- 判斷傳入上下文對象是否存在,如果不存在,則設(shè)置為 window 。
- 將函數(shù)作為上下文對象的一個屬性。
- 判斷參數(shù)值是否傳入
- 使用上下文對象來調(diào)用這個方法,并保存返回結(jié)果。
- 刪除剛才新增的屬性
- 返回結(jié)果
代碼實現(xiàn):
let A = {
name: 'zs',
getName: function(msg){
return msg + this.name
}
}
let B = {
name: 'lis'
}
Function.prototype.myApply = function(context) {
//判斷調(diào)用對象是否為函數(shù)
if(typeof this !== 'function') {
console.error('type err');
return;
}
//判斷傳入上下文對象是否存在,如果不存在,則設(shè)置為window
context = context || window;
//將函數(shù)作為上下文對象的一個屬性
context.fn = this;
let res = null;
//判斷參數(shù)是否傳入
console.log(arguments)
if(arguments[1]) {
res = context.fn(...arguments[1]);
} else {
res = context.fn();
}
//刪除剛剛新增的屬性
delete context.fn;
//返回結(jié)果
return res
}
console.log('封裝',A.getName.myApply(B,['信息']));//封裝 信息lis
console.log('原生',A.getName.apply(B,['原生'])); //原生 原生 lis
3.bind()函數(shù)的實現(xiàn)步驟
- 接收的參數(shù):第一個參數(shù)為this的指向,后面的參數(shù)就是函數(shù)接收的參數(shù)了。this是必需提供的參數(shù),其他的參數(shù)都是非必需的;
- 返回值:返回值應(yīng)該是一個函數(shù);
代碼實現(xiàn)
let A = {
name: 'zs',
getName: function(msg){
return msg + this.name
}
}
let B = {
name: 'lis'
}
//封裝bind函數(shù)
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.防抖節(jié)流實現(xiàn)和應(yīng)用場景
1.防抖實現(xiàn)
觸發(fā)高頻事件后,n秒內(nèi)函數(shù)之后執(zhí)行一次,如果n秒內(nèi)再次觸發(fā),會重新計算事件。
- 應(yīng)用場景(一般用于點擊發(fā)送請求)
應(yīng)用場景:一般是onrize,onscroll等這些頻繁觸發(fā)的函數(shù),比如你想獲取滾動條的位置,然后執(zhí)行下一步動作;鼠標不斷點擊觸發(fā),mousedown(單位時間內(nèi)只觸發(fā)一次),input輸入....
- 代碼實現(xiàn)
function debounce(fn, wait) {
let timer=null;
let that = this;
let args = arguments;
return function () {
//每次點擊清除當前定時器
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
//將傳入函數(shù)this指向當前函數(shù)。
fn.apply(that,args);
}, wait)
}
}
<input type="text" placeholder="請輸入">
let input = document.querySelector('input');
input.addEventListener('input',debounce(()=>{
console.log('input');
},1000))
// 輸入完成之后一秒后才會執(zhí)行回調(diào)
2.節(jié)流實現(xiàn)
觸發(fā)高頻事件后,n秒內(nèi)函數(shù)函數(shù)只會執(zhí)行一次。
- 1.應(yīng)用場景
應(yīng)用場景: search搜索聯(lián)想,用戶在不斷輸入值時
- 代碼實現(xiàn)
function throttle(fn, wait) {
let timer=null;
let that = this;
let args = arguments;
return function () {
//wait秒之內(nèi)用戶點擊只會執(zhí)行一次定時器
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))
//一秒之內(nèi)只會觸發(fā)一次回調(diào)函數(shù)
5.淺拷貝和深拷貝實的現(xiàn)步驟
5.1淺拷貝的實現(xiàn)
只拷貝對象或數(shù)組第一層
更深層次對象級別的只拷貝引用(只將地址拷貝給了新對象)
注意:第一層不是對象時,淺拷貝也是深拷貝,修改值時不會影響新對象的值變化
代碼實現(xiàn):
const target = {
a: '1',
b: {c:'1'}
}
//只拷貝對象或數(shù)組第一層
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深拷貝實現(xiàn)(舉兩個例子)
深拷貝(復(fù)雜):復(fù)制變量值,對于非基本類型的變量,則遞歸至基本類型變量后,再復(fù)制。
基本數(shù)據(jù)類型(undefined,null,number,String,boolean,bigInt):存放在棧中,
引用數(shù)據(jù)類型(Object,Array,Function): 存放在堆中。
-
1.第一種實現(xiàn)方法
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)
- 第二種實現(xiàn)方法 用遞歸去復(fù)制所有層級屬性
function deepCopyTwo(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj == 'object') {
for (const key in obj) {
//判斷obj子元素是否為對象,如果是,遞歸復(fù)制
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepCopyTwo(obj[key]);
} else {
//如果不是,簡單復(fù)制
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 操作實現(xiàn)的步驟
new 操作過程具體做了什么
- 創(chuàng)建一個新的對象
obj - 將對象與構(gòu)建函數(shù)通過原型鏈連接起來
- 將構(gòu)建函數(shù)中的
this綁定到新建的對象obj上 - 根據(jù)構(gòu)建函數(shù)返回類型作判斷,如果是原始值則被忽略,如果是返回對象,需要正常處理。
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指向?qū)ο蟮脑玩溕? 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 的實現(xiàn)步驟
instanceof運算符用于檢測構(gòu)造函數(shù)的prototype屬性是否出現(xiàn)在某個實例對象的原型鏈上。
//封裝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() 底層實現(xiàn)
Object.create()方法創(chuàng)建一個新對象,使用現(xiàn)有的對象來提供新創(chuàng)建的對象的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 實現(xiàn)的步驟
- 原生Ajax五個基本步驟
1、創(chuàng)建ajax對象
var xhr = new XMLHttpRequest();
2、用ajax對象的open方法設(shè)置連接服務(wù)器的參數(shù)
xhr.open( method,url,async);
method:請求類型,post或get
url:請求文件的具體地址
async::是否異步(true為異步,false為同步)
3、設(shè)置發(fā)送數(shù)據(jù)的頭部,一般用來說明數(shù)據(jù)格式,如:
xhr.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded”);
注: multipart/form-data(一般在發(fā)送文件時使用,以二進制形式發(fā)送)和application/json(發(fā)送json格式數(shù)據(jù))
4、發(fā)送請求,xhr.send(數(shù)據(jù))
5、判斷通訊狀態(tài)或接收返回數(shù)據(jù),這個是寫在第三個步驟回調(diào)函數(shù)里邊的
function ajax(url) {
let res =null;
//1.創(chuàng)建異步請求對象
let xhr = new XMLHttpRequest();
// 2.配置 ajax 請求地址
xhr.open('get',url,true);
// 3.設(shè)置響應(yīng)頭
xhr.setRequestHeader(
"Content-Type",
"application/x-www-form-urlencoded"
);
// 4.綁定監(jiān)聽事件
xhr.onreadystatechange=()=>{
// 當異步請求狀態(tài)為4時,并且返回的狀態(tài)碼為200,接收響應(yīng)成功。
if(xhr.readyState =='4' && xhr.status == '200') {
res = JSON.parse(xhr.responseText)
}
res.xhr.responseText;
}
// 5.發(fā)送請求
xhr.send(null);
}
10.使用promise封裝ajax請求
function ajax(url) {
return new Promise((resolve, reject) => {
//1.創(chuàng)建異步請求對象
let xhr = new XMLHttpRequest();
// 2.配置 ajax 請求地址
xhr.open('get', url, true);
// 3.設(shè)置響應(yīng)頭
xhr.setRequestHeader(
"Content-Type",
"application/x-www-form-urlencoded"
);
// 4.綁定監(jiān)聽事件
//每當readyState改變時,都會調(diào)用這個函數(shù)。
xhr.onreadystatechange = () => {
// 當異步請求狀態(tài)為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.發(fā)送請求
xhr.send(null);
})
}
//調(diào)用
ajax('/api/user')
.then(res=>{ })
.catch(err=>console.log(err))