<04>介紹 JavaScript 原型,原型鏈?有何特點?

JavaScript高階筆試題

1. 判斷以下程式的輸出結果:

var age=100;

function test(){

this。age=50;

return function(){

return this。age;

}

}

var m=new test();

alert(m());

var n=test();

alert(n());

答案:

100 50

建構函式一旦返回一個物件,就不再建立新物件

m獲得的是function(){ return this。age; }

n=test(),this指向window。先將全域性變數age變為50,又返回一個函式function(){ return this。age; }儲存在變數n中

呼叫n時,this指向window。

2. 判斷以下程式的輸出結果:

var name=“The Window”;

var obj={

name:“My obj”,

getName:function(){

return function(){

return this。name;

}

}

};

console。log(obj。getName()());

答案:

the window

obj。getName() 返回一個函式物件function(){ return this。name; }

(function(){ return this。name; }()) 相當於匿名函式自調,this指向window

3. 判斷以下程式的輸出結果:

var length=10;

function fn(){

console。log(this。length);

}

var obj={

length:5,

method:function(fn){

fn();

arguments[0]();

}

};

obj。method(fn,1)

答案:

10 2

fn() this指向window,所以輸出10

arguments[0]() 屬於特殊情況,this->arguments,相當於arguments。0(), 所以,this指向arguments。所以length輸出的是obj。method()的引數個數,為2。

4. 統計一個字串中出現次數最多的字元是? 共出現多少次

答案:

var dict={};

var c=“”, max=1;

for(var i=0;i

var char=str[i];

if(dict[char]===undefined)

dict[char]=1;

else{

dict[char]+=1;

if(dict[char]>max){

max=dict[char];

c=char;

}

}

}

console。log(c,max);

提前建立一個空物件,用於儲存每個字母出現的次數。

提前建立變數,準備儲存出現次數最多的字元和出現的次數。

然後,遍歷字串中每個字母,每遍歷一個字母就判斷結果物件中是否包含以當前字母為屬性名的屬性。如果不包含以當前字母為屬性名的屬性,說明是首次遇見該字母,就向結果物件中強行新增以該字母為屬性名的屬性,值暫時為1。如果結果物件中已經包含以當前字母為屬性名的屬性,說明不是第一次碰見該字母。則取出該字母名屬性對應的次數+1。只要當前字母出現的次數>之前變數中記錄的最大次數,就用當前字母和出現次數,取而代之。

5. 判斷以下程式的輸出結果:

for(var i=0;i<5;i++){

setTimeout(function(){

console。log(i);

},0)

}

console。log(i);

答案:

5 5 5 5 5

函式定義時,函式內容是不執行的,所以i還是i,不會變成0,1,2,3,4

定時器中的回撥函式只能在主程式執行完才能開始執行

當主程式執行完,迴圈變數i,已經被改為5了。

6. 判斷以下程式的輸出結果:

window。color=“red”;

let color=“green”;

let obj={

color:“blue”

};

let sayColor=()=>{

return this。color;

}

console。log(sayColor。apply(obj));

let a=10;

console。log(window。a);

答案:

red undefined

let相當於匿名函式自調,所以,let宣告的變數,不會自動加入到window

箭頭函式內外this通用,所以apply也無法替換sayColor函式內的this,所以this指向window,所以輸出red

7. 判斷以下程式的輸出結果:

var c=1;

function c(c){

console。log(c);

var c=3;

}

c(2);

答案:

報錯: TypeError: c不是一個函式

function c(c){} 整體被宣告提前,後又被c=1代替。所以,c最後不是一個函式,而是數字1

8. 判斷以下程式的輸出結果:

function change(){

alert(typeof fn)

function fn(){ alert(‘hello’) }

var fn;

}

change();

答案:

function

function fn(){…}被整體宣告提前了

var fn發現已經有fn變量了,就不再重複建立,所以,var fn沒作用。

9. 判斷以下程式的輸出結果:

a=3

a。prop=4;

alert(a+a。prop)

答案:

NaN

a。prop=4,等效於new Number(a)。prop=4, 但是new Number(a),使用後自動釋放,4也不存在了

再次使用a。prop,又等效於新的new Number(a),所以沒有prop屬性,值為undefined。

數字+undefined, undefined隱式轉為數字NaN,導致計算結果為NaN

10. 判斷以下程式的輸出結果:

var o={

a:10,

b:{

a:12,

fn:function(){

var a=13;

console。log(this。a);

}

}

}

o。b。fn();

答案:

12

this指。前的o。b物件,所以a為12

11. 判斷以下程式的輸出結果:

var obj1 = {

name: ‘obj1’,

fn: function() {

document。write(this。name);

}

};

var obj2 = {name: ‘obj2’};

var obj3 = {name: ‘obj3’};

obj1。fn();

var newFn = obj1。fn;

newFn();

newFn。call(obj2);

obj3。fn = newFn;

obj3。fn();

答案:

obj1 空字串 obj2 obj3

this指。前的obj1

因為newFn呼叫時,前邊沒有。,所以this->window,

call是強行替換newFn中的this為obj2

this指。前的obj3

12. 一個數組 par 中存放有多個人員的資訊,每個人員的資訊由年齡 age 和姓名 name 組成,如{age: 2, name: 'xx'}。請寫一段 JS 程式,對這個陣列按年齡從小到大進行排序。

答案:

function parSort(arr, propName) {

arr。sort(function(a, b) {

return a[propName]-b[propName];

});

}

parSort(arr, “age”)

1) 陣列的sort函式的引數,是一個比較器函式。比較器函式的形參a和b,指當前要排序的陣列中的任意兩個作比較的元素。如果作比較的a和b兩個元素是簡單的數字型別,則a直接和b相減,就可比較兩數大小。但是,如果a和b都是物件型別的元素。要比較兩個物件中某個屬性的值得大小,就必須用a。屬性-b。屬性。如果屬性名是靈活的,來自於變數,則必須用a[屬性名變數]-b[屬性名變數],就可比出兩個物件的某個屬性值得大小。

2) 最後,陣列是引用型別的物件,在函式內修改陣列,等效於修改原陣列。所以不用返回值。

13. 有字串 var = 'abc345efgabcab',請寫出 3 條 JS 語句分別實現如下 3 個功能:

1)去掉字串中的a、b、c 字元,形成結果:‘345efg’

2)將字串中的數字用中括號括起來,形成結果:‘abc[345]efgabcab’

3)將字串中的每個數字的值分別乘以 2,形成結果:‘abc6810efgabcab’

答案:

1)str。replace(/([a-c])/g, ‘’);

2)str。replace(/(\d+)/g, ‘[$1]’);

3)str。replace(/(\d)/g, function(num) {return num*2;});

1) 將字串中的a,b,c三個字元都替換為空字串

2) 找到字串中多個連續的數字,分為一組,然後將這一組的內容,替換為用[]包裹。$1,可獲得關鍵詞中第一個()包裹的內容。

3) 找到字串中每個數字,*2後,再放回原位置。

14. 判斷以下程式的輸出結果:

var a=10;

var obj={

a:20,

intr:function(){

var a=30;

console。log(this。a);

}

}

obj。intr();

var intr=obj。intr;

intr();

答案:

20 10

obj。intr(),this指。前的obj,所以輸出20

intr(), this指window,所以輸出10

15. 判斷以下程式的輸出結果:

function fun(){

for(var i=0,arr=[];i<3;i++){

arr[i]=function(){

console。log(i);

}

}

return arr;

}

var funs=fun();

funs[0]();

funs[1]();

funs[2]();

答案:

3 3 3

16. 定義函式實現深克隆一個物件:

答案:

var lilei={

sname:“Li Lei”,

score:null,

friends:[“jack”,“rose”],

address:{

prov:“北京”,

city:“北京”,

area:“海淀”,

street:“萬壽路”

},

sage:11

}

function clone(obj){

if(obj===null){

return null;

}else if({}。toString。call(obj)===“[object Array]”){

var newArr=[];

newArr=obj。slice();

return newArr;

}

var newObj={};

//遍歷原obj的每個屬性

for(var key in obj){

//如果原物件中當前屬性值是原始型別

if(typeof obj[key]!==“object”){

//在新物件中新增和原物件中同名的屬性

newObj[key]=obj[key];

//原始型別複製,就是複製副本

}else{//否則,當前屬性不是原始型別的值,再次呼叫clone函式,繼續複製當前屬性值

newObj[key]=clone(obj[key])

}

}

return newObj;

}

console。log(lilei);

var lilei2=clone(lilei);

console。log(lilei2);

console。log(lilei==lilei2);//true

lilei2。address。area=“朝陽”;

console。log(lilei。address);

17. 介紹 JavaScript 的原型,原型鏈?有什麼特點?

答案:

原型:

- JavaScript 的所有物件中都包含了一個 [proto] 內部屬性,這個屬性所對應的就是該物件的原型

- JavaScript 的函式物件,除了原型 [proto] 之外,還預置了 prototype 屬性

- 當函式物件作為建構函式建立例項時,該 prototype 屬性值將被作為例項物件的原型 [proto]。

原型鏈:

- 當一個物件呼叫的屬性/方法自身不存在時,就會去自己 [proto] 關聯的前輩 prototype 物件上去找

- 如果沒找到,就會去該 prototype 原型 [proto] 關聯的前輩 prototype 去找。依次類推,直到找到屬性/方法或 undefined 為止。從而形成了所謂的“原型鏈”

原型特點:

- JavaScript 物件是透過引用來傳遞的,當修改原型時,與之相關的物件也會繼承這一改變

18. 談談Javascript 垃圾回收方法

答案:

標記清除(mark and sweep)

- 這是 JavaScript 最常見的垃圾回收方式,當變數進入執行環境的時候,比如函式中宣告一個變數,垃圾回收器將其標記為“進入環境”,當變數離開環境的時候(函式執行結束)將其標記為“離開環境”

- 垃圾回收器會在執行的時候給儲存在記憶體中的所有變數加上標記,然後去掉環境中的變數以及被環境中變數所引用的變數(閉包),在這些完成之後仍存在標記的就是要刪除的變量了

引用計數(reference counting)

- 在低版本 IE 中經常會出現記憶體洩露,很多時候就是因為其採用引用計數方式進行垃圾回收。引用計數的策略是跟蹤記錄每個值被使用的次數,當聲明瞭一個 變數並將一個引用型別賦值給該變數的時候這個值的引用次數就加 1,如果該變數的值變成了另外一個,則這個值得引用次數減 1,當這個值的引用次數變為 0 的時 候,說明沒有變數在使用,這個值沒法被訪問了,因此可以將其佔用的空間回收,這樣垃圾回收器會在執行的時候清理掉引用次數為 0 的值佔用的空間

19. 說說嚴格模式的限制

答案:

嚴格模式主要有以下限制:

- 變數必須聲明後再使用

- 函式的引數不能有同名屬性,否則報錯

- 不能使用 with 語句

- 不能對只讀屬性賦值,否則報錯

- 不能使用字首 0 表示八進位制數,否則報錯

- 不能刪除不可刪除的屬性,否則報錯

- 不能刪除變數 delete prop,會報錯,只能刪除屬性 delete global[prop]

- eval 不會在它的外層作用域引入變數

- eval 和 arguments 不能被重新賦值

- arguments 不會自動反映函式引數的變化

- 不能使用 arguments。callee

- 不能使用 arguments。caller

- 禁止 this 指向全域性物件

- 不能使用 fn。caller 和 fn。arguments 獲取函式呼叫的堆疊

- 增加了保留字(比如 protected、static 和 interface)

20. 使用正則表示式驗證郵箱格式

答案:

var reg = /^(\w)+(\。\w+)*@(\w)+((\。\w{2,3}){1,3})$/;

var email = “example@qq。com”;

console。log(reg。test(email)); // true

21. 使用typeof bar ===“object”來確定bar是否是一個物件時有什麼潛在的缺陷?這個陷阱如何避免?

答案:

儘管typeof bar ===“object”是檢查bar是否是物件的可靠方法,但JavaScript中令人驚訝的問題null也被認為是一個物件!

因此,對於大多數開發人員來說,下面的程式碼會將true(而不是false)列印到控制檯:

var bar = null;

console。log(typeof bar === “object”); // logs true!

只要知道這一點,就可以透過檢查bar是否為空來輕鬆避免該問題:

console。log((bar !== null) && (typeof bar === “object”)); // logs false

為了讓我們的答案更加的完整,還有兩件事值得注意: 首先,如果bar是一個函式,上面的解決方案將返回false。在大多數情況下,這是所期望的行為,但是在您希望函式返回true的情況下,您可以將上述解決方案修改為:

console。log((bar !== null) && ((typeof bar === “object”) || (typeof bar === “function”)));

其次,如果bar是陣列,則上述解決方案將返回true(例如,如果var bar = [];)。在大多數情況下,這是所希望的行為,因為陣列確實是物件,但是在您想要對陣列也是false的情況下,可以將上述解決方案修改為:

console。log((bar !== null) && (typeof bar === “object”) && (toString。call(bar) !== “[object Array]”));

但是,還有一個替代方法對空值,陣列和函式返回false,但對於物件則為true:

console。log((bar !== null) && (bar。constructor === Object));

或者,如果您使用jQuery:

console。log((bar !== null) && (typeof bar === “object”) && (! $。isArray(bar)));

ES5使得陣列的情況非常簡單,包括它自己的空檢查:

console。log(Array。isArray(bar));

22. 以下程式碼的輸出是什麼?解釋你的答案

var a={},

b={key:‘b’},

c={key:‘c’};

a[b]=123;

a[c]=456;

console。log(a[b]);

答案:

456

原因如下:設定物件屬性時,JavaScript會隱式地將引數值串聯起來。在這種情況下,由於b和c都是物件,它們都將被轉換為“[object Object]”。因此,a [b]和a [c]都等價於[“[object Object]”],並且可以互換使用。因此,設定或引用[c]與設定或引用[b]完全相同。