導(dǎo)航
-
2021/07/21更新
-
2021/07/22更新
[深入01] 執(zhí)行上下文
[深入02] 原型鏈
[深入03] 繼承
[深入04] 事件循環(huán)
[深入05] 柯里化 偏函數(shù) 函數(shù)記憶
[深入06] 隱式轉(zhuǎn)換 和 運(yùn)算符
[深入07] 瀏覽器緩存機(jī)制(http緩存機(jī)制)
[深入08] 前端安全
[深入09] 深淺拷貝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模塊化
[深入13] 觀察者模式 發(fā)布訂閱模式 雙向數(shù)據(jù)綁定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手寫Promise
[深入20] 手寫函數(shù)
[部署01] Nginx
[部署02] Docker 部署vue項(xiàng)目
[部署03] gitlab-CI
[源碼-webpack01-前置知識(shí)] AST抽象語法樹
[源碼-webpack02-前置知識(shí)] Tapable
[源碼-webpack03] 手寫webpack - compiler簡單編譯流程
[源碼] Redux React-Redux01
[源碼] axios
[源碼] vuex
[源碼-vue01] data響應(yīng)式 和 初始化渲染
構(gòu)造函數(shù)的缺點(diǎn)
- 通過構(gòu)造函數(shù)生成實(shí)例對象非剃,屬性和方法都生成在實(shí)例上压真,多個(gè)實(shí)例之間屬性和方法不能共享
prototype屬性
- javaScript的繼承機(jī)制的設(shè)計(jì)思想:原型對象的所有屬性和方法都能被實(shí)例對象所共享
- 所有的函數(shù)都有一個(gè)prototype屬性,指向一個(gè)對象
- 對于構(gòu)造函數(shù)來說:在構(gòu)造函數(shù)生成實(shí)例的時(shí)候维雇,構(gòu)造函數(shù)的prototype屬性會(huì)成為實(shí)例對象的原型
原型對象
- 原型對象上的屬性不是實(shí)例對象自身的屬性,只要修改原型對象,變動(dòng)就會(huì)立刻反應(yīng)到所有實(shí)例對象上
- 如何實(shí)例對象和原型對象有同名的屬性和方法暂氯,則實(shí)例對象讀取該屬性時(shí),會(huì)讀取自身的屬性亮蛔,而不會(huì)讀取原型上的屬性
- 原型對象的作用:定義所有實(shí)例共享的屬性和方法
原型鏈
- js規(guī)定痴施,所有對象都有原型對象
- 所有對象都可以成為其他對象的原型,原型對象也是對象究流,也有自己的原型辣吃,形成一個(gè)鏈條
- 一層層上溯,最終都會(huì)上溯到Object.prototype芬探,即Object構(gòu)造函數(shù)的prototype屬性
即所有對象都繼承了 Object.prototype 對象上的屬性和方法神得,這就是所有對象都具有valueOf和toString的原因
Object.prototype的原型是null,null沒有任何屬性和方法偷仿,也沒有自己的原型哩簿,原型鏈終止
null是為了防止死鏈
覆蓋 overriding
- 讀取對象的屬性時(shí),自身和原型上有同名的屬性和方法酝静,優(yōu)先讀取自身屬性节榜,這叫做覆蓋
讀取對對象的屬性時(shí),自身沒有會(huì)到原型上找别智,原型沒有會(huì)到原型的原型上找宗苍,直到Object.prototype,還是沒有返回undefined
- 一級級向上薄榛,在整個(gè)原型鏈上尋找某個(gè)屬性讳窟,對性能是有影響的。
- overriding:覆蓋的意思
function A(){} //---------------------- 定義構(gòu)造函數(shù)A
A.prototype = new Array() // ---------- 將A.prototype指向?qū)嵗龜?shù)組敞恋,那么A的實(shí)例就能繼承數(shù)組原型鏈上的屬性和方法
A.prototype.constructor = A // -------- 修改prototype的同時(shí)丽啡,也要修改constructor防止意外
const a = new A()
a.push(1)
a instanceof Array // true
constructor
- prototype對象有一個(gè)constructor屬性,默認(rèn)指向prototype屬性所在的構(gòu)造函數(shù)
因?yàn)閏onstructor屬性在prototype對象上耳舅,所以constructor屬性被所有實(shí)例繼承
- 作用:
- (1) constructor的作用是碌上,可以得知一個(gè)實(shí)例對象,到底是由哪個(gè)構(gòu)造函數(shù)產(chǎn)生的
- (2) constructor的另一個(gè)作用:可以用一個(gè)實(shí)例新建另一個(gè)實(shí)例
- 注意:<font color=red>constructor表示原型對象和構(gòu)造函數(shù)之間的關(guān)連關(guān)系浦徊,如果修改原型對象馏予,一般會(huì)同時(shí)修改constructor屬性,防止引用的時(shí)候報(bào)錯(cuò)盔性,=> 因?yàn)樾薷牧藀rototype之后霞丧,A.prototype.constructor指向的已經(jīng)不是A了,而是最新賦值給prototype對象的對象指向的那個(gè)constructor</font>
- 修改原型對象要一同修改構(gòu)造函數(shù)冕香,防止引用報(bào)錯(cuò)S汲ⅰ:笤ァ!M荒恰4炷稹!愕难!
- name屬性
- constructor.name => 構(gòu)造函數(shù)名
<script>
function A(){}
const a = new A()
console.log(A.prototype.constructor === A) // true
console.log(a.constructor === A.prototype.constructor) // true早龟,constructor屬性在prototype對象上,所以能被實(shí)例繼承并訪問
console.log(a.constructor === A) // true猫缭,constructor的作用是可以確定實(shí)例由哪個(gè)構(gòu)造函數(shù)產(chǎn)生
console.log(a.constructor === RegExp) // false
console.log(a.hasOwnProperty('constructor'), "a.hasOwnProperty('constructor')") // constructor不是實(shí)例自身屬性葱弟,繼承的
console.log(new a.constructor(), 'constructor的另一個(gè)作用: 一個(gè)實(shí)例可以借助constructor新建另一個(gè)實(shí)例')
const b = new a.constructor()
console.log( b instanceof A) // true , 即b是A的其中一個(gè)實(shí)例
</script>
constructor
constructor表示原型對象和構(gòu)造函數(shù)之間的關(guān)聯(lián)關(guān)系猜丹,修改prototype芝加,需要同時(shí)修改constructor,防止引用出錯(cuò)
<script>
function A () {
console.log('A構(gòu)造函數(shù)')
}
console.log(A.prototype, '修改之前的A.prototype')
console.log(A.prototype.constructor, '修改之前的A.prototype.constructor') // A
console.log(A.prototype.constructor.name, '修改之前的A.prototype.constructor.name => 修改前的constrctor名稱')
A.prototype = {name: 'woow_wu7'}
console.log(A.prototype.constructor, '修改之后的A.prototype.constructor') // Object
console.log(A.prototype.constructor.name, '修改之后的A.prototype.constructor.name => 修改后的constrctor名稱')
A.prototype.constructor = A
console.log(A.prototype.constructor, '修改prototype后射窒,要重新指定A.prototype.constructor=A,防止引用出錯(cuò)')
</script>
instanceof
- 返回一個(gè)布爾值藏杖,表示對象是否為某個(gè)構(gòu)造函數(shù)的實(shí)例
- instanceof左邊是實(shí)例對象,右邊是構(gòu)造函數(shù)
- <font color=red> instanceof會(huì)檢查右邊構(gòu)造函數(shù)的原型對象prototype轮洋,是否在左邊對象的原型鏈上</font>
- <font color=red> 由于instanceof檢查整個(gè)原型鏈制市,因此同一個(gè)實(shí)例對象,可能對多個(gè)構(gòu)造函數(shù)返回true</font>
- 有一個(gè)特殊情況:如果左邊對象的原型鏈上只有null(即左邊對象的原型對象是null)弊予,這時(shí)instanceof判斷就會(huì)失真
- instanceof的一個(gè)用處就是判斷值類型,(但是只能用于判斷對象开财,不能用于判斷原始類型的值)
- 對于undefined和null汉柒,instanceOf運(yùn)算符總是返回false
- <font color=red>利用instanceof可以巧妙的解決,調(diào)用構(gòu)造函數(shù)時(shí)忘記加new命令的問題</font>
instanceof
function A(){}
const a = new A()
console.log(a instanceof A, 'instanceof的原理是檢查右邊構(gòu)造函數(shù)的原型對象是否在左邊對象的原型鏈上')
console.log(A.prototype.isPrototypeOf(a), 'instanceof等價(jià)于這樣')
var d = new Date();
d instanceof Date
// true
// d既是Date的實(shí)例责鳍,也是Object的實(shí)例
d instanceof Object
// true
// 因?yàn)閕nstanceof檢查的是右邊構(gòu)造函數(shù)的實(shí)例碾褂,是否在左邊實(shí)例對象的原型鏈上,檢查整個(gè)原型鏈
// 所以同一個(gè)實(shí)例历葛,可能對多個(gè)構(gòu)造函數(shù)返回ture
var obj = Object.create(null); // 以null為原型對象創(chuàng)建實(shí)例
typeof obj // "object"
Object.create(null) instanceof Object // false
installof可以解決調(diào)用構(gòu)造函數(shù)時(shí)正塌,忘記加new命令的情況
function A() {
if(!(this instanceof A)) { // 如果this不是A的實(shí)例,說明不是new命令調(diào)用的恤溶,那么執(zhí)行new
return new A()
}
else {
this.name = 'woow_wu7'
}
}
const a = new A()
console.log(a)
Object.getPrototypeOf
- 返回參數(shù)對象的原型對象
- Object.getPrototypeOf 是獲取原型對象的標(biāo)準(zhǔn)方法
- 注意:<font color=red>參數(shù)是對象乓诽,也可以是函數(shù),因?yàn)楹瘮?shù)也是對象咒程,在es6中可以判斷類的繼承鸠天,Object.getPrototypeOf(ColorPoint) === Point
// true,ColorPoint是一個(gè)類帐姻,即是一個(gè)函數(shù)</font>
Object.getPrototypeOf
- 獲取參數(shù)對象的原型對象稠集,是獲取原型對象的標(biāo)準(zhǔn)方法
Object.getPrototypeOf(Object.prototype) === null // true
Object.getPrototypeOf(Function.prototype) === Object.prototype // true
Object.getPrototypeOf({}) === Object.prototype // true
Object.setPrototypeOf
- 將第一個(gè)參數(shù)對象的原型設(shè)置為第二個(gè)參數(shù)對象
Object.setPrototypeOf(現(xiàn)有對象奶段,原型對象)
- 返回值:返回第一個(gè)參數(shù)對象
- 參數(shù):第一個(gè)參數(shù)是現(xiàn)有對象,第二個(gè)參數(shù)是原型對象
const a = {}
const b = {name: 'woow_wu7'}
const res = Object.setPrototypeOf(a, b) // 將b設(shè)置成a的原型剥纷,注意返回值是a
Object.getPrototypeOf(a) === b // true
console.log(a.name)
console.log(res) // a, 返回值是a
- new命令可以用Object.setPrototypeOf來模擬
var F = function () {
this.foo = 'bar';
};
var f = new F();
// 等同于
var f = Object.setPrototypeOf({}, F.prototype);
// 返回值f是第一個(gè)參數(shù) {}
// Object.setPrototypeOf({}, F.prototype) => 相當(dāng)于 {}.__proto__ = F.prototype
F.call(f);
Object.create
- 生成實(shí)例對象
- 通過new命令執(zhí)行構(gòu)造函數(shù)的方式生成
(構(gòu)造函數(shù)其實(shí)就是普通的函數(shù)痹籍,只是用new命令調(diào)用時(shí),this指向了實(shí)例晦鞋,并且首字母大寫來區(qū)分蹲缠,不大寫也行,但約定俗成)
- 通過一個(gè)對象生成鳖宾,
有時(shí)候只能拿到一個(gè)對象吼砂,要生成實(shí)例對象。Object.create鼎文,該實(shí)例完全繼承原型對象的屬性和方法
- 通過new命令執(zhí)行構(gòu)造函數(shù)的方式生成
- <font color=red>Object.create() 以參數(shù)對象為原型返回實(shí)例對象渔肩,該實(shí)例完全繼承原型對象的屬性和方法</font>
手動(dòng)實(shí)現(xiàn)一個(gè) Object.create
Object._create = function(obj) {
function F(){}
F.prototype = obj // 新建一個(gè)構(gòu)造函數(shù),將構(gòu)造函數(shù)的prototype指向傳入的對象拇惋,執(zhí)行構(gòu)造函數(shù)周偎,即實(shí)例的原型指向了傳入的對象
return new F()
}
- 如果想要生成一個(gè)不繼承任何屬性和方法的對象,可以使用 Object.create(null)
- <font color=red>如果Object.create()的參數(shù)為空撑帖,或者不是對象就會(huì)報(bào)錯(cuò)</font>
Object.prototype.isPrototypeOf
- 實(shí)例對象的 isPrototypeOf 屬性用于判斷該對象是否是參數(shù)對象的原型
Object.prototype.__proto__
- 實(shí)例對象的原型對象
- 根據(jù)語言標(biāo)準(zhǔn)蓉坎,只有瀏覽器需要部署
__proto__
,其他環(huán)境中沒有__proto__
屬性 - 不建議使用
__proto__
- 而是用標(biāo)準(zhǔn)的
Object.getPrototypeOf讀取原型
胡嘿,Object.setPrototypeOf(現(xiàn)有對象蛉艾,原型對象)來設(shè)置原型
獲取原型對象的方法比較
- `obj.__proto__` // 只有瀏覽器才有,不建議使用
- obj.constructor.prototype // 手動(dòng)修改原型時(shí)衷敌,可能會(huì)失真
- Object.getPrototypeOf() // 推薦的獲取方法
直接修改原型對象時(shí)勿侯,需要同時(shí)修改constructor防止失真
function A(){}
const a = new A()
function B() {}
B.prototype = a
const b = new B()
b.constructor.prototype === a // false
// 因?yàn)椋篵.constructor === a.constructor === A.prototype.constructor === A
// 所以:b.constructor.prototype === A.prototype
// 結(jié)論:當(dāng)直接修改prototype屬性時(shí),一定要修改constructor屬性 =陕蕖V觥!C婷ァ1ァ!I嘟纭>蚱!Y骱帷Fㄒ!!D鸺8纯鳌!g缘铡5抻(重要)
// 如果是下面這樣則:
function A(){}
const a = new A()
function B() {}
B.prototype = a
B.prototype.constructor = B // 修改了prototype,同時(shí)修改constructor則引用不會(huì)出錯(cuò)
const b = new B()
b.constructor.prototype === a // true
Object.prototype.hasOwnProperty
- 返回一個(gè)布爾值妇蛀,表示是否是對象自身的屬性耕突,不包含原型鏈上的屬性
Date.hasOwnProperty('length') // true
Date.hasOwnProperty('toString') // false
in運(yùn)算符
- in運(yùn)算符返回一個(gè)布爾值,表示屬性在對象中是否存在评架,不區(qū)分自身屬性還是繼承的屬性
- 注意:<font color=red>in 運(yùn)算符不區(qū)分自身屬性和繼承屬性</font>
例子:
function X(){}
X.prototype.name = 'woow_wu7';
let x = new X()
'name' in X // true
// 因?yàn)椋篿n 運(yùn)算符返回一個(gè)布爾值眷茁,表示屬性是否在對象中存在,不區(qū)分自身還是繼承
// 所以:'name' in X => 返回 true
for in 和 for of
- for in 可以遍歷對象和數(shù)組
- for of 只能遍歷數(shù)組
for in
- 用于數(shù)組纵诞,i表示:key
- 用于對象上祈,i表示:key
- 用于對象時(shí),for...in會(huì)遍歷自身屬性和繼承的屬性浙芙,
for of
- 用于數(shù)組:i表示 value
const objP = {sex: 'man'}
const obj = {name: 'woow_wu7', age: 20, address: 'hangzhou'};
Object.setPrototypeOf(obj, objP)
for(let i in obj) {
console.log(i, 'for in 循環(huán) => 用于對象登刺,會(huì)遍歷自身和繼承的屬性') // name,age,address,sex
if (obj.hasOwnProperty(i)) {
console.log(i, '如果只希望遍歷自身屬性,可以用Object.prototype.hanOwnProperty(屬性名)來過濾')// name,age,address
}
}
易錯(cuò)點(diǎn)總結(jié)
- constructor
- constructor表示構(gòu)造函數(shù)和原型對象之間的關(guān)聯(lián)關(guān)系嗡呼,如果修改了原型對象纸俭,需要一起修改構(gòu)造函數(shù),防止引用出錯(cuò)南窗。
-(每一個(gè)構(gòu)造函數(shù)都有一個(gè)prototype屬性揍很,prototype的constructor指向prototype所在的構(gòu)造函數(shù))
- instanceof
- 原理:instanceof是檢查(右邊構(gòu)造函數(shù)的prototype屬性)是否在(左邊對象)的原型鏈上
- instanceof失效的情況:
- 如果一個(gè)對象的__proto__屬性指向null,則instanceof就會(huì)失效
- 因?yàn)橛疫吺菢?gòu)造函數(shù)的prototype => 終點(diǎn)是Object.prototype,是否在左邊對象的原型鏈上
- Object.prototype.__prototo__ === null
- Object.prototype instanceof Ojbect // false
// Object.create(null) instanceof Object // false万伤,因?yàn)閯?chuàng)建的實(shí)例沒有任何屬性和方法女轿,也沒有原型
2021/07/24更新
- null沒有任何屬性和方法
- 如何生成一個(gè)沒有任何屬性和方法的對象
- Object.create(null)
- 如何模擬一個(gè)Object.create
-
修改prototype屬性時(shí),一定要同時(shí)修改constructor屬性壕翩,防止引用出錯(cuò),不然會(huì)指向被賦值對象的構(gòu)造函數(shù)的prototype上的constructor