原生JS基礎(chǔ)鞏固
原生js基礎(chǔ)鞏固
原型和原型鏈函數(shù)柯里化call/apply實(shí)現(xiàn)bind模擬實(shí)現(xiàn)最優(yōu)繼承instanceof實(shí)現(xiàn)
原型和原型鏈
提到j(luò)s,原型和原型鏈?zhǔn)且?guī)避不過(guò)的,這之中存在幾個(gè)容易混淆的概念,理解這些概念后,原型和原型鏈也就理解了。話不多說(shuō),先看一張?jiān)玩準(zhǔn)疽鈭D。

圖片來(lái)源
名詞解釋
構(gòu)造函數(shù)
簡(jiǎn)單來(lái)說(shuō),構(gòu)造函數(shù)本質(zhì)就是一個(gè)函數(shù)而已,不同的是它可以用來(lái)創(chuàng)建對(duì)象(使用new操作符調(diào)用)
function Person(){} ? ?這段代碼中Person就是構(gòu)造函數(shù)
實(shí)例
使用構(gòu)造函數(shù)創(chuàng)建出來(lái)的對(duì)象
let p=new Person() 這段代碼中的p就是構(gòu)造函數(shù)Person的一個(gè)實(shí)例
原型對(duì)象
原型對(duì)象本質(zhì)是一個(gè)對(duì)象,它的constructor屬性指向創(chuàng)建該實(shí)例的構(gòu)造函數(shù)。這里Person.prototype就是原型對(duì)象,Person.prototype.constructor屬性指向構(gòu)造函數(shù)Person。實(shí)例p可以通過(guò)__proto__屬性訪問(wèn)其原型對(duì)象
查找機(jī)制
上述原型對(duì)象Person.prototype也有自己的原型對(duì)象,就是Object.prototype。換句話說(shuō),Person.prototype是Object.prototype的一個(gè)實(shí)例。自身沒(méi)有的屬性,會(huì)去原型上查找,這個(gè)查找過(guò)程就是原型鏈查找,上述藍(lán)線就是一整條原型鏈。
驗(yàn)證

函數(shù)柯里化
柯里化是指的是將接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)的函數(shù),如果其他的參數(shù)是必要的,返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)。
example
核心是所有參數(shù)的保留和第一個(gè)參數(shù)與后續(xù)參數(shù)的拆分
function?add(...args)?{
????const?iterator?=?(...rest)?=>?{
????????args?=?[...rest,?...args];
????????return?iterator;
????};
????iterator.toString?=?()?=>?args.reduce((a,?b)?=>?a?+?b);
????return?iterator;
}
//?注意觀察調(diào)用方式的區(qū)別
add(1,?2)(3)(4);//10
add(1)(2)(3)(4);//10
add(1,2,3,4);//10
call/apply實(shí)現(xiàn)
在實(shí)際開(kāi)發(fā)中,call,和apply常用來(lái)改變this指向,這里簡(jiǎn)單模擬一下它們的內(nèi)部實(shí)現(xiàn)
example
//先來(lái)個(gè)簡(jiǎn)單的例子感受一下
var?stu?=?{
??name:"tom",
??age:?18,
};
function?say()?{
??console.log(`my?name?is?${this.name},${this.age}?years?old`);
}
say.call(stu);?//?my?name?is?tom,18?years?old
功能點(diǎn):
say函數(shù)中的this指向由window變?yōu)閟tu
say函數(shù)依然執(zhí)行了
why?
仔細(xì)看看上述的兩個(gè)功能點(diǎn)是如何實(shí)現(xiàn)的?其實(shí)很簡(jiǎn)單,只要
把say函數(shù)作為對(duì)象stu的一個(gè)內(nèi)置方法即可
//改寫后如下
var?stu?=?{
??name:?"tom",
??age:?18,
??say()?{
????console.log(`my?name?is?${this.name},${this.age}?years?old`);
??}
};
//調(diào)用
stu.say()?//my?name?is?tom,18?years?old
這樣并沒(méi)有結(jié)束,stu多了一個(gè)冗余方法,顯然不合適,應(yīng)該刪除掉
call模擬實(shí)現(xiàn)
??//call_v1
??Function.prototype.call?=?function?(context)?{
????//這里的context相當(dāng)于上述stu
????//這里的this指向目標(biāo)調(diào)用函數(shù),相當(dāng)于上述say
????//這里可以理解為為stu添加say方法
????context.fn?=?this;
????context.fn()//相當(dāng)于執(zhí)行stu.say()
????delete?context.fn//刪除冗余方法,相當(dāng)于delete?stu.say
??}
??//call_v2
??//下面加入傳參處理,默認(rèn)上下文處理和返回值處理
Function.prototype.call?=?function?(context?=?window,?...args)?{
??//context=window?設(shè)置默認(rèn)上下文為window
??context.fn?=?this;
??let?result?=?context.fn(...args)//處理傳參
??delete?context.fn
??return?result?//返回值
}
apply模擬實(shí)現(xiàn)
這個(gè)和call基本一樣,區(qū)別在于傳參是數(shù)組形式
Function.prototype.apply=?function?(context?=?window,?args=[])?{
??//context=window?設(shè)置默認(rèn)上下文為window
??context.fn?=?this;
??let?result?=?context.fn(...args)//處理傳參
??delete?context.fn
??return?result?//返回值
}
bind模擬實(shí)現(xiàn)
bind的作用和call/apply類似,都可以改變this指向,不同的是,bind執(zhí)行后返回的是一個(gè)函數(shù),并不會(huì)自己調(diào)用。mdn上這樣解釋:bind()方法創(chuàng)建一個(gè)新的函數(shù),在bind()被調(diào)用時(shí),這個(gè)新函數(shù)的this被bind的第一個(gè)參數(shù)指定,其余的參數(shù)將作為新函數(shù)的參數(shù)供調(diào)用時(shí)使用。
example
var?stu?=?{
??name:"tom",
??age:?18,
};
function?say()?{
??console.log(`my?name?is?${this.name},${this.age}?years?old`);
}
var?bindSay=say.bind(stu);?
bindSay();//?my?name?is?tom,18?years?old
bind模擬實(shí)現(xiàn)
/**
?*?改變this指向
?*?返回一個(gè)函數(shù)
?*?可傳參
?*?柯里化
?*/
Function.prototype.bind?=?function?(context,?...args)?{
??if?(typeof?this?!==?"function")?{
????throw?new?Error("Function.prototype.bind?-?what?is?trying?to?be?bound?is?not?callable");
??}
??let?self?=?this
??let?MiddleFn?=?function?()?{?}
??let?BindFn?=?function?(...rest)?{
????return?self.apply(this?instanceof?BindFn???this?:?context,?args.concat(rest));
??}
??MiddleFn.prototype?=?this.prototype
??BindFn.prototype?=?new?MiddleFn()
??return?BindFn;
}
最優(yōu)繼承
function?Person(name)?{
????this.name?=?name;
}
Person.prototype.say=function(){
????console.log(`My?name?is?${this.name}`)
}
function?Student(name)?{
????Person.call(this,?name)
}
//?Object.create方法創(chuàng)建一個(gè)新對(duì)象,使用現(xiàn)有的對(duì)象來(lái)提供新創(chuàng)建的對(duì)象的__proto__
Student.prototype=Object.create(Person.prototype);
//?修正構(gòu)造函數(shù)指向?Person=>Student
Student.prototype.constructor=Student;
var?stu=new?Student("tom");
stu.say()//My?name?is?tom
instanceof實(shí)現(xiàn)
typeof 可用于判斷基本類型值,instanceof操作符用于判斷具體的引用類型
function?_instanceof(left,?right)?{
????//如果是基本數(shù)據(jù)類型,返回false??
????//?typeof?null?是object?,要處理這種情況?
????if?(typeof?(left)?!==?"object"?||?left?==?undefined)?{
????????return?false;
????}
????//?獲取原型對(duì)象
????//?舉個(gè)例子:Object.getPrototypeOf([])===[].__proto__??true
????let?prototype?=?Object.getPrototypeOf(left);
????while?(true)?{
????????//一直按著原型鏈查找??找到頂層還找不到,返回null
????????if?(prototype?===?null)?{
????????????return?false;
????????}
????????//?如果left?right?原型對(duì)象一樣?返回true
????????if?(prototype?==?right.prototype)?{
????????????return?true;
????????}
????????//?不滿足條件的??繼續(xù)查找
????????//一直到???Object.getPrototypeOf(Object.getPrototypeOf({}))?null
????????prototype?=?Object.getPrototypeOf(prototype);
????}
}
