1璧亮、JavaScript初識(shí)
1. 說幾條 JavaScript 的基本規(guī)范?
(1)一個(gè)函數(shù)作用域中所有的變量聲明應(yīng)該盡量提到函數(shù)首部蒸健,用一個(gè) var 聲明衡创,不允許出現(xiàn)兩個(gè)連續(xù)的 var 聲明,聲明時(shí) 如果變量沒有值峡蟋,應(yīng)該給該變量賦值對(duì)應(yīng)類型的初始值坟桅,便于他人閱讀代碼時(shí),能夠一目了然的知道變量對(duì)應(yīng)的類型值蕊蝗。
(2)代碼中出現(xiàn)地址仅乓、時(shí)間等字符串時(shí)需要使用常量代替。
(3)在進(jìn)行比較的時(shí)候吧蓬戚,盡量使用'===', '!=='代替'==', '!='夸楣。
(4)不要在內(nèi)置對(duì)象的原型上添加方法,如 Array, Date。
(5)switch 語句必須帶有 default 分支豫喧。
(6)for 循環(huán)必須使用大括號(hào)石洗。
(7)if 語句必須使用大括號(hào)。
2. JavaScript 代碼中的 “use strict” 是什么意思紧显?
use strict是一種ECMAscript 5 添加的(嚴(yán)格)運(yùn)行模式,這種模式使得 Javascript 在更嚴(yán)格的條件下運(yùn)行,
使JS編碼更加規(guī)范化的模式,消除Javascript語法的一些不合理讲衫、不嚴(yán)謹(jǐn)之處,減少一些怪異行為鸟妙。
默認(rèn)支持的糟糕特性都會(huì)被禁用焦人,比如不能用with,也不能在意外的情況下給全局變量賦值;
全局變量的顯示聲明,函數(shù)必須聲明在頂層重父,不允許在非函數(shù)代碼塊內(nèi)聲明函數(shù),arguments.callee也不允許使用花椭;
消除代碼運(yùn)行的一些不安全之處,保證代碼運(yùn)行的安全,限制函數(shù)中的arguments修改房午,嚴(yán)格模式下的eval函數(shù)的行為和非嚴(yán)格模式的也不相同;
3. 說說嚴(yán)格模式的限制矿辽?
1,變量必須聲明后再使用
2郭厌,函數(shù)的參數(shù)不能有同名屬性袋倔,否則報(bào)錯(cuò)
3,不能使用with語句
4折柠,不能對(duì)只讀屬性賦值宾娜,否則報(bào)錯(cuò)
5,不能使用前綴0表示八進(jìn)制數(shù)扇售,否則報(bào)錯(cuò)
6前塔,不能刪除變量delete prop,會(huì)報(bào)錯(cuò)承冰,只能刪除屬性delete global[prop]
7华弓,eval不會(huì)在它的外層作用域引入變量
8,eval和arguments不能被重新賦值
9困乒,arguments不會(huì)自動(dòng)反映函數(shù)參數(shù)的變化
10寂屏,不能使用arguments.callee
11,禁止this指向全局對(duì)象
12娜搂,不能使用fn.caller和fn.arguments獲取函數(shù)調(diào)用的堆棧
13迁霎,增加了保留字(比如protected、static和interface)
參考文件:https://github.com/ecomfe/spec/blob/master/javascript-style-guide.md
2百宇、運(yùn)算符欧引、運(yùn)算符優(yōu)先級(jí)
1. NaN 是什么?有什么特別之處恳谎?
NaN 屬性是代表非數(shù)字值的特殊值芝此。該屬性用于指示某個(gè)值不是數(shù)字憋肖。可以把 Number 對(duì)象設(shè)置為該值婚苹,來指示其不是數(shù)字值岸更。
提示:請(qǐng)使用 isNaN() 全局函數(shù)來判斷一個(gè)值是否是 NaN 值。
Number.NaN 是一個(gè)特殊值膊升,說明某些算術(shù)運(yùn)算(如求負(fù)數(shù)的平方根)的結(jié)果不是數(shù)字怎炊。方法 parseInt() 和 parseFloat() 在不能解析指定的字符串時(shí)就返回這個(gè)值。對(duì)于一些常規(guī)情況下返回有效數(shù)字的函數(shù)廓译,也可以采用這種方法评肆,用Number.NaN 說明它的錯(cuò)誤情況。
JavaScript 以 NaN 的形式輸出 Number.NaN非区。請(qǐng)注意瓜挽,NaN 與其他數(shù)值進(jìn)行比較的結(jié)果總是不相等的,包括它自身在內(nèi)征绸。因此久橙,不能與 Number.NaN 比較來檢測(cè)一個(gè)值是不是數(shù)字,而只能調(diào)用 isNaN() 來比較管怠。
2. == 與 === 有什么區(qū)別淆衷?
==:運(yùn)算符稱作相等,用來檢測(cè)兩個(gè)操作數(shù)是否相等渤弛,這里的相等定義的非常寬松祝拯,可以允許進(jìn)行類型轉(zhuǎn)換
===:用來檢測(cè)兩個(gè)操作數(shù)是否嚴(yán)格相等
1、對(duì)于string,number等基礎(chǔ)類型她肯,==和===是有區(qū)別的
不同類型間比較佳头,==之比較“轉(zhuǎn)化成同一類型后的值”看“值”是否相等,===如果類型不同辕宏,其結(jié)果就是不等
同類型比較畜晰,直接進(jìn)行“值”比較砾莱,兩者結(jié)果一樣
2瑞筐、對(duì)于Array,Object等高級(jí)類型,==和===是沒有區(qū)別的
3腊瑟、基礎(chǔ)類型與高級(jí)類型聚假,==和===是有區(qū)別的
對(duì)于==,將高級(jí)轉(zhuǎn)化為基礎(chǔ)類型闰非,進(jìn)行“值”比較膘格,因?yàn)轭愋筒煌?==結(jié)果為false
3. console.log(1+"2") 和 console.log(1-"2") 的打印結(jié)果?
// 12 和 1-2
4. 為什么 console.log(0.2+0.1==0.3) 輸出 false 财松?
在JavaScript中的二進(jìn)制的浮點(diǎn)數(shù)0.2和0.6并不是十分精確瘪贱,在他們相加的結(jié)果并非正好等于0.6纱控,而是一個(gè)比較接近的數(shù)字 0.6000000000000001,所以條件判斷結(jié)果為 false菜秦。
那么應(yīng)該怎樣來解決0.2+0.4等于0.6呢? 最好的方法是設(shè)置一個(gè)誤差范圍值甜害,通常稱為”機(jī)器精度“,而對(duì)于Javascript來說球昨,這個(gè)值通常是2^-52,而在ES6中尔店,已經(jīng)為我們提供了這樣一個(gè)
屬性:Number.EPSILON,而這個(gè)值正等于2^-52主慰。這個(gè)值非常非常小嚣州,在底層計(jì)算機(jī)已經(jīng)幫我們運(yùn)算好,并且無限接近0共螺,但不等于0,该肴。這個(gè)時(shí)候我們只要判斷(0.2+0.4)-0.6小于Number.EPSILON,在這個(gè)誤差的范圍內(nèi)就可以判定0.2+0.4===0.6為true璃谨。
5. 請(qǐng)用三元運(yùn)算符(問號(hào)冒號(hào)表達(dá)式)改寫以下代碼:
? ? if(a > 10) {
? ? ? b = a
? ? }else {
? ? ? b = a - 2
? ? }
//b = a > 10? ?? a? :? a-2;
6. 以下代碼輸出的結(jié)果是沙庐?
? ? var a = 1;?
? ? a+++a;? //3
? ? typeof a+2; //number2
7. 以下代碼輸出什么?
? ? var d = a = 3, b = 4
? ? console.log(d) //3
8. 以下代碼輸出什么佳吞?
? ? var d = (a = 3, b = 4)
? ? console.log(d) //4
9. 以下代碼輸出結(jié)果是拱雏?為什么?
? ? var a = 1, b = 2, c = 3;
? ? var val = typeof a + b || c >0? //typeof a? 輸出值為number 底扳,
? ? console.log(val) //number2
? ? var d = 5;
? ? var data = d ==5 && console.log('bb') // bb
? ? console.log(data) //undefined
分析:1.因?yàn)榘凑者\(yùn)算符優(yōu)先級(jí)铸抑,先算&&,
2.然后算 d==5是true
3.然后就繼續(xù) 執(zhí)行右側(cè) log
4.然后返回右側(cè) log 方法的return返回值
5.因?yàn)?log 沒有返回值,所以使用函數(shù)默認(rèn)的衷模,默認(rèn)的是 undefine所以data是 undefined鹊汛。
? ? var data2 = d = 0 || console.log('haha') //haha
? ? console.log(data2) // undefined
解析:var data2 = d = 0 || console.log('haha')等同于var data2 = (d = 0) || [console.log('haha')],先給d賦值為0阱冶,進(jìn)行判斷時(shí)結(jié)果為false刁憋,所以要繼續(xù)進(jìn)行后面的判斷,后面的判斷同上題
? ? var x = !!"Hello" + (!"world", !!"from here!!"); // true+true
? ? console.log(x) //2
解析:var x = !!"Hello" + (!"world", !!"from here!!");等同于var x = 1 + (0, 1);木蹬,進(jìn)行加法時(shí)會(huì)選擇最后一個(gè)數(shù)字進(jìn)行相加
10. 以下代碼輸出結(jié)果是至耻?為什么?
? ? var a = 1;
? ? var b = 3;
? ? console.log( a+++b );? //4
解析:a+++b等同于(a++)+b镊叁,在最終結(jié)果計(jì)算完之后再將a+1
11. 以下代碼輸出的結(jié)果是尘颓?為什么?
? ? console.log(1+1); //2
? ? console.log("2"+"4"); //24
? ? console.log(2+"4"); // 24
? ? console.log(+"4"); 4
3 晦譬、變量疤苹、值、數(shù)據(jù)類型敛腌、數(shù)據(jù)類型轉(zhuǎn)換
1. JavaScript 定義了幾種數(shù)據(jù)類型卧土?哪些是原始類型惫皱?哪些是復(fù)雜類型?null 是對(duì)象嗎尤莺?
原始數(shù)據(jù)類型 (不是對(duì)象且沒有方法):Boolean逸吵、Null、Undefined缝裁、Number扫皱、String、Symbol(ES6 新增)捷绑、BigInt(ES10)
復(fù)雜數(shù)據(jù)類型:Object
Null 類型只有一個(gè)值"null"韩脑。
null 值表示一個(gè)空對(duì)象指針,所以使用typeof會(huì)返回"object";
undefined值是派生于null的粹污,因此會(huì)有null == undefined //true段多。
null轉(zhuǎn)為數(shù)字時(shí),自動(dòng)變成0壮吩。
null表示空值进苍,即該處的值現(xiàn)在為空。調(diào)用函數(shù)時(shí)鸭叙,某個(gè)參數(shù)未設(shè)置任何值觉啊,這時(shí)就可以傳入null,表示該參數(shù)為空沈贝。
無論在何種情況下都沒必要把一個(gè)變量顯式地設(shè)置為undefined杠人,但是只要意在保存對(duì)象的變量還沒有真正保存對(duì)象,就應(yīng)該明確地讓該變量保存null值宋下。
null 有時(shí)會(huì)被當(dāng)作一種對(duì)象類型嗡善,但是這其實(shí)只是語言本身的一個(gè) bug,即對(duì) null 執(zhí)行typeof null 時(shí)會(huì)返回字符串 "object"学歧。實(shí)際上罩引,null 本身是基本類型
2. 對(duì)象類型和原始類型的不同之處?函數(shù)參數(shù)是對(duì)象會(huì)發(fā)生什么問題枝笨?
在 JS 中袁铐,除了原始類型那么其他的都是對(duì)象類型了。對(duì)象類型和原始類型不同的是伺帘,原始類型存儲(chǔ)的是值昭躺,對(duì)象類型存儲(chǔ)的是地址(指針)忌锯。當(dāng)你創(chuàng)建了一個(gè)對(duì)象類型的時(shí)候伪嫁,計(jì)算機(jī)會(huì)在內(nèi)存中幫我們開辟一個(gè)空間來存放值,但是我們需要找到這個(gè)空間偶垮,這個(gè)空間會(huì)擁有一個(gè)地址(指針)张咳。
const a=[]對(duì)于常量a來說帝洪,假設(shè)內(nèi)存地址(指針)為#001,那么在地址#001的位置存放了值[]脚猾,常量a存放了地址(指針)#001葱峡,再看以下代碼
const a=[] ; const b= ab.push(1);
當(dāng)我們將變量賦值給另外一個(gè)變量時(shí),復(fù)制的是原本變量的地址(指針)龙助,也就是說當(dāng)前變量b存放的地址(指針)也是#001砰奕,當(dāng)我們進(jìn)行數(shù)據(jù)修改的時(shí)候程剥,就會(huì)修改存放在地址(指針)#001上的值铅匹,也就導(dǎo)致了兩個(gè)變量的值都發(fā)生了改變是整。
function test(person) {
? person.age = 26
? person = {
? ? name: 'yyy',
? ? age: 30
? }
? return person
}
const p1 = {
? name: 'yck',
? age: 25
}
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?
對(duì)于以上代碼似芝,你是否能正確的寫出結(jié)果呢扳还?接下來讓我為了解析一番:
? 首先耀鸦,函數(shù)傳參是傳遞對(duì)象指針的副本
? 到函數(shù)內(nèi)部修改參數(shù)的屬性這步材蹬,我相信大家都知道赴魁,當(dāng)前p1的值也被修改了
? 但是當(dāng)我們重新為了person分配了一個(gè)對(duì)象時(shí)就出現(xiàn)了分歧赡鲜,請(qǐng)看下圖
所以最后person擁有了一個(gè)新的地址(指針)空厌,也就和p1沒有任何關(guān)系了,導(dǎo)致了最終兩個(gè)變量的值是不相同的银酬。
3. 怎樣判斷“值”屬于哪種類型嘲更?typeof 是否能正確判斷類型?instanceof 呢揩瞪?instanceof 有什么作用哮内??jī)?nèi)部邏輯是如何實(shí)現(xiàn)的?
1壮韭、值類型的類型判斷用typeof
// 值類型
console.log(typeof(x));? ? // undefined
console.log(typeof(10));?? // number
console.log(typeof('abc')); // string
console.log(typeof(true));? // boolean
//雖然function也是一個(gè)引用類型對(duì)象北发,但是可以通過typeof判斷:
var fn = function() {};
console.log(typeof fn); // function
如何判斷null類型?用 ===:
console.log(null === null); // true
不能用 == 喷屋,因?yàn)椋?/p>
console.log(null == undefined); // true
console.log(null === undefined); // false
2琳拨、引用類型的類型判斷用instanceof
//引用類型
console.log(new String('string') instanceof String); // true
console.log(new Number(10) instanceof Number); // true
console.log(new Boolean(true) instanceof Boolean); // true
console.log(new Array(3,4,5) instanceof Array); // true
console.log([] instanceof Array); // true
var fn = function() {}
console.log(fn instanceof Function); // true
3、Object.prototype.toString.call( ) 方法
Object屯曹。prototype.toString.call()的實(shí)現(xiàn)原理
首先typeof 能夠判斷基本數(shù)據(jù)類型狱庇,但是除了null,typeof null 返回的是object
但是對(duì)于對(duì)象來說typeof不能準(zhǔn)確判斷類型恶耽,typeof 函數(shù)會(huì)返回function密任,除此之外全部都是object,不能準(zhǔn)確判斷類型
instanceof可以判斷復(fù)雜數(shù)據(jù)類型偷俭,基本數(shù)據(jù)類型不可以
instanceof是通過原型鏈來判斷的 浪讳,A instanceof B,在A的原型鏈中層層查找涌萤,是否有原型等于B.prototype淹遵,如果一直找到A的原型鏈的頂端(null口猜,即Object.prototype._proto_),仍然不等于B透揣,那么返回false济炎,否則返回true
4. null,undefined 的區(qū)別辐真?
null表示"沒有對(duì)象"须尚,即該處不應(yīng)該有值。典型用法是:
(1) 作為函數(shù)的參數(shù)侍咱,表示該函數(shù)的參數(shù)不是對(duì)象恨闪。
(2) 作為對(duì)象原型鏈的終點(diǎn)。
Object.getPrototypeOf(Object.prototype)// null
undefined表示"缺少值"放坏,就是此處應(yīng)該有一個(gè)值咙咽,但是還沒有定義。典型用法是:
(1)變量被聲明了淤年,但沒有賦值時(shí)钧敞,就等于undefined。
(2) 調(diào)用函數(shù)時(shí)麸粮,應(yīng)該提供的參數(shù)沒有提供溉苛,該參數(shù)等于undefined。
(3)對(duì)象沒有賦值的屬性弄诲,該屬性的值為undefined愚战。
(4)函數(shù)沒有返回值時(shí),默認(rèn)返回undefined齐遵。
var i;
i // undefined
function f(x){console.log(x)}
f() // undefined
var? o = new Object();
o.p // undefined
var x = f();
x // undefined
5. 說一下 JS 中類型轉(zhuǎn)換的規(guī)則寂玲?
6. 以下代碼的輸出?為什么梗摇?
? ? console.log(a); //undefined
? ? var a = 1;
? ? console.log(b); //報(bào)異常 b is not defined
7. 以下代碼輸出什么拓哟?
? ? var a = typeof 3+4
? ? console.log(a) //number4
8. 以下代碼輸出什么?
? ? var a = typeof typeof 4+4
? ? console.log(a) //string4
分析:typeof 4 輸出為"number"伶授;typeof "number"輸出為"string"
4断序、流程控制語句
1. break 與 continue 有什么區(qū)別?
2. switch...case 語句中的 break 有什么作用糜烹?
3. for...of违诗、 for...in 和 forEach、map 的區(qū)別疮蹦?
4. 寫出如下知識(shí)點(diǎn)的代碼范例:
? ? ① if...else 的用法诸迟;
? ? ② switch...case 的用法;
? ? ③ while 的用法;
? ? ④ do...while 的用法亮蒋;
? ? ⑤ for 遍歷數(shù)組的用法;
? ? ⑥ for...in 遍歷對(duì)象的用法妆毕;
? ? ⑦ break 和 continue 的用法慎玖。
5. 以下代碼輸出什么?
? ? var a = 2
? ? if(a = 1) {
? ? ? console.log("a 等于 1")
? ? }else {
? ? ? console.log("a 不等于 1")
? ? }
輸出為:// a 等于 1
5笛粘、JS 函數(shù)
1. 寫一個(gè)函數(shù)趁怔,返回參數(shù)的平方和?
? ? function sumOfSquares() {
? ? ? // 補(bǔ)全
??????? let arr = Array.from(arguments)
???????? return arr.reduce((result,item)=>{
?????????????? return result += item*item
??????? },0)
? ? }
? ? var result = sumOfSquares(2, 3, 4)
? ? var result2 = sumOfSquares(1, 3)
? ? console.log(result) // 29
? ? console.log(result2) // 10
2. 如下代碼的輸出薪前?為什么润努?
? ? sayName("world");
? ? sayAge(10);
? ? function sayName(name) {
? ? ? console.log("hello ", name);
? ? }
? ? var sayAge = function(age) {
? ? ? console.log(age);
? ? };
// 先輸出:sayAge is not a function,然后再輸出 hello world
3. 如下代碼的輸出示括?為什么铺浇?
? ? var x = 10;
? ? bar()
? ? function bar() {
? ? ? var x = 30;
? ? ? function foo() {
? ? ? ? console.log(x)
? ? ? }
? ? ? foo();
? ? }
//輸出:30
4. 如下代碼的輸出?為什么垛膝?
? ? var x = 10
? ? bar()
? ? function foo() {
? ? ? console.log(x)
? ? }
? ? function bar() {
? ? ? var x = 30
? ? ? foo()
? ? }
輸出: 10
5. 如下代碼的輸出鳍侣?為什么?
? ? var a = 1
? ? function fn1() {
? ? ? function fn3() {
? ? ? ? function fn2() {
? ? ? ? ? console.log(a)
? ? ? ? }
? ? ? ? fn2()
? ? ? ? var a = 4
? ? ? }
? ? ? var a = 2
? ? ? return fn3
? ? }
? ? var fn = fn1()
? ? fn() // 吼拥?undefined
6. 如下代碼的輸出倚聚?為什么?
? ? var a = 1
? ? function fn1() {
? ? ? function fn2() {
? ? ? ? console.log(a)
? ? ? }
? ? ? function fn3() {
? ? ? ? var a = 4
? ? ? ? fn2()
? ? ? }
? ? ? var a = 2
? ? ? return fn3
? ? }
? ? var fn = fn1()
? ? fn() // 2
7. 如下代碼的輸出凿可?為什么惑折?
? ? var a = 1
? ? function fn1() {
? ? ? function fn3() {
? ? ? ? var a = 4
? ? ? ? fn2()
? ? ? }
? ? ? var a = 2
? ? ? return fn3
? ? }
? ? function fn2() {
? ? ? console.log(a)
? ? }
? ? var fn = fn1()
? ? fn() // 1
8. 如下代碼的輸出?為什么枯跑?
? ? var a = 1
? ? var c = {name: "oli", age: 2}
? ? function f1(n) {
? ? ? ++n
? ? }
? ? function f2(obj) {
? ? ? ++obj.age
? ? }
? ? f1(a)
? ? f2(c)
? ? f1(c.age)
? ? console.log(a)
? ? console.log(c)
輸出結(jié)果:
//1
//{age: 3,name: "oli"}
9. 如下代碼的輸出惨驶?為什么?
? ? var obj1 = {a:1, b:2};
? ? var obj2 = {a:1, b:2};
? ? console.log(obj1 == obj2);
? ? console.log(obj1 = obj2);
? ? console.log(obj1 == obj2);
輸出結(jié)果:
// false
//{a: 1,b: 2}
// true
5.1 嵌套函數(shù)敛助、作用域和閉包
1. 閉包是什么敞咧?閉包的作用是什么?閉包有哪些使用場(chǎng)景辜腺?
1休建、變量作用域
要理解閉包,首先要理解javascript的特殊的變量作用域评疗。
變量的作用域無非就兩種:全局變量和局部變量测砂。
javascript語言的特別之處就在于:函數(shù)內(nèi)部可以直接讀取全局變量,但是在函數(shù)外部無法讀取函數(shù)內(nèi)部的局部變量百匆。
注意點(diǎn):在函數(shù)內(nèi)部聲明變量的時(shí)候砌些,一定要使用var命令。如果不用的話,你實(shí)際上聲明的是一個(gè)全局變量存璃!
2仑荐、如何從外部讀取函數(shù)內(nèi)部的局部變量?
出于種種原因纵东,我們有時(shí)候需要獲取到函數(shù)內(nèi)部的局部變量粘招。但是,上面已經(jīng)說過了偎球,正常情況下洒扎,這是辦不到的!只有通過變通的方法才能實(shí)現(xiàn)衰絮。
那就是在函數(shù)內(nèi)部袍冷,再定義一個(gè)函數(shù)。
? ? function f1(){
? ? var n=999;
? ? function f2(){
? ? alert(n); // 999
? ? }
? ? }
在上面的代碼中猫牡,函數(shù)f2就被包括在函數(shù)f1內(nèi)部胡诗,這時(shí)f1內(nèi)部的所有局部變量,對(duì)f2都是可見的淌友。但是反過來就不行乃戈,f2內(nèi)部的局部變量,對(duì)f1就是不可見的亩进。
這就是Javascript語言特有的"鏈?zhǔn)阶饔糜?結(jié)構(gòu)(chain scope)症虑,
子對(duì)象會(huì)一級(jí)一級(jí)地向上尋找所有父對(duì)象的變量。所以归薛,父對(duì)象的所有變量谍憔,對(duì)子對(duì)象都是可見的,反之則不成立主籍。
既然f2可以讀取f1中的局部變量习贫,那么只要把f2作為返回值,我們不就可以在f1外部讀取它的內(nèi)部變量了嗎千元!
3苫昌、閉包的概念
上面代碼中的f2函數(shù),就是閉包幸海。
各種專業(yè)文獻(xiàn)的閉包定義都非常抽象祟身,我的理解是: 閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。
由于在javascript中物独,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量袜硫,所以說,閉包可以簡(jiǎn)單理解成“定義在一個(gè)函數(shù)內(nèi)部的函數(shù)“挡篓。
所以婉陷,在本質(zhì)上帚称,閉包是將函數(shù)內(nèi)部和函數(shù)外部連接起來的橋梁。
4秽澳、閉包的用途
閉包可以用在許多地方闯睹。它的最大用處有兩個(gè),一個(gè)是前面提到的可以讀取函數(shù)內(nèi)部的變量担神,另一個(gè)就是讓這些變量的值始終保持在內(nèi)存中楼吃,不會(huì)在f1調(diào)用后被自動(dòng)清除。
為什么會(huì)這樣呢杏瞻?原因就在于f1是f2的父函數(shù)所刀,而f2被賦給了一個(gè)全局變量衙荐,這導(dǎo)致f2始終在內(nèi)存中捞挥,而f2的存在依賴于f1,因此f1也始終在內(nèi)存中忧吟,不會(huì)在調(diào)用結(jié)束后砌函,被垃圾回收機(jī)制(garbage collection)回收。
這段代碼中另一個(gè)值得注意的地方溜族,就是"nAdd=function(){n+=1}"這一行讹俊,首先在nAdd前面沒有使用var關(guān)鍵字,因此nAdd是一個(gè)全局變量煌抒,而不是局部變量仍劈。其次,nAdd的值是一個(gè)匿名函數(shù)(anonymous function)寡壮,而這個(gè)匿名函數(shù)本身也是一個(gè)閉包贩疙,所以nAdd相當(dāng)于是一個(gè)setter,可以在函數(shù)外部對(duì)函數(shù)內(nèi)部的局部變量進(jìn)行操作况既。
5这溅、閉包的優(yōu)點(diǎn)
(1)邏輯連續(xù),當(dāng)閉包作為另一個(gè)函數(shù)調(diào)用參數(shù)時(shí)棒仍,避免脫離當(dāng)前邏輯而單獨(dú)編寫額外邏輯悲靴。
(2)方便調(diào)用上下文的局部變量。
(3)加強(qiáng)封裝性莫其,是第2點(diǎn)的延伸癞尚,可以達(dá)到對(duì)變量的保護(hù)作用。
6乱陡、使用閉包的注意點(diǎn)(缺點(diǎn))
(1)由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中否纬,內(nèi)存消耗很大,所以不能濫用閉包蛋褥,否則會(huì)造成網(wǎng)頁的性能問題临燃,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前膜廊,將不使用的局部變量全部刪除乏沸。
(2)閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值爪瓜。所以蹬跃,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用,把閉包當(dāng)作它的公用方法(Public Method)铆铆,把內(nèi)部變量當(dāng)作它的私有屬性(private value)蝶缀,這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值薄货。
7翁都、閉包的特性
(1)作為函數(shù)變量的一個(gè)引用。當(dāng)函數(shù)返回時(shí)谅猾,其處于激活狀態(tài)柄慰。
(2)閉包就是當(dāng)一個(gè)函數(shù)返回時(shí),并沒有釋放資源的棧區(qū)税娜。
8坐搔、閉包對(duì)頁面的影響
通過使用閉包,我們可以做很多事情敬矩。比如模擬面向?qū)ο蟮拇a風(fēng)格概行;更優(yōu)雅、更簡(jiǎn)潔的表達(dá)出代碼弧岳;在某些方面提升代碼的執(zhí)行效率凳忙。
9、閉包的工作原理
因?yàn)殚]包只有在被調(diào)用時(shí)才執(zhí)行操作缩筛,所以它可以被用來定義控制結(jié)構(gòu)消略。多個(gè)函數(shù)可以使用同一個(gè)環(huán)境,這使得他們可以通過改變那個(gè)環(huán)境相互交流瞎抛。
10艺演、使用場(chǎng)景
(1)采用函數(shù)引用方式的setTimeout調(diào)用。 例子
(2)將函數(shù)關(guān)聯(lián)到對(duì)象的實(shí)例方法桐臊。
(3)封裝相關(guān)的功能集胎撤。
2. 使用遞歸完成 1 到 100 的累加?
function add(i) {
if(i==1)
return i;
else
return i+add(i-1);
}
var? s=add(100);
console.log(s) //5050
3. 談?wù)劺厥諜C(jī)制的方式及內(nèi)存管理断凶?
回收機(jī)制方式
1伤提、定義和用法:垃圾回收機(jī)制(GC:Garbage Collection),執(zhí)行環(huán)境負(fù)責(zé)管理代碼執(zhí)行過程中使用的內(nèi)存。
2认烁、原理:垃圾收集器會(huì)定期(周期性)找出那些不在繼續(xù)使用的變量肿男,然后釋放其內(nèi)存介汹。但是這個(gè)過程不是實(shí)時(shí)的,因?yàn)槠溟_銷比較大舶沛,所以垃圾回收器會(huì)按照固定的時(shí)間間隔周期性的執(zhí)行嘹承。
3、實(shí)例如下:
function fn1() {
var obj = {name: ‘hanzichi’, age: 10};
}
function fn2() {
var obj = {name:‘hanzichi’, age: 10};
return obj;
}
var a = fn1();
var b = fn2();
fn1中定義的obj為局部變量如庭,而當(dāng)調(diào)用結(jié)束后叹卷,出了fn1的環(huán)境,那么該塊內(nèi)存會(huì)被js引擎中的垃圾回收器自動(dòng)釋放坪它;在fn2被調(diào)用的過程中骤竹,返回的對(duì)象被全局變量b所指向,所以該塊內(nèi)存并不會(huì)被釋放往毡。
4蒙揣、垃圾回收策略:標(biāo)記清除(較為常用)和引用計(jì)數(shù)。
標(biāo)記清除:
定義和用法:當(dāng)變量進(jìn)入環(huán)境時(shí)卖擅,將變量標(biāo)記"進(jìn)入環(huán)境"鸣奔,當(dāng)變量離開環(huán)境時(shí)墨技,標(biāo)記為:“離開環(huán)境”惩阶。某一個(gè)時(shí)刻,垃圾回收器會(huì)過濾掉環(huán)境中的變量扣汪,以及被環(huán)境變量引用的變量断楷,剩下的就是被視為準(zhǔn)備回收的變量。
到目前為止崭别,IE冬筒、Firefox、Opera茅主、Chrome舞痰、Safari的js實(shí)現(xiàn)使用的都是標(biāo)記清除的垃圾回收策略或類似的策略,只不過垃圾收集的時(shí)間間隔互不相同诀姚。
引用計(jì)數(shù):
定義和用法:引用計(jì)數(shù)是跟蹤記錄每個(gè)值被引用的次數(shù)响牛。
基本原理:就是變量的引用次數(shù),被引用一次則加1赫段,當(dāng)這個(gè)引用計(jì)數(shù)為0時(shí)呀打,被視為準(zhǔn)備回收的對(duì)象。
內(nèi)存管理
1糯笙、什么時(shí)候觸發(fā)垃圾回收贬丛?
垃圾回收器周期性運(yùn)行,如果分配的內(nèi)存非常多给涕,那么回收工作也會(huì)很艱巨豺憔,確定垃圾回收時(shí)間間隔就變成了一個(gè)值得思考的問題额获。
IE6的垃圾回收是根據(jù)內(nèi)存分配量運(yùn)行的,當(dāng)環(huán)境中的變量恭应,對(duì)象咪啡,字符串達(dá)到一定數(shù)量時(shí)觸發(fā)垃圾回收。垃圾回收器一直處于工作狀態(tài)暮屡,嚴(yán)重影響瀏覽器性能撤摸。
IE7中线得,垃圾回收器會(huì)根據(jù)內(nèi)存分配量與程序占用內(nèi)存的比例進(jìn)行動(dòng)態(tài)調(diào)整掉冶,開始回收工作。
2凌蔬、合理的GC方案:(1)莺掠、遍歷所有可訪問的對(duì)象; (2)衫嵌、回收已不可訪問的對(duì)象。
3彻秆、GC缺陷:(1)楔绞、停止響應(yīng)其他操作;
4唇兑、GC優(yōu)化策略:(1)酒朵、分代回收(Generation GC);(2)、增量GC
4. 談?wù)勀銓?duì) JS 執(zhí)行上下文棧和作用域鏈的理解扎附?
一蔫耽、JS執(zhí)行上下文
執(zhí)行上下文就是當(dāng)前 JavaScript 代碼被解析和執(zhí)行時(shí)所在環(huán)境的抽象概念, JavaScript 中運(yùn)行任何的代碼都是在執(zhí)行上下文中運(yùn)行留夜。
執(zhí)行上下文類型分為:全局執(zhí)行上下文和函數(shù)執(zhí)行上下文匙铡。執(zhí)行上下文創(chuàng)建過程中,需要做以下幾件事:
(1)創(chuàng)建變量對(duì)象:首先初始化函數(shù)的參數(shù)arguments碍粥,提升函數(shù)聲明和變量聲明鳖眼。
(2)創(chuàng)建作用域鏈(Scope Chain):在執(zhí)行期上下文的創(chuàng)建階段,作用域鏈?zhǔn)窃谧兞繉?duì)象之后創(chuàng)建的嚼摩。
(3)確定this的值钦讳,即 ResolveThisBinding
二、作用域
作用域就是變量和函數(shù)的可訪問范圍低斋,控制這個(gè)變量或者函數(shù)可訪問行和生命周期蜂厅。
作用域有兩種工作模型:詞法作用域和動(dòng)態(tài)作用域,JS采用的是詞法作用域工作模型膊畴,詞法作用域意味著作用域是由書寫代碼時(shí)變量和函數(shù)聲明的位置決定的掘猿。( with 和 eval 能夠修改詞法作用域,但是不推薦使用唇跨,對(duì)此不做特別說明)
在 js 中是詞法作用域稠通,意思就是你的變量函數(shù)的作用域是由你的編碼中的位置決定的衬衬,當(dāng)然可以通過 apply、call改橘、 bind 等函數(shù)進(jìn)行修改滋尉。
在 ES6 之前,js 中的作用域分為兩種:函數(shù)作用域和全局作用域飞主,現(xiàn)在作用域分為:全局作用域狮惜、函數(shù)作用域、塊級(jí)作用域碌识。
全局作用域顧名思義碾篡,瀏覽器下就是 window ,作用域鏈的頂級(jí)就是它筏餐,那么只要不是被函數(shù)包裹的變量或者函數(shù)开泽,它的作用域就是全局。
而函數(shù)作用域魁瞪,就是在函數(shù)的體內(nèi)聲明的變量穆律、函數(shù)及函數(shù)的參數(shù),它們的作用域都是在這個(gè)函數(shù)內(nèi)部导俘。
三峦耘、JS執(zhí)行上下文棧(后面簡(jiǎn)稱執(zhí)行棧)
執(zhí)行棧,也叫做調(diào)用棧趟畏,具有 LIFO (后進(jìn)先出) 結(jié)構(gòu)贡歧,用于存儲(chǔ)在代碼執(zhí)行期間創(chuàng)建的所有執(zhí)行上下文滩租。
規(guī)則如下:
首次運(yùn)行JavaScript代碼的時(shí)候,會(huì)創(chuàng)建一個(gè)全局執(zhí)行的上下文并Push到當(dāng)前的執(zhí)行棧中赋秀,每當(dāng)發(fā)生函數(shù)調(diào)用,引擎都會(huì)為該函數(shù)創(chuàng)建一個(gè)新的函數(shù)執(zhí)行上下文并Push當(dāng)前執(zhí)行棧的棧頂律想。
當(dāng)棧頂?shù)暮瘮?shù)運(yùn)行完成后猎莲,其對(duì)應(yīng)的函數(shù)執(zhí)行上下文將會(huì)從執(zhí)行棧中Pop出,上下文的控制權(quán)將移動(dòng)到當(dāng)前執(zhí)行棧的下一個(gè)執(zhí)行上下文技即。
四著洼、作用域鏈
我們知道函數(shù)在執(zhí)行時(shí)是有個(gè)執(zhí)行棧,在函數(shù)執(zhí)行的時(shí)候會(huì)創(chuàng)建執(zhí)行環(huán)境而叼,也就是執(zhí)行上下文身笤,在上下文中有個(gè)大對(duì)象,保存執(zhí)行環(huán)境定義的變量和函數(shù)葵陵,在使用變量的時(shí)候液荸,就會(huì)訪問這個(gè)大對(duì)象,這個(gè)對(duì)象會(huì)隨著函數(shù)的調(diào)用而創(chuàng)建脱篙,函數(shù)執(zhí)行結(jié)束出棧而銷毀娇钱,那么這些大對(duì)象組成一個(gè)鏈伤柄,就是作用域鏈。那么函數(shù)內(nèi)部未定義的變量文搂,就會(huì)順著作用域鏈向上查找适刀,一直找到同名的屬性。
5. 如下代碼輸出多少煤蹭?如果想輸出 3笔喉,那如何改造代碼?
? ? var fnArr = [];
? ? for(var i=0; i<10; i++) {
? ? ? fnArr[i] =? function() {
? ? ? ? return i
? ? ? };
? ? }
? ? console.log(fnArr[3]())
//輸出10
//如果想輸出 3 方法一
var fnArr = [];
for (var i = 0; i < 10; i ++) {
? (function(i){
? ? fnArr[i] =? function(){
? ? ? return i
? ? };
? })(i)
}
console.log( fnArr[3]() )
//方法二
var fnArr = [];
for (var i = 0; i < 10; i ++) {
? fnArr[i] =? (function(j){
? ? return function(){
? ? ? return j
? ? }
? })(i)
}
console.log( fnArr[3]() )
//方法三
var fnArr = []
for (let i = 0; i < 10; i ++) {
? fnArr[i] =? function(){
? ? return i
? }
}
console.log( fnArr[3]() )
6. 封裝一個(gè) Car 對(duì)象硝皂。
? ? var Car = (function() {
? ? ? var speed = 0;
? ? ? // 補(bǔ)充
? ? ? return {
? ? ? ? setSpeed: setSpeed,
? ? ? ? getSpeed: getSpeed,
? ? ? ? speedUp: speedUp,
? ? ? ? speedDown: speedDown
? ? ? }
? ? })()
? ? Car.setSpeed(30)
? ? Car.getSpeed() // 30
? ? Car.speedUp()
? ? Car.getSpeed() // 31
? ? Car.speedDown()
? ? Car.getSpeed() // 30
實(shí)現(xiàn):
var Car = (function(){
? var speed = 0;
? function setSpeed(val){
? ? speed = val
? ? return speed
? }
? function getSpeed(){
? ? console.log(speed)
? ? return speed
? }
? function speedUp(){
? ? ? speed++
? }
? function speedDown(){
? ? ? speed--
? }
? return {
? ? ? setSpeed: setSpeed,
? ? ? getSpeed: getSpeed,
? ? ? speedUp: speedUp,
? ? ? speedDown: speedDown
? }
})()
Car.setSpeed(30)
Car.getSpeed() //30
Car.speedUp()
Car.getSpeed() //31
Car.speedDown()
Car.getSpeed()? //30
7. 如下代碼輸出多少然遏?如何連續(xù)輸出 0, 1, 2, 3, 4?
? ? for(var i=0; i<5; i++) {
? ? ? setTimeout(function() {
? ? ? ? console.log("delayer:" + i)
? ? ? }, 0)
? ? }
// 輸出5個(gè)delayer:5
//改造 方法一
for(var i=0; i<5; i++){
? (function(i){
? ? setTimeout(function(){
? ? ? console.log('delayer:' + i )
? ? }, 0)
? })(i)
}
//方法二
for(var i=0; i<5; i++){
? setTimeout((function(i){
? ? return function(){
? ? ? console.log('delayer:' + i )
? ? }
? })(i), 0)
}
//方法三
for(let i=0; i<5; i++){
? setTimeout(function(){
? ? console.log('delayer:' + i )
? }, 0)
}
8. 如下代碼輸出多少吧彪?
? ? function makeCounter() {
? ? ? var count = 0
? ? ? return function() {
? ? ? ? return count++
? ? ? };
? ? }
? ? var counter = makeCounter()
? ? var counter2 = makeCounter();
? ? console.log(counter()) // 0
? ? console.log(counter()) // 1
? ? console.log(counter2()) // 0
? ? console.log(counter2()) // 1
6待侵、JS 數(shù)組
1. 寫一個(gè)函數(shù) squireArr,其參數(shù)是一個(gè)數(shù)組姨裸,作用是把數(shù)組中的每一項(xiàng)變?yōu)樵档钠椒健?/b>
? ? var arr = [3, 4, 6]
? ? function squireArr(arr) {
? ? ? // 補(bǔ)全
??????? window.arr?=??arr.map(function(a){return?a*a})
? ? }
? ? squireArr(arr)
? ? console.log(arr) // [9, 16, 36]
2. 寫一個(gè)函數(shù) squireArr秧倾,其參數(shù)是一個(gè)數(shù)組,返回一個(gè)新的數(shù)組傀缩,新數(shù)組中的每一項(xiàng)是原數(shù)組
?? 對(duì)應(yīng)值的平方那先,原數(shù)組不變。
? ? var arr = [3, 4, 6]
? ? function squireArr(arr) {
? ? ? // 補(bǔ)全
?????? return arr.map(function(a){returna*a});
? ? }
? ? var arr2 = squireArr(arr)
? ? console.log(arr) // [3, 4, 6]
? ? console.log(arr2) // [9, 16, 36]
3. 遍歷 company 對(duì)象赡艰,輸出里面每一項(xiàng)的值售淡。
? ? var company = {
? ? ? name: "qdywxs",
? ? ? age: 3,
? ? ? sex: "男"
? ? }
代碼:
for(key in company) {
? console.log(company[key]);
}
4. 遍歷數(shù)組,打印數(shù)組里的每一項(xiàng)的平方慷垮。
? ? var arr = [3, 4, 5]
?? arr.forEach((ite)=>{console.log(ite*ite)});
7揖闸、JS 對(duì)象
1. 介紹 JS 有哪些內(nèi)置對(duì)象?
時(shí)間對(duì)象date料身,字符串對(duì)象string汤纸,數(shù)學(xué)對(duì)象Math,數(shù)值對(duì)象Number芹血,數(shù)組對(duì)象Array贮泞,函數(shù)對(duì)象function, 正則表達(dá)式對(duì)象RegExp,函數(shù)參數(shù)集合arguments,布爾對(duì)象Boolean,錯(cuò)誤對(duì)象Error,基礎(chǔ)對(duì)象Object
2. 以下代碼輸出什么幔烛?
? ? var name = "sex"
? ? var company = {
? ? ? name: "qdywxs",
? ? ? age: 3,
? ? ? sex: "男"
? ? }
? ? console.log(company[name]) //男
3. 以下代碼輸出什么啃擦?
? ? var name = "sex"
? ? var company = {
? ? ? name: "qdywxs",
? ? ? age: 3,
? ? ? sex: "男"
? ? }
? ? console.log(company.name) // qdywxs
7.1 ES3 數(shù)組方法
1. 數(shù)組的哪些 API 會(huì)改變?cè)瓟?shù)組?
修改原數(shù)組的API有:splice()饿悬,reverse()令蛉,fill(),copyWithin()乡恕,sort()言询,push()俯萎,pop(),unshift()运杭,shift()
不修改原數(shù)組的有:slice()夫啊,map(),forEach()辆憔,every()撇眯,filter(),reduce()虱咧,entry()熊榛,entries(),find()
2. 寫一個(gè)函數(shù)腕巡,操作數(shù)組玄坦,返回一個(gè)新數(shù)組,新數(shù)組中只包含正數(shù)绘沉。
? ? function filterPositive(arr) {
? ? ? // 補(bǔ)全
?????? var newArr = []
??? for(var i = 0; i < arr.length; i++ ){
????? if (typeof arr[i] === 'number') {
? ????? if (arr[i] > 0) [
? ? ????? newArr.push(arr[i])
? ????? ]
? ? ? }
??? }
??? return newArr
? ? }
? ? var arr = [3, -1, 2, true]
? ? filterPositive(arr)
? ? console.log(filterPositive(arr)) // [3, 2]
3. 補(bǔ)全代碼煎楣,實(shí)現(xiàn)數(shù)組按姓名、年紀(jì)车伞、任意字段排序择懂。
? ? var users = [
? ? ? {name: "John", age: 20, company: "Baidu"},
? ? ? {name: "Pete", age: 18, company: "Alibaba"},
? ? ? {name: "Ann", age: 19, company: "Tecent"}
? ?? ]
????? function byField(field){
???????????? return function(user1, user2){
? ? ? ? ? ? ? ? ? return user1[field] > user2[field]//因?yàn)閒ield是字符串,所以不能用.來訪問
??????? }
? ? }
? ? users.sort(byField("age"))
? ? users.sort(byField("company"))
4. 用 splice 函數(shù)分別實(shí)現(xiàn) push另玖、pop困曙、shift、unshift 方法谦去。
?? 如:
? ? function push(arr, value) {
? ? ? arr.splice(arr.length, 0, value)
? ? ? return arr.length
? ? }
?? function pop(arr) {
? ? ? return arr.splice(arr.length-1,1);
? ? }
?? function shift(arr) {
? ? ? return arr.splice(0,1);
? ? }
? function unshift(arr,value) {
? ? ? return arr.splice(arr.length-1,1,value);
? ? }
? ? var arr = [3, 4, 5]
? ? arr.push(10) // arr 變成 [3, 4, 5, 10]慷丽,返回 4。
??? arr.pop(10) // arr 變成 [3, 4]哪轿,返回 5盈魁。
??? arr.shift() //3
??? arr.unshift() //4
7.2 ES5 數(shù)組方法
1. for...of、 for...in 和 forEach窃诉、map 的區(qū)別?
1赤套、for..in
for..in可以將JavaScript中的對(duì)象的屬性依次循環(huán)出來飘痛,當(dāng)for..in作用于數(shù)組時(shí)得到的是該元素的下標(biāo),且該下標(biāo)是一個(gè)String對(duì)象而不是一個(gè)Number對(duì)象容握。(注意:for..in實(shí)際上是歷史遺留問題宣脉,其遍歷的實(shí)際上是對(duì)象的屬性,之所以能夠遍歷數(shù)組剔氏,是因?yàn)閿?shù)組實(shí)際上是一個(gè)對(duì)象塑猖,而其屬性就是下標(biāo)竹祷,所以使用for..in遍歷數(shù)組得到的下標(biāo)是String類型)
for..in的問題在于,當(dāng)我們?yōu)橐粋€(gè)數(shù)組添加了一個(gè)不是數(shù)字下標(biāo)的屬性時(shí)羊苟,遍歷數(shù)組時(shí)會(huì)將這個(gè)屬性當(dāng)做是下標(biāo)遍歷出來:
let a = ['A', 'B', 'C'];
a.name = 'Hello';
for (let x in a) {
? ? console.log(x); // '0', '1', '2', 'name'
}
2塑陵、for..of
for..of就是為了遍歷集合而專門設(shè)計(jì)的,它只遍歷數(shù)組的元素:
let a = ['A', 'B', 'C'];
a.name = 'Hello';
for (let x of a) {
? ? console.log(x); // 'A', 'B', 'C'
}
并且當(dāng)for..of直接遍歷對(duì)象時(shí)會(huì)報(bào)錯(cuò):
let obj = {
? ? ? ? ? name:'123123',
? ? ? ? ? age:15
? ? ? }
for (let cc of obj) {//Uncaught TypeError: obj is not iterable
? ? ? console.log(cc)
}
for..of用于遍歷對(duì)象需要使用Object.keys()才能正確遍歷其屬性(而Object.keys()方法實(shí)際上是將對(duì)象的屬性鍵值以數(shù)組的對(duì)象返回了回來蜡励,實(shí)際上也是遍歷數(shù)組令花。所以說for..of不能遍歷對(duì)象其實(shí)也沒有錯(cuò)):
let obj = {
? ? ? ? ? name:'123123',
? ? ? ? ? age:15
? ? ? }
for (let cc of Object.keys(obj)) {
? ? ? console.log(cc)//name,age
}
3、forEach
forEach是Iterable的內(nèi)置方法凉倚,是一個(gè)高階函數(shù)兼都,其接受一個(gè)函數(shù)作為參數(shù),每次迭代就回調(diào)該函數(shù)稽寒。是遍歷Iterable(Array,Map,Set)最好的方式(但是相對(duì)于for..of有一個(gè)壞處就是不能通過break退出循環(huán)):
let arr = ['A', 'B', 'C'];
arr.forEach(function (element, index, array) {
? ? // element: 指向當(dāng)前元素的值
? ? // index: 指向當(dāng)前索引
? ? // array: 指向Array對(duì)象本身
? ? console.log(element + ', index = ' + index);
});
let set = new Set(['A', 'B', 'C']);
set.forEach(function (element, sameElement, set) {
? ? //Set沒有索引扮碧,因此回調(diào)函數(shù)的前兩個(gè)參數(shù)都是元素本身
? ? console.log(element);
});
let map = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
map.forEach(function (value, key, map) {
? ? //map接受的參數(shù)為值,鍵和其自身
? ? console.log(value);
});
2.? 如何消除一個(gè)數(shù)組里面重復(fù)的元素杏糙?
1.兼容Set 和?Array.from() 的環(huán)境下:
??? let orderedArray = Array.from(new Set(myArray));
??? var myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'];
??? var myOrderedArray = myArray.reduce(function (accumulator, currentValue) {
? ? ?? if (accumulator.indexOf(currentValue) === -1) {
? ? ? ? ?? accumulator.push(currentValue);
?????? }
? ? ? return accumulator
?? }, [])
?? console.log(myOrderedArray);
? 2.利用arr.reduce()去重
???? //簡(jiǎn)單去重
??? let arr = [1,2,3,4,4,1]
??? let newArr = arr.reduce((pre,cur)=>{
? ????? if(!pre.includes(cur)){
? ? ????? return pre.concat(cur)
? ????? }else{
? ? ????? return pre
? ????? }
??? },[])
??? console.log(newArr);// [1, 2, 3, 4]
??? 3.擴(kuò)展運(yùn)算符數(shù)組去重
??? var arr = [1,2,3,4,5,2,3,1];
??? var set =?new Set(arr);
??? var newArr = [...set ];
??? 4.去重?cái)?shù)組并排序
??? let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4];
??? let result = arr.sort().reduce((init, current) => {
? ????? if(init.length === 0 || init[init.length-1] !== current) {
? ? ? ????? init.push(current);
? ????? }
? ????? return init;
??? }, []);
??? console.log(result); //[1,2,3,4,5]
3. 判斷一個(gè)變量是否是數(shù)組芬萍,有哪些辦法?
1搔啊、instanceof
function isArray (obj) {
? return obj instanceof Array;
}
2柬祠、Array對(duì)象的 isArray方法
function isArray (obj) {
? return Array.isArray(obj);
}
3、Object.prototype.toString
function isArray (obj) {
? return Object.prototype.toString.call(obj) === '[object Array]';
}
4. ["1", "2", "3"].map(parseInt) 答案是多少负芋?
輸出:[ 1,NaN,NaN ]
解析:
map方法
引入MDN的解釋漫蛔,map()方法創(chuàng)建一個(gè)新數(shù)組,其結(jié)果是該數(shù)組中的每個(gè)元素都調(diào)用一個(gè)提供的函數(shù)后返回的結(jié)果旧蛾。
var arr = [1, 2, 3];
var arr1 = arr.map(x => x+1);
console.log(arr1); // 2,3,4
可以看出map方法中接受一個(gè)函數(shù)function莽龟,用來處理遍歷數(shù)組中的每一個(gè)元素。
new_array = [].map(function callback(currentValue[,index[,array]]){ // Return element for new_array }[, thisArg]);
這個(gè)callback一共可以接收三個(gè)參數(shù)锨天,其中第一個(gè)參數(shù)代表當(dāng)前被處理的元素毯盈,第二個(gè)參數(shù)代表該元素的索引。
parseInt函數(shù)
parseInt是用來解析字符串病袄,使字符串成為指定基數(shù)的整數(shù)搂赋,parseInt的基本語法,
parseInt(string, radix)
接收兩個(gè)參數(shù)益缠,第一個(gè)表示被處理的值(字符串)脑奠,第二個(gè)表示為解析時(shí)的基數(shù)。radix是一個(gè)介于2-36之間的整數(shù)幅慌,返回解析后的整數(shù)值宋欺。 如果被解析參數(shù)的第一個(gè)字符無法被轉(zhuǎn)化成數(shù)值類型,則返回 NaN
praseInt('111') // 111
parseInt('111', 0) // 111
// radix為0時(shí),且string參數(shù)不以“0x”和“0”開頭時(shí)齿诞,按照10為基數(shù)處理
parseInt('111', 1) // NaN 【2 <= radix <= 36】
parseInt('111', 2) // 7
從上面可以看出根據(jù)指定不同的radix酸休,返回不同的值。
radix參數(shù)為n將會(huì)把第一個(gè)參數(shù)看作是一個(gè)數(shù)的n進(jìn)制表示祷杈,而返回的值則是十進(jìn)制斑司。
parseInt('123', 5) // 將'123'看作5進(jìn)制數(shù),返回十進(jìn)制數(shù)38 => 1*5^2 + 2*5^1 + 3*5^0 = 38
下面我們來分析一下[‘1’, ‘2’, ‘3’].map(parseInt);
parseInt(‘1’, 0); // radix為0時(shí)吠式,使用默認(rèn)的10進(jìn)制陡厘。
parseInt(‘2’, 1); // radix值在2-36,無法解析特占,返回NaN
parseInt(‘3’, 2); // 基數(shù)為2糙置,2進(jìn)制數(shù)表示的數(shù)中,最大值小于3是目,無法解析谤饭,返回NaN
map函數(shù)返回的是一個(gè)數(shù)組,所以最后結(jié)果為[1, NaN, NaN]懊纳。
5. 取數(shù)組的最大值(ES5揉抵、ES6)?
1.apply()應(yīng)用某一對(duì)象的一個(gè)方法嗤疯,用另一個(gè)對(duì)象替換當(dāng)前對(duì)象
var max = Math.max.apply(null,arr);
console.log(max)
由于max()里面參數(shù)不能為數(shù)組冤今,所以借助apply(funtion,args)方法調(diào)用Math.max(),function為要調(diào)用的方法茂缚,args是數(shù)組對(duì)象戏罢,當(dāng)function為null時(shí),默認(rèn)為上文,即相當(dāng)于apply(Math.max,arr)
2.call()調(diào)用一個(gè)對(duì)象的一個(gè)方法脚囊,以另一個(gè)對(duì)象替換當(dāng)前對(duì)象
var max1 = Math.max.call(null,7,2,0,-3,5)
console.log(max1)
call()與apply()類似龟糕,區(qū)別是傳入?yún)?shù)的方式不同,apply()參數(shù)是一個(gè)對(duì)象和一個(gè)數(shù)組類型的對(duì)象悔耘,call()參數(shù)是一個(gè)對(duì)象和參數(shù)列表
3.sort()+reverse()
//sort()排序默認(rèn)為升序讲岁,reverse()將數(shù)組掉個(gè)
var max3 = arr.sort().reverse()[0];
console.log(max3)
4.sort()
//b-a從大到小,a-b從小到大
var max2 = arr.sort(function(a,b){
? return b-a;
})[0];
console.log(max2);
5.es6擴(kuò)展語法
??? var arr = [1, 2, 3];
??? var max = Math.max(...arr);
6. 實(shí)現(xiàn)一個(gè) reduce 函數(shù)衬以,作用和原生的 reduce 類似下面的例子缓艳。
?? Ex:
? ? var sum = reduce([1, 2, 3], function(memo, num) {return memo + num;}, 0); => 6
如下:
function newReduce(arr, iteratee, initValue){
? var tmpArr = (initValue === undefined ? [] : [initValue]).concat(arr);//判斷是否有原始值
? while(tmpArr.length > 1){
? ? tmpArr.splice(0, 2, iteratee(tmpArr[0], tmpArr[1]));
? }
? return tmpArr[0];
}
var sum = newReduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
console.log(sum);
7. 怎樣用原生 JS 將一個(gè)多維數(shù)組拍平?
方法一:concat
function flatten(arr) {
? ? var result = [];
? ? arr.forEach(function(value, index, array) {
? ? ? ? if(Array.isArray(value)) {
? ? ? ? ? ? result = result.concat(flatten(value));
? ? ? ? } else {
? ? ? ? ? ? result = result.concat(value);
? ? ? ? }
? ? });
? ? return result;
}
方法二:閉包
function flatten(arr) {
? ? var result = [];
? ? function _flatten(arr) {
? ? ? ? arr.forEach(function(value, index, array){
? ? ? ? ? ? if(Array.isArray(value)) {
? ? ? ? ? ? ? ? _flatten(value);
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? result.push(value);
? ? ? ? ? ? }
? ? ? ? });
? ? }
? ? _flatten(arr);
? ? return result;
}
方法三:reduce
function flatten(arr) {
? return arr.reduce(function(a, b){
? ? ? if(Array.isArray(b)) {
? ? ? ? ? return a.concat(flatten(b));
? ? ? } else {
? ? ? ? ? return a.concat(b);
? ? ? }
? },[]);
}
function newFlatten(arr){
? return arr.reduce(function(initArr, currentArr){
? ? ? return initArr.concat(Array.isArray(currentArr) ? newFlatten(currentArr) : [currentArr]);
? } ,[]);
}
var result = newFlatten([1, [2], [3, [[4]]]]);
console.log(result);
?console.log(flat(array)) // [1, 2, 3, 4, 5]
8泄鹏、面向?qū)ο缶幊?/h3>8.1 對(duì)象構(gòu)造函數(shù)
1. new 的原理是什么郎任?通過 new 的方式創(chuàng)建對(duì)象和通過字面量創(chuàng)建有什么區(qū)別?
new的原理:
創(chuàng)建一個(gè)新對(duì)象备籽。
這個(gè)新對(duì)象會(huì)被執(zhí)行[[原型]]連接。
將構(gòu)造函數(shù)的作用域賦值給新對(duì)象,即this指向這個(gè)新對(duì)象.
如果函數(shù)沒有返回其他對(duì)象车猬,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象霉猛。
function new(func) {
? ? lat target = {};
? ? target.__proto__ = func.prototype;
? ? let res = func.call(target);
? ? if (typeof(res) == "object" || typeof(res) == "function") {
? ? return res;
? ? }
? ? return target;
}
字面量創(chuàng)建對(duì)象,不會(huì)調(diào)用 Object構(gòu)造函數(shù), 簡(jiǎn)潔且性能更好;
new Object() 方式創(chuàng)建對(duì)象本質(zhì)上是方法調(diào)用珠闰,涉及到在proto鏈中遍歷該方法惜浅,當(dāng)找到該方法后,又會(huì)生產(chǎn)方法調(diào)用必須的 堆棧信息伏嗜,方法調(diào)用結(jié)束后坛悉,還要釋放該堆棧,性能不如字面量的方式承绸。
通過對(duì)象字面量定義對(duì)象時(shí)裸影,不會(huì)調(diào)用Object構(gòu)造函數(shù)。
2. Object.create 有什么作用军熏?
語法:
Object.create(proto, [propertiesObject])
//方法創(chuàng)建一個(gè)新對(duì)象轩猩,使用現(xiàn)有的對(duì)象來提供新創(chuàng)建的對(duì)象的proto。
參數(shù):
proto : 必須荡澎。表示新建對(duì)象的原型對(duì)象均践,即該參數(shù)會(huì)被賦值到目標(biāo)對(duì)象(即新對(duì)象,或說是最后返回的對(duì)象)的原型上摩幔。該參數(shù)可以是null彤委,對(duì)象, 函數(shù)的prototype屬性(創(chuàng)建空的對(duì)象時(shí)需傳null , 否則會(huì)拋出TypeError異常)或衡。
propertiesObject : 可選焦影。 添加到新創(chuàng)建對(duì)象的可枚舉屬性(即其自身的屬性,而不是原型鏈上的枚舉屬性)對(duì)象的屬性描述符以及相應(yīng)的屬性名稱薇宠。這些屬性對(duì)應(yīng)Object.defineProperties()的第二個(gè)參數(shù)偷办。
3 返回值:
在指定原型對(duì)象上添加新屬性后的對(duì)象。
案例說明:
1)創(chuàng)建對(duì)象的方式不同
new Object() 通過構(gòu)造函數(shù)來創(chuàng)建對(duì)象, 添加的屬性是在自身實(shí)例下澄港。
Object.create() es6創(chuàng)建對(duì)象的另一種方式椒涯,可以理解為繼承一個(gè)對(duì)象, 添加的屬性是在原型下。
// new Object() 方式創(chuàng)建
var a = {? rep : 'apple' }
var b = new Object(a)
console.log(b) // {rep: "apple"}
console.log(b.__proto__) // {}
console.log(b.rep) // {rep: "apple"}
// Object.create() 方式創(chuàng)建
var a = { rep: 'apple' }
var b = Object.create(a)
console.log(b)? // {}
console.log(b.__proto__) // {rep: "apple"}
console.log(b.rep) // {rep: "apple"}
Object.create()方法創(chuàng)建的對(duì)象時(shí)回梧,屬性是在原型下面的废岂,也可以直接訪問 b.rep // {rep: "apple"} ,
此時(shí)這個(gè)值不是吧b自身的,是它通過原型鏈proto來訪問到b的值狱意。
2)創(chuàng)建對(duì)象屬性的性質(zhì)不同
// 創(chuàng)建一個(gè)以另一個(gè)空對(duì)象為原型,且擁有一個(gè)屬性p的對(duì)象
o = Object.create({}, { p: { value: 42 } })
// 省略了的屬性特性默認(rèn)為false,所以屬性p是不可寫,不可枚舉,不可配置的:
o.p = 24
o.p
//42
o.q = 12
for (var prop in o) {
? console.log(prop)
}
//"q"
delete o.p
//false
Object.create() 用第二個(gè)參數(shù)來創(chuàng)建非空對(duì)象的屬性描述符默認(rèn)是為false的湖苞,而構(gòu)造函數(shù)或字面量方法創(chuàng)建的對(duì)象屬性的描述符默認(rèn)為true。
3)創(chuàng)建空對(duì)象時(shí)不同
當(dāng)用構(gòu)造函數(shù)或?qū)ο笞置媪糠椒▌?chuàng)建空對(duì)象時(shí)详囤,對(duì)象時(shí)有原型屬性的财骨,即有_proto_;
當(dāng)用Object.create()方法創(chuàng)建空對(duì)象時(shí)镐作,對(duì)象是沒有原型屬性的。
3. 怎樣判斷“值”屬于哪種數(shù)據(jù)類型隆箩?typeof 是否能正確判斷類型该贾?instanceof 呢?
首先typeof 能夠判斷基本數(shù)據(jù)類型捌臊,但是除了null杨蛋,typeof null 返回的是object
但是對(duì)于對(duì)象來說typeof不能準(zhǔn)確判斷類型,typeof 函數(shù)會(huì)返回function理澎,除此之外全部都是object逞力,不能準(zhǔn)確判斷類型
instanceof可以判斷復(fù)雜數(shù)據(jù)類型,基本數(shù)據(jù)類型不可以糠爬,instanceof是通過原型鏈來判斷的 寇荧,A instanceof B,在A的原型鏈中層層查找秩铆,是否有原型等于B.prototype砚亭,如果一直找到A的原型鏈的頂端(null,即Object.prototype._proto_)殴玛,仍然不等于B捅膘,那么返回false,否則返回true
4. instanceof 有什么作用滚粟??jī)?nèi)部邏輯是如何實(shí)現(xiàn)的寻仗?
nstanceOf判斷一個(gè)對(duì)象是不是某個(gè)類型的實(shí)例
[1, 2, 3] instanceof Array; //true
可以看到[1, 2, 3]是類型Array的實(shí)例
[1, 2, 3] instanceof Object; //true
5.JavaScript 有哪些方法定義對(duì)象?
方式一:
通過對(duì)象字面量表示法(又稱為直接量凡壤、原始方式)署尤。
var obj = {name:"moyu"};
方式二:
通過new和構(gòu)造函數(shù)Object()、String()等亚侠。
var obj = new Object();
方式三:
自定義一個(gè)對(duì)象的構(gòu)造函數(shù)曹体,然后實(shí)例化對(duì)象。
function a(o){
this.name = "moyu"
}
var obj = new a();
方式四:
通過Object.create()
var o1 = Object.create({x:1, y:2}); ? ? ?// o1繼承了屬性x和y
6. 如下代碼中硝烂?new 一個(gè)函數(shù)本質(zhì)上做了什么箕别?
? ? function Modal(msg) {
? ? ? this.msg = msg
? ? }
? ? var modal = new Modal()
1.執(zhí)行 new Modal()
1.1創(chuàng)建一個(gè)空對(duì)象 {},假設(shè)名字是 tmpObj
1.2執(zhí)行 Modal 函數(shù)滞谢,執(zhí)行過程中對(duì) this 操作就是對(duì) tmpObj 進(jìn)行操作
1.3 函數(shù)執(zhí)行完后返回剛剛創(chuàng)建的 tmpObj
2.把 tmpObj 賦值給 modal (modal指向同一個(gè)對(duì)象)
8.2 使用原型
1. JS 原型是什么旦装?如何理解原型鏈偏瓤?
一叛买、什么是原型
原型是Javascript中的繼承的基礎(chǔ)爽柒,JavaScript的繼承就是基于原型的繼承。
1.1 函數(shù)的原型對(duì)象
在JavaScript中橄教,我們創(chuàng)建一個(gè)函數(shù)A(就是聲明一個(gè)函數(shù)), 那么瀏覽器就會(huì)在內(nèi)存中創(chuàng)建一個(gè)對(duì)象B清寇,而且每個(gè)函數(shù)都默認(rèn)會(huì)有一個(gè)屬性 prototype 指向了這個(gè)對(duì)象( 即:prototype的屬性的值是這個(gè)對(duì)象 )喘漏。這個(gè)對(duì)象B就是函數(shù)A的原型對(duì)象,簡(jiǎn)稱函數(shù)的原型颗管。這個(gè)原型對(duì)象B 默認(rèn)會(huì)有一個(gè)屬性 constructor 指向了這個(gè)函數(shù)A ( 意思就是說:constructor屬性的值是函數(shù)A )陷遮。
看下面的代碼:
<body>
? ? <script type="text/javascript">
? ? /*
? ? 聲明一個(gè)函數(shù)滓走,則這個(gè)函數(shù)默認(rèn)會(huì)有一個(gè)屬性叫 prototype 垦江。而且瀏覽器會(huì)自動(dòng)按照一定的規(guī)則
? ? 創(chuàng)建一個(gè)對(duì)象,這個(gè)對(duì)象就是這個(gè)函數(shù)的原型對(duì)象搅方,prototype屬性指向這個(gè)原型對(duì)象比吭。這個(gè)原型對(duì)象
? ? 有一個(gè)屬性叫constructor 執(zhí)行了這個(gè)函數(shù)
注意:原型對(duì)象默認(rèn)只有屬性:constructor。其他都是從Object繼承而來姨涡,暫且不用考慮衩藤。
*/
? ? function Person () {
? ? } ? ?
? ? </script>
</body>
下面的圖描述了聲明一個(gè)函數(shù)之后發(fā)生的事情:
1.2 使用構(gòu)造函數(shù)創(chuàng)建對(duì)象
當(dāng)把一個(gè)函數(shù)作為構(gòu)造函數(shù) (理論上任何函數(shù)都可以作為構(gòu)造函數(shù)) 使用new創(chuàng)建對(duì)象的時(shí)候,那么這個(gè)對(duì)象就會(huì)存在一個(gè)默認(rèn)的不可見的屬性涛漂,來指向了構(gòu)造函數(shù)的原型對(duì)象赏表。 這個(gè)不可見的屬性我們一般用 [[prototype]] 來表示,只是這個(gè)屬性沒有辦法直接訪問到匈仗。
看下面的代碼:
<body>
? ? <script type="text/javascript">
? ? function Person () {
? ? }
? ? ? ? /*
? ? ? ? 利用構(gòu)造函數(shù)創(chuàng)建一個(gè)對(duì)象瓢剿,則這個(gè)對(duì)象會(huì)自動(dòng)添加一個(gè)不可見的屬性 [[prototype]], 而且這個(gè)屬性
? ? ? ? 指向了構(gòu)造函數(shù)的原型對(duì)象。
? ? ? ? */
? ? ? var p1 = new Person();
? ? </script>
</body>
觀察下面的示意圖:
說明:
? ? 從上面的圖示中可以看到悠轩,創(chuàng)建p1對(duì)象雖然使用的是Person構(gòu)造函數(shù)间狂,但是對(duì)象創(chuàng)建出來之后,這個(gè)p1對(duì)象其實(shí)已經(jīng)與Person構(gòu)造函數(shù)沒有任何關(guān)系了火架,p1對(duì)象的[[ prototype ]]屬性指向的是Person構(gòu)造函數(shù)的原型對(duì)象鉴象。
? ? 如果使用new Person()創(chuàng)建多個(gè)對(duì)象,則多個(gè)對(duì)象都會(huì)同時(shí)指向Person構(gòu)造函數(shù)的原型對(duì)象何鸡。
? ? 我們可以手動(dòng)給這個(gè)原型對(duì)象添加屬性和方法纺弊,那么p1,p2,p3…這些對(duì)象就會(huì)共享這些在原型中添加的屬性和方法。
? ? 如果我們?cè)L問p1中的一個(gè)屬性name骡男,如果在p1對(duì)象中找到淆游,則直接返回。如果p1對(duì)象中沒有找到洞翩,則直接去p1對(duì)象的[[prototype]]屬性指向的原型對(duì)象中查找稽犁,如果查找到則返回。(如果原型中也沒有找到骚亿,則繼續(xù)向上找原型的原型—原型鏈已亥。 后面再講)。
? ? 如果通過p1對(duì)象添加了一個(gè)屬性name来屠,則p1對(duì)象來說就屏蔽了原型中的屬性name虑椎。 換句話說:在p1中就沒有辦法訪問到原型的屬性name了震鹉。
? ? 通過p1對(duì)象只能讀取原型中的屬性name的值,而不能修改原型中的屬性name的值捆姜。 p1.name = “李四”; 并不是修改了原型中的值传趾,而是在p1對(duì)象中給添加了一個(gè)屬性name。
? ? 看下面的代碼:
<body>
? ? <script type="text/javascript">
? ? function Person () {? ?
? ? }
? ? ? // 可以使用Person.prototype 直接訪問到原型對(duì)象
? ? //給Person函數(shù)的原型對(duì)象中添加一個(gè)屬性 name并且值是 "張三"
? ? Person.prototype.name = "張三";
? ? Person.prototype.age = 20;
? var p1 = new Person();
? /*
? 訪問p1對(duì)象的屬性name泥技,雖然在p1對(duì)象中我們并沒有明確的添加屬性name浆兰,但是
? p1的 [[prototype]] 屬性指向的原型中有name屬性,所以這個(gè)地方可以訪問到屬性name
? 就值珊豹。
? 注意:這個(gè)時(shí)候不能通過p1對(duì)象刪除name屬性簸呈,因?yàn)橹荒軇h除在p1中刪除的對(duì)象。
? */
? alert(p1.name);? // 張三
? var p2 = new Person();
? alert(p2.name);? // 張三? 都是從原型中找到的店茶,所以一樣蜕便。
? alert(p1.name === p2.name);? // true
? // 由于不能修改原型中的值,則這種方法就直接在p1中添加了一個(gè)新的屬性name贩幻,然后在p1中無法再訪問到
? //原型中的屬性轿腺。
? p1.name = "李四";
? alert("p1:" + p1.name);
? // 由于p2中沒有name屬性,則對(duì)p2來說仍然是訪問的原型中的屬性丛楚。
? alert("p2:" + p2.name);? // 張三?
? ? </script>
</body>
二族壳、與原型有關(guān)的幾個(gè)屬性和方法
2.1 prototype屬性
prototype 存在于構(gòu)造函數(shù)中 (其實(shí)任意函數(shù)中都有,只是不是構(gòu)造函數(shù)的時(shí)候prototype我們不關(guān)注而已) 鸯檬,他指向了這個(gè)構(gòu)造函數(shù)的原型對(duì)象决侈。
參考前面的示意圖。
2.2 constructor屬性
constructor屬性存在于原型對(duì)象中喧务,他指向了構(gòu)造函數(shù)
看下面的代碼:
<script type="text/javascript">
function Person () {
}
alert(Person.prototype.constructor === Person); // true
var p1 = new Person();
? //使用instanceof 操作符可以判斷一個(gè)對(duì)象的類型赖歌。?
? //typeof一般用來獲取簡(jiǎn)單類型和函數(shù)。而引用類型一般使用instanceof功茴,因?yàn)橐妙愋陀胻ypeof 總是返回object庐冯。
alert(p1 instanceof Person); // true
</script>
? ? 我們根據(jù)需要,可以Person.prototype 屬性指定新的對(duì)象坎穿,來作為Person的原型對(duì)象展父。
? ? 但是這個(gè)時(shí)候有個(gè)問題,新的對(duì)象的constructor屬性則不再指向Person構(gòu)造函數(shù)了玲昧。
看下面的代碼:
<script type="text/javascript">
function Person () {
}
//直接給Person的原型指定對(duì)象字面量栖茉。則這個(gè)對(duì)象的constructor屬性不再指向Person函數(shù)
Person.prototype = {
name:"志玲",
age:20
};
var p1 = new Person();
alert(p1.name);? // 志玲
alert(p1 instanceof Person); // true
alert(Person.prototype.constructor === Person); //false
? //如果constructor對(duì)你很重要,你應(yīng)該在Person.prototype中添加一行這樣的代碼:
? /*
? Person.prototype = {
? ? ? constructor : Person //讓constructor重新指向Person函數(shù)
? }
? */
</script>
2.3 __proto__ 屬性(注意:左右各是2個(gè)下劃線)
用構(gòu)造方法創(chuàng)建一個(gè)新的對(duì)象之后孵延,這個(gè)對(duì)象中默認(rèn)會(huì)有一個(gè)不可訪問的屬性 [[prototype]] , 這個(gè)屬性就指向了構(gòu)造方法的原型對(duì)象吕漂。
但是在個(gè)別瀏覽器中,也提供了對(duì)這個(gè)屬性[[prototype]]的訪問(chrome瀏覽器和火狐瀏覽器尘应。ie瀏覽器不支持)惶凝。訪問方式:p1.__proto__
但是開發(fā)者盡量不要用這種方式去訪問吼虎,因?yàn)椴僮鞑簧鲿?huì)改變這個(gè)對(duì)象的繼承原型鏈。
<script type="text/javascript">
function Person () {
}
//直接給Person的原型指定對(duì)象字面量苍鲜。則這個(gè)對(duì)象的constructor屬性不再指向Person函數(shù)
Person.prototype = {
constructor : Person,
name:"志玲",
age:20
};
var p1 = new Person();
alert(p1.__proto__ === Person.prototype); //true
</script>
2.4 hasOwnProperty() 方法
大家知道思灰,我們用去訪問一個(gè)對(duì)象的屬性的時(shí)候,這個(gè)屬性既有可能來自對(duì)象本身混滔,也有可能來自這個(gè)對(duì)象的[[prototype]]屬性指向的原型洒疚。
那么如何判斷這個(gè)對(duì)象的來源呢?
hasOwnProperty方法遍坟,可以判斷一個(gè)屬性是否來自對(duì)象本身拳亿。
<script type="text/javascript">
function Person () {
}
Person.prototype.name = "志玲";
var p1 = new Person();
p1.sex = "女";
? //sex屬性是直接在p1屬性中添加,所以是true
alert("sex屬性是對(duì)象本身的:" + p1.hasOwnProperty("sex"));
? // name屬性是在原型中添加的愿伴,所以是false
alert("name屬性是對(duì)象本身的:" + p1.hasOwnProperty("name"));
? //? age 屬性不存在,所以也是false
alert("age屬性是存在于對(duì)象本身:" + p1.hasOwnProperty("age"));
</script>
? ? 所以电湘,通過hasOwnProperty這個(gè)方法可以判斷一個(gè)對(duì)象是否在對(duì)象本身添加的隔节,但是不能判斷是否存在于原型中,因?yàn)橛锌赡苓@個(gè)屬性不存在寂呛。
? ? 也即是說怎诫,在原型中的屬性和不存在的屬性都會(huì)返回fasle。
? ? 如何判斷一個(gè)屬性是否存在于原型中呢贷痪?
2.5 in 操作符
in操作符用來判斷一個(gè)屬性是否存在于這個(gè)對(duì)象中幻妓。但是在查找這個(gè)屬性時(shí)候,現(xiàn)在對(duì)象本身中找劫拢,如果對(duì)象找不到再去原型中找肉津。換句話說,只要對(duì)象和原型中有一個(gè)地方存在這個(gè)屬性舱沧,就返回true
<script type="text/javascript">
function Person () {
}
Person.prototype.name = "志玲";
var p1 = new Person();
p1.sex = "女";
alert("sex" in p1); // 對(duì)象本身添加的妹沙,所以true
alert("name" in p1); //原型中存在,所以true
alert("age" in p1); //對(duì)象和原型中都不存在熟吏,所以false
</script>
回到前面的問題距糖,如果判斷一個(gè)屬性是否存在于原型中:
如果一個(gè)屬性存在,但是沒有在對(duì)象本身中牵寺,則一定存在于原型中悍引。
<script type="text/javascript">
function Person () {
}
Person.prototype.name = "志玲";
var p1 = new Person();
p1.sex = "女";
//定義一個(gè)函數(shù)去判斷原型所在的位置
function propertyLocation(obj, prop){
if(!(prop in obj)){
alert(prop + "屬性不存在");
}else if(obj.hasOwnProperty(prop)){
alert(prop + "屬性存在于對(duì)象中");
}else {
alert(prop + "對(duì)象存在于原型中");
}
}
propertyLocation(p1, "age");
propertyLocation(p1, "name");
propertyLocation(p1, "sex");
</script
三、組合原型模型和構(gòu)造函數(shù)模型創(chuàng)建對(duì)象
3.1 原型模型創(chuàng)建對(duì)象的缺陷
原型中的所有的屬性都是共享的帽氓。也就是說趣斤,用同一個(gè)構(gòu)造函數(shù)創(chuàng)建的對(duì)象去訪問原型中的屬性的時(shí)候,大家都是訪問的同一個(gè)對(duì)象杏节,如果一個(gè)對(duì)象對(duì)原型的屬性進(jìn)行了修改唬渗,則會(huì)反映到所有的對(duì)象上面典阵。
但是在實(shí)際使用中,每個(gè)對(duì)象的屬性一般是不同的镊逝。張三的姓名是張三壮啊,李四的姓名是李四。
**但是撑蒜,這個(gè)共享特性對(duì) 方法(屬性值是函數(shù)的屬性)又是非常合適的歹啼。**所有的對(duì)象共享方法是最佳狀態(tài)。這種特性在c#和Java中是天生存在的座菠。
3.2 構(gòu)造函數(shù)模型創(chuàng)建對(duì)象的缺陷
在構(gòu)造函數(shù)中添加的屬性和方法狸眼,每個(gè)對(duì)象都有自己獨(dú)有的一份,大家不會(huì)共享浴滴。這個(gè)特性對(duì)屬性比較合適拓萌,但是對(duì)方法又不太合適。因?yàn)閷?duì)所有對(duì)象來說升略,他們的方法應(yīng)該是一份就夠了微王,沒有必要每人一份,造成內(nèi)存的浪費(fèi)和性能的低下品嚣。
<script type="text/javascript">
function Person() {
? ? this.name = "李四";
? ? this.age = 20;
? ? this.eat = function() {
? ? ? ? alert("吃完東西");
? ? }
}
var p1 = new Person();
var p2 = new Person();
//每個(gè)對(duì)象都會(huì)有不同的方法
alert(p1.eat === p2.eat); //fasle
</script>
可以使用下面的方法解決:
<script type="text/javascript">
function Person() {
? ? this.name = "李四";
? ? this.age = 20;
? ? this.eat = eat;
}
? function eat() {
? ? alert("吃完東西");
? ? }
var p1 = new Person();
var p2 = new Person();
//因?yàn)閑at屬性都是賦值的同一個(gè)函數(shù)炕倘,所以是true
alert(p1.eat === p2.eat); //true
</script>
? ? 但是上面的這種解決方法具有致命的缺陷:封裝性太差。使用面向?qū)ο蠛渤牛康闹痪褪欠庋b代碼罩旋,這個(gè)時(shí)候?yàn)榱诵阅苡忠汛a抽出對(duì)象之外,這是反人類的設(shè)計(jì)眶诈。
3.3 使用組合模式解決上述兩種缺陷
原型模式適合封裝方法涨醋,構(gòu)造函數(shù)模式適合封裝屬性,綜合兩種模式的優(yōu)點(diǎn)就有了組合模式册养。
<script type="text/javascript">
//在構(gòu)造方法內(nèi)部封裝屬性
function Person(name, age) {
? ? this.name = name;
? ? this.age = age;
}
//在原型對(duì)象內(nèi)封裝方法
Person.prototype.eat = function (food) {
alert(this.name + "愛吃" + food);
}
Person.prototype.play = function (playName) {
alert(this.name + "愛玩" + playName);
}
var p1 = new Person("李四", 20);
var p2 = new Person("張三", 30);
p1.eat("蘋果");
p2.eat("香蕉");
p1.play("志玲");
p2.play("鳳姐");
</script>
四东帅、動(dòng)態(tài)原型模式創(chuàng)建對(duì)象
前面講到的組合模式,也并非完美無缺球拦,有一點(diǎn)也是感覺不是很完美靠闭。把構(gòu)造方法和原型分開寫,總讓人感覺不舒服坎炼,應(yīng)該想辦法把構(gòu)造方法和原型封裝在一起愧膀,所以就有了動(dòng)態(tài)原型模式。
動(dòng)態(tài)原型模式把所有的屬性和方法都封裝在構(gòu)造方法中谣光,而僅僅在需要的時(shí)候才去在構(gòu)造方法中初始化原型檩淋,又保持了同時(shí)使用構(gòu)造函數(shù)和原型的優(yōu)點(diǎn)。
? ? 看下面的代碼:
<script type="text/javascript">
//構(gòu)造方法內(nèi)部封裝屬性
function Person(name, age) {
//每個(gè)對(duì)象都添加自己的屬性
? ? this.name = name;
? ? this.age = age;
? ? /*
? ? 判斷this.eat這個(gè)屬性是不是function,如果不是function則證明是第一次創(chuàng)建對(duì)象蟀悦,
? ? 則把這個(gè)funcion添加到原型中媚朦。
? ? 如果是function,則代表原型中已經(jīng)有了這個(gè)方法日戈,則不需要再添加询张。
? ? perfect!完美解決了性能和代碼的封裝問題浙炼。
? ? */
? ? if(typeof this.eat !== "function"){
? ? Person.prototype.eat = function () {
? ? alert(this.name + " 在吃");
? ? }
? ? }
}
var p1 = new Person("志玲", 40);
p1.eat();
</script>
? ? 說明:
? ? 組合模式和動(dòng)態(tài)原型模式是JavaScript中使用比較多的兩種創(chuàng)建對(duì)象的方式份氧。
? ? 建議以后使用動(dòng)態(tài)原型模式。他解決了組合模式的封裝不徹底的缺點(diǎn)弯屈。
原文鏈接:https://blog.csdn.net/u012468376/java/article/details/53121081
五蜗帜、原型鏈
每個(gè)實(shí)例對(duì)象( object )都有一個(gè)私有屬性(稱之為 __proto__ )指向它的構(gòu)造函數(shù)的原型對(duì)象(prototype )。該原型對(duì)象也有一個(gè)自己的原型對(duì)象( __proto__ ) 资厉,層層向上直到一個(gè)對(duì)象的原型對(duì)象為null厅缺。根據(jù)定義,null沒有原型酌住,并作為這個(gè)原型鏈中的最后一個(gè)環(huán)節(jié)店归。
2. JS 如何實(shí)現(xiàn)繼承?
一酪我、原型鏈實(shí)現(xiàn)繼承
原型鏈實(shí)現(xiàn)繼承的思想:利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。
原型鏈的基本概念:? 當(dāng)一個(gè)原型對(duì)象等于另一個(gè)類型的實(shí)例且叁,此時(shí)的原型對(duì)象將包含一個(gè)指向另一個(gè)指向另一個(gè)原型的指針都哭。同時(shí),另一個(gè)原型中也包含著一個(gè)指向另一個(gè)構(gòu)造函數(shù)的指針逞带。如果另一個(gè)原型是另一個(gè)類型的實(shí)例欺矫,此時(shí)實(shí)例和原型就構(gòu)成了原型鏈
function SuperType(){
? ? ? this.property=true;
}
SuperType.prototype.getSuperValue=function(){
? ? ? returnthis.property;
}
function SubType(){
? ? ? this.subproperty=false;
}//通過創(chuàng)建SuperType的實(shí)例繼承了SuperTypeSubType.prototype=new SuperType();SubType.prototype.getSubValue=function(){
? ? ? returnthis.subproperty;
}
var instance=new SubType();
alert(instance.getSuperValue());? //true
對(duì)于代碼的解釋:
首先定義了兩個(gè)類型SuperType和SubType。此時(shí)SubType通過創(chuàng)建SuperType的實(shí)例(new SuperType())展氓,并將該實(shí)例(new?SuperType())賦給了SubType的原型SubType.prototype的方式? ?繼承了 SuperType穆趴。
此時(shí)存在于SuperType中實(shí)例中的所有屬性和方法,也存在于SubType.prototype中遇汞。此時(shí)未妹,實(shí)例、原型和構(gòu)造函數(shù)之間的關(guān)系如下圖所示空入。
SubType的實(shí)例instance指向SubType的原型SubType.prototype络它,SubType.prototype又指向SuperType的原型。
注意:getSuperValue()方法仍然還在SuperType.prototype中歪赢,但property則位于SubType.prototype中化戳。這是因?yàn)閜roperty是一個(gè)實(shí)例屬性,而getSuperValue()則是一個(gè)原型方法埋凯。
原型鏈存在的問題:
1)包含引用類型值的原型屬性會(huì)被所有實(shí)例共享点楼,這會(huì)導(dǎo)致對(duì)一個(gè)實(shí)例的修改會(huì)影響另一個(gè)實(shí)例扫尖。在通過原型來實(shí)現(xiàn)繼承時(shí),原型實(shí)際上會(huì)變成另一個(gè)類型的實(shí)例掠廓。原先的實(shí)例屬性就變成了現(xiàn)在的原型屬性
(2)在創(chuàng)建子類型的實(shí)例時(shí)换怖,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)
二、借用構(gòu)造函數(shù)實(shí)現(xiàn)繼承
借用構(gòu)造函數(shù)的基本思想却盘,即在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)狰域。函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對(duì)象,因此通過使用apply()和call()方法可以在新創(chuàng)建的對(duì)象上執(zhí)行構(gòu)造函數(shù)黄橘。
function SuperType(){
? ? ? this.colors=["red", "blue", "green"];
}function SubType(){
? ? ? //繼承SuperType? SuperType.call(this);}varinstance1=new SubType();
instance1.colors.push("black");
alert(instance1.colors);? //red,bllue,green,blackvarinstance2=new SubType();
alert(instance2.colors);? //red,blue,green
代碼的解釋:
SuperType.call(this);“借調(diào)”了超類型的構(gòu)造函數(shù)兆览。通過使用call()方法(或者apply()方法),在新創(chuàng)建的SubType實(shí)例的環(huán)境下調(diào)用了SuperType構(gòu)造函數(shù)塞关。這樣一來就會(huì)在新的SubType對(duì)象上執(zhí)行SuperType()函數(shù)中定義的所有對(duì)象初始化代碼抬探。結(jié)果,SubType的每個(gè)實(shí)例都會(huì)有自己的colors屬性副本
借用構(gòu)造函數(shù)的優(yōu)勢(shì):可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)
function SuperType(name){
? ? ? this.name=name;
}function SubType(){
? ? ? //繼承了SuperType,同時(shí)還傳遞了參數(shù)SuperType.call(this,"mary");//this(SubType的實(shí)例)調(diào)用SuperType構(gòu)造函數(shù)
? ? ? //實(shí)例屬性this.age=22;
}varinstance=new SubType();
alert(instance.name);? //maryalert(instance.age);//29
借用構(gòu)造函數(shù)的問題:
1)無法避免構(gòu)造函數(shù)模式存在的問題帆赢,方法都在構(gòu)造函數(shù)中定義小压,因此無法復(fù)用函數(shù)。
2)在超類型的原型中定義的方法椰于,對(duì)子類型而言是不可見的怠益。因此這種技術(shù)很少單獨(dú)使用。
三瘾婿、組合繼承
組合繼承:指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一起蜻牢。思路是使用原型鏈實(shí)現(xiàn)對(duì)原型方法的繼承,而通過借用構(gòu)造函數(shù)來實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承偏陪。這樣抢呆,既通過在原型上定義方法實(shí)現(xiàn)了函數(shù)的復(fù)用,又能夠保證每個(gè)實(shí)例都有它自己的屬性笛谦。
function SuperType(name){
? ? ? this.name=name;
? ? ? this.colors=["red", "blue", "green"];
}
SuperType.prototype.sayName=function(){
? ? ? alert(this.name);
};function SubType(name, age){
? ? ? //繼承屬性? ? 使用借用構(gòu)造函數(shù)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承? ? SuperType.call(this,name);
? ? ? this.age=age;
}//繼承方法? ? 使用原型鏈實(shí)現(xiàn)SubType.prototype=new SuperType();SubType.prototype.constructor=SubType;
subType.prototype.sayAge=function(){
? ? ? alert(this.age);
};varinstance1=newSubType("mary", 22);
instance1.colors.push("black");
alert(instance1.colors);? //red,blue,green,blackinstance1.sayName();//maryinstance1.sayAge();//22varinstance2=newSubType("greg", 25);
alert(instance2.colors);? //red,blue,greeninstance2.sayName();//greginstance2.sayAge();//25
代碼解釋:
? SuperType構(gòu)造函數(shù)定義了兩個(gè)屬性:name和colors抱虐。SuperType的原型定義了一個(gè)方法sayName()。
? SubType構(gòu)造函數(shù)在調(diào)用SuperType構(gòu)造函數(shù)時(shí)傳入了name參數(shù)饥脑。并定義了自己的屬性age恳邀。
然后,將SuperType實(shí)例賦值給SubType的原型好啰,然后又在新原型上定義了sayAge方法轩娶。
此時(shí)可以讓兩個(gè)不同的SubType實(shí)例分別擁有自己的屬性,又可以使用相同的方法
組合繼承的優(yōu)勢(shì):
避免了原型鏈和借用構(gòu)造函數(shù)的缺點(diǎn)框往,融合了他們的優(yōu)點(diǎn)鳄抒,是JavaScript中最常用的繼承模式。instanceof和isprototypeOf()也能夠用于識(shí)別基于組合繼承創(chuàng)建的對(duì)象
四、原型式繼承
五许溅、寄生式繼承
六瓤鼻、寄生組合式繼承
3. 實(shí)現(xiàn)一個(gè)函數(shù) clone 可以對(duì) JavaScript 中的五種主要數(shù)據(jù)類型(Number、string贤重、Object茬祷、Array、
? ? Boolean)進(jìn)行復(fù)制并蝗?
function clone(obj) {
? var o;
? switch (typeof obj) {
? ? case "undefined":
? ? ? ? break;
? ? case "string":
? ? ? ? o = obj + "";
? ? ? ? break;
? ? case "number":
? ? ? ? o = obj - 0;
? ? ? ? break;
? ? case "boolean":
? ? ? ? o = obj;
? ? ? ? break;
? ? case "object": // object 分為兩種情況 對(duì)象(Object)或數(shù)組(Array)
? ? if (obj === null) {
? ? ? ? o = null;
? ? } else {
? ? ? if (Object.prototype.toString.call(obj).slice(8, -1) === "Array") {
? ? ? ? o = [];
? ? ? ? for (var i = 0; i < obj.length; i++) {
? ? ? ? ? ? o.push(clone(obj[i]));
? ? ? ? }
? ? ? } else {
? ? ? ? o = {};
? ? ? ? for (var k in obj) {
? ? ? ? ? ? o[k] = clone(obj[k]);
? ? ? ? }
? ? ? }
? ? }
? ? break;
? default:
? ? o = obj;
? ? break;
? }
? return o;
}
4. 對(duì) String 做擴(kuò)展祭犯,實(shí)現(xiàn)如下方式獲取字符串中頻率最高的字符:
var str = 'ahbbccdeddddfg';
//補(bǔ)充方法
String.prototype.getMostOften = function(){
? var obj = {}
? for(var i = 0; i<str.length; i++){
??? var letter = str.charAt(i)
??? if(!obj[letter]){
????? obj[letter] = 1
??? }else{
????? obj[letter]++
??? }
? }
? console.log(obj)
? var maxIdx = 0
? var maxLetter = ''
? for(var key in obj){
??? if(maxIdx < obj[key]){
????? maxIdx = obj[key]
????? maxLetter = key
??? }
? }
? return maxLetter
}
//
var ch = str.getMostOften()
console.log(ch) // d,因?yàn)?d 出現(xiàn)了 5 次
5. 有如下代碼滚停,代碼中并未添加 toString 方法沃粗,這個(gè)方法是哪里來的?畫出原型鏈圖進(jìn)行解釋:
? ? function People() {
? ? }
? ? var p = new People()
? ? p.toString()
分析如下:
記當(dāng)前對(duì)象為p键畴,查找p屬性最盅、方法
沒有找到,通過p的proto屬性起惕,找到其類型People的prototype屬性(記為prop)繼續(xù)查找
沒有找到涡贱,把prop記為obj做遞歸重復(fù)步驟一,通過類似方法找到prop的類型Object的 prototype進(jìn)行查找惹想,直至找到toString方法
6. 有如下代碼问词,解釋 Person、 prototype嘀粱、__proto__戏售、p、constructor 之間的關(guān)聯(lián):
? ? function Person(name) {
? ? ? this.name = name;
? ? }
? ? Person.prototype.sayName = function() {
? ? ? console.log("My name is :" + this.name);
? ? }
? ? var p = new Person("Oli")
? ? p.sayName();
分析:
1.我們通過函數(shù)定義了類Person草穆,類(函數(shù))Person自動(dòng)獲得屬性prototype
2.Person是一個(gè)構(gòu)造函數(shù),構(gòu)造函數(shù)構(gòu)造出實(shí)例 p搓译,
3.p是構(gòu)造函數(shù)Person的一個(gè)實(shí)例悲柱,p的 proto 指向了Person的prototype屬性,
4.prototype是構(gòu)造函數(shù)內(nèi)部的原型對(duì)象些己,所以擁有contructor和proto屬性豌鸡,其中contructor屬性指向構(gòu)造函數(shù)Person,proto指向該對(duì)象的原型.
7. 下面兩種寫法有什么區(qū)別段标?
? ? // 方法一:
? ? function People(name, sex) {
? ? ? this.name = name;
? ? ? this.sex = sex;
? ? ? this.printName = function() {
? ? ? ? console.log(this.name);
? ? ? }
? ? }
? ? var p1 = new People("Oli", 2)
? ? // 方法二:
? ? function Person(name, sex) {
? ? ? this.name = name;
? ? ? this.sex = sex;
? ? }
? ? Person.prototype.printName = function() {
? ? ? console.log(this.name);
? ? }
? ? var p1 = new Person("Aman", 2);
方法一是直接將方法放在自己的私有空間內(nèi)涯冠,沒有放在原型鏈上,并沒有起到公共代碼的作用
方法二通過將方法放在原型鏈上逼庞,起到了公共代碼的作用蛇更,節(jié)省了代碼量,提升了性能。
8. 補(bǔ)全代碼派任,實(shí)現(xiàn)繼承:
? ? function Person(name, sex){
? ? ? // 補(bǔ)全
????? this.name=name; // todo ...
????? this.sex=sex
? ? };
? ? Person.prototype.getName = function() {
? ? ? // 補(bǔ)全?????
??????? console.log(this.name)// todo ...
? ? };
? ? function Male(name, sex, age) {
? ? ? // 補(bǔ)全
????? Person.call(this,name,sex)
????? this.age=age //todo ...
? ? };
? ? ? // 補(bǔ)全
????? Male.prototype=Object.create(Person.prototype);//todo ...
????? Male.prototype.constructor=Male
? ? Male.prototype.getAge = function() {
? ? ? // 補(bǔ)全
????? onsole.log(this.age)//todo ...
? ? };
? ? var catcher = new Male("Oli", "男", 2);
? ? catcher.getName();
9. 如下代碼中 call 的作用是什么砸逊?
? ? function Person(name, sex) {
? ? ? this.name = name;
? ? ? this.sex = sex;
? ? }
? ? function Male(name, sex, age) {
? ? ? Person.call(this, name, sex); // 這里的 call 有什么作用?
? ? ? this.age = age;
? ? }
call調(diào)用了Person構(gòu)造函數(shù)掌逛,將Person里面的this替換成指向Male構(gòu)造函數(shù)的對(duì)象
10.判斷字符串是否同構(gòu):如果 *__s __*中的字符可以被替換得到?*t *师逸,那么這兩個(gè)字符串是同構(gòu)的。所有出現(xiàn)的字符都必須用另一個(gè)字符替換豆混,同時(shí)保留字符的順序篓像。兩個(gè)字符不能映射到同一個(gè)字符上,但字符可以映射自己本身皿伺。
比如輸入: s = "egg", t = "add"员辩,輸出: true
輸入: s = "foo", t = "bar"輸出: false
var isIsomorphic = function(s, t) {
? ? let i = 0
? ? while (i < s.length) {
? ? ? if (s.indexOf(s[i]) != t.indexOf(t[i])){
? ? ? ? return false
? ? ? }
? ? ? i++
? ? }
? ? return true
};
console.log(isIsomorphic('foo','bar'));