如何写一个作用域安全的构造函数
基礎(chǔ)部分
構(gòu)造函數(shù)本質(zhì)上就是一個(gè)使用new操作符調(diào)用的函數(shù),使用new調(diào)用時(shí),構(gòu)造函數(shù)內(nèi)用到的this對(duì)象會(huì)指向新創(chuàng)建的對(duì)象實(shí)例:
function Girlfriend(name, age, height) {this.name = name;this.age = age;this.height = height; }// 使用new操作符來(lái)分配這些屬性 var girlfriend = new Girlfriend("Ying", 23, 170);平時(shí)寫(xiě)變量時(shí),如果因?yàn)槭д`忘記使用new操作符時(shí),會(huì)造成一個(gè)糟糕的影響————因?yàn)閠his對(duì)象是在運(yùn)行時(shí)綁定的,直接調(diào)用構(gòu)造函數(shù),this會(huì)映射到全局對(duì)象window上,導(dǎo)致錯(cuò)誤對(duì)象屬性的增加,增添不必要的變量到全局對(duì)象中:
var girlfriend = new Girlfriend("Ying", 23, 170); console.log(window.name); // "Ying" console.log(window.age); // 23 console.log(window.height); // 170特別的,當(dāng)你自己構(gòu)造函數(shù)內(nèi)的某些變量名與window變量名重名時(shí)(像一些常用變量名name、length等),對(duì)這些屬性的偶然覆蓋就很可能導(dǎo)致其他地方出錯(cuò),并且這個(gè)bug還相當(dāng)難找!
在這種情況下構(gòu)造一個(gè)作用域安全的構(gòu)造函數(shù)就顯得很有必要:
function Girlfriend(name, age, height) {// 首先確認(rèn)this對(duì)象是正確類型的實(shí)例,// 如果不是就創(chuàng)建新的實(shí)例并返回if (this instanceof Girlfriend) { // 添加一個(gè)檢查console.log('created');this.name = name;this.age = age;this.height = height;} else {console.log('new');return new Girfriend(name, age, height);} }var girlfriend1 = Girlfriend("Ying", 23, 170); // "new" "created" console.log(window.name); // "" console.log(girfriend1.name); // "Ying"var girlfriend2 = new Girlfriend("Lin", 22, 165); // "created" console.log(girfriend1.name); // "Lin"girlfriend1背后構(gòu)造函數(shù)先new了一個(gè)實(shí)例并返回實(shí)例(打印“new”),再對(duì)實(shí)例進(jìn)行賦值(打印“created”)。
girlfriend2自己就先new了一個(gè)實(shí)例,直接對(duì)該實(shí)例進(jìn)行賦值(只打印“created”)。
這樣在任何情況下就都可以返回一個(gè)安全作用域的實(shí)例了。
進(jìn)階部分
使用上面添加一個(gè)檢查的方法可以創(chuàng)建一個(gè)作用域安全的構(gòu)造函數(shù),但如果有的函數(shù)竊取該函數(shù)的繼承且沒(méi)有使用原型鏈,那這個(gè)繼承將被破壞不生效:
function Bmi(sex, weight=1, height=1) { // ES6開(kāi)始支持的默認(rèn)值if (this instanceof Bmi) {this.sex = sex;this.weight = weight;this.height = height;this.getBmi = function() {return this.weight / (this.height ** 2);};} else {return new Bmi(sex);} }function People(height, weight) {Bmi.call(this, 'male');this.height = height;this.weight = weight; }var guy = new People(1.75, 68); // 單位是m和kg console.log(guy.sex) // undefinedBmi構(gòu)造函數(shù)作用域是安全的,但People并不是。新創(chuàng)建一個(gè)People實(shí)例后,這個(gè)實(shí)例準(zhǔn)備通過(guò)Bmi.call()來(lái)繼承Bmi的sex屬性,但由于Bmi的作用域是安全的,this對(duì)象并非是Bmi的實(shí)例,所以Bmi會(huì)先自己創(chuàng)建一個(gè)新的Bmi對(duì)象,不會(huì)把新的Bmi對(duì)象的值傳遞到People中去。
這樣People中的this對(duì)象并沒(méi)有得到增長(zhǎng),同時(shí)Bmi.call()返回的值也沒(méi)有用到,所以People實(shí)例中就不會(huì)有sex、weight、height屬性和getBmi()函數(shù)。
解決辦法: 構(gòu)造函數(shù)結(jié)合使用原型鏈或寄生組合:
function Bmi(sex, weight=1, height=1) {if (this instanceof Bmi) {this.sex = sex;this.weight = weight;this.height = height;this.getBmi = function() {return this.weight / (this.height ** 2);};} else {return new Bmi(sex);} }function People(height, weight) {Bmi.call(this, 'male');this.height = height;this.weight = weight; }People.prototype = new Bmi(); // 重點(diǎn)var guy = new People(1.75, 68); console.log(guy.sex) // "male"這樣寫(xiě)的話,一個(gè)People的實(shí)例同時(shí)也是一個(gè)Bmi的實(shí)例,所以Bmi.call()才會(huì)去執(zhí)行,為People實(shí)例添加上屬性和函數(shù)。
總結(jié)
當(dāng)多個(gè)人一同構(gòu)建一個(gè)項(xiàng)目時(shí),作用域構(gòu)安全函數(shù)就非常必要,對(duì)全局對(duì)象意外的更改可能就會(huì)導(dǎo)致一些常常難以追蹤的錯(cuò)誤,這和平常設(shè)置空變量和空函數(shù)一樣避免因?yàn)槠渌丝赡馨l(fā)生的錯(cuò)誤而阻塞程序執(zhí)行。
更多專業(yè)前端知識(shí),請(qǐng)上 【猿2048】www.mk2048.com
總結(jié)
以上是生活随笔為你收集整理的如何写一个作用域安全的构造函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: innerHTML的用法
- 下一篇: http协议以及防盗链技术