.我們可以在下列哪個 HTML 元素中放置 Javascript 代碼?()
A.<script>
B.<javascript>
C.<js>
D.<scripting>
2.寫 "Hello World" 的正確 Javascript 語法是?()
A.("Hello World")
B."Hello World"
C.response.write("Hello World")
D.document.write("Hello World")
3.插入 Javacript 的正確位置是?()
A.<body>部分
B.<head>部分
C.<body>部分和<head>部分均可
4.引用名為 "xxx.js" 的外部腳本的正確語法是?()
A.<script src="xxx.js">
B.<script href="xxx.js">
C.<script name="xxx.js">
5.如何在警告框中寫入 "Hello World"?()
A.alertBox="Hello World"
B.msgBox("Hello World")
C.alert("Hello World")
D.alertBox("Hello World")
6.如何創建函數?()
A.function:myFunction()
B.function myFunction()
C.function=myFunction()
7.如何調用名為 "myFunction" 的函數?()
A.call function myFunction
B.call myFunction()
C.myFunction()
8.如何編寫當 i 等于 5 時執行一些語句的條件語句?()
A.if (i==5)
B.if i=5 then
C.if i=5
D.if i==5 then
9.如何編寫當 i 不等于 5 時執行一些語句的條件語句?()
A.if =! 5 then
B.if >< 5
C.if (i >< 5)
D.if (i != 5)
10.在 JavaScript 中,有多少種不同類型的循環?()
A.兩種。for 循環和 while 循環。
B.四種。for 循環、while 循環、do...while 循環以及 loop...until 循環。
C.一種。for 循環。
11.for 循環如何開始?()
A.for (i <= 5; i++)
B.for (i = 0; i <= 5; i++)
C.for (i = 0; i <= 5)
D.for i = 1 to 5
12.如何在 JavaScript 中添加注釋?()
A.' This is a comment
B.<!--This is a comment-->
C.//This is a comment
13.可插入多行注釋的 JavaScript 語法是?()
A./*This comment has more than one line*/
B.//This comment has more than one line//
C.<!--This comment has more than one line-->
14.定義 JavaScript 數組的正確方法是?()
A.var txt = new Array="George","John","Thomas"
B.var txt = new Array(1:"George",2:"John",3:"Thomas")
C.var txt = new Array("George","John","Thomas")
D.var txt = new Array:1=("George")2=("John")3=("Thomas")
15.如何把 7.25 四舍五入為最接近的整數?()
A.round(7.25)
B.rnd(7.25)
C.Math.rnd(7.25)
D.Math.round(7.25)
16.如何求得 2 和 4 中最大的數?()
A.Math.ceil(2,4)
B.Math.max(2,4)
C.ceil(2,4)
D.top(2,4)
17.打開名為 "window2" 的新窗口的 JavaScript 語法是?()
A.open.new("http://www.w3cschool.cn","window2")
B.new.window("http://www.w3cschool.cn","window2")
C.new("http://www.w3cschool.cn","window2")
D.window.open("http://www.w3cschool.cn","window2")
18.如何在瀏覽器的狀態欄放入一條消息?()
A.statusbar = "put your message here"
B.window.status = "put your message here"
C.window.status("put your message here")
D.status("put your message here")
19.如何獲得客戶端瀏覽器的名稱?()
A.client.navName
B.navigator.appName
C.browser.name
20.外部腳本必須包含 <script> 標簽。()
A.正確
B.錯誤
請把你的答案寫在留言區。^_^
者:Mark A
譯者:前端小智
來源:dev
在理解undefined和null之間的差異之前,我們先來看看它們的相似類。
它們屬于 JavaScript 的 7 種基本類型。
?let?primitiveTypes?=?['string','number','null','undefined','boolean','symbol',?'bigint'];
它們是屬于虛值,可以使用Boolean(value)或!!value將其轉換為布爾值時,值為false。
console.log(!!null);?//?false
console.log(!!undefined);?//?false
console.log(Boolean(null));?//?false
console.log(Boolean(undefined));?//?false
接著來看看它們的區別。
undefined是未指定特定值的變量的默認值,或者沒有顯式返回值的函數,如:console.log(1),還包括對象中不存在的屬性,這些 JS 引擎都會為其分配 undefined 值。
let?_thisIsUndefined;
const?doNothing?=?()?=>?{};
const?someObj?=?{
??a?:?"ay",
??b?:?"bee",
??c?:?"si"
};
console.log(_thisIsUndefined);?//?undefined
console.log(doNothing());?//?undefined
console.log(someObj["d"]);?//?undefined
null是“不代表任何值的值”。null是已明確定義給變量的值。在此示例中,當fs.readFile方法未引發錯誤時,我們將獲得null值。
fs.readFile('path/to/file',?(e,data)?=>?{
???console.log(e);?//?當沒有錯誤發生時,打印?null
???if(e){
?????console.log(e);
???}
???console.log(data);
?});
在比較null和undefined時,我們使用==時得到true,使用===時得到false:
?console.log(null?==?undefined);?//?true
?console.log(null?===?undefined);?//?false
&& 也可以叫邏輯與,在其操作數中找到第一個虛值表達式并返回它,如果沒有找到任何虛值表達式,則返回最后一個真值表達式。它采用短路來防止不必要的工作。
console.log(false?&&?1?&&?[]);?//?false
console.log("?"?&&?true?&&?5);?//?5
使用if語句
const?router:?Router?=?Router();
router.get('/endpoint',?(req:?Request,?res:?Response)?=>?{
???let?conMobile:?PoolConnection;
???try?{
??????//do?some?db?operations
???}?catch?(e)?{
???if?(conMobile)?{
????conMobile.release();
???}
??}
});
使用&&操作符
const?router:?Router?=?Router();
router.get('/endpoint',?(req:?Request,?res:?Response)?=>?{
??let?conMobile:?PoolConnection;
??try?{
?????//do?some?db?operations
??}?catch?(e)?{
????conMobile?&&?conMobile.release()
??}
});
||也叫或邏輯或,在其操作數中找到第一個真值表達式并返回它。這也使用了短路來防止不必要的工作。在支持 ES6 默認函數參數之前,它用于初始化函數中的默認參數值。
console.log(null?||?1?||?undefined);?//?1
function?logName(name)?{
??var?n?=?name?||?"Mark";
??console.log(n);
}
logName();?//?"Mark"
根據MDN文檔,+是將字符串轉換為數字的最快方法,因為如果值已經是數字,它不會執行任何操作。
DOM 代表文檔對象模型,是 HTML 和 XML 文檔的接口(API)。當瀏覽器第一次讀取(解析)HTML文檔時,它會創建一個大對象,一個基于 HTM L文檔的非常大的對象,這就是DOM。它是一個從 HTML 文檔中建模的樹狀結構。DOM 用于交互和修改DOM結構或特定元素或節點。
假設我們有這樣的 HTML 結構:
<!DOCTYPE?html>
<html?lang="en">
<head>
???<meta?charset="UTF-8">
???<meta?name="viewport"?content="width=device-width,?initial-scale=1.0">
???<meta?http-equiv="X-UA-Compatible"?content="ie=edge">
???<title>Document?Object?Model</title>
</head>
<body>
???<div>
??????<p>
?????????<span></span>
??????</p>
??????<label></label>
??????<input>
???</div>
</body>
</html>
等價的DOM是這樣的:
JS 中的document對象表示DOM。它為我們提供了許多方法,我們可以使用這些方法來選擇元素來更新元素內容,等等。
當事件發生在DOM元素上時,該事件并不完全發生在那個元素上。在“冒泡階段”中,事件冒泡或向上傳播至父級,祖父母,祖父母或父級,直到到達window為止;而在“捕獲階段”中,事件從window開始向下觸發元素 事件或event.target。
事件傳播有三個階段:
當事件發生在DOM元素上時,該事件并不完全發生在那個元素上。在冒泡階段,事件冒泡,或者事件發生在它的父代,祖父母,祖父母的父代,直到到達window為止。
假設有如下的 HTML 結構:
<div?class="grandparent">
??<div?class="parent">
????<div?class="child">1</div>
??</div>
</div>
對應的 JS 代碼:
function?addEvent(el,?event,?callback,?isCapture?=?false)?{
??if?(!el?||?!event?||?!callback?||?typeof?callback?!==?'function')?return;
??if?(typeof?el?===?'string')?{
????el?=?document.querySelector(el);
??};
??el.addEventListener(event,?callback,?isCapture);
}
addEvent(document,?'DOMContentLoaded',?()?=>?{
??const?child?=?document.querySelector('.child');
??const?parent?=?document.querySelector('.parent');
??const?grandparent?=?document.querySelector('.grandparent');
??addEvent(child,?'click',?function?(e)?{
????console.log('child');
??});
??addEvent(parent,?'click',?function?(e)?{
????console.log('parent');
??});
??addEvent(grandparent,?'click',?function?(e)?{
????console.log('grandparent');
??});
??addEvent(document,?'click',?function?(e)?{
????console.log('document');
??});
??addEvent('html',?'click',?function?(e)?{
????console.log('html');
??})
??addEvent(window,?'click',?function?(e)?{
????console.log('window');
??})
});
addEventListener方法具有第三個可選參數useCapture,其默認值為false,事件將在冒泡階段中發生,如果為true,則事件將在捕獲階段中發生。如果單擊child元素,它將分別在控制臺上記錄child,parent,grandparent,html,document和window,這就是事件冒泡。
當事件發生在 DOM 元素上時,該事件并不完全發生在那個元素上。在捕獲階段,事件從window開始,一直到觸發事件的元素。
假設有如下的 HTML 結構:
<div?class="grandparent">
??<div?class="parent">
????<div?class="child">1</div>
??</div>
</div>
對應的 JS 代碼:
function?addEvent(el,?event,?callback,?isCapture?=?false)?{
??if?(!el?||?!event?||?!callback?||?typeof?callback?!==?'function')?return;
??if?(typeof?el?===?'string')?{
????el?=?document.querySelector(el);
??};
??el.addEventListener(event,?callback,?isCapture);
}
addEvent(document,?'DOMContentLoaded',?()?=>?{
??const?child?=?document.querySelector('.child');
??const?parent?=?document.querySelector('.parent');
??const?grandparent?=?document.querySelector('.grandparent');
??addEvent(child,?'click',?function?(e)?{
????console.log('child');
??});
??addEvent(parent,?'click',?function?(e)?{
????console.log('parent');
??});
??addEvent(grandparent,?'click',?function?(e)?{
????console.log('grandparent');
??});
??addEvent(document,?'click',?function?(e)?{
????console.log('document');
??});
??addEvent('html',?'click',?function?(e)?{
????console.log('html');
??})
??addEvent(window,?'click',?function?(e)?{
????console.log('window');
??})
});
addEventListener方法具有第三個可選參數useCapture,其默認值為false,事件將在冒泡階段中發生,如果為true,則事件將在捕獲階段中發生。如果單擊child元素,它將分別在控制臺上打印window,document,html,grandparent和parent,這就是事件捕獲。
event.preventDefault() 方法可防止元素的默認行為。如果在表單元素中使用,它將阻止其提交。如果在錨元素中使用,它將阻止其導航。如果在上下文菜單中使用,它將阻止其顯示或顯示。event.stopPropagation()方法用于阻止捕獲和冒泡階段中當前事件的進一步傳播。
我們可以在事件對象中使用event.defaultPrevented屬性。它返回一個布爾值用來表明是否在特定元素中調用了event.preventDefault()。
const?obj?=?{};
console.log(obj.someprop.x);
顯然,由于我們嘗試訪問someprop屬性中的x屬性,而 someprop 并沒有在對象中,所以值為 undefined。記住對象本身不存在的屬性,并且其原型的默認值為undefined。因為undefined沒有屬性x,所以試圖訪問將會報錯。
簡單來說,event.target是發生事件的元素或觸發事件的元素。
假設有如下的 HTML 結構:
<div?onclick="clickFunc(event)"?style="text-align:?center;margin:15px;
border:1px?solid?red;border-radius:3px;">
????<div?style="margin:?25px;?border:1px?solid?royalblue;border-radius:3px;">
????????<div?style="margin:25px;border:1px?solid?skyblue;border-radius:3px;">
??????????<button?style="margin:10px">
?????????????Button
??????????</button>
????????</div>
????</div>
?</div>
JS 代碼如下:
function?clickFunc(event)?{
??console.log(event.target);
}
如果單擊 button,即使我們將事件附加在最外面的div上,它也將打印 button 標簽,因此我們可以得出結論event.target是觸發事件的元素。
event.currentTarget是我們在其上顯式附加事件處理程序的元素。
假設有如下的 HTML 結構:
<div?onclick="clickFunc(event)"?style="text-align:?center;margin:15px;
border:1px?solid?red;border-radius:3px;">
????<div?style="margin:?25px;?border:1px?solid?royalblue;border-radius:3px;">
????????<div?style="margin:25px;border:1px?solid?skyblue;border-radius:3px;">
??????????<button?style="margin:10px">
?????????????Button
??????????</button>
????????</div>
????</div>
?</div>
JS 代碼如下:
function?clickFunc(event)?{
??console.log(event.currentTarget);
}
如果單擊 button,即使我們單擊該 button,它也會打印最外面的div標簽。在此示例中,我們可以得出結論,event.currentTarget是附加事件處理程序的元素。
==用于一般比較,===用于嚴格比較,==在比較的時候可以轉換數據類型,===嚴格比較,只要類型不匹配就返回flase。
先來看看 == 這兄弟:
強制是將值轉換為另一種類型的過程。在這種情況下,==會執行隱式強制。在比較兩個值之前,==需要執行一些規則。
假設我們要比較x == y的值。
注意:toPrimitive首先在對象中使用valueOf方法,然后使用toString方法來獲取該對象的原始值。
舉個例子。
xyx == y55true1'1'truenullundefinedtrue0falsetrue'1,2'[1,2]true'[object Object]'{}true
這些例子都返回true。
第一個示例符合條件1,因為x和y具有相同的類型和值。
第二個示例符合條件4,在比較之前將y轉換為數字。
第三個例子符合條件2。
第四個例子符合條件7,因為y是boolean類型。
第五個示例符合條件8。使用toString()方法將數組轉換為字符串,該方法返回1,2。
最后一個示例符合條件8。使用toString()方法將對象轉換為字符串,該方法返回[object Object]。
xyx === y55true1'1'falsenullundefinedfalse0falsefalse'1,2'[1,2]false'[object Object]'{}false
如果使用===運算符,則第一個示例以外的所有比較將返回false,因為它們的類型不同,而第一個示例將返回true,因為兩者的類型和值相同。
具體更多規則可以對參考我之前的文章:
我對 JS 中相等和全等操作符轉化過程一直很迷惑,直到有了這份算法
先看下面的例子:
let?a?=?{?a:?1?};
let?b?=?{?a:?1?};
let?c?=?a;
console.log(a?===?b);?//?打印?false,即使它們有相同的屬性
console.log(a?===?c);?//?true
JS 以不同的方式比較對象和基本類型。在基本類型中,JS 通過值對它們進行比較,而在對象中,JS 通過引用或存儲變量的內存中的地址對它們進行比較。這就是為什么第一個console.log語句返回false,而第二個console.log語句返回true。a和c有相同的引用地址,而a和b沒有。
!!運算符可以將右側的值強制轉換為布爾值,這也是將值轉換為布爾值的一種簡單方法。
console.log(!!null);?//?false
console.log(!!undefined);?//?false
console.log(!!'');?//?false
console.log(!!0);?//?false
console.log(!!NaN);?//?false
console.log(!!'?');?//?true
console.log(!!{});?//?true
console.log(!![]);?//?true
console.log(!!1);?//?true
console.log(!![].length);?//?false
可以使用逗號運算符在一行中計算多個表達式。它從左到右求值,并返回右邊最后一個項目或最后一個操作數的值。
let?x?=?5;
x?=?(x++?,?x?=?addFive(x),?x?*=?2,?x?-=?5,?x?+=?10);
function?addFive(num)?{
??return?num?+?5;
}
上面的結果最后得到x的值為27。首先,我們將x的值增加到6,然后調用函數addFive(6)并將6作為參數傳遞并將結果重新分配給x,此時x的值為11。之后,將x的當前值乘以2并將其分配給x,x的更新值為22。然后,將x的當前值減去5并將結果分配給x x更新后的值為17。最后,我們將x的值增加10,然后將更新的值分配給x,最終x的值為27。
提升是用來描述變量和函數移動到其(全局或函數)作用域頂部的術語。
為了理解提升,需要來了解一下執行上下文。執行上下文是當前正在執行的“代碼環境”。執行上下文有兩個階段:編譯和執行。
編譯-在此階段,JS 引薦獲取所有函數聲明并將其提升到其作用域的頂部,以便我們稍后可以引用它們并獲取所有變量聲明(使用var關鍵字進行聲明),還會為它們提供默認值:undefined。
執行——在這個階段中,它將值賦給之前提升的變量,并執行或調用函數(對象中的方法)。
注意:只有使用var聲明的變量,或者函數聲明才會被提升,相反,函數表達式或箭頭函數,let和const聲明的變量,這些都不會被提升。
假設在全局使用域,有如下的代碼:
console.log(y);
y?=?1;
console.log(y);
console.log(greet("Mark"));
function?greet(name){
??return?'Hello?'?+?name?+?'!';
}
var?y;
上面分別打印:undefined,1, Hello Mark!。
上面代碼在編譯階段其實是這樣的:
function?greet(name)?{
??return?'Hello?'?+?name?+?'!';
}
var?y;?//?默認值?undefined
//?等待“編譯”階段完成,然后開始“執行”階段
/*
console.log(y);
y?=?1;
console.log(y);
console.log(greet("Mark"));
*/
編譯階段完成后,它將啟動執行階段調用方法,并將值分配給變量。
function?greet(name)?{
??return?'Hello?'?+?name?+?'!';
}
var?y;
//start?"execution"?phase
console.log(y);
y?=?1;
console.log(y);
console.log(greet("Mark"));
JavaScript 中的作用域是我們可以有效訪問變量或函數的區域。JS 有三種類型的作用域:全局作用域、函數作用域和塊作用域(ES6)。
//global?namespace
var?g?=?"global";
function?globalFunc(){
??function?innerFunc(){
????console.log(g);?//?can?access?"g"?because?"g"?is?a?global?variable
??}
?innerFunc();
}??
function?myFavoriteFunc(a)?{
??if?(true)?{
????var?b?=?"Hello?"?+?a;
??}
??return?b;
}
myFavoriteFunc("World");
console.log(a);?//?Throws?a?ReferenceError?"a"?is?not?defined
console.log(b);?//?does?not?continue?here?
?function?testBlock(){
???if(true){
?????let?z?=?5;
???}
???return?z;?
?}
?testBlock();?//?Throws?a?ReferenceError?"z"?is?not?defined
作用域也是一組用于查找變量的規則。如果變量在當前作用域中不存在,它將向外部作用域中查找并搜索,如果該變量不存在,它將再次查找直到到達全局作用域,如果找到,則可以使用它,否則引發錯誤,這種查找過程也稱為作用域鏈。
???/*?作用域鏈
?????內部作用域->外部作用域->?全局作用域
??*/
??//?全局作用域
??var?variable1?=?"Comrades";???
??var?variable2?=?"Sayonara";
??function?outer(){
??//?外部作用域
????var?variable1?=?"World";
????function?inner(){
????//?內部作用域
??????var?variable2?=?"Hello";
??????console.log(variable2?+?"?"?+?variable1);
????}
????inner();
??}??
??outer();?//?Hello?World
這可能是所有問題中最難的一個問題,因為閉包是一個有爭議的話題,這里從個人角度來談談,如果不妥,多多海涵。
閉包就是一個函數在聲明時能夠記住當前作用域、父函數作用域、及父函數作用域上的變量和參數的引用,直至通過作用域鏈上全局作用域,基本上閉包是在聲明函數時創建的作用域。
看看小例子:
???//?全局作用域
???var?globalVar?=?"abc";
???function?a(){
?????console.log(globalVar);
???}
???a();?//?"abc"?
在此示例中,當我們聲明a函數時,全局作用域是a閉包的一部分。
變量globalVar在圖中沒有值的原因是該變量的值可以根據調用函數a的位置和時間而改變。但是在上面的示例中,globalVar變量的值為abc。
來看一個更復雜的例子:
var?globalVar?=?"global";
var?outerVar?=?"outer"
function?outerFunc(outerParam)?{
??function?innerFunc(innerParam)?{
????console.log(globalVar,?outerParam,?innerParam);
??}
??return?innerFunc;
}
const?x?=?outerFunc(outerVar);
outerVar?=?"outer-2";
globalVar?=?"guess"
x("inner");
上面打印結果是 guess outer inner。
當我們調用outerFunc函數并將返回值innerFunc函數分配給變量x時,即使我們為outerVar變量分配了新值outer-2,outerParam也繼續保留outer值,因為重新分配是在調用outerFunc之后發生的,并且當我們調用outerFunc函數時,它會在作用域鏈中查找outerVar的值,此時的outerVar的值將為 "outer"。
現在,當我們調用引用了innerFunc的x變量時,innerParam將具有一個inner值,因為這是我們在調用中傳遞的值,而globalVar變量值為guess,因為在調用x變量之前,我們將一個新值分配給globalVar。
下面這個示例演示沒有理解好閉包所犯的錯誤:
const?arrFuncs?=?[];
for(var?i?=?0;?i?<?5;?i++){
??arrFuncs.push(function?(){
????return?i;
??});
}
console.log(i);?//?i?is?5
for?(let?i?=?0;?i?<?arrFuncs.length;?i++)?{
??console.log(arrFuncs[i]());?//?都打印?5
}
由于閉包,此代碼無法正常運行。var關鍵字創建一個全局變量,當我們 push 一個函數時,這里返回的全局變量i。因此,當我們在循環后在該數組中調用其中一個函數時,它會打印5,因為我們得到i的當前值為5,我們可以訪問它,因為它是全局變量。
因為閉包在創建變量時會保留該變量的引用而不是其值。我們可以使用IIFES或使用 let 來代替 var 的聲明。
?const?falsyValues?=?['',?0,?null,?undefined,?NaN,?false];
簡單的來說虛值就是是在轉換為布爾值時變為 false 的值。
使用 Boolean 函數或者 !! 運算符。
"use strict" 是 ES5 特性,它使我們的代碼在函數或整個腳本中處于嚴格模式。嚴格模式幫助我們在代碼的早期避免 bug,并為其添加限制。
嚴格模式的一些限制:
設立”嚴格模式”的目的,主要有以下幾個:
基本上,this指的是當前正在執行或調用該函數的對象的值。this值的變化取決于我們使用它的上下文和我們在哪里使用它。
const?carDetails?=?{
??name:?"Ford?Mustang",
??yearBought:?2005,
??getName(){
????return?this.name;
??},
??isRegistered:?true
};
console.log(carDetails.getName());?//?Ford?Mustang
這通常是我們期望結果的,因為在getName方法中我們返回this.name,在此上下文中,this指向的是carDetails對象,該對象當前是執行函數的“所有者”對象。
接下我們做些奇怪的事情:
var?name?=?"Ford?Ranger";
var?getCarName?=?carDetails.getName;
console.log(getCarName());?//?Ford?Ranger
上面打印Ford Ranger,這很奇怪,因為在第一個console.log語句中打印的是Ford Mustang。這樣做的原因是getCarName方法有一個不同的“所有者”對象,即window對象。在全局作用域中使用var關鍵字聲明變量會在window對象中附加與變量名稱相同的屬性。請記住,當沒有使用“use strict”時,在全局作用域中this指的是window對象。
console.log(getCarName?===?window.getCarName);?//?true
console.log(getCarName?===?this.getCarName);?//?true
本例中的this和window引用同一個對象。
解決這個問題的一種方法是在函數中使用apply和call方法。
console.log(getCarName.apply(carDetails));?//?Ford?Mustang
console.log(getCarName.call(carDetails));??//?Ford?Mustang
apply和call方法期望第一個參數是一個對象,該對象是函數內部this的值。
IIFE或立即執行的函數表達式,在全局作用域內聲明的函數,對象內部方法中的匿名函數和內部函數的this具有默認值,該值指向window對象。
???(function?(){
?????console.log(this);
???})();?//?打印?"window"?對象
???function?iHateThis(){
??????console.log(this);
???}
???iHateThis();?//?打印?"window"?對象
???const?myFavoriteObj?=?{
?????guessThis(){
????????function?getName(){
??????????console.log(this.name);
????????}
????????getName();
?????},
?????name:?'Marko?Polo',
?????thisIsAnnoying(callback){
???????callback();
?????}
???};
???myFavoriteObj.guessThis();?//?打印?"window"?對象
???myFavoriteObj.thisIsAnnoying(function?(){
?????console.log(this);?//?打印?"window"?對象
???});
如果我們要獲取myFavoriteObj對象中的name屬性(即Marko Polo)的值,則有兩種方法可以解決此問題。
一種是將 this 值保存在變量中。
const?myFavoriteObj?=?{
?guessThis(){
??const?self?=?this;?//?把?this?值保存在?self?變量中
??function?getName(){
????console.log(self.name);
??}
??getName();
?},
?name:?'Marko?Polo',
?thisIsAnnoying(callback){
???callback();
??}
};
第二種方式是使用箭頭函數
const?myFavoriteObj?=?{
??guessThis(){
?????const?getName?=?()?=>?{?
???????console.log(this.name);
?????}
?????getName();
??},
??name:?'Marko?Polo',
??thisIsAnnoying(callback){
???callback();
??}
};
箭頭函數沒有自己的 this。它復制了這個封閉的詞法作用域中this值,在這個例子中,this值在getName內部函數之外,也就是myFavoriteObj對象。
簡單地說,原型就是對象的藍圖。如果它存在當前對象中,則將其用作屬性和方法的回退。它是在對象之間共享屬性和功能的方法,這也是JavaScript實現繼承的核心。
const?o?=?{};
console.log(o.toString());?//?logs?[object?Object]?
即使o對象中不存在o.toString方法,它也不會引發錯誤,而是返回字符串[object Object]。當對象中不存在屬性時,它將查看其原型,如果仍然不存在,則將其查找到原型的原型,依此類推,直到在原型鏈中找到具有相同屬性的屬性為止。原型鏈的末尾是Object.prototype。
console.log(o.toString?===?Object.prototype.toString);?//?logs?true
由于篇幅過長,我將此系列分成上中下三篇,下篇我們在見。
原文:
https://dev.to/macmacky/70-javascript-interview-questions-5gfi#1-whats-the-difference-between-undefined-and-null
const arr = [1,1,1,1,1,1,3,3,3,3,32,2,2,2,2,3,3,4,-1,-10,-10,-1,4,4,5,5,9]
// set方式去重
// 不改變源數組的數據
console.log([...new Set(arr)])
// includes方式去重, 時間復雜度 O(n)
function uniq(arr) {
let _result = []
for(let i=0; i < arr.length; i++) {
if (!_result.includes(arr[i])) {
_result.push(arr[i])
}
}
return _result
}
console.log(uniq(arr))
let arr = [1,2,5,3,1,6,7,3,4,10,12,3,21]
function bubbleSort(arr) {
// 淺克隆, 對外部傳入的參數不進行更改, 保證函數是一個純函數
let _arr = [].concat(arr)
// 核心邏輯
for(let i=0; i<_arr.length -1; i++) {
for(let j=0; j<_arr.length-i-1; j++) {
if (_arr[j] > _arr[j+1]) {
let temp = _arr[j]
_arr[j] = _arr[j + 1]
_arr[j + 1] = temp
}
}
}
return _arr
}
console.log(bubbleSort(arr))
let arr = [300,432,1342,543,23,656,45,6465,4345,232,87,97,754,345]
function quickSort(arr) {
if(arr.length <= 1) {
return arr
}
const pivot = arr[0]
let bigger = []
let smaller = []
for(let i=1; i<arr.length; i++) {
if (arr[i] > pivot) {
bigger.push(arr[i])
}
}
for(let i=1; i<arr.length; i++) {
if (arr[i] < pivot) {
smaller.push(arr[i])
}
}
return quickSort(smaller).concat(pivot, quickSort(bigger))
}
console.log(quickSort(arr))
案例一:
function curry(fn) {
return function() {
let arg = arguments
return function() {
return fn(...arg, ...arguments)
}
}
}
function fn(a,b,c,d) {
return a+b+c+d
}
let fun = curry(fn)
let fun2 = fun(1,2,3)
console.log(fun2(5))
案例二:
let fn = a => b => c => a+b+c
console.log(fn(1)(2)(3))
let arr = [0,[3,4,5],[[[[12,5,6,7,54,34],43,34],33]], {a:1}]
function flatten(arr) {
let _arr = []
for(let i=0; i<arr.length; i++) {
const leixing = Object.prototype.toString.call(arr[i])
if (leixing !== '[object Array]') {
_arr.push(arr[i])
} else {
_arr = _arr.concat(flatten(arr[i]))
}
}
return _arr
}
console.log(flatten(arr))
Array.prototype.max = function max() {
console.log(this)
return Math.max.apply(Math, this)
}
let array = [1,2,3,4]
console.log(array.max())
let arr = [1,2,3,[4,5,6], {a:1}]
function deepClone(o) {
if (
typeof o == 'number'
||
typeof o == 'string'
||
typeof o == 'boolean'
||
typeof o == 'undefined'
) {
return o
} else if(Array.isArray(o)) {
let _arr = []
for(let i=0; i<o.length; i++) {
_arr.push(deepClone(o[i]))
}
return _arr
} else if(typeof o == 'object') {
let _o = {}
for(let k in o) {
_o[k] = deepClone(o[k])
}
return _o
}
}
let deep = deepClone(arr)
console.log(arr[3] == deep[3]) // false
// 功能: 傳入一個數值, 隨機生成指定范圍內的樣本數據
// 參數: 樣本個數
// start: 樣本起始數值
// end: 樣本結束數值
function sample(num, start, end) {
end -= 1
let _arr = []
while(num != _arr.length){
let data = parseInt(Math.random() * end) + start
if (!_arr.includes(data)) {
_arr.push(data)
}
}
return _arr
}
console.log(sample(30, 2, 32))
// 輸出結果
// [
// 9, 27, 18, 28, 24, 13, 31, 11, 6,
// 19, 7, 17, 21, 26, 30, 22, 8, 25,
// 10, 3, 2, 5, 4, 12, 20, 14, 29,
// 15, 32, 23
// ]
字符串反轉函數
let str = 'abcde'
function myReverse(str) {
return str.split('').reverse().join('')
}
let res = myReverse(str)
console.log(res)
偏函數的作用: 調用之后能夠獲得一個特定功能的函數
// 需求: 實現一個檢查類型的偏函數
function checkType(type) {
return function(o) {
return Object.prototype.toString.call(o) == `[object ${type}]`
}
}
let checkIsArray = checkType('Array')
console.log(checkIsArray([1,2,3]))
// 輸出結果
// true
閉包的特點: 調用永久記住當前作用域的變量
案例一:
var a = 2
function foo() {
var a = 1
function bar() {
console.log(a)
}
bar()
}
foo()
//輸出結果
//1
案例二:
var a = 1
function bar() {
console.log(a)
}
(function(fn) {
var a = 2
fn()
})(bar)
// 輸出結果
// 1
// 箭頭函數this跟定義時上下文永遠綁定
// 普通函數的this, 視運行環境而改變
function fun() {
return () => {
console.log(this)
}
}
let laowang = {name: 'laowang'}
let xiaoliu = {name: 'xiaoliu'}
let arrowFun = fun.call(laowang)
arrowFun() // { name: 'laowang' }
arrowFun.call(xiaoliu) // { name: 'laowang' }
arrowFun = arrowFun.bind(xiaoliu)
arrowFun() // { name: 'laowang' }
// 函數非嚴格模式下實參與實參列表的關系
function fun(a, b) {
a = (typeof a !== 'undefined') ? a : 10
b = (typeof b !== 'undefined') ? b : 20
console.log(a == arguments[0])
console.log(b == arguments[1])
a = 123
b = 456
console.log(a == arguments[0])
console.log(b == arguments[1])
}
fun(1, 2)
// 輸出結果
// true
// true
// true
// true
// 函數嚴格模式下實參與實參列表的關系
function fun(a, b) {
'use strict'
a = (typeof a !== 'undefined') ? a : 10
b = (typeof b !== 'undefined') ? b : 20
console.log(a == arguments[0])
console.log(b == arguments[1])
a = 123
b = 456
console.log(a == arguments[0])
console.log(b == arguments[1])
}
fun(1, 2)
// true
// true
// false
// false
// 函數改良
// 這種方式跟非嚴格模式下的執行結果是一致的
function fun(a=10, b=20) {
console.log(a == arguments[0])
console.log(b == arguments[1])
a = 123
b = 456
console.log(a == arguments[0])
console.log(b == arguments[1])
}
fun(1, 2)
// true
// true
// false
// false
解釋: 變量在定義之后, 但沒有聲明的情況下, 是暫時不能訪問的
// 案例一: 函數后面的默認參數可以訪問前面的參數
// 實參其實相當于使用 let來聲明一個變量
function foo(a) {
return a + 5
}
function fun(a, b = foo(a)) {
console.log(a + b)
}
fun(1) // 7
fun(1, 2) // 3
// 案例二: 函數后面的參數無法訪問前面參數的值
function add(a = b, b) {
return a + b
}
console.log(add(1, 2)) // 3
console.log(add(undefined, 2)) // ReferenceError: Cannot access 'b' before initialization
// 不使用展開運算符
let arr = [1,2,3,4]
let max = Math.max.apply(null,arr)
console.log(max)
// 改進
let arr = [1,2,3,4]
let max = Math.max(...arr)
console.log(max)
參考資料: https://www.runoob.com/js/js-strict.html
use strict解釋: 為什么使用嚴格模式:
消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為;
消除代碼運行的一些不安全之處,保證代碼運行的安全;
提高編譯器效率,增加運行速度;
為未來新版本的Javascript做好鋪墊。
"嚴格模式"體現了Javascript更合理、更安全、更嚴謹的發展方向,包括IE 10在內的主流瀏覽器,都已經支持它,許多大項目已經開始全面擁抱它。
另一方面,同樣的代碼,在"嚴格模式"中,可能會有不一樣的運行結果;一些在"正常模式"下可以運行的語句,在"嚴格模式"下將不能運行。掌握這些內容,有助于更細致深入地理解Javascript,讓你變成一個更好的程序員。
// 需求: 封裝一個迭代器
function arrIterator(arr) {
let i = 0
return {
next: function() {
let done = i > arr.length - 1 ? true : false
let value = !done ? arr[i++] : 'undefined'
return {
done,
value
}
}
}
}
let arr = [1,2,3,4,5]
let iterator = arrIterator(arr)
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
// 輸出結果
// { done: false, value: 1 }
// { done: false, value: 2 }
// { done: false, value: 3 }
// { done: false, value: 4 }
// { done: false, value: 5 }
// { done: true, value: 'undefined' }
// 生成器
// 作用: 用于生成迭代器
function *generator(arr) {
for(let i=0; i<arr.length; i++) {
yield arr[i]
}
}
let arr = [1,2,3,4,5]
const arrIterator = generator(arr)
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
// 生成器
// 作用: 用于生成迭代器
// 案例一
function *generator(arr) {
for(let i=0; i<arr.length; i++) {
yield arr[i]
}
}
let arr = [1,2,3,4,5]
const arrIterator = generator(arr)
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
console.log(arrIterator.next())
// 輸出結果
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
// { value: 4, done: false }
// { value: 5, done: false }
// { value: undefined, done: true }
// 案例二
// 生成器可以yield 一個Promise實例對象
function *generator() {
yield sumAfter1000ms(1,2).then(res => console.log(res))
yield sumAfter1000ms(2,2).then(res => console.log(res))
}
function sumAfter1000ms(a,b) {
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(a+b)
}, 2000)
})
}
const iterator = generator()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
// 輸出結果
// { value: Promise { <pending> }, done: false }
// { value: Promise { <pending> }, done: false }
// { value: undefined, done: true }
// 3
// 4
promise作用:
- 解決毀掉地獄的問題
- 使代碼調用更加清晰易懂
promise的三種狀態:
- pending
- resolve
- reject.
promise特點:
- 使用resolve和reject封裝結果回調函數
- Promise的實例會調用.then方法
// 案例一
// 不使用promise的情況
function fun(a, b, cb) {
setTimeout(function(){
return cb(a + b)
}, 2000)
}
fun(1,2, res => {
console.log(res)
fun(3,4, res => {
console.log(res)
fun(4, 5, res => {
console.log(res)
})
})
})
// 案例二: 使用promise的寫法
function sum(a,b) {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve(a + b)
}, 2000)
})
}
sum(1,2)
.then(res => {
console.log(res)
})
// 案例三: 使用promise寫法封裝一個讀文件操作函數
const fs = require('fs')
function readFile(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, (err, res) => {
if (err) {
reject(new Error('所讀的文件不存在'))
return
}
resolve(res.toString())
})
})
}
readFile('./1.txt').then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
*請認真填寫需求信息,我們會在24小時內與您取得聯系。