一、ES6 和 JavaScript
ECMAScript、JavaScript、NodeJs,它們的區別是什麼?
ECMAScript:簡稱ES,是一個語言標準(循環、判斷、變量、數組等數據類型)
JavaScript:運行在瀏覽器端的語言,該語言使用ES標準。 ES + web api = JavaScript
NodeJs:運行在服務器端的語言,該語言使用ES標準。 ES + node api = JavaScript
無論JavaScript,還是NodeJs,它們都是ES的超集(super set)
ES6.0: 2015, 從該版本開始,不再使用數字作爲編號,而使用年份
二、let和const命令
1、使用var聲明變量
(1)允許重複的變量聲明:導致數據被覆蓋
var a = 100
//一萬行代碼
var a = 1
(2)變量提升:怪異的數據訪問、閉包問題(只有函數級作用域,沒有塊級作用域)
//先聲明一個var a,再執行if-else
if(Math.random()<0.5){
var a = "abc"
console.log(a)
}else{
console.log(a)
}
console.log(a)
(3)全局變量掛載到全局對象:全局對象成員污染問題
var a = 10
console.log(window.a)//10
2、使用let聲明變量
塊級作用域:代碼執行時遇到花括號,會創建一個塊級作用域,花括號結束,銷燬塊級作用域。
let
命令,用來聲明變量。但是所聲明的變量,只在let
命令所在的代碼塊內有效。
(1)let聲明的變量不會掛載到全局對象
頂層對象
頂層對象,在瀏覽器環境指的是window
對象,在 Node 指的是global
對象。ES5 之中,頂層對象的屬性與全局變量是等價的。
ES6 規定,爲了保持兼容性,var
命令和function
命令聲明的全局變量,依舊是頂層對象的屬性;let
命令、const
命令、class
命令聲明的全局變量,不屬於頂層對象的屬性。全局變量將逐步與頂層對象的屬性脫鉤。
var a = 1;
console.log(window.a) // 1
let a = "ll"
console.log(window.a)//undefined
(2)不允許當前作用域範圍內重複聲明。在塊級作用域中用let定義的變量,在作用域外不能訪問
console.log(name)//會報錯
let name = "ll"
console.log(name)//"ll"
let name = "lk" //報錯,檢查到當前作用域已經聲明過,不允許重複聲明
(3)使用let不會有變量提升,因此,不能在定義let變量之前使用它
// var 的情況
console.log(a); // 輸出undefined
var a = 2;
// let 的情況
console.log(b); // 報錯ReferenceError
let b = 2;
暫時性死區
var a = 10
if(true){
a = 5
let a = 10
}
存在全局變量a,但是if後的{}塊級作用域內let又聲明瞭一個局部變量a,導致後者綁定這個塊級作用域,所以在let聲明變量前,對a賦值會報錯。
ES6 明確規定,如果區塊中存在let
和const
命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。
底層實現上,let聲明的變量實際上也會有提升,但是,提升後會將其放入到“暫時性死區”,如果訪問的變量位於暫時性死區,則會報錯:“Cannot access ‘a’ before initialization”。當代碼運行到該變量的聲明語句時,會將其從暫時性死區中移除。
在循環中,用let聲明的循環變量,會特殊處理,每次進入循環體,都會開啓一個新的作用域,並且將循環變量綁定到該作用域(每次循環,使用的是一個全新的循環變量)。在循環中使用let聲明的循環變量,在循環結束後會銷燬
3、使用const聲明常量
const
和let
完全相同,僅在於const
聲明一個只讀的常量。一旦聲明,常量的值就不能改變。
實際上,在開發中,應該儘量使用const來聲明變量,以保證變量的值不會隨意篡改,原因如下:
1. 根據經驗,開發中的很多變量,都是不會更改,也不應該更改的。
2. 後續的很多框架或者是第三方JS庫,都要求數據不可變,使用常量可以一定程度上保證這一點。
(1)const
聲明的變量不得改變值,這意味着,const
一旦聲明變量,就必須立即初始化,不能留到以後賦值。
const a = 1
a = 3;// TypeError: Assignment to constant variable.
const b;// SyntaxError: Missing initializer in const declaration
(2)常量不可變,是指聲明的常量的內存空間不可變,並不保證內存空間中的地址指向的其他空間不可變。
const a = {};
// 爲 a 添加一個屬性,可以成功
a.prop = 123;
console.log(a.prop) // 123
// 將 a 指向另一個對象,就會報錯
a = {}; // TypeError: "a" is read-only
(3)命名規範
1. 特殊的常量:該常量從字面意義上,一定是不可變的,比如圓周率、月地距地或其他一些絕不可能變化的配置。通常,該常量的名稱全部使用大寫,多個單詞之間用下劃線分割
2. 普通的常量:使用和之前一樣的命名即可
(4)在for循環中,循環變量不可以使用常量,for-in循環可以
三、字符串的擴展
1、更好的Unicode支持
早期,由於存儲空間寶貴,Unicode使用16位二進制來存儲文字。我們將一個16位的二進制編碼叫做一個碼元(Code Unit)。後來,由於技術的發展,Unicode對文字編碼進行了擴展,將某些文字擴展到了32位(佔用兩個碼元),並且,將某個文字對應的二進制數字叫做碼點(Code Point)。
一個字符對應一個碼點(且每個字符的碼點都是唯一的),但一個碼點可能需要一個代碼單元或者兩個代碼單元才能表示。
charCodeAt():讀取一個碼元
例子 𠮷:碼點是0x20BB7
,UTF-16 編碼爲0xD842 0xDFB7
const text = "𠮷"; //佔用了兩個碼元(32位)
console.log("字符串長度:", text.length); //2
console.log("使用正則測試:", /^.$/.test(text));//false 讀到了兩個碼元,希望匹配一個
console.log("得到第一個碼元:", text.charCodeAt(0));//55362
console.log("得到第二個碼元:", text.charCodeAt(1));//57271
codePointAt():根據字符串碼元的位置得到其碼點。能夠正確處理 4 個字節儲存的字符,返回一個字符的碼點。
//𠮷:\ud842\udfb7
console.log("得到第一個碼點:", text.codePointAt(0));//134071 獲取完整的
console.log("得到第二個碼點:", text.codePointAt(1));//57271
同時,ES6爲正則表達式添加了一個flag: u,如果添加了該配置,則匹配時,使用碼點匹配
console.log("使用正則測試:", /^.$/u.test(text));
/**
* 判斷字符串char,是32位,還是16位
* @param {*} char
*/
function is32bit(char, i) {
//如果碼點大於了16位二進制的最大值,則其是32位的
return char.codePointAt(i) > 0xffff;
}
/**
* 得到一個字符串碼點的真實長度
* @param {*} str
*/
function getLengthOfCodePoint(str) {
var len = 0;
for (let i = 0; i < str.length; i++) {
//i在索引碼元
if (is32bit(str, i)) {
//當前字符串,在i這個位置,佔用了兩個碼元
i++;
}
len++;
}
return len;
}
console.log("𠮷 是否是32位的:", is32bit("𠮷", 0))
console.log("ab𠮷ab 的碼點長度:", getLengthOfCodePoint("ab𠮷ab"))
字符串的遍歷器for...of
循環遍歷
const text = "𠮷as";
for(let i=0;i<text.length;i++)
{
console.log(text[i]) //� � a s
}
//普通for循環無法正確識別。一次只能讀取一個代碼單元
//for-of可以正確識別一個完整的符號
const text = "𠮷as";
for(const item of text)
{
console.log(item) //𠮷 a s
}
2、字符串新增API
以下均爲字符串的實例方法
(1)includes():判斷字符串中是否包含指定的子字符串,第二個參數指定從哪個位置開始查找,返回true和false
const str = "今天天氣很好啊!"
const result1 = str.includes("氣")
const result2 = str.includes("氣",5)
console.log(result1,result2)
(2)startsWith():判斷字符串中是否以指定的字符串開始,也可以傳第二個參數
const str = "今天天氣很好啊!"
const result = str.startsWith("氣")
console.log(result)
(3)endsWith():判斷字符串中是否以指定的字符串結尾
const str = "今天天氣很好啊!"
const result = str.endsWith("!")
console.log(result)
(4)repeat():將字符串重複指定的次數,然後返回一個新字符串。
const str = "我很帥!"
const result = str.repeat(4)
console.log(result)//我很帥!我很帥!我很帥!我很帥!
//如果repeat的參數是負數或者Infinity,會報錯。
const result = 'a'.repeat(Infinity)// RangeError
const result = 'a'.repeat(-1)// RangeError
補充:
ES2017 引入了字符串補全長度的功能。如果某個字符串不夠指定長度,會在頭部或尾部補全。padStart()
用於頭部補全,padEnd()
用於尾部補全。實例方法
(5)padStart():第一個參數是字符串補全生效的最大長度,第二個參數是用來從起始位置補全的字符串。
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(4) // 'x '
(6)padEnd() :第一個參數是字符串補全生效的最大長度,第二個參數是用來從結束位置補全的字符串。
'x'.padEnd(5, 'ab') // 'xabab'
'12'.padStart(10, '0') // "0000000012"
注意:省略第二個參數,則默認用空格補全。padEnd方法常用來數字前補零
ES2019對字符串實例新增了trimStart()
和trimEnd()
這兩個方法。它們的行爲與trim()
一致,trimStart()
消除字符串頭部的空格,trimEnd()
消除尾部的空格。它們返回的都是新字符串,不會修改原始字符串。
(7)trimStart()
和trimEnd()
:消除頭部/尾部空格
const str = ' abc ';
consst r1 = str.trim() // "abc"
consst r2 = str.trimStart() // "abc "
consst r3 = str.trimEnd() // " abc"
注意:除了空格鍵,這兩個方法對字符串頭部(或尾部)的 tab 鍵、換行符等不可見的空白符號也有效。瀏覽器還部署了額外的兩個方法,trimLeft()
是trimStart()
的別名,trimRight()
是trimEnd()
的別名。
3-3. [擴展]正則中的粘連標記 幾乎用不到
標記名:y
含義:匹配時,完全按照正則對象中的lastIndex位置開始匹配,並且匹配的位置必須在lastIndex位置。
const text = "Hello World!!!"
const reg = /w\w+/y;//以w開頭的單詞
reg.lastIndex = 6;
//從lastIndex的位置必須匹配,一開始lastIndex爲0
console.log(reg.test(text))
3、模板字符串
模板字符串是增強版的字符串,用反引號(`)標識。它可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。
const text =
`我很帥
我真的很帥 `;
console.log(text);
如果要在字符串中拼接js表達式,只需要在模板字符串中使用${JS表達式}
const age = 18
console.log(`我今年${age}歲`)
console.log(`計算結果:${1+2+3*2}`)
4、模板字符串標記
在模板字符串書寫之前,可以加上標記:
標記名`模板字符串`
標記是一個函數,函數參數如下:
- 參數1:被插值分割的字符串數組
- 後續參數:所有的插值
const name = "llx"
const age = 18
// 相當於 text = myTag(["我的名字叫:",",我今年","歲"],"llx",18)
var text = myTag`我的名字叫:${name},我今年${age}歲`
function myTag(parts,arg1,arg2){
console.log(parts)
console.log(arg1)
console.log(arg2)
}
console.log(text)//(3) ["我的名字叫:", ",我今年", "歲", raw: Array(3)]
const name = "llx"
const age = 18
var text = myTag`我的名字叫:${name},我今年${age}歲`
// 相當於
// text = myTag(["我的名字叫:",",我今年","歲"],"llx",18)
function myTag(parts){
console.log(parts)
//先將arguments轉化爲真實數組,再除去第一個參數,parts
const values = Array.prototype.slice.apply(arguments).splice(1)
console.log(values)
let str = ""
for(let i=0;i<parts.length;i++){
if(parts.length-1 == i){
str += parts[i]
}else{
str += parts[i]+values[i]
}
}
return str
}
console.log(text)
String.raw將轉義字符原樣輸出
const text = String.raw`abc\ndef`
console.log(text)
四、函數的擴展
1、 參數默認值
在書寫形參時,直接給形參賦值,附的值即爲默認值。這樣一來,當調用函數時,如果沒有給對應的參數賦值(給它的值是undefined),則會自動使用默認值。
//c的默認值是2
function sum(a = 0,b = 0,c = 2){
return a + b + c;
}
console.log(sum(1,undefined,3))
注意:參數變量是默認聲明的,所以不能用let
或const
再次聲明。
對arguments的影響
只要給函數加上參數默認值,該函數會自動變量嚴格模式下的規則:arguments和形參脫離
比較二者區別
function test(a,b){
console.log("arguments:",arguments[0],arguments[1])
console.log("a:",a," ,b:",b)
a = 3
console.log("arguments:",arguments[0],arguments[1])
console.log("a:",a," ,b:",b)
}
test(1,2)
function test(a,b=1){
console.log("arguments:",arguments[0],arguments[1])
console.log("a:",a," ,b:",b)
a = 3
console.log("arguments:",arguments[0],arguments[1])
console.log("a:",a," ,b:",b)
}
test(1,2)
暫時性死區
形參和ES6中的let或const聲明一樣,具有作用域,並且根據參數的聲明順序,存在暫時性死區。
function test(a,b = a){
console.log(a,b)
}
test(1,2)
test(undefined,1)
function test1(a = b,b){
console.log(a,b)
}
test1(1,2)
test1(undefined,1)//報錯 Cannot access 'b' before initialization
2、rest參數
ES6的rest參數用於獲取函數的多餘參數,這樣就不需要使用arguments
對象了。rest 參數會將多餘的參數放入數組中。形如:...形參名
arguments
對象不是數組,而是一個類似數組的對象。所以爲了使用數組的方法,必須使用Array.prototype.slice.call
先將其轉爲數組。rest 參數就不存在這個問題,它就是一個真正的數組,數組特有的方法都可以使用。
function test(a,...args){
console.log(a,args)//1 [2, 3, 4, 5]
}
test(1,2,3,4,5)
function sum(...args){
let result = 0;
for(let i=0;i<args.length;i++){
result += args[i];
}
return result
}
console.log(sum(1,2,3,4,5))
注意:一個函數,僅能出現一個剩餘參數。一個函數,如果有剩餘參數,剩餘參數必須是最後一個參數
補充:從 ES5 開始,函數內部可以設定爲嚴格模式。ES2016 做了一點修改,規定只要函數參數使用了默認值、解構賦值、或者擴展運算符,那麼函數內部就不能顯式設定爲嚴格模式,否則會報錯。
3、展開運算符
使用方式:...要展開的東西
(1)對數組展開 ES6
//克隆數組arr1到arr2
const arr1 = [1,2,3,4,5]
const arr2 = [...arr1]
例子:
function sum(...args){
let result = 0;
for(let i=0;i<args.length;i++){
result += args[i];
}
return result
}
function getRandomNumbers(length){
let arr = []
for(let i=0;i<length;i++){
arr.push(Math.random())
}
return arr
}
const numbers = getRandomNumbers(10)
//展開的內容是numbers數組中的,相當於傳遞了10個參數
const result = sum(...numbers)
console.log(result)
(2)對對象展開 (ES2017)
const obj1 = {
name:"lkx",
age:18,
address:{
country:"山東"
}
}
//淺克隆
const obj2 = {
...obj1,
age:10
}
//克隆深度對象
const obj3 = {
...obj,
age:10,
address:{
...obj1.address
}
}
柯里化:用戶固定某個函數的前面的參數,得到一個新的函數,新的函數調用時,接收剩餘的參數
function cal(a,b,c,d){
return a + b *c -d
}
// 柯里化函數
function curry(func,...args){
return function(...subArgs){
const allArgs = [...args,...subArgs]
if(allArgs.length>=func.length){
//參數夠了
return func(...allArgs)
}else{
//參數不夠,繼續固定
return curry(func,...allArgs)
}
}
}
const newCal = curry(cal,1,2)
console.log(newCal(2,4))
4、 明確函數的雙重用途
ES6提供了一個特殊的API,可以使用該API在函數內部,判斷該函數是否使用了new來調用
new.target
//該表達式,得到的是:如果沒有使用new來調用函數,則返回undefined
//如果使用new調用函數,則得到的是new關鍵字後面的函數本身
function Person(firstName,lastName){
//判斷是否是使用new的方式來調用的函數
//過去的判斷方式,判斷this指向,但是用call和apply可能繞開
if(this instanceof Person){
throw new Error("該函數沒有使用new創建")
}
//es6提供的判斷方式
if(new.target === undefined){
throw new Error("該函數沒有使用new創建")
}
this.firstName = firstName
this.lastName = lastName
this.fullName = firstName + lastName
}
const p1 = new Person("張","飛")
console.log(p1)
const p2 = Person("張","飛")
console.log(p2)
5、箭頭函數
ES6 允許使用“箭頭”(=>
)定義函數。形如:(參數1, 參數2, ...)=>{ //函數體 }
var func = value => value;
// 等同於
var func = function (value) {
return value;
};
箭頭函數是一個函數表達式,理論上,任何使用函數表達式的場景都可以使用箭頭函數
const printNums = (num1,num2,num3) =>{
const result = num1 + num2 * num3
console.log(result)
}
如果參數只有一個,可以省略小括號
const print = num =>{
num += 1
console.log(num)
}
如果箭頭函數只有一條返回語句,可以省略大括號,和return關鍵字
//返回true和false
const isOdd = num => num%2 !== 0
注意:由於大括號被解釋爲代碼塊,所以如果箭頭函數直接返回一個對象,必須在對象外面加上括號,否則會報錯。
// 報錯
let getObject = id => { id: id, name: "ll" };
// 不報錯
let getObject = id => ({ id: id, name: "ll" });
關於this指向
在以往的學習中,對於this指向
1. 通過對象調用函數,this指向對象
2. 直接調用函數,this指向全局對象
3. 如果通過new調用函數,this指向新創建的對象
4. 如果通過apply、call、bind調用函數,this指向指定的數據
5. 如果是DOM事件函數,this指向事件源
//想要在定時器,事件中使用this,需要提前保存起來
const obj = {
count : 0,
start:function(){
let _this = this
setInterval(function(){
_this.count++
console.log(_this.count)
}, 1000);
},
regEvent:function(){
let _this = this
window.onclick = function(){
console.log(_this.count)
}
}
}
obj.start()
obj.regEvent()
而箭頭函數中的this指向,他是固定的。函數體內的this
對象,就是定義時所在的對象,而不是使用時所在的對象。
箭頭函數有幾個使用注意點。
(1)函數體內的`this`對象,就是定義時所在的對象,而不是使用時所在的對象。
(2)不可以當作構造函數,也就是說,不可以使用`new`命令,否則會拋出一個錯誤。
(3)不可以使用`arguments`對象,該對象在函數體內不存在。如果要用,可以用 rest 參數代替。
(4)不可以使用`yield`命令,因此箭頭函數不能用作 Generator 函數。
上面四點中,第一點尤其值得注意。`this`對象的指向是可變的,但是在箭頭函數中,它是固定的。
箭頭函數中,不存在this、arguments、new.target,如果使用了,則使用的是函數外層的對應的this、arguments、new.target
//箭頭函數中的this取決於聲明箭頭函數的位置
const obj = {
count: 0,
start:function(){
// let _this = this
setInterval(() => {
this.count++
console.log(this.count)
}, 1000);
},
regEvent:function(){
window.onclick = ()=>{
console.log(this.count)
}
}
}
obj.start()
obj.regEvent()
const func = ()=>{
console.log(this)//window
console.log(arguments)//Uncaught ReferenceError
}
const obj = {
method:func
}
obj.method(234)
const obj = {
method:function(){
const func = ()=>{
console.log(this)//obj
console.log(arguments)//method的arguments
}
func()
}
}
obj.method(234)
應用場景
- 臨時性使用的函數,並不會可以調用它,比如:
- 事件處理函數
- 異步處理函數,settimeout,setinterval
- 其他臨時性的函數
- 爲了綁定外層this的函數
- 在不影響其他代碼的情況下,保持代碼的簡潔,最常見的,數組方法中的回調函數
const numbers = [3,5,6,32,78,24]
const result = numbers.filter(num=>num%2!==0).map(num=>num*2).reduce((a,b)=>a+b)
console.log(result)
6、Function.prototype.toString()
ES2019 對函數實例的**toString()**方法做出了修改。
toString()
方法返回函數代碼本身,以前會省略註釋和空格。
7、catch 命令的參數省略
JavaScript 語言的try...catch
結構,以前明確要求catch
命令後面必須跟參數,接受try
代碼塊拋出的錯誤對象。ES2019對try...catch
,允許catch語句省略參數。
try {
// ...
} catch (err) {
// 處理錯誤
}
//---Es2019以後
try {
// ...
} catch {
// ...
}
五、對象的擴展
1、新增的對象字面量語法
(1)成員速寫
如果對象字面量初始化時,成員的名稱來自於一個變量,並且和變量的名稱相同,則可以進行簡寫
const name = "張飛"
const age = 18
const obj = {
name,
age
}
(2)方法速寫
對象字面初始化時,方法可以省略冒號和function關鍵字
const obj = {
sayHellow(){
console.log("hello world")
}
}
(3)計算屬性名
有的時候,初始化對象時,某些屬性名可能來自於某個表達式的值,在ES6,可以使用中括號來表示該屬性名是通過計算得到的。
const key = "name"
const name = "lkx"
const obj = {
[key]:name
}
(4)對象屬性的遍歷
ES6 一共有 5 種方法可以遍歷對象的屬性。(在Es6後續中介紹)
(1)for…in
for...in
循環遍歷對象自身的和繼承的可枚舉屬性(不含 Symbol 屬性)。
(2)Object.keys(obj)
Object.keys
返回一個數組,包括對象自身的(不含繼承的)所有可枚舉屬性(不含 Symbol 屬性)的鍵名。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames
返回一個數組,包含對象自身的所有屬性(不含 Symbol 屬性,但是包括不可枚舉屬性)的鍵名。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols
返回一個數組,包含對象自身的所有 Symbol 屬性的鍵名。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys
返回一個數組,包含對象自身的所有鍵名,不管鍵名是 Symbol 或字符串,也不管是否可枚舉。
以上的 5 種方法遍歷對象的鍵名,都遵守同樣的屬性遍歷的次序規則。
- 首先遍歷所有數值鍵,按照數值升序排列。
- 其次遍歷所有字符串鍵,按照加入時間升序排列。
- 最後遍歷所有 Symbol 鍵,按照加入時間升序排列。
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]
上面代碼中,Reflect.ownKeys
方法返回一個數組,包含了參數對象的所有屬性。這個數組的屬性次序是這樣的,首先是數值屬性2
和10
,其次是字符串屬性b
和a
,最後是 Symbol 屬性。
2、鏈判斷運算符和Null 判斷運算符
(1)鏈判斷運算符
鏈判斷運算符有三種用法:
1、obj?.prop // 對象屬性
2、obj?.[expr] // 同上
3、func?.(...args) // 函數或對象方法的調用
在項目中經常會讀取某個對象中的屬性。例如:result.message.user.firstName
我們通常的寫法爲。
const firstName = (result && result.message && result.message.user&& result.message.user.firstName) || '暫無數據';
這樣的層層判斷非常麻煩,因此ES2020引入了鏈判斷運算符: ?.
,簡化上面的寫法。
const firstName = result?.message?.user?.firstName || '暫無數據';
使用了?.
運算符,直接在鏈式調用的時候判斷,左側的對象是否爲null
或undefined
。如果是的,就不再往下運算,而是返回undefined
。
(2)Null判斷運算符
我們在讀取對象屬性的時候,如果某個屬性的值是null
或undefined
,有時候需要爲它們指定默認值。我們經常用||
運算符指定默認值。
const firstName = result.firstName || '暫無數據';
但是屬性的值如果爲空字符串或false
或0
,默認值也會生效。爲了避免這種情況,ES2020 引入了一個新的 Null 判斷運算符??
。它的行爲類似||
,但是隻有運算符左側的值爲null
或undefined
時,纔會返回右側的值。
const firstName = result.firstName ?? '暫無數據';
該運算符一般配合.?
使用
const firstName = result?.message?.user?.firstName ?? '暫無數據';
3、Object的新增API
(1)Object.is()
用於判斷兩個數據是否相等,基本上跟嚴格相等(===)是一致的,除了以下兩點:
- NaN和NaN相等
- +0和-0不相等
console.log(NaN === NaN) //false
console.log(+0 === -0) //true
console.log(Object.is(NaN,NaN)) //true
console.log(Object.is(+0,-0)) //false
(2)Object.assign(target,source1,source2…)
方法的第一個參數是目標對象,後面的參數都是源對象。將源對象(source)的所有可枚舉屬性,複製到目標對象(target)。
const obj1 = {
name:"l"
}
const obj2 = {
name:"lkx",
age:18
}
const obj = Object.assign(obj1,obj2)
console.log(obj)
console.log(obj1 === obj) //true
注意:用於混合對象,後面的覆蓋前面的,會改變obj1
//通常我們會把第一個參數寫爲一個空對象
const lastObj = Object.assign({},obj1,obj2)
再注意:方法實行的是淺拷貝,而不是深拷貝。也就是說,如果源對象某個屬性的值是對象,那麼目標對象拷貝得到的是這個對象的引用。一旦遇到同名屬性,Object.assign
的處理方法是替換,而不是添加。
(3)Object.getOwnPropertyNames
Object.getOwnPropertyNames方法之前就存在,只不過,官方沒有明確要求,對屬性的順序如何排序,如何排序,完全由瀏覽器廠商決定。
ES6規定了該方法返回的數組的排序方式如下:
- 先排數字,並按照升序排序
- 再排其他,按照書寫順序排序
const obj = {
a:"a",
b:"b",
c:"c",
0:0,
1:1
}
console.log(Object.getOwnPropertyNames(obj))
//["0", "1", "a", "b", "c"]
(4)Object.setPrototypeOf()和Object.getPrototypeOf()
setPrototypeOf() 該函數用於設置某個對象的隱式原型__proto__
比如: Object.setPrototypeOf(obj1, obj2),
相當於: obj1.__proto__ = obj2
const obj1 = {
a:1
}
const obj2 = {
b:2
}
Object.setPrototypeOf(obj1,obj2)
//將obj1的隱式原型改爲obj2
Object.getPrototypeOf():該方法與Object.setPrototypeOf
方法配套,用於讀取一個對象的原型對象。
(5)Object.keys(),Object.values(),Object.entries() ,Object.fromEntries()
Object.keys():返回一個數組,返回對象的所有屬性的鍵名。(ES5 )
var obj = { a: 1, b: 2 };
Object.keys(obj) // ["a", "b"]
Object.values():方法返回一個數組,返回對象的所有屬性的鍵值。(ES2017)
var obj = { a: 1, b: 2 };
Object.values(obj) // ["1", "2"]
Object.entries():方法返回一個數組,返回對象的所有屬性鍵值對數組。(ES2017)
var obj = { a: 1, b: 2 };
Object.entries(obj) // [["a","1"], ["b","2"]]
Object.fromEntries() :是Object.entries()
的逆操作,用於將一個鍵值對數組轉爲對象。
var arr = [["a","1"], ["b","2"]]
Object.fromEntries(arr)//{ a: 1, b: 2 }
六、變量的解構賦值
解構:使用ES6的一種語法規則,將一個對象或數組的某個屬性提取到某個變量中。解構不會對被解構的目標造成任何影響。
1、對象解構
const user = {
name:"lkx",
age:18,
address:{
provice:"山東",
city:"濟南"
}
}
const {name,age,sex,address} = user
console.log(name,age,address) //lkx 18 {provice: "山東", city: "濟南"}
在解構中使用默認值
//{同名變量 = 默認值}
const {name,age,address,sex="男"} = user
console.log(name,age,address,sex)//lkx 18 {provice: "山東", city: "濟南"} 男
2、非同名屬性解構
{屬性名:變量名}
const {name,age:nianling=20,address,sex="男"} = user
console.log(name,nianling,address,sex)
3、數組解構
const numbers = ["a","b","c","d"]
const [n0,n1] = numbers
console.log(n0,n1)
const {2:n2,3:n3} = numbers
console.log(n2,n3)
4、參數解構
function print({ name, age, sex, address: {province,city} }) {
console.log(`姓名:${name}`)
console.log(`年齡:${age}`)
console.log(`性別:${sex}`)
console.log(`省份:${province}`)
console.log(`城市:${city}`)
}
const user = {
name: "lkx",
age: 18,
sex: "男",
address: {
province: "山東",
city: "濟南"
}
}
print(user)