一個簡單的緣由就是,js在設計之初只是進行一些簡單的表單校驗,這徹底不須要多線程,單線程徹底能夠勝任這項工做。即使後來前端發展迅速,承載的能力愈來愈多,也沒有發展到非多線程不可的程度。javascript
並且還有一個主要的緣由,設想一下,若是js是多線程的,在運行時多個線程同時對DOM元素進行操做,那具體以哪一個線程爲主就是個問題了,線程的調度問題是一個比較複雜的問題。css
HTML5新的標準中容許使用new Worker的方式來開啓一個新的線程,去運行一段單獨的js文件腳本,可是在這個新線程中嚴格的要求了可使用的功能,好比說他只能使用ECMAScript, 不能訪問DOM和BOM。這也就限制死了多個線程同時操做DOM元素的可能。html
元素寬高設置爲0,經過border屬性來設置,讓其它三個方向的border顏色爲透明或者和背景色保持一致,剩餘一條border的顏色設置爲須要的顏色。前端
div { width: 0; height: 0; border: 5px solid #transparent; border-top-color: red;}
我通常只使用兩種方式 定位 或者 flex,我以爲夠用了。java
div { width: 100px; height: 100px; position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto;}
父級控制子集居中react
.parent {
display: flex;
justify-content: center;
align-items: center;
}
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
overflow: auto;
-webkit-overflow-scrolling: touch;
隱藏div元素的滾動條webpack
div::-webkit-scrollbar {
display: none;
}
div::-webkit-scrollbar 滾動條總體部分ios
div::-webkit-scrollbar-thumb 滾動條裏面的小方塊,能向上向下移動(或往左往右移動,取決因而垂直滾動條仍是水平滾動條)git
div::-webkit-scrollbar-track 滾動條的軌道(裏面裝有Thumb)web
div::-webkit-scrollbar-button 滾動條的軌道的兩端按鈕,容許經過點擊微調小方塊的位置。
div::-webkit-scrollbar-track-piece 內層軌道,滾動條中間部分(除去)
div::-webkit-scrollbar-corner 邊角,即兩個滾動條的交匯處
div::-webkit-resizer 兩個滾動條的交匯處上用於經過拖動調整元素大小的小控件
注意此方案有兼容性問題,通常須要隱藏滾動條時我都是用一個色塊經過定位蓋上去,或者將子級元素調大,父級元素使用overflow-hidden截掉滾動條部分。暴力且直接。
ios手機在使用audio或者video播放的時候,個別機型沒法實現自動播放,可以使用下面的代碼hack。
// 解決ios audio沒法自動播放、循環播放的問題var music = document.getElementById('video');var state = 0;
document.addEventListener('touchstart', function(){ if(state==0){ music.play(); state=1; }}, false);
document.addEventListener("WeixinJSBridgeReady", function () { music.play();}, false);
//循環播放music.onended = function () { music.load(); music.play();}
display-none: 元素不會佔用空間,在頁面中不顯示,子元素也不會顯示。
opacity-0: 元素透明度將爲0,但元素仍然存在,綁定的事件仍舊有效仍可觸發執行。
visibility-hidden:元素隱藏,但元素仍舊存在,佔用空間,頁面中沒法觸發該元素的事件。
一提到前端工程化不少人想到的都是webpack,這是不對的,webpack僅僅是前端工程化中的一環。在整個工程化過程當中他幫咱們解決了絕大多數的問題,但並無解決全部問題。
前端工程化是經過工具提高效率,下降成本的一種手段。
近些年被普遍的關注和探討,究其緣由主要是由於現代化前端應用功能要求不斷提升,業務邏輯日益複雜,做爲當下互聯網時代惟一不可或缺的技術,前端能夠說是佔據了整個開發行業的半壁江山。從傳統的網站,到如今的H5,移動App,桌面應用,以及小程序。前端技術幾乎是無所不能的全面覆蓋。
在這些表象的背後呢,其實是行業對開發人員的要求發生了天翻地覆的變化,以往前端寫demo,套模板,調頁面這種刀耕火種的方式已經徹底不符合當下對開發效率的要求,前端工程化就是在這樣一個背景下被提上臺面,成爲前端工程師必備的手段之一。
通常來講前端工程包含,項目初始化,項目開發,集成,構建,打包,測試,部署,監控等流程。工程化就是以工程的角度來解決這些問題。好比項目初始化咱們通常使用npm init
, 建立頁面模板使用plop,咱們喜歡使用ES6+開發,可是須要經過babel編碼成ES5,持續集成的時候咱們使用git/ci,可是爲了保持開發規範咱們引入了ESLint,部署通常使用git/cd或者jenkins等等。
前端工程化是一個比較大的話題,後面咱們會單開話題來說。
html中大部分標籤都是不能夠編輯的,可是添加了contenteditable屬性以後,標籤會變成可編輯狀態。
<div contenteditable="true"></div>
不過經過這個屬性把標籤變爲可編輯狀態後只有input事件,沒有change事件。也不能像表單同樣經過maxlength控制最大長度。我也忘記我在什麼狀況下用到過了,後面想起來再補吧。
這是一個css屬性,我通常稱之爲css表達式。能夠計算css的值。最有趣的是他能夠計算不一樣單位的差值。很好用的一個功能,缺點是不容易閱讀。接盤俠沒辦法一眼看出20px是啥。
div { width: calc(25% - 20px);}
獲取當前時間毫秒值
// 方式一Date.now(); // 1606381881650// 方式二new Date() - 0; // 1606381881650// 方式三new Date().getTime() // 1606381881650
建立Date對象的兼容性問題。
// window和安卓支持,ios和mac不支持new Date('2020-11-26'); // window和安卓支持,ios和mac支持new Date('2020/11/26');
Proxy的意思是代理,我通常叫他攔截器,能夠攔截對象上的一個操做。用法以下,經過new的方式建立對象,第一個參數是被攔截的對象,第二個參數是對象操做的描述。實例化後返回一個新的對象,當咱們對這個新的對象進行操做時就會調用咱們描述中對應的方法。
new Proxy(target, { get(target, property) {
}, set(target, property) {
}, deleteProperty(target, property) {
}})
Proxy區別於Object.definedProperty。
Object.defineProperty只能監聽到屬性的讀寫,而Proxy除讀寫外還能夠監聽屬性的刪除,方法的調用等。
一般狀況下咱們想要監視數組的變化,基本要依靠重寫數組方法的方式實現,這也是Vue的實現方式,而Proxy能夠直接監視數組的變化。
const list = [1, 2, 3];const listproxy = new Proxy(list, { set(target, property, value) { target[property] = value; return true; // 標識設置成功 }});
list.push(4);
Proxy是以非***的方式監管了對象的讀寫,而defineProperty須要按特定的方式定義對象的屬性。
他是ES2015新增的對象,純靜態對象也就是不能被實例畫,只能經過靜態方法的方式調用,和Math對象相似,只能相似Math.random()的方式調用。
Reflect內部封裝了一系列對對象的底層操做,一共14個,其中1個被廢棄,還剩下13個。
Reflect的靜態方法和Proxy描述中的方法徹底一致。也就是說Reflect成員方法就是Proxy處理對象的默認實現。
Proxy對象默認的方法就是調用了Reflect內部的處理邏輯,也就是若是咱們調用get方法,那麼在內部,proxy就是將get原封不動的交給了Reflect,以下。
const proxy = new Proxy(obj, { get(target, property) { return Reflect.get(target, property); }})
Reflect和Proxy沒有絕對的關係,咱們通常將他們兩個放在一塊兒講是爲了方便對兩者的理解。
那爲何會有Reflect對象呢,其實他最大的用處就是提供了一套統一操做Object的API。
判斷對象是否存在某一個屬性,可使用in操做符,可是不夠優雅,還可使用Reflect.has(obj, name); 刪除一個屬性可使用delete,也可使用Reflect.deleteProperty(obj, name); 獲取全部屬性名可使用Object.keys, 也可使用Reflect.ownKeys(obj); 咱們更推薦使用Reflect的API來操做對象,由於他纔是將來。
經過replace方法獲取url中的參數鍵值對,能夠快速解析get參數。
const q = {};location.search.replace(/([^?&=]+)=([^&]+)/g,(_,k,v)=>q[k]=v);console.log(q);
能夠經過建立a標籤,給a標籤賦值href屬性的方式,獲取到協議,pathname,origin等location對象上的屬性。
// 建立a標籤const aEle = document.createElement('a');// 給a標籤賦值href路徑aEle.href = '/test.html';// 訪問aEle中的屬性aEle.protocol; // 獲取協議aEle.pathname; // 獲取pathaEle.origin;aEle.host;aEle.search;...
localStorage是H5提供的永久存儲空間,通常最大可存儲5M數據,而且支持跨域隔離,他的出現極大提升了前端開發的可能性。localStorage的使用不少人都知道setItem,getItem, removeItem, 但他也能夠直接以成員的方式操做。
// 存儲localStorage.name = 'yd';// 獲取localStorage.name; // yd// 刪除delete localStorage.name;// 清除所有localStorage.clear();
// 遍歷for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); // 獲取本地存儲的Key localStorage[key]; // 獲取本地存儲的value}
localStorage滿了的狀況下仍繼續存儲並不會覆蓋其餘的值,而是直接報錯(QuotaExceededError),而且當前存儲的值也會被清空。瀏覽器支持每一個域名下存儲5M數據。
sessionStorage和localStorage的區別是,存在當前會話,不少人理解的是瀏覽器關閉,這是不對的,假設你在A頁面存儲了sessionStorage,新開選項卡將A頁面的連接粘貼進去打開頁面,sessionStorage也是不存在的。
因此sessionStorage存在的條件是頁面間的跳轉,A頁面存儲了sessionStorage,他要經過超連接或者location.href或者window.open來打開另外一個同域頁面才能訪問sessionStorage。
這一點在原生嵌套H5的開發模式中尤其重要,若是以新開webview的方式打開頁面,極可能sessionStorage就沒有了。
cookie在設置的時候若是不設置過時時間,就表示是個會話cookie,之前我覺得關閉瀏覽器會話cookie就消失了,然而...喜提bug一個。
在多數狀況下windows系統或者安卓系統確實是這樣的。可是在macOS系統或者ios系統中,關閉瀏覽器並不會清除掉會話cookie,結束瀏覽器進程才行。
模板字符串支持在前面添加一個函數,第一個參數是一個有固定內容組成的數組,後面參數依次爲傳入的變量,函數返回值爲模板字符串真正展現的值。不過這個功能我的感受沒啥用。
const tag = (params, ...args) => { return params[0] + args[0]; // 返回值爲模板字符串的真實值。}
const str = tag`hello ${'world'}`;
includes(); 字符串中是否包含某個字符串,這個不說了,其實就是indexOf的替代方案,用起來更優雅,
startsWith(); 字符串是否爲某個字符串開始,我通常用它判斷url是否有http
endsWith(); 字符串是否爲某個字符串結尾。判斷後綴名的時候尤爲有效。
repeat(number); 獲得一個重複number次的字符串。額...我也不知道何時有用,通常我用它造測試數據。
'abc'.padEnd(5, '1'); // abc11; 用給定的字符串在尾部拼接到指定長度,第一個參數爲長度,第二個參數爲用於拼接的值。
'abc'.padStart(5, '1'); // 11abc; 用給定的字符串在首部拼接到指定長度第一個參數爲長度,第二個參數爲用於拼接的值。首部補0?
應該不少人都知道這個,數組轉換成Set, 再轉換爲數組,不過這種去重方式只能去除基本數據類型組成的數組。
const arr = [1, 2, 3, 4, 5, 6];
const arr2 = new Set(arr);
const arr3 = [...arr2];
通常咱們經常使用Object.keys,返回一個對象的鍵組成的數組,其實還有Object.values,返回對象值組成的數組,Object.entries 將對象轉成數組,每一個元素是鍵值對組成的數組,可使用此功能快速將對象轉爲Map
const obj = {name: 'yd', age: 18};
Object.keys(obj); // ['name', 'age'];
Object.values(obj); // ['yd', 18];
const l = Object.entries(obj); // [['name', 'yd'], ['age': 18]];
const m = new Map(l);
獲取對象的描述信息
Object.assign 複製時,將對象的屬性和方法當作普通屬性來複制,並不會複製完整的描述信息,好比this。
const p1 = { a: 'y', b: 'd', get name() { return `${this.a} ${this.b}`; }}const p2 = Object.assign({}, p1);
p2.a = 'z';
p2.name; // y d; 發現並無修改p2.a的值,是由於this仍舊指向p1
使用 Object.getOwnPropertyDescriptors 獲取完整描述信息
JavaScript能夠處理的最大數字是2的53次方 - 1,這一點咱們能夠在 Number.MAX_SAFE_INTEGER 中看到。
consoel.log(Number.MAX_SAFE_INTEGER); //9007199254740991
更大的數字則沒法處理,ECMAScript2020引入BigInt數據類型來解決這個問題。經過把字母n放在末尾, 能夠運算大數據。
BigInt可使用算數運算符進行加、減、乘、除、餘數及冪等運算。它能夠由數字和十六進制或二進制字符串構造。此外它還支持 AND、OR、NOT 和 XOR 之類的按位運算。惟一無效的位運算是零填充右移運算符。
const bigNum = 100000000000000000000000000000n;console.log(bigNum * 2n); // 200000000000000000000000000000n
const bigInt = BigInt(1);console.log(bigInt); // 1n;
const bigInt2 = BigInt('2222222222222222222');console.log(bigInt2); // 2222222222222222222n;
BigInt是一個大整數,因此他不能用來存儲小數。
假設變量a不存在,咱們但願給系統一個默認值,通常咱們會使用||
運算符。可是在javascript中空字符串,0,false都會執行||
運算符,因此ECMAScript2020引入合併空運算符解決該問題,只容許在值爲null或未定義時使用默認值。
const name = '';
console.log(name || 'yd'); // yd;console.log(name ?? 'yd'); // '';
業務代碼中常常會遇到這樣的狀況,a對象有個屬性b, b也是一個對象有個屬性c,
咱們須要訪問c,常常會寫成 a.b.c,可是若是f不存在時,就會出錯。
const a = { b: { c: 123, }}console.log(a.b.c); // 123;console.log(a.f.c); // f不存在因此會報錯
ECMAScript2020定義可選鏈運算符解決該問題,經過在.以前添加一個?將鍵名變成可選
let person = {};console.log(person?.profile?.age ?? 18); // 18
import是ECMAScript2015當中定義的一套ES Module模塊系統,語法特性絕大多數瀏覽器已經支持了,經過給script標籤添加type=module的屬性就可使用ES Module的標準去執行javascript代碼了。
<script type="module">console.log('this is es module');</script>
在ES Module規範下,會採用嚴格模式(use strict)運行javascript代碼。每一個ES Module都運行在單獨的做用域中,也就意味着變量間不會互相干擾。外部js文件是經過CORS的方式請求的,因此要求咱們外部的js文件地址要支持跨域請求,也就是文件服務器要支持CORS。咱們能夠在任意網站控制檯輸入下面代碼。
const script = document.createElement('script');
script.type = 'module';
script.innerHTML = `import React from 'https://cdn.bootcdn.net/ajax/libs/react/17.0.1/cjs/react-jsx-dev-runtime.development.js';`;
document.body.append(script);
能夠發如今network中請求了https://cdn.bootcdn.net/ajax/libs/react/17.0.1/cjs/react-jsx-dev-runtime.development.js資源。
ES Module的script標籤會延遲腳本加載,等待網頁請求完資源以後才執行,和使用deffer的方式加載資源相同。
須要注意的是,import {} from 'xx' 導入模塊的時候,並非對象的解構,而是import的固定語法,這一點不少人容易弄錯。
而且ECMAScript2020中import開始支持動態導入功能,在此以前import只能寫在模塊代碼的頂部,一開始就要聲明模塊依賴的其它模塊。支持動態引入後就能夠按需引入對應的模塊,這個功能咱們早在SPA中就已經用到了。動態導入返回的是一個Promise。
a.js
const a = 123;
export { a };
b.js
import('./a.js').then(data => {
console.log(data.a); // 123;
})
console.log(0.1+0.2); // 0.30000000000000004
在JS當中,Number類型其實是double類型,運算小數時存在精度問題。由於計算機只認識二進制,在進行運算時,須要將其餘進制的數值轉換成二進制,而後再進行計算
小數用二進制表達時是無窮的。
// 將0.1轉換成二進制console.log(0.1.toString(2)); // 0.0001100110011001100110011001100110011001100110011001101
// 將0.2轉換成二進制console.log(0.2.toString(2)); // 0.001100110011001100110011001100110011001100110011001101
雙精度浮點數的小數部分最多支持53位二進制位,因此二者相加後,因浮點數小數位的限制而截斷的二進制數字,再轉換爲十進制,就成了 0.30000000000000004,這樣在進行算術計算時會產生偏差。
ES6 在Number對象上面,新增一個極小的常量Number.EPSILON。根據規格,它表示 1 與大於 1 的最小浮點數之間的差。對於64位浮點數來講,大於1的最小浮點數至關於二進制的1.00..001,小數點後面有連續51個零。這個值減去1以後,就等於2的-52次方。
Number.EPSILON === Math.pow(2, -52)// trueNumber.EPSILON// 2.220446049250313e-16Number.EPSILON.toFixed(20)// "0.00000000000000022204"
Number.EPSILON其實是 JavaScript 可以表示的最小精度。偏差若是小於這個值,就能夠認爲已經沒有意義了,即不存在偏差了。
引入一個這麼小的量的目的,在於爲浮點數計算,設置一個偏差範圍。咱們知道浮點數計算是不精確的。
Number.EPSILON能夠用來設置「可以接受的偏差範圍」。好比,偏差範圍設爲 2 的-50 次方(即Number.EPSILON * Math.pow(2, 2)),即若是兩個浮點數的差小於這個值,咱們就認爲這兩個浮點數相等。
(0.1 + 0.2 - 0.3) < Number.EPSILON // true
歡迎關注,更多內容持續更新