表單驗證輸入是我們經常做的一項工作,比如價格的輸入,就只能輸入小數而且保留最多兩位小數,但是翻看input事件屬性,onchange、oninput、onkeyup似乎都可以完成,真的是這樣嗎?其實那只是你還是沒有吃透這幾個事件的使用而已,下面我們就來具體看看它們有什么不同。
onchange
定義和用法:事件會在域的內容改變時發生。
支持該事件的 HTML 標簽:<input type="text">, <select>,<textarea>
所有主流瀏覽器都支持 onchange 屬性。
oninput
定義和用法:事件在用戶輸入時觸發。
支持該事件的 HTML 標簽: <input>,<textarea>
瀏覽器支持 :
onkeyup
定義和用法:事件會在鍵盤按鍵被松開時發生。
支持該事件的 HTML 標簽:
很多
所有主流瀏覽器都支持 onkeyup 屬性。
從以上對比可以看出很多不同,在這里我還是重點說下它們觸發的時機是不一樣的:
oninput 事件在元素值發生變化時立即觸發(頁面顯示的是回調函數處理過后的內容)
onchange 在元素失去焦點時立即觸發(首先顯示的是未處理的內容,失去焦點的情況在才去進行回調函數的執行,改變輸入的內容)
onkeyup是按鍵松開時觸發(首先顯示的是未處理的內容,一直按住不放一直進行輸入,只有按鍵松開時才會觸發事件)
所以說,比如我們進行價格輸入驗證,就只能用oninput,對于ie9一下可以使用onpropertychange(有興趣的同學可以去查下它),完了希望童鞋們多去試試,熟練掌握。
一種為HTML表單元素添加onchange事件處理器是一種可行的方法,這也是用的最多的,但是onchange的實現有一些問題存在:
1.如果用戶改變表單域的值,然后再修改回原始值,程序仍將認為表單的修改已經發生。
2.如果表單項的值是通過Javascript動態修改的,onchange事件不會被自動觸發。
3.為每一個表單元素增加onchange事件會引起性能問題,特別是較大的表單。
4.如果將表單元素從DOM中增加或移除,你需要相應的注冊或移除事件偵聽。
5.checkbox和radio的onchange事件在某些瀏覽器下不能按預期工作(你應該知道是哪個瀏覽器)。
6.除了onchange,還有更簡單有效的方案。
<script type="text/javascript" src="jquery.js"></script>
<script>
$(function() {
$("#myform :input").change(function(){
$("#myform").data("changed",true);
});
})
者:魔王哪吒 來自掘金
小伙伴們,下午好。關于JavaScript學習相關文章,小編我最少發布了十幾篇文章了,具體請見本篇文章底部,有興趣的小伙伴可以看看。
var a=[1, 2, 5];
for(var k in a){
console.log(k); // k 為當前元素的下標
}
for(var m of a){
console.log(m); // m 為當前元素的值
}
VM215:3 0
VM215:3 1
VM215:3 2
VM215:6 1
VM215:6 2
VM215:6 5
復制代碼
代碼:
// ES5
var a='web';
window.a; // 'web'
// ES6
let b='web';
window.b; // undefined
復制代碼
代碼:
// 單行注釋
/*
多行注釋
*/
復制代碼
代碼:
// ES5及之前
console.log(a); // undefined
var a=1;
console.log(a); // 1
// ES6開始
console.log(b); // Uncaught ReferenceError: b1 is not defined
let b=2;
console.log(b); // 2
復制代碼
代碼:
// 函數聲明
f(); // 'web'
function(){
console.log('web');
};
復制代碼
// 函數表達式
g(); // Uncaught TypeError: g is not a function
var g=function(){ // 換成 let 聲明也一樣
console.log('web');
}
復制代碼
示例{}包含的內容表示一個代碼塊
代碼:
if(test1=="red") {
test1="blue";
alert(test1);
}
復制代碼
JavaScript關鍵字:
break,else,new,var
case,finally,return,void
catch,for,switch,while
continue,function,this,with
default,if,throw
delete,in,try
do,instanceof,typeof
復制代碼
在javascript中,變量是存儲信息的容器,變量存在兩種類型的值,即為原始值和引用值。
代碼:
console.log( null==undefined); // true
復制代碼
使用isFinite()方法判斷參數值是否是有窮的。
示例:
console.log(NaN==NaN) // false
console.log(isNaN("66")); // false
復制代碼
返回值:
undefined,變量是Undefined類型
boolean,變量是Boolean類型的
number,變量是Number類型的
string,變量是String類型的
object,變量是一種引用類型或者Null類型
復制代碼
示例:
console.log(typeof 12); // number
復制代碼
typeof運算符對null的值返回Object。
示例:
<script>
var a=new Array();
if(a instanceof Array) {
console.log('a是一個數組類型');
}else{
console.log('a不是一個數組類型');
}
</script>
復制代碼
示例:
賦值運算符的符號為=算數運算符:+,-,*,/,%
比較運算符:>,>=,<,<=,!=,==,===,!==邏輯運算符:
&&,邏輯與,表示表達式前后全為true才能返回true
||,邏輯或,表示表達式前后只要有一個true就返回true
!,邏輯取反,表示表達式后若為true,則返回false,否則反之。
復制代碼
示例:
if(條件 1) {
當條件1為true時執行的代碼
}else if(條件 2){
當條件2為true時執行的代碼
}else{
當條件1和條件2都不為true時執行的代碼
}
復制代碼
示例:
switch(n){
case1:
執行代碼塊1
break;
case2:
執行代碼塊2
break;
default:
...
}
復制代碼
示例:
for(語句1;語句2;語句3){
被執行的代碼塊
}
復制代碼
示例:
for(鍵 in 對象) {
代碼塊
}
復制代碼
示例:
while(表達式){
代碼塊
}
復制代碼
示例:
do {
代碼
}while(表達式)
復制代碼
數組的屬性和方法:
concat()
連接兩個或更多的數組,并返回一個新數組。
語法:
arr.concat(a1, a2, ..., an)
復制代碼
參數:
join()
使用指定分隔符,連接兩個或多個數組的元素,返回一個字符串。
pop()和push()
shift()和unshift()
示例:
let arr=[1, 2, 3, 5, 6];
let a1=arr.slice(2); // [3, 5, 6]
let a2=arr.slice(2,3); // [3]
let arr=[1, 2, 3, 4];
let a=arr.splice(1, 2, "web", "a");
// a=> [2, 3]
// arr=> [1, "web", "a", 4]
復制代碼
代碼:
let a=[1,3,5,7];
a.forEach(function(val, index, arr){
arr[index]=val * 2
})
a ; // [2, 6, 10, 14]
復制代碼
代碼:
arr.every(callback)
測試數組的所有元素是否都通過了指定函數的測試。
some()
測試數組中的某些元素是否通過由提供的函數實現的測試。
復制代碼
示例:
let a=[1, "", "aa", 2, 6];
let res=a.filter(function(val, index, arr){
return typeof val=="number";
})
res;//[1, 2, 6]
復制代碼
對每個元素執行此方法,并返回一個執行后的數組。
示例:
let a=[1, 3, 5];
let b=a.map(function(val, index, arr){
return val + 1;
})
b; //[2, 4, 6]
復制代碼
拓展運算符使用(...)
示例:
console.log(...[1, 2, 3]); // 1 2 3
console.log(1, ...[2,3], 4); // 1 2 3 4
復制代碼
// 通常情況 淺拷貝
let a1=[1, 2];
let a2=a1;
a2[0]=3;
console.log(a1,a2); // [3,2] [3,2]
// 拓展運算符 深拷貝
let a1=[1, 2];
let a2=[...a1];
// let [...a2]=a1; // 作用相同
a2[0]=3;
console.log(a1,a2); // [1,2] [3,2]
復制代碼
let [a, ...b]=[1, 2, 3, 4];
// a=> 1 b=> [2,3,4]
let [a, ...b]=[];
// a=> undefined b=> []
let [a, ...b]=["abc"];
// a=> "abc" b=> []
復制代碼
new Array(3).fill('a'); // ['a','a','a']
[1,2,3].fill('a'); // ['a','a','a']
[1,2,3].fill('a',1,2);// [1, "a", 3]
復制代碼
代碼:
[1,2,3].includes(3,3); // false
[1,2,3].includes(3,4); // false
[1,2,3].includes(3,-1); // true
[1,2,3].includes(3,-4); // true
復制代碼
示例:
var arr1=[1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2=[1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
var arr3=[1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
復制代碼
var arr4=[1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]
復制代碼
語法
var new_array=arr.flatMap(function callback(currentValue[, index[, array]]) {
// return element for new_array
}[, thisArg])
復制代碼
var arr1=[1, 2, 3, 4];
arr1.map(x=> [x * 2]);
// [[2], [4], [6], [8]]
arr1.flatMap(x=> [x * 2]);
// [2, 4, 6, 8]
// only one level is flattened
arr1.flatMap(x=> [[x * 2]]);
// [[2], [4], [6], [8]]
復制代碼
let arr1=["it's Sunny in", "", "California"];
arr1.map(x=> x.split(" "));
// [["it's","Sunny","in"],[""],["California"]]
arr1.flatMap(x=> x.split(" "));
// ["it's","Sunny","in", "", "California"]
復制代碼
reduce() 方法對數組中的每個元素執行一個由您提供的reducer函數(升序執行),將其結果匯總為單個返回值。
var sum=[0, 1, 2, 3].reduce(function (accumulator, currentValue) {
return accumulator + currentValue;
}, 0);
// 和為 6
var total=[ 0, 1, 2, 3 ].reduce(
( acc, cur )=> acc + cur,
0
);
復制代碼
語法
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
initialValue可選
作為第一次調用 callback函數時的第一個參數的值。 如果沒有提供初始值,則將使用數組中的第一個元素。 在沒有初始值的空數組上調用 reduce 將報錯。
復制代碼
字符串對象屬性
字符串對象方法
indexOf(),lastIndexOf(),search()和match()。
3種字符串截取方法:substring(),slice(),substr()
字符串替換
replace(),replace(正則表達式/要被替換的字符串,要替換成為的子字符串)。
字符串切割
split()用于將一個字符串分割成字符串數組,語法為字符串。split(用于分割的子字符串,返回數組的最大長度),返回數組的最大長度一般情況下不設置。
事件流:
事件的處理過程主要有三個階段:捕獲階段,目標階段,冒泡階段 事件流包含三個階段:事件捕獲階段,處于目標階段和事件冒泡階段。
事件冒泡和事件捕獲
事件觸發方式
代碼:
addEventListener("click","doSomething","true")
復制代碼
第三個參數為true,表示采用事件捕獲,若false,表示采用事件冒泡。
<!DOCTYPE html>
<html lang="en>
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
html,body{
width:100%;
height:100%;
}
</style>
<script>
window.onload=function(){
d1=document.getElementById("d1");
d2=document.getElementById("d2");
d3=document.getElementById("d3");
// true 表示在捕獲階段響應
// false 表示在冒泡階段響應
d1.addEventListener("click",function(event){
console.log("d1")
},"true");
d2.addEventListener("click",function(event){
console.log("d2")
},"true")
d3.addEventListener("click",function(event){
console.log("d3")
},"true")
}
</script>
</head>
<body>
<div id="d1" style="background: #0000ff; width: 500px; height: 500px">
<div id="d2" style="background: #00ff00; width: 400px; height: 400px">
<div id="d3" style="background: #ff0000; width: 200px; height: 200px">
</div>
</div>
</div>
</body>
</html>
復制代碼
addEventListener網頁,點擊跳轉:addEventListener.html
一個響應事件委托到另一個元素。
<ul id="btn">
<li id="btn1">按鈕1</li>
<li id="btn2">按鈕2</li>
<li id="btn3">按鈕3</li>
</ul>
var btn1=document.getElementById('btn1');
var btn2=document.getElementById('btn2');
var btn3=document.getElementById('btn3');
webbtn.myAddFun(btn1, 'click', function(event){
alert('1點擊');
});
webbtn.myAddFun(btn2, 'click', function(event){
alert('2點擊');
});
webbtn.myAddFun(btn3, 'click', function(event){
alert('3點擊');
});
復制代碼
添加一個事件處理函數,來做事件委托
var btn=document.getElementById('btn');
webbtn.myAddFun(btn, 'click', function(event){
event=webbtn.getMyEvent(event);
var target=webbtn.getMyTarget(event);
switch(target.id){
case "btn1":
alert('1點擊');
break;
case "btn2":
alert('2點擊');
break;
case "btn3":
alert('3點擊');
break;
}
});
復制代碼
鍵盤事件就是有關鍵盤操作所觸發的世界。
鍵盤事件:
鼠標綁定onmousedown(),onmousemove(),onmouseup()事件。
mouse網頁,點擊跳轉:mouse.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>mouse</title>
<style>
html,body{
width: 100%;
height: 100%;
}
#dd {
width: 120px;
height: 120px;
background: #00ff00;
position: absolute;
}
</style>
<script>
var dd;
var mflag=false;
function ondown() {
dd=document.getElementById('dd');
mflag=true;
}
function onmove(e){
if(mflag) {
dd.style.left=e.clientX - 60 + "px";
dd.style.top=e.clientY - 60 + "px";
}
}
function onup() {
mflag=false;
}
</script>
</head>
<body onmousemove="onmove(event)">
<div id="dd" onmousedown="ondown()" onmouseup="onup()" style="left: 80px;top: 120px;"
</body>
</html>
復制代碼
鼠標事件:
示例:
function web(e) {
mouseX=e.clientX;
mouseY=e.clientY;
console.log("x:"+mouseX + "," + "y:"+mouseY)
}
<body onclick="web(event)">
復制代碼
窗口事件:
load事件,表示當頁面完全加載完之后,就會觸發window上面的load事件。包含所有的圖像,js文件,css文件等外部資源。
示例:
window.onload=function(){}
復制代碼
當頁面完全加載完之后執行其中的函數。
示例:
<script>
window.onload=function() {
var mydiv=document.getElementById("mydiv");
console.log(mydiv.innerText);
}
</script>
<body>
<div id="mydiv"></div>
</body>
復制代碼
示例:
function imgLoad() {
myimg=document.getElementById("myimg");
// 圖片加載完成后,給圖片加載框
myimg.style.border="9px solid $00ff00";
}
<img id="myimg src="" onload="imgLoad()">
復制代碼
resize事件
示例:
document.body.clientWidth和document.body.clientHeight獲得窗口的寬和高。
html,body {
width: 100%;
height: 100%;
}
<script>
function winChange() {
winWidth=document.body.clientWidth;
winHeight=document.body.clientHeight;
}
</script>
<body onresize="winChange()">
</body>
復制代碼
scrol事件,文檔或者瀏覽器窗口被滾動時觸發scroll事件
示例:
<script>
function scrollChange() {
srpos=document.getElementById("srpos");
srpos.innerText=document.documentElement.scrollTop;
srpos.style.top=docuemnt.documentElement.scrollTop+"px";
}
</script>
<body onscroll="scrollChange()">
<div style="height:300%;">
<br/>
<font id="srpos" style="position: relative;top: 0px">滾動條滾動到0px</font>
</div>
</body>
復制代碼
焦點事件
示例:
<script>
var note;
function myfocus(fname,notename) {
note=document.getElementById(notename);
note.innerText=fname+'獲得焦點';
}
function myblur(fname,notename) {
note=document.getElementById(notename);
note.innerText=fname + '失去焦點';
}
</script>
<body>
<form name="myform">
<input type="text" name="uname" onfocus="myfocus('uname','unote')" onblur="myblur('uname','unote')"/><font id="unote"></font>
<br/>
<input type="text" name="pwd" onfocus="myfocus('pwd','pnot')" onblur="myblur('pwd','pnote')"/><font id="pnote"></font>
</form>
</body>
復制代碼
事件方法
窗口事件
鼠標事件
鍵盤事件與事件冒泡,獲取
JavaScript的DOM操作,包含獲取節點,獲取,設置元素的屬性值,創建,添加節點,刪除節點,屬性操作。
獲取節點的方法:
document.getElementById(idName)
復制代碼
document.getElementsByName(name)
復制代碼
document.getElementsByClassName(className)
復制代碼
document.getElementsByTagName(tagName)
復制代碼
獲取,設置元素的屬性值
示例:
<script>
window.onload=function(){
mytable=document.getElementById('mytable');
// 獲取mytable中標簽名為tr的字節點
trs=mytable.getElementsByTagName("tr");
len=trs.length;
flag=true;
for(i=0;i<len;i++){
if(flag){
trs[i].setAttribute('bgcolor','#cccccc');
flag=false;
}else{
flag=true;
}
}
ww=mytable.getAttribute('width');
}
</script>
<body>
<table id="mytable' align='center' width="80%" border="1">
<tr bgcolor="#cccccc">
<td>aaa</td>
<td>bbb</td>
<td>ccc</td>
</tr>
</table>
</body>
復制代碼
創建,添加節點
代碼:
// 創建節點:
document.createElement("h1");
document.createTextNode(String);
document.createAttribute("class");
復制代碼
代碼:
element.appendChild(Node);
element.insertBefore(newNode, existingNode);
復制代碼
代碼:
element.removeChild(Node)
復制代碼
屬性操作:獲取當前元素的父節點,獲取當前元素的子節點,獲取當前元素的同級元素,獲取當前元素的文本,獲取當前節點的節點類型,設置樣式。
代碼:
element.parentNode
復制代碼
代碼:
element.chlidren
復制代碼
代碼:
element.nextElementSibling
element.previousElementSibling
復制代碼
代碼:
element.innerHTML
element.innerText
復制代碼
代碼:
node.nodeType
復制代碼
document對象
document屬性和方法:
location對象
location屬性和方法:
navigator 對象
navigator對象包含有關瀏覽器的信息
screen對象
screen對象的屬性:
history對象
history對象的屬性:
數學函數
日期函數
代碼:
function 函數名(參數){
函數體
return 返回值
}
復制代碼
代碼:
function web1 () {
document.write("1");
}
web1();
var web2=function(){
document.write("2")
}
web2();
// 無須調用,直接執行,此方法不常用
var web3=new function(
document.write("3")
);
復制代碼
在js中一個函數在另一個函數中定義,就可以訪問到父函數的成員,內部的函數就稱為閉合函數。
閉合是詞法閉包的簡稱,是引用了自由變量的函數。
閉包函數的特點:
代碼:
function init() {
var name="web"; // name 是一個被 init 創建的局部變量
function displayName() { // displayName() 是內部函數,一個閉包
alert(name); // 使用了父函數中聲明的變量
}
displayName();
}
init();
復制代碼
init() 創建了一個局部變量 name 和一個名為 displayName() 的函數。
displayName() 是定義在 init() 里的內部函數,并且僅在 init() 函數體內可用。
displayName() 沒有自己的局部變量。然而,因為它可以訪問到外部函數的變量,所以 displayName() 可以使用父函數 init() 中聲明的變量 name 。
displayName() 函數內的 alert() 語句成功顯示出了變量 name 的值(該變量在其父函數中聲明)。
這個詞法作用域的例子描述了分析器如何在函數嵌套的情況下解析變量名。
詞法指,詞法作用域根據源代碼中聲明變量的位置來確定該變量在何處可用。嵌套函數可訪問聲明于它們外部作用域的變量。
閉包是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。
JavaScript中所有的function都是一個閉包。不過一般來說,嵌套的function所產生的閉包更為強大,也是大部分時候我們所謂的“閉包”。
閉包的作用
在a執行完并返回后,閉包使得Javascript的垃圾回收機制GC不會收回a所占用的資源,因為a的內部函數b的執行需要依賴a中的變量。
代碼如下:
for(var i=0 ; i<10; i++){
setTimeout(function(){
console.log(i);
},100);
}
復制代碼
返回的是10個10。
解決:
for(var i=0; i<10 ; i++){
(function(i){
setTimeout(function(){
console.log(i);
}, i*100);
})(i);
}
復制代碼
ES6之前,使用var聲明變量會變量提升問題:
for(var i=0 ; i<10; i++)
{
console.log(i)
};
console.log(i);
// 變量提升 返回10
復制代碼
示例:
// 1
var Person=function(id,name){
this.id=di;
this.name=name;
}
var user1=new Person(1,"web");
// 2
var web1={id:1,name:"web"};
var web2=Object.create({id:2,name:"web"});
復制代碼
創建正則表達式
使用一個正則表達式字面量:
let reg=/ab+c/;
let reg=/^[a-zA-z]/gi;
復制代碼
字符串方法
正則對象方法
RegExp對象方法
[a-z]
匹配小寫字母從a到z中的任意一個字符
復制代碼
[A-Z]
匹配大寫字母從a到z中的任意一個字符
復制代碼
[0-9]
匹配數字0到9中任意一個字符,等于 \d
復制代碼
[0-9a-z]
匹配數字0到9或者小寫字母a到z中任意一個字符。
復制代碼
[0-9a-zA-Z]
匹配數字0到9或小寫a到z或大寫A到Z中任意一個字符
復制代碼
[abcd]
匹配字符abcd中的任意一個字符
復制代碼
[^a-z]
匹配除小寫字母a到z外的任意一個字符
復制代碼
[^0-9]
匹配除數字0到9外的任意一個字符
復制代碼
[^abcd]
匹配除abcd外的任意一個字符
復制代碼
元字符是擁有特殊含義的字符:
.
查找單個字符,除了換行和行結束符。
復制代碼
\w
查找單詞字符。
復制代碼
\W
查找非單詞字符。
復制代碼
\d
查找數字。
復制代碼
\D
查找非數字字符。
復制代碼
\s
查找空白字符。
\S
查找非空白字符。
復制代碼
\0
查找 NUL 字符。
\n
查找換行符。
\f
查找換頁符。
\r
查找回車符。
\t
查找制表符。
\v
查找垂直制表符。
復制代碼
\xxx
查找以八進制數 xxx 規定的字符。
\xdd
查找以十六進制數 dd 規定的字符。
\uxxxx
查找以十六進制數 xxxx 規定的 Unicode 字符。
復制代碼
量詞描述
.定位符
定位符可以將一個正則表達式固定在一行的開始或者結束,也可以創建只在單詞內或者只在單詞的開始或者結尾處出現的正則表達式。
復制代碼
^
匹配輸入字符串的開始位置
復制代碼
$
匹配輸入字符串的結束位置
復制代碼
\b
匹配一個單詞邊界
復制代碼
\B
匹配非單詞邊界
復制代碼
/^[\d]{4}-[\d]{1,2}-[\d]{1,2}${1,2}$]/
日期字符
復制代碼
轉義符
使用轉義符(反斜杠\)進行轉義
復制代碼
new RegExp(str[, attr])接收2個參數,str是一個字符串,指定正則表達式匹配規則,attr可選,表示匹配模式,值有g(全局匹配),i(區分大小寫的匹配)和m(多行匹配)。
表達式:g,i,m
g 表示全局模式
應用于所有字符串,而非在發現第一個匹配項就停止
i 表示不區分大小寫模式
m 表示多行模式
繼續查找下一行中是否存在模式匹配的項
復制代碼
函數的實際參數會被保存在一個類數組對象 arguments 對象中,通過索引訪問具體的參數:
var a=arguments[i]
復制代碼
onBlur:當失去輸入焦點后產生該事件
onFocus:當輸入獲得焦點后,產生該文件
Onchange:當文字值改變時,產生該事件
Onselect:當文字加亮后,產生該文件
1.如果本文對你有幫助,就點個贊支持下吧,你的「贊」是我創作的動力。
《關于前端174道 JavaScript知識點匯總(一)》
《關于前端174道 JavaScript知識點匯總(二)》
《關于前端174道 JavaScript知識點匯總(三)》
《JavaScript ECMAScript語法概括【思維導圖】》
《都2020年了,你還不會JavaScript 裝飾器?》
《100個原生JavaScript代碼片段知識點詳細匯總【實踐】》
《送你 43 道 JavaScript 面試題》
《70個JavaScript知識點詳細總結(上)【實踐】》
《70個JavaScript知識點詳細總結(下)【實踐】》
《3個很棒的小眾JavaScript庫,你值得擁有》
《Echa哥教你徹底弄懂 JavaScript 執行機制》
《3個很棒的小眾JavaScript庫,你值得擁有》
《幾個非常有意思的javascript知識點總結【實踐】》
《開源了一個 JavaScript 版敏感詞過濾庫》
《推薦7個很棒的JavaScript產品步驟引導庫》
《一個合格的中級前端工程師需要掌握的 28 個 JavaScript 技巧》
作者:魔王哪吒
轉發鏈接:https://juejin.im/post/5e8089dde51d4546d72d2099
*請認真填寫需求信息,我們會在24小時內與您取得聯系。