es和數據類型


js=es+dom+bom,dom和bom前面已經講完了

es是js的本體,是指數據類型,和對於數據的操作手段,他的版本更新得很快

這些功能不是html文件提供的,也不是瀏覽器提供的,即使脫離了dom和bom,在nodejs服務器端,es照常使用,照樣運行,他是最底層的操作方式,所有的js框架都是基於es的api封裝的,包括前端的三大框架,vue,react,anglues;所以框架可以不學,es的更新一定要跟上,跟不上es的更新就真的落伍了,而且es學得好,框架什么的上手特別的快

數據類型在所有電腦語言上都是一樣的,js有數字,有字符串,java有,php有,python有,而且連操作方法都幾乎是一樣的,雖然寫法不一樣,但原理相同,所以懂一個語言去學另一個速度飛快

es是寫在script標簽里或者js文件里的,否則不會被識別

》》正式內容(這一篇只會講es第五代和五代以下的內容)

聲明
聲明就是被一個數據起名字的意思

// 把數字123叫做aa
var aa = 123;
// 把字符串123叫做bb
var bb = "123";
// 后續可以直接用aa,bb進行操作,並且數據被aa和bb的名字鎖住了,於是可以反復操作

es5總共有數據類型是

  • number 數字類型,整數,小數,沒內容可講
  • Boolean 布爾類型,只有true/false
  • string 字符串
  • RegExp 正則表達式
  • date 日期
  • math 數學
  • array 數組
  • object 對象
  • function 函數/方法
  • json/xml
  • error 錯誤

如何檢查數據類型
有三種方法

  1. typeof "aa"
  2. "aa" instanceof String
  3. Object.prototype.toString.call("aa")

前兩種驗證都有漏洞,用第三種,具體看面試題二

number

// 精度丟失
// 為了應付這種情況,需要乘法要套一層四舍五入
console.log(0.7*0.1) //0.06999999999999999
console.log(0.7*0.7) //0.48999999999999994
// 解決方案,都乘以100,1000后變成整數再計算

boolean
常用在判斷是不是第一次執行,也就是只有對錯的判斷上使用

// true 和 false 不能加雙引號""
var isCan = true;
isCan = false;
// 取反
isCan = !isCan; 

String
字符串用雙引號""聲明,用單引號''也行,只是java的字符串只能用雙引號,做個習慣記憶

var str = "a1b2c3d4";
// 字符串的長度
str.length

// 拼接字符串,加等於+=是邏輯運算符,下面會講
str += "e5f6" + "...";
str.concat("e5f6","...") //參數是無限的
// 如果數據里有相同的引號會出錯
// 比如 str = "aa"666"
// 他會把"aa"當做一部分然后666"沒辦法被識別這是什么意思,於是報錯,需要轉義
str = "aa\"666"
// 【\】斜桿在字符串里用於轉義他的后一位,然后自己消失,那怎么才能讓斜桿出現在字符串里呢,用斜桿轉義斜桿
str = "aa\\666"
// 轉義最常用的就是在拼接標簽元素時
str = "<div style=\"width=100px\" onclick=\"click(\"aa\")\">哈哈</div>" 
// 字符串的拼接不能直接換行,換行需要用加號收尾,而且這樣的轉義太惡心,也很難理解
// 但是在新es還有更簡易的方法,查看《es678910篇》


// 字符串的切割split,會改變原值,支持正則
// 是一個從開始到結尾的循環,只要找到符合條件的,會切割,然后判斷切割前后是否都有數據,沒有會補上一個空字符串
var str = "aaxaa"
str.split("x")  // ["aa","aa"]
var str = "xaax"
str.split("x")  // ["","aa",""]

// 字符串的替換,不會改變原值,所以需要新聲明,返回的是新字符串
// 把str里的第一個找到的x改成y
var newStr = str.replace("x","y")
// 如果要改所有的x,不是第一個,使用正則,第一個參數支持正則
var newStr = str.replace(/x/g,"y")
// 第二個參數可以是函數,可以做更多的處理,比如所有的數字加一,所有的字母大寫,但需要返回值
var newStr = str.replace(/\d/g,function(x){ return x+1; })

// 字符串的大小寫,不會改變原值,所以需要新聲明,返回的是新字符串
var newStr = "1a2x45".toUpperCase()
var newStr = "X66L6".toLowerCase()

// 去除字符串兩邊的空格,不會改變原值,所以需要新聲明
var newStr = "   666   ".trim()   // "666"

// 找出想要的某些數據match,不會改變原值,所以需要新聲明,返回的是數組
// 可以傳字符串,但是這個函數是設計給正則使用的,傳字符串毫無意義,
// 常用在把日期的數字取出來,重新改格式
var arr = "2019-11-05".match(/\d+/g)  //["2019","11","05"]

// 轉碼加密加密,用於把字符串里的特殊符號
encodeURIComponent(",/?:@&=+$#");  // %2C%2F%3F%3A%40%26%3D%2B%24%23
decodeURIComponent("%2C%2F%3F%3A%40%26%3D%2B%24%23")  // ",/?:@&=+$#"

RegExp
正則是用來操作字符串的,除了上面String里出現的split,match,replace外
正則需要去記住背誦他的操作方式,表達式怎么寫,可以百度,如果能手寫就更牛逼了
全面的筆記查看《正則RegExp》筆記

// 聲明正則
var reg = /\d/
var reg = new RegExp("/\d/")

// 檢查正則的格式對不對,test方法,返回的是boolean
// 常用來檢查電話號碼,郵箱,身份證
/\d/.test(1)
reg.test(2)

Date

// 當前時間對象
var date = new Date();

// 當前時間戳
var now = new Date().getTime()

// 當前時間的詳細信息
now.getFullYear() // 年份(4位,1970)
now.getMonth() // 月份(0-11,0代表1月,用的時候記得加上1)
now.getDate() // 日(1-31)
now.getTime() // 時間戳(從1970.1.1開始的毫秒數)
now.getHours() // 獲取小時數(0-23)
now.getMinutes() // 分鍾數(0-59)
now.getSeconds() // 秒數(0-59)
now.getDay() // 星期幾(0-6,0代表星期天)

// 這個月第一天0點時刻
var monthBegDate = new Date(now.getFullYear(),now.getMonth(),1) 

// 這個月最后一天0點時刻
let monthEndDate = new Date(date.getFullYear(),date.getMonth() + 1,0) 

// 日期轉時間戳
// str支持的格式有"2019-06-11 12:12:50" 或者 "2019/06/11 12:12:50"
// 但是第一個橫桿拼接的數據蘋果手機api不識別,統一用第二種,還有其他兼容查看兼容篇
var time = new Date(str).getTime()

Math

// 字符串轉數字,最准確,但是是騷操作,
// 不能用加號,加號除了是數學運算符,還能拼接字符串,自帶強轉類型的缺點
"15.11" - 0

// 丟棄小數部分,保留整數部分,這個不是Math的方法,傳(字符串 | 數字)都行
parseInt("2.75") 

// 向上取整,有小數就整數部分加1 ,傳(字符串 | 數字)都行
Math.ceil("2.15") 

// 向下取整,傳(字符串 | 數字)都行
Math.floor("2.75")

// 四舍五入,傳(字符串 | 數字)都行
 Math.round("2.4") 

// 去小數點后幾位,沒有會留0,最后一位數字會四舍五入
(21.5).toFixed(2)  // 21.50
(21.55).toFixed(1)  // 21.6

// 隨機數
Math.random()

// max | min,可以無限傳
Math.max(10,"4",8)
Math.min(10,"4",8)

// 知識點apply,讓max和min支持傳 數組
Math.max.apply("",[10,"4",8])
Math.min.apply("",[10,"4",8])

Array
有序的有長度的有很多數組方法的特殊對象,是復雜數據類型

// 用構造函數的方式去創建數組
var arr = new Array(1,2,3)
// 簡易寫法,這其實也是調用了構造函數的方法,跟上面一樣的,這叫語法糖
var arr = [1,"2",["a","b"],"c",{name:"name",age:18}];

// 數組的長度
arr.length

// 存入數據,可以無限傳
arr.push("d","e","f")

// 快速合並,方式一
arr.push.apply(arr,[1,2,3])
// 跟上面實際是一樣的
Array.prototype.push.apply(arr1, arr2);
// 存入多個數據,方式二
arr.concat([1,2,3])
// 新es還有更簡易的方法,查看新es篇

// 數組轉字符串, 對象數據類型不適合轉字符串
// 方式一,join,參數決定了拼接間隔
[1,"2",["a",[2],"b"],"c"].join(",")  //"1,2,a,2,b,c"
// 方式二,toString,是用逗號拼接的,還能自動解開多維數組
[1,"2",["a",[2],"b"],"c"].toString() //"1,2,a,2,b,c"

// 排序英文單詞
arr.sort()
// 排序數字,sort會把數字轉字符串在排序,不行,需要二次封裝一下
arr.sort(function(x,y){ return x-y });

// 反轉數組
arr.reverse()

// 添加/刪除/替換
arr.splice(start,deleteNum,...add) 

// 刪除數組的第一個元素,並返回被刪掉的值
arr.shift()

// 刪除數組的最后一個元素,並返回被刪掉的值
arr.pop()

// 查找數組中某元素的第一個索引值。[不改變原數組] 如果沒有就返回-1
arr.indexOf("xx")
// 這個方式有些特殊數據找不到,新es里有新方法

// 數組的循環(for,forEach)
// js里有一期關於遍歷的總結,去哪里看吧

談一談偽數組
偽數組Array-Like,是帶有數字序號以及length屬性的對象格式
打印出來的時候看着跟數組一樣,但是數組的很多方法偽數組是沒有的
最常見的偽數組是document.querySelectorAll取到的偽數組,dom元素的自定義屬性以及所有參數arguments偽數組

// 常見偽數組一
Object.prototype.toString.call(document.querySelectorAll('div')) // [object NodeList]

// 常見偽數組二
function xx(){
   Object.prototype.toString.call(arguments)  //[object Arguments]
}
xx(1,2,3,4)

// 常見的偽數組三
document.querySelector('#aa').attributes

// 還有這個也是偽數組 
var arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// 除了用正常的辦法鑒別偽數據,也可以執行一下forEach,偽數組沒有forEach的方法
// 這也是為什么document.querySelectorAll取到的數據沒辦法forEach的原因

// 偽數組轉數組
// 方法一,創建一個數組,原生for循環依次存進去
// 方法二,用call
Array.prototype.slice.call(偽數組)
// 新es還有更簡易的方法,查看新es篇

Object
無序的無長度的,鍵值對(key-value)格式的數據儲存模式,復雜數據類型
你能遇到的對象有window對象,dom對象,自己創建的對象(來自自定義構造函數)
對象有屬性(專業點叫靜態屬性)和方法(專業點叫靜態方法)和原型屬性和原型方法

image.png

// 這是用構造函數去創造對象
var obj = new Object();
// 簡易寫法,這其實也是調用了構造函數的方法,跟上面一樣的,這叫語法糖
var obj = {name:"name",age:18,str:"aa666bb",obj:{},arr:[1,2,3]}

// 取值
obj.name
obj["name"]
// 可以傳參,但不能加雙引號,也不能直接用點【.】
var a = "name";
obj[a]

// 判斷有沒有這個key,答案是(true/false),如果直接用取值去判斷,沒有會得到underfind
console.log(key in obj)

// 有就修改,沒有就添加
obj.name = "newName"
obj["name"] = "newnewName"
var a = "name";
obj[a] = "newnewnewName"

// 執行對象的方法
obj.say()

// 查看對象的原型,返回是對象格式,獲取原型對象的屬性,執行原型對象的方法【同上】
obj.__proto__

// 刪除key-value
delete obj.name
// 多層也一樣
delete obj1.obj2.name

// 對象的遍歷,雖然對象沒有長度,但也能循環
// for-in,往下看有關於遍歷的
// 這個返回一個key的無序數組
Object.keys(obj)

// 對象的合並
// 只能通過上面的遍歷方式往另一個對象添加

// 對象的一個神級API
Object.defineProperty(obj,key, handle)
// 這個牛逼的地方就在於它是Vue2.0的設計核心,這個放在《vue的設計原理》說

Function
function叫函數,也叫方法,他的參數寫在中括號里,他的內容在大括號里,他自帶作用域(函數作用域),並且函數被執行時有個隱藏的this對象,在dom就說過,一切的函數執行都有執行者,執行者就是函數內部隱藏的this對象

// 聲明一個函數
var init = function(){ ... }
function init(){ ... }
var init = new Function(["canshu1","canshu2"],`console.log(arguments)`)
// 兩種聲明方式用第二種,第一種有變量提升的缺點

// 參數,js的參數是無限的,什么類型都能傳
// 實參,實參在括號里拿,如果沒有就是underfind,如果數量不夠就拿不到
function init(x,y){ console.log(x,y) }
init(1) //x是1,y是underfind
init(1,2,3) // 第三個拿不到
// 形參,形參就是不需要從中括號里拿的,在作用域里有一個隱藏的值叫arguments
// 就是上面說過的偽數組之一
function init(){ console.log(arguments) } //[1,2,3]
init(1,2,3)

// 自執行函數,window加載就會自動執行,不需要名字,匿名
// 自執行函數被用來制造插件
// 自執行函數也是第一版本的全局變量安全作用域
(function(){ ... })();

// 構造函數
// 構造函數是保存作用域變量,用來批量生產統一格式但內容不同的對象,是一個對象的生產工廠
// 構造函數是用new去執行的,new命令把函數的靜態屬性/方法,原型屬性/方法賦予一個空對象
// 一般來說靜態用添加屬性,原型添加方法
function User(name){
   // 靜態屬性
   this.name = name;
   // 靜態方法
   this.say = function(){ ... }
}
// 原型屬性
User.prototype.prototypeName = "proName";
// 原型方法
User.prototype.prototypeSay = function(){ ... }
// new一個對象,這個user就是上面對象部分的截圖
var user = new User("pdt")

// 查看function的原型,返回格式是對象,可以往里面添加屬性和方法
User.prototype

// 跳出function執行
return;

// 其實最上面的聲明function的寫法也是一種語法糖,function是可以用構造函數創造的
// 沒錯function也是一個對象,是一個畸形的變異的對象
var init = new Function()
// 因為function是對象,所以他有自己的屬性和方法,繼承自Function
// function的原型上有個name屬性是指這個function的名字
// function的原型上還有apply,call,bind三個改變執行者的方法
// 在上面的筆記里有Array.prototype.slice.call() 和 Math.max.apply() 就是執行了Function的方法
// 這些方法不常用,但是要知道,詳細查看《上下文和作用域》

新手建議 如果你不想制作一些BUG出來加班,聲明函數都寫在最高級的作用域里,不要在函數里聲明函數,不要在if或者for里聲明函數,函數可以在任何需要執行的地方並且可以執行的地方調用,但是不是說可以在任何地方聲明的,否則后果自負

JSON/XML
前后端數據交互的數據格式,為什么要存在着兩種格式,因為語言不通,前端直接往后端甩一個對象{}或者一個數組[] ,后端是不識別的,而共同的數據格式是JSON和XML
在最開始是用xml用標簽的格式去傳的

<name>pdt</name>
<age>18</age>

后來xml不好寫,而且數據量大,升級成了json

// json可以長這樣
var json = '{"name":"pdt","age":18,"arr":[1,"2","tom"]}'
// json也可以長這樣
var json = '[1,2,{"name":"pdt"}]'

注意,json不是對象,也不是數組,他是字符串,是字符串
ajax篇里說的能傳遞復雜數據類型方式就是用post-json,因為那個上傳方式是把數據轉成了一個json字符串,后端拿到一個字符串后可以用自己的方法拆成他看得懂的格式

// 由對象轉字符串
JSON.stringify({
   name: "name",
   age: 18
})

// 由數組轉字符串
JSON.stringify([1,"2","tom"])

// 由字符串轉對象,最兩邊不能是雙引號
JSON.parse('{"name":"pdt","age":18,"arr":[1,"2","tom"]}')

// 由字符串轉數組,最兩邊不能是雙引號
JSON.parse('[1,2,{"name":"pdt"}]')

注意,JSON.stringify() 的參數是有規定的,這個參數內容必須是有限的,也就是有盡頭的,什么是沒有盡頭的,function,如果是一個function,或者含有function,是沒法轉字符串的,不信你可以試試能不能把function一層層打開直到打不開為止

Error
錯誤對象,拋出錯誤,這個對象可以用來阻斷正在執行的無法return的函數,是個騷操作

throw new Error("我是一個錯誤")

》》數學運算符
等於,加減乘除余,加加,減減

  • 等於【=】用在聲明變量上作為賦值
  • 加號【+】
// 當一側為String類型,被識別為字符串拼接,並會優先將另一側轉換為字符串類型。
// 當一側為Number類型,另一側為原始類型,則將原始類型轉換為Number類型。
// 當一側為Number類型,另一側為引用類型,將引用類型和Number類型轉換成字符串后拼接。
123 + '123' // 123123   (規則1)
123 + null  // 123    (規則2)
123 + true // 124    (規則2)
123 + {}  // 123[object Object]    (規則3)
  • 減號【-】,普通的數學計算
  • 乘號【*】,普通的數學計算
  • 除號【/】,普通的數學計算
  • 取余【%】,取余最常用在循環判斷間隔
for(var i==0;i<100;i++){
   if(i%3==0){ ... }
   else if(i%3==1){ ... }
   else if(i%3==2){ ... }
}
  • 加加【++】,加加就是加一的意思,跟加一不同的是,加加會把值添加給原值,加一不會
var i = 0;
i++; // 實際是指 i = i+1;
i+1; // 實際是指 i+1; 沒有賦值給原值
// 還有一個面試題叫i++和++i的區別,一個是先加后賦值,一個是賦值后加
  • 減減【--】,同上

》》邏輯運算

大於小於

// 基礎用法
if(a > 1){ ... }
// 多條件
if(a > 1 && a < 5){ ... }
// 上面的多條件不能這么寫
if(1 < a < 5){ ... }

// 為什么
1 < 2 < 5  // true
1 < 10 < 5  // true
// 因為1<10 是true,true是1,1<5 所以是true

等於等於和全等於

  • 等於等於【】用在判斷上,若兩側類型相同,則比較結果和=相同,否則會發生隱式轉換,隱式轉換表在面試題二
  • 全等於【===】用在判斷上,應該習慣用這個

或者,並且

// 或者【||】,這個就是一個if-else,一個個往下判斷,直到找到一個true的值就返回這個值,所有到了最后一個還沒有true,就返回最后一個
console.log(0 || 1);  //1
console.log(0 || "");  //""
console.log(0 || 1 || NaN );  //1

// 並且【&&】,這也是一個if-else,一個個往下判斷,直到找到一個false的值就返回這個值,所有到了最后一個還不是,就返回最后一個
console.log(1 && 2)  //2
console.log(1 && 0)  //0
console.log(1 && 0 && 2) //0

//兩者並用
console.log( false&&"A" || 1&&"B" || ""&&"C") //"B",自己捋捋 

if-else
if是可以不寫大括號的,但是別這么做,不僅不裝而且很傻

// 如果符合就...,如果不符合什么事都不做
if(true){ ... }
// 如果符合就...,如果不符合就...
if(true){ ... }else{ ... }
// 如果符合A就...如果符合B就...最后支持else
if(A){ ... }else if(B){ ... }else{ ... }

// if-if
if(A){ ... }
if(B){ ... }
// 當兩個if並列會同時判斷,那個符合就執行那個,當兩個同時符合會同時執行
// 但是這是不建議這么寫的,因為當判斷量很大,邏輯很亂
// 如果只想執行一個,可以改用if-else
// 如果想執行多個可以改成先判斷A,再A里再判斷B

// if的代替寫法
function find(color){
    if(color=="red"){ return "..." }
    else if(color=="green"){ return "..." }
}
// 改成
function find(color){
   var ifObj = {
     "red": "..."
     "green": "..."
   }
   return ifObj[color];
}

// 0是代表false,"0"是true,""是false,underfind是false,null是false,
// NaN是false,空數組[]是false,非數組![]也是false,空對象{}是false

循環
原生的循環有for,for-in
封裝在數組方法上的遍歷方法有forEach
更優秀的遍歷方式出現在新es版本里,會有一期循環的專題

// 循環數組
for(var i=0;i<5;i++){ console.log(i) }
// 循環對象
for(var key in obj){ console.log(key,obj[key]) }
// for-in會遍歷原型鏈上的屬性,需要判斷
if(obj.hasOwnProperty(key)){ console.log("這個key是原型屬性") }

// forEach,回調函數第一個參數是value,第二個參數是序號
[1,2,3].forEach(function(x,index){ console.log(x,index) })

三元運算符

// 如果A就X,否則Y
var realSex = (sex=="1") ? "男" :"女"

》》其他

變量提升
查看《js樹和調試》那一篇

作用域
作用域會專門挑出來發
外層的作用域沒有辦法取到內層的作用域的數據
內層可以取到外層作用域的數據
這是就是如果把所有的js寫在非全局作用域,比如寫在一個自執行函數里,我們就沒辦法在F12的console調試區里取到別的作用域里的值,這也就是數據安全的第一層保護機制
目前的已知的作用域是function

var a = 1;
function x(){
  var b = 2;
}
console.log(a); // 1
console.log(b); // underfind

隊列和棧和堆
這三個是看起來高端但是一點都不高端的詞
隊列是一種模式,是指先進先出的一種存取模式,用數組模擬隊列,就是存值用push,取值有shift,被人稱為拉屎模式
棧也是一種模式,是指先進后出的一種存取模式,用數組模擬棧,就是存值用push,取值用pop,被人稱為嘔吐模式
堆也是一種模式,沒有順序,沒有存的順序也沒有取的順序,對象的存取就是堆模式,內存的分配也是堆模式

復雜數據類型
簡單數據類型和復雜數據類型的區別就是
簡單數據類型A被另一個變量引用B時,會被完全拷貝,即使修改了B的值,A的值也不會改變

var A = 1;
var B = A;
B = B+1;
console.log(B)  //2
console.log(A)  //1

復雜數據類型因為數據量太大,拷貝成本太高,所以被重新指向的是同一個數據,這種拷貝叫淺拷貝

var A = {
  name:"tom"
  age:18
}
var B = A;
B.age = 19;
console.log(B.age)  //19
console.log(A.age)  //19
// 數組和對象和函數都屬於復雜數據類型
// 但是也有需要用到完全拷貝的情況,所以就有了一個技術叫【深拷貝】
// 深拷貝的最簡單應用就是JSON
// 把數據改成字符串再改回來,就跟原來燈獨立了
// 有個缺點是JSON不能操作無限無盡的數據,所以JSON只是個騷操作
// 要完全適用所有的格式拷貝,是個很高深的學問,需要判斷很多的數據類型
var B = JSON.parse(JSON.stringify(A));

實戰:array 和 object 的靈活應用

需求是:頁面上要顯示一個列表,列表需要有刪除功能,當請求數據獲得一個裝着10個對象的數組,每個對象都有id,name等詳細數據,我們把數組循環,顯示到頁面上,要求用戶操作后最后一次性把數據提交

首先應該把請求到的數據存起來,看到這樣的需求,我一開始想在按鈕上添加序號,當點擊按鈕時獲取序號,用splice去刪掉數據的對應序號的數組里的對象,但是問題來了,刪掉第三個按鈕后,數組里只剩9個對象,這時再點擊刪掉第四個對象時,原本的第四個序號已經自動變成三個,所以我們刪掉的第四個其實是第五個,千萬不要用順序去當標識

所以改成了用id做標識,每次刪除一個數據后需要遍歷整個對象數組,找到一樣的id,然后刪掉這個對象,計算量大了很多

正確操作: 先把一開始的數組循環,拆成一個只裝着id的數組,和一個用id做key,value等於自身的大對象,然后點擊刪除時,用index去數組找到位置,把id刪掉,最后提交的時候,把數組遍歷,把數據從大對象里重新取出來裝回到數組對應id的序號里,提交上去


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM