本文目錄一覽:
- 1、解釋JavaScript中的變數聲明提升?
- 2、Web前端工程師應該知道的提高JavaScript技能的技巧!
- 3、var a=0; function fun(){ a++; if(a
- 4、js中if是一個代碼塊嘛? 寫在if裡面的函數聲明和var變數會得到提升嘛?
- 5、Jsp中有大量js和css,如何提高頁面載入速度
- 6、js判斷對象里是否有內容
解釋JavaScript中的變數聲明提升?
由於JavaScript在設計之初就是弱類型,所以很多地方設計的不是特別嚴格。JavaScript代碼是自上而下執行的,變數提升指的就是你明明在第10行通過 var 申明了變數並賦值,但是在程序運行時還沒載入到第10行的時候,變數就被申明,但是到第10行的時候變數才被賦值。這個就是變數提升。所以一般都是把變數申明放在JavaScript的最頂端 或者使用let const 申明避免變數的提升
Web前端工程師應該知道的提高JavaScript技能的技巧!
今天小編要跟大家分享的文章是關於Web前端工程師應該知道的提高JavaScript
技能的技巧!熟悉web前端工作的小夥伴都知道,JavaScript是前端工程師的必備技能。JavaScript
是一種複雜的語言。如果是你是高級或者初級web開發人員,了解它的基本概念非常重要。本篇文章小編就為大家介紹幾種提高JavaScript
技能的技巧,下面讓我們一起來看一看吧!
01、變數賦值(值vs引用)
理解JavaScript如何給變數賦值可以幫助我們減少一些不必要的bug。如果你不理解這一點,可能很容易地編寫被無意中更改值的代碼。
JavaScript總是按照值來給變數賦值。這一部分非常重要:當指定的值是JavaScript的五種基本類型之一(即
Boolean,null,undefined,String和Number)時,將分配實際值。但是,當指定的值是
Array,Function或Object時,將分配對內存中對象的引用給變數。
在以下代碼段中,使用var1對var2進行賦值。由於var1是基本類型(String),因此var2的值等於var1的String
值,並且可以認為此時與var1完全不同。因此,重新賦值var2對var1沒有影響。
letvar1=’Mystring’;
letvar2=var1;
var2=’Mynewstring’;
console.log(var1);
//’Mystring’
console.log(var2);
//’Mynewstring’
接著,與對象賦值進行比較。
letvar1={name:’Jim’}
letvar2=var1;
var2.name=’John’;
console.log(var1);
//{name:’John’}
console.log(var2);
//{name:’John’}
如果你期望它會像原始類型賦值那樣,很可能會出問題!如果你創建了一個無意中會改變對象的函數,就會出現一些非預期的行為。
02、閉包
閉包是一個重要的JavaScript模式,可以私有訪問變數。在本例中,createGreeter返回一個匿名函數,這個函數可以訪問參數
greeting(在這裡是「Hello」)。在後續的調用中,sayHello將有權訪問這個greeting!
functioncreateGreeter(greeting){
returnfunction(name){
console.log(greeting+’,’+name);
}
}
constsayHello=createGreeter(‘Hello’);
sayHello(‘Joe’);
//Hello,Joe
在更真實的場景中,你可以設想一個初始函數apiConnect(apiKey),它返回一些使用APIkey的方法。在這種情況下,apiKey
只需要提供一次即可。
functionapiConnect(apiKey){
functionget(route){
returnfetch(`${route}?key=${apiKey}`);
}
functionpost(route,params){
returnfetch(route,{
method:’POST’,
body:JSON.stringify(params),
headers:{
‘Authorization’:`Bearer${apiKey}`
}
})
}
return{get,post}
}
constapi=apiConnect(‘my-secret-key’);
//NoneedtoincludetheapiKeyanymore
api.get(‘#/get-endpoint’);
api.post(‘#/post-endpoint’,{name:’Joe’});
03、解構
JavaScript參數解構可以從對象中干中提取所需屬性的常用方法。
constobj={
ame:’Joe’,
food:’cake’
}
const{name,food}=obj;
console.log(name,food);
//’Joe”cake’
如果要以其他名稱提取屬性,可以使用如下方式:
constobj={
ame:’Joe’,
food:’cake’
}
const{name:myName,food:myFood}=obj;
console.log(myName,myFood);
//’Joe”cake’
解構經常也用於直接用於提取傳給函數的參數。如果你熟悉React,可能已經見過這個:
constperson={
ame:’Eddie’,
age:24
}
functionintroduce({name,age}){
console.log(`I’m${name}andI’m${age}yearsold!`);
}
console.log(introduce(person));
//”I’mEddieandI’m24yearsold!”
04、展開運算
ES6的一個常用之一的特性就是展開(…)運算符了,在下面的例子中,Math.max不能應用於arr
數組,因為它不將數組作為參數,但它可以將各個元素作為參數傳入。展開運算符…可用於提取數組的各個元素。
constarr=[4,6,-1,3,10,4];
constmax=Math.max(…arr);
console.log(max);
//10
05、剩餘參數
剩餘參數語法和展開語法看起來的一樣的,不同的是展開語法是為了結構數組和對象;而剩餘參數和展開運算符是相反的,剩餘參數收集多個元素合成一個數組。
functionmyFunc(…args){
console.log(args[0]+args[1]);
}
myFunc(1,2,3,4);
//3
restparameters和arguments的區別
1.arguments是偽數組,包含所有的實參
2.剩餘參數是標準的數組,可以使用數組的方法
06、數組方法
JavaScript數組方法通常可以提供令人難以置信的、優雅的方法來執行所需的數據轉換。作為StackOverflow
的貢獻者,我經常看到關於如何以某種方式操縱對象數組的問題,這往往也是數組方法的完美用例。
map、filter、reduce
JavaScript數組方法map、filter和reduce容易混淆,這些都是轉換數組或返回聚合值的有用方法。
map:返回一個數組,其中每個元素都使用指定函數進行過轉換。
constarr=[1,2,3,4,5,6];
constmapped=arr.map(el=el+20);
console.log(mapped);
//[21,22,23,24,25,26]
filter:返回一個數組,只有當指定函數返回true時,相應的元素才會被包含在這個數組中。
constarr=[1,2,3,4,5,6];
constfiltered=arr.filter(el=el===2||el===4);
console.log(filtered);
//[2,4]
reduce:按函數中指定的值累加
constarr=[1,2,3,4,5,6];
constreduced=arr.reduce((total,current)=total+current);
console.log(reduced);
//21
find,findIndex,indexOf
find:返回與指定條件匹配的第一個實例,如果查到不會繼續查找其他匹配的實例。
constarr=[1,2,3,4,5,6,7,8,9,10];
constfound=arr.find(el=el5);
console.log(found);
//6
再次注意,雖然5之後的所有元素都滿足條件,但是只返回第一個匹配的元素。當你發現匹配項時,通常會中斷for循環,在這種情況下,這實際上非常有用。
findIndex:這與find幾乎完全相同,但不是返回第一個匹配元素,而是返回第一個匹配元素的索引。
constarr=[‘Nick’,’Frank’,’Joe’,’Frank’];
constfoundIndex=arr.findIndex(el=el===’Frank’);
console.log(foundIndex);
//1
indexOf:與findIndex幾乎完全相同,但它不是將函數作為參數,而是採用一個簡單的值。
當w你需要更簡單的邏輯並且不需要使用函數來檢查是否存在匹配時,可以使用此方法。
constarr=[‘Nick’,’Frank’,’Joe’,’Frank’];
constfoundIndex=arr.indexOf(‘Frank’);
console.log(foundIndex);
//1
push,pop,shift,unshift
push:這是一個相對簡單的方法,它將一個項添加到數組的末尾。它就地修改數組,函數本身會返回添加到數組中的項。
letarr=[1,2,3,4];
constpushed=arr.push(5);
console.log(arr);
//[1,2,3,4,5]
console.log(pushed);
//5
pop:這將從數組中刪除最後一項。同樣,它在適當的位置修改數組,函數本身返回從數組中刪除的項。
letarr=[1,2,3,4];
constpopped=arr.pop();
console.log(arr);
//[1,2,3]
console.log(popped);
//4
shift:從數組中刪除第一項。同樣,它在適當的位置修改數組。函數本身返回從數組中刪除的項。
letarr=[1,2,3,4];
constshifted=arr.shift();
console.log(arr);
//[2,3,4]
console.log(shifted);
//1
unshift:將一個或多個元素添加到數組的開頭。同樣,它在適當的位置修改數組。與許多其他方法不同,函數本身返回數組的新長度。
letarr=[1,2,3,4];
constunshifted=arr.unshift(5,6,7);
console.log(arr);
//[5,6,7,1,2,3,4]
console.log(unshifted);
//7
splice,slice
splice:通過刪除或替換現有元素和/或添加新元素來更改數組的內容,此方法會修改了數組本身。
下面的代碼示例的意思是:在數組的位置1上刪除0個元素,並插入b。
letarr=[‘a’,’c’,’d’,’e’];
arr.splice(1,0,’b’)
slice:從指定的起始位置和指定的結束位置之前返回數組的淺拷貝。如果未指定結束位置,則返回數組的其餘部分。
重要的是,此方法不會修改數組,而是返回所需的子集。
letarr=[‘a’,’b’,’c’,’d’,’e’];
constsliced=arr.slice(2,4);
console.log(sliced);
//[‘c’,’d’]
console.log(arr);
//[‘a’,’b’,’c’,’d’,’e’]
sort
sort:根據提供的函數對數組進行排序。這個方法就地修改數組。如果函數返回負數或0,則順序保持不變。如果返回正數,則交換元素順序。
letarr=[1,7,3,-1,5,7,2];
constsorter=(firstEl,secondEl)=firstEl-secondEl;
arr.sort(sorter);
console.log(arr);
//[-1,1,2,3,5,7,7]
07、Generators(生成器)
生成器是一種特殊的行為,實際上是一種設計模式,我們通過調用next()方法來遍歷一組有序的值。想像一下,例如使用遍歷器對數組[1,2,3,4,5]進行遍歷。第一次調用next()方法返回1,第二次調用next()方法返回2,以此類推。當數組中的所有值都返回後,調用next()方法將返回null或false或其它可能的值用來表示數組中的所有元素都已遍歷完畢。
function*greeter(){
yield’Hi’;
yield’Howareyou?’;
yield’Bye’;
}
constgreet=greeter();
console.log(greet.next().value);
//’Hi’
console.log(greet.next().value);
//’Howareyou?’
console.log(greet.next().value);
//’Bye’
console.log(greet.next().value);
//undefined
使用生成器生成無限個值:
function*idCreator(){
leti=0;
while(true)
yieldi++;
}
constids=idCreator();
console.log(ids.next().value);
//0
console.log(ids.next().value);
//1
console.log(ids.next().value);
//2
//etc…
08、恆等運算符(===)與相等運算符(==)
大家一定要知道JavaScript中的恆等運算符(===)和相等運算符(==)之間的區別!
==運算符在比較值之前會進行類型轉換,而===運算符在比較之前不會進行任何類型轉換。
console.log(0==’0′);
//true
console.log(0===’0′);
//false
09、對象比較
我看到JavaScript新手所犯的錯誤是直接比較對象。變數指向內存中對象的引用,而不是對象本身!實際比較它們的一種方法是將對象轉換為JSON
字元串。這有一個缺點:對象屬性順序不能保證!比較對象的一種更安全的方法是引入專門進行深度對象比較的庫(例如,lodash的isEqual)。
下面的對象看起來是相等的,但實際上它們指向不同的引用。
constjoe1={name:’Joe’};
constjoe2={name:’Joe’};
console.log(joe1===joe2);
//false
相反,下面的計算結果為true,因為一個對象被設置為與另一個對象相等,因此指向相同的引用(內存中只有一個對象)。
constjoe1={name:’Joe’};
constjoe2=joe1;
console.log(joe1===joe2);
//true
相反,以下計算結果為true,因為一個對象設置為等於另一個對象,因此指向相同的引用(內存中只有一個對象)。
constjoe1={name:’Joe’};
constjoe2=joe1;
console.log(joe1===joe2);
//true
10、回調函數
很多人都被JavaScript回調函數嚇倒了!他們很簡單,舉個例子。console.log函數作為回調傳遞給myFunc。
它在setTimeout完成時執行。
functionmyFunc(text,callback){
setTimeout(function(){
callback(text);
},2000);
}
myFunc(‘Helloworld!’,console.log);
//’Helloworld!’
11、Promises
一旦你理解了JavaScript回調,很快就會發現自己陷入了「回調地獄」中。這個時候可以使用promise,將非同步邏輯包裝在promise
中,成功時resolve或在失敗時reject使用「then」來處理成功的情況,使用catch來處理異常。
constmyPromise=newPromise(function(res,rej){
setTimeout(function(){
if(Math.random()
returnres(‘Hooray!’);
}
returnrej(‘Ohno!’);
},1000);
});
myPromise
.then(function(data){
console.log(‘Success:’+data);
})
.catch(function(err){
console.log(‘Error:’+err);
});
//IfMath.random()returnslessthan0.9thefollowingislogged:
//”Success:Hooray!”
//IfMath.random()returns0.9orgreaterthefollowingislogged:
//”Error:Onno!”
12、Async/Await
在掌握了promise的用法後,你可能也會喜歡asyncawait,它只是一種基於promise
的「語法糖」。在下面的示例中,我們創建了一個async函數,並awaitgreeterpromise。
constgreeter=newPromise((res,rej)={
setTimeout(()=res(‘Helloworld!’),2000);
})
asyncfuncti
var a=0; function fun(){ a++; if(a
var a=0; function fun(){ a++; if(a=10){ fun(); } console.log(a+=1) } fun();
為什麼結果是十一個十一?理由如下:
帶 var 的只聲明還沒有被定義,帶 function 的已經聲明和定義。所以在代碼執行前有帶 var 的就提前聲明。
變數提升示例:
console.log(a) // undefinedvar
a = 10
變數提升是當棧內存作用域形成時,JS代碼執行前,瀏覽器會將帶有var, function關鍵字的變數提前進行聲明 declare(值默認就是 undefined)。
定義 defined(就是賦值操作),這種預先處理的機制就叫做變數提升機制也叫預定義。
在變數提升階段:帶 var 的只聲明還沒有被定義,帶 function 的已經聲明和定義。所以在代碼執行前有帶 var 的就提前聲明,比如這裡的 a 就賦值成 undefined,在代碼執行過程中遇到創建函數的代碼瀏覽器會直接跳過。
變數提升只發生在當前作用域。比如:在頁面開始載入時,只有全局作用域發生變數提升,這時候的函數中存儲的都是代碼字元串。
全局作用域中不帶var聲明變數雖然也可以但是建議帶上 var聲明變數,不帶 var 的相當於給window對象設置一個屬性罷了。
私有作用域(函數作用域),帶 var 的是私有變數。不帶 var 的是會向上級作用域查找,如果上級作用域也沒有那麼就一直找到 window 為止,這個查找過程叫作用域鏈。
全局作用域中使用 var 申明的變數會映射到 window 下成為屬性。
js中if是一個代碼塊嘛? 寫在if裡面的函數聲明和var變數會得到提升嘛?
你好,你的這個問題是這樣的首先第一個列印a,是在源碼開頭定義的var變數a。
緊接著在第一個if內嵌套定義了一個名為a的函數,因為js是弱語言,所以實現對應的變數a被重新指向了函數a,所以在最後一次列印a的時候是函數a
Jsp中有大量js和css,如何提高頁面載入速度
載入速度不必刻意強求,真正項目尤其是前端開發講究的是可讀性,如真的項目寫好後可以考慮壓縮css和js代碼,css帶走方向的複合屬性如邊框,外邊距,補白等最好用一個屬性代替,順序為上,右,下,左。表格盡量少用。
js判斷對象里是否有內容
Javascript語言的設計不夠嚴謹,很多地方一不小心就會出錯。
舉例來說,請考慮以下情況。
現在,我們要判斷一個全局對象myObj是否存在,如果不存在,就對它進行聲明。用自然語言描述的演算法如下:
複製代碼代碼如下:
if (myObj不存在){
聲明myObj;
}
你可能會覺得,寫出這段代碼很容易。但是實際上,它涉及的語法問題,遠比我們想像的複雜。Juriy Zaytsev指出,判斷一個Javascript對象是否存在,有超過50種寫法。只有對Javascript語言的實現細節非常清楚,才可能分得清它們的區別。
第一種寫法
根據直覺,你可能覺得可以這樣寫:
複製代碼代碼如下:
if (!myObj) {
myObj = { };
}
但是,運行這段代碼,瀏覽器會直接拋出ReferenceError錯誤,導致運行中斷。請問錯在哪裡?
對了,if語句判斷myObj是否為空時,這個變數還不存在,所以才會報錯。改成下面這樣,就能正確運行了。
複製代碼代碼如下:
if (!myObj) {
var myObj = { };
}
為什麼加了一個var以後,就不報錯了?難道這種情況下,if語句做判斷時,myObj就已經存在了嗎?
要回答這個問題,就必須知道Javascript解釋器的工作方式。Javascript語言是”先解析,後運行”,解析時就已經完成了變數聲明,所以上面的代碼實際等同於:
複製代碼代碼如下:
var myObj;
if (!myObj) {
var myObj = { };
}
因此,if語句做判斷時,myObj確實已經存在了,所以就不報錯了。這就是var命令的”代碼提升”(hoisting)作用。Javascript解釋器,只”提升”var命令定義的變數,對不使用var命令、直接賦值的變數不起作用,這就是為什麼不加var會報錯的原因。
第二種寫法
除了var命令,還可以有另一種改寫,也能得到正確的結果:
複製代碼代碼如下:
if (!window.myObj) {
myObj = { };
}
window是javascript的頂層對象,所有的全局變數都是它的屬性。所以,判斷myobj是否為空,等同於判斷window對象是否有myobj屬性,這樣就可以避免因為myObj沒有定義而出現ReferenceError錯誤。不過,從代碼的規範性考慮,最好還是對第二行加上var:
複製代碼代碼如下:
if (!window.myObj) {
var myObj = { };
}
或者寫成這樣:
if (!window.myObj) {
window.myObj = { };
}
第三種寫法
上面這種寫法的缺點在於,在某些運行環境中(比如V8、Rhino),window未必是頂層對象。所以,考慮改寫成:
複製代碼代碼如下:
if (!this.myObj) {
this.myObj = { };
}
在全局變數的層面中,this關鍵字總是指向頂層變數,所以就可以獨立於不同的運行環境。
第四種寫法
但是,上面這樣寫可讀性較差,而且this的指向是可變的,容易出錯,所以進一步改寫:
複製代碼代碼如下:
var global = this;
if (!global.myObj) {
global.myObj = { };
}
用自定義變數global表示頂層對象,就清楚多了。
第五種寫法
還可以使用typeof運算符,判斷myObj是否有定義。
複製代碼代碼如下:
if (typeof myObj == “undefined”) {
var myObj = { };
}
這是目前使用最廣泛的判斷javascript對象是否存在的方法。
第六種寫法
由於在已定義、但未賦值的情況下,myObj的值直接等於undefined,所以上面的寫法可以簡化:
複製代碼代碼如下:
if (myObj == undefined) {
var myObj = { };
}
這裡有兩個地方需要注意,首先第二行的var關鍵字不能少,否則會出現ReferenceError錯誤,其次undefined不能加單引號或雙引號,因為這裡比較的是undefined這種數據類型,而不是”undefined”這個字元串。
第七種寫法
上面的寫法在”精確比較”(===)的情況下,依然成立:
複製代碼代碼如下:
if (myObj === undefined) {
var myObj = { };
}
第八種寫法
根據javascript的語言設計,undefined == null,所以比較myObj是否等於null,也能得到正確結果:
複製代碼代碼如下:
if (myObj == null) {
var myObj = { };
}
不過,雖然運行結果正確,但是從語義上看,這種判斷方法是錯的,應該避免。因為null指的是已經賦值為null的空對象,即這個對象實際上是有值的,而undefined指的是不存在或沒有賦值的對象。因此,這裡只能使用”比較運算符”(==),如果這裡使用”精確比較運算符”(===),就會出錯。
第九種寫法
還可以使用in運算符,判斷myObj是否為頂層對象的一個屬性:
複製代碼代碼如下:
if (!(‘myObj’ in window)) {
window.myObj = { };
}
第十種寫法
最後,使用hasOwnProperty方法,判斷myObj是否為頂層對象的一個屬性:
複製代碼代碼如下:
if (!this.hasOwnProperty(‘myObj’)) {
this.myObj = { };
}
總結
1. 如果只判斷對象是否存在,推薦使用第五種寫法。
2. 如果除了對象是否存在,還要判斷對象是否有null值,推薦使用第一種寫法。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/227468.html