ES6+js原生注意點
阿新 • • 發佈:2018-12-26
Symbol
屬性私有化,資料保護
let _gender = Symbol('gender')
function Person(name, gender) {
this.name = name;
this[_gender] = gender
}
Person.prototype.say = function () {
console.log(this[_gender]);
}
p1.say()
let
不支援變數宣告預解析(先聲明後使用)
支援塊作用域
不允許重複宣告(暫存死區)
const
不支援變數宣告預解析(先聲明後使用)
支援塊作用域
不允許重複宣告(暫存死區)
常量(值一旦確定,不允許修改,所有必須初始化)
事件委託
<button class="active">選項一</button> <button>選項二</button> <button>選項三</button> <p class="active">內容一</p> <p>內容二</p> <p>內容三</p> <script> var buttons = document.querySelectorAll('button') var ps = document.querySelectorAll('p') for (let i = 0; i < buttons.length; i++) { buttons[i].onclick = function () { for (let i = 0; i < buttons.length; i++) { buttons[i].className = ''; ps[i].className = ''; } this.className = 'active'; ps[i].className = 'active'; } } <script>
解構賦值
let [a,b,c]=[1,2,3]
let {left:L,top:T}={left:100,top:200}
let [a]='miaov'
擴充套件運算子
... let a = [1, 2, 3, 4]; console.log(...a); console.log(Math.max(...a)); let b = ['a', 'c', 'v']; console.log([...a, 'skd', ...b]); let obj1 = {left: 100, right: 200}; let obj2 = {top: 200, height: 200} let obj3 = { ...obj1, ...obj2 }; console.log(obj3);
字串擴充套件
\u{1F602} unicode 表示法
``
${表示式}
進位制
二進位制 0b
八進位制 0o(ES6之前:0開頭表示八進位制)
十進位制 非0開頭
十六進位制 0x
迭代
for... in: 以原始插入的順序迭代物件的可列舉屬性
for... of 根據迭代物件的迭代器具體實現迭代物件資料
let arr = ['a', 'b', 'c']; for (let string of arr) { console.log(string); }
箭頭函式
//當引數只有一個的時候小括號可以省略
let fn = (x) => {
console.log('fn1')
};
let fn = x => {
console.log('fn1')
};
//如果多於1條語句,或者返回的是一個物件
let fn = x => {
return {
x: 1,
y: 2
}
};
箭頭函式的this在函式建立件就繫結好了,箭頭函式的this指向建立該作用域的物件
箭頭函式的注意事項
內部this物件指向建立上下文物件
不能作用建構函式
沒有arguments
不能作為生成器函式
類
class Person {
//類
constructor(name) {//建構函式: 物件初始化的時候執行的函式
this.name = name
}
getName() {
console.log(this);
}
}
// 繼承
class Student extends Person {
constructor(name, type) {
//super
super(name);
//如果子類需要有自己的初始化過程,
// 有自己的constructor函式,需要在子類呼叫父類的建構函式
this.type = type;
}
}
promise
避免回撥地獄(就是回撥巢狀回撥無限巢狀)
接受一個引數:callback,我們把要執行的非同步任務放置在這個callback中
Promise物件內部會維護一個狀態
預設是:pending
成功:resolved
失敗:rejected
Promise物件下有一個方法:then,該方法在Promise物件的狀態發生改變的時候觸發then的回撥
catch
let p1 = new Promise((resolove, reject) => {
setTimeout(function () {
resolove(2)
}, Math.random() * 1000)
})
let p2 = new Promise((resolove, reject) => {
setTimeout(function () {
resolove(3)
}, Math.random() * 1000)
})
Promise.all([p1, p2]).then(data => {
console.log(data);
}, err => {
console.log(err);
})
迭代器
yield* 後面跟的是可遍歷的結構,它會呼叫該結構的遍歷器介面
function* fn() {
console.log(0);
yield console.log(1);
yield* [1,3,4];
yield console.log(2);
}
//返回一個迭代器函式
let f = fn();
f.next()
f.next()
程式碼規範(這一大推寫著寫著就變成這樣了)
在let和const之間優先使用const
儘管const宣告的變數不能直接修改值,但是對於物件和陣列,卻是不受保護可以修改的
靜態程式碼一律使用單引號或者反引號
解構
const [a,b]=1,2
cosnt {first,last}=obj
物件
單行定義的物件最後不以逗號結尾
多行定義的物件最後以逗號結尾
const a = { k1: v1, k2: v2 };
const b = {
k1: v1,
k2: v2,
};
物件儘量靜態化
cosnt a={x:null}
a.x=3
物件的屬性和方法簡寫
// bad
const atom = {
ref: ref,
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// good
const atom = {
ref,
value: 1,
addValue(value) {
return atom.value + value;
},
};
陣列
const itemsCopy=[...items]
Array.from方法 將類似陣列的物件轉為陣列
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
箭頭函式
[1,2,3].map((x)=>{
return x*x
})
可以指定原型
cosnt anObject={y:'y'}
const x={
__proto__:anObject
}
super()
const anObject = {y: 'y', test: () => 'zoo'};
const x = {
__proto__: anObject,
test() {
return super.test() + 'x'
}
};
console.log(x.test());
動態屬性
const x = {
['a' + '_' + 'b']: 'z'
}
x.a_b //z
reduce 有點難理解
第三個引數可以是索引,預設給值,可以給{} ,[]
JSON.stringfiy('第一個引數是序列化的引數',
'第二個引數是一個函式key,value操作',
'讓程式碼清晰些4相當於4個空格')
JSON.parse() 一樣也具有這個特點
//indexOf 如果找不到返回-1 如果重複返回第一個的索引
箭頭函式
let getTempItem = id => ({ id: id, name: "Temp" });//如果不加括號會報錯
如果不需要返回值 void
let fn = () => void console.log(2);
箭頭函式注意點
函式體內的this物件,就是定義時所在的物件,而不是使用所在的物件
不可以當作建構函式,就是說不能new
箭頭函式沒有自己的作用域
不可以使用arguments
不可以使用yield
也就是說,this物件的指向是可變的,但是在箭頭函式中,this的指向是固定的
因為箭頭函式沒有this ,如果真的需要this需要重寫定義this
function foo() {
var _this = this;
setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}
箭頭函式裡面根本沒有自己的this,而是應用外層的this
箭頭函式是的this從動態變成靜態,所有需要引用動態this的時候不要使用箭頭函式
Array.from
可以將各種值轉為真正的陣列,並且還提供map功能
Array.from({ length: 2 }, () => 'jack') //[ 'jack', 'jack' ]
Array.of
用於將一組值,轉換為陣列
Array.of(3, 11, 8) // [3,11,8]
比如 Array(3)//[,,,] 一個值得時候表示的是陣列的長度
Array.of 相當於 [].slice.call(arguments)
n**m 指數運算子
雙冒號運算子 取代call apply bind
f::b
等同於 b.bind(f); b.apply(f,arguments);
find
找到第一個複合條件的陣列,引數是一個回撥函式,返回第一個返回值為true的成員
[1,2,3,4,5,6].find(a=>a>3) //4
entries() keys() values()
entries() 對鍵值對遍歷 keys() 對鍵名遍歷 values() 對鍵值遍歷
includes 用於表示陣列是否包含特定的值 第二個引數是起始位置預設為0
[1,3,4].includes(3,2) //false
物件的擴充套件
const rest = (a, b) => ({a, b});
console.log(rest('張三', '李四'));
// { a: '張三', b: '李四' }
let a = {
fun () {
return 'leo';
}
}
// 等同於
let a = {
fun : function(){
return 'leo';
}
}
Object.assign()
深拷貝需要使用其他方法,因為Object.assign() 拷貝的是屬性值,
假如原物件的屬性值是一個指向物件的引用,它也只拷貝那個引用值
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
合併物件,合併屬性的時候後續引數中具有相同屬性的覆蓋
var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
Set
資料結構 所有成員唯一性 add 新增新成員 size 成員總數 delete has clear
Map
提供了"值-值"的對應關係,是一種更完善的hash結構實現 set get size has delete clear
for... of
當遇到next返回值得done屬性為true就會終止,return 返回的不包括在for...of 迴圈中
function * f(){
yield 1;
yield 2;
yield 3;
yield 4;
return 5;
}
for (let k of f()){
console.log(k);
}
// 1 2 3 4 沒有 5
當變數值為null時,布林值環境為false ,值型別為0
當變數值為undefined,布林值環境為false,值型別為NaN
ES6開始全部變數和頂層物件(window)的屬性是分開的
let b = 'leo';
window.b; // undefined
字面量
簡單理解就是變數賦值是右邊的都是字面量
let a='hi len'
`hi len` 為字串字面量 a為變數名
閉包
閉包的函式繼承了父級容器的引數和變數,內部函式包含外部函式的作用域
內容函式只能在外部函式中訪問
內部函式形成閉包:可以訪問外部函式的引數和變數,
但外部函式卻不能使用這個內部函式的引數和變數
外部函式的返回值為內部函式名
function f(a) {
function g(b){
return a + b;
}
return g;
}
//預設引數 剩餘引數
function f(a,b=1,...c) {
}
Proxy???
正則
正則 gi g全域性i區分大小寫
exec
注意分組的引數
若檢索成功,返回匹配的陣列,否則返回null
let result = /len/g.exec('hello len!!');
//[ 'len', index: 6, input: 'hello len!!' ]
test
若匹配成功返回true否則返回false
let str = "hello leo!";
let res = /leo/.test(str); // true
search
若檢索成功,返回第一個與正則物件匹配的字串的起始位置,否則返回-1
let str = "hello leo!";
let res = str.search(/leo/g); // 6
match
若檢索成功,返回與reg匹配的所有結果的一個數組,否則返回null
str.match(regSS)
Object.defineProperty
定義新屬性或者修改原有的屬性
let obj={
test: 'hello'
};
//物件已有的屬性新增特性
Object.defineProperty(obj,'test',{
value:"張三",
writable:true,//屬性的值是否可以被重寫,設定為true可以被重寫
enumerable:false,//屬性是否可以被列舉(for...in或者object.keys())
configurable:true,
//目標屬性是否可以使用delete刪除,目標屬性是否可以再次設定特性
});
//定義的新屬性,特性中configurable,enumerable,writable都為預設的false
//當使用get或者set方法,不允許使用writable和value這兩個屬性
var obj = {};
Object.defineProperty(obj,"newKey",{
get:function (){} | undefined,
set:function (value){} | undefined,
configurable: true | false,
enumerable: true | false,
});
//getter 獲得屬性值的方法,setter設定屬性值的方法
//get或者set不是必須成對出現的,任寫一個就可以
this的呼叫
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 單獨執行
f() // 1
// obj 環境執行
obj.f() // 2
javaScript的祕密花園
hasOwnProperty函式
為了判斷一個物件是否包含自定義屬性而不是原型鏈上的屬性
唯一一個處理屬性但是一查詢原型鏈上的函式
使用外部的hasOwnProperty函式獲取正確的結果
Object.hasOwnProperty.call(foo,'bar')//指向的物件,屬性->返回true,false
this是基於原型賴以生存的土壤
賦值
- 基礎資料型別:賦值,賦值之後兩個變數互不影響
- 引用資料型別: 賦址,兩個變數具有相同的引用,指向同一個物件,相互影響
對於基本型別進行賦值操作,兩個變數互不影響
let a='alex';
let b=a;
a='alex02';
console.log(a);//alex02
console.log(b);//alex
//對於引用型別
let a = {
name: "muyiy",
book: {
title: "You Don't Know JS",
price: "45"
}
}
let b = a;
b.name="張三";
淺拷貝
* object.assign() //如果說它是深拷貝是不準確的
用於將所有可列舉屬性的值從一個或者多個源物件賦值到目標物件
let a = {
name: "muyiy",
book: {
title: "You Don't Know JS",
price: "45"
}
}
let b = Object.assign({}, a);
console.log(b);
* 展開語法 ...
* Array.prototype.slice()
let a = [0, "1", [2, 3]];
let b = a.slice(1);
console.log(b);
// ["1", [2, 3]]
a[1] = "99";
a[2][0] = 4;
console.log(a);
// [0, "99", [4, 3]]
console.log(b);
// ["1", [4, 3]]
淺拷貝:第一層為基礎資料型別不會是原資料一同改變,
但是原資料子物件會發現改變
深拷貝
JSON.stringify()
JSON.parse()
都不會發生改變
new
* 訪問建構函式裡的屬性
* 訪問到原型裡的屬性
注意 不能使用new Symbol();因為Symbol是基本資料型別,每個Symbol()返回的symbol值都是唯一的
第一種情況
例項中只能返回返回物件中的屬性
function Car(color, name) {
this.color = color;
return {
name: name
}
}
var car = new Car("black", "BMW");
car.color;
// undefined
第二種情況
沒有return 返回undefined
且 例項只能訪問建構函式中的屬性
function Car(color, name) {
this.color = color;
}
var car = new Car("black", "BMW");
car.color;
// black
car.name;
// undefined
總結
* 建立了一個全新的物件
* 這個物件會被執行__proto__連結
* 生成的新物件會繫結到函式呼叫的this
* 通過new 建立的每個物件將最終被__proto__ 連結到這個函式的prototype物件上
求和函式sum,使輸入sum(2)(3)或輸入sum(2,3)
函式柯里化
多個引數的函式換成接受一個單一引數
用閉包把引數儲存起來,當引數的數量足夠執行函數了,就開始執行函式
let curry=(fn,...args)=>{
fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
};
function sum() {
if (arguments.length === 1) {
let sum01=arguments[0];
let add=function (arg) {
sum01+=arg;
return add
};
add.valueOf=function () {
return sum01
};
return add;
}else{
let sum3=0;
for (let i = 0; i < arguments.length; i++) {
sum3+=arguments[i];
}
return sum3;
}
}
console.log(sum(1)(2));
//toString valueOf都可以
你不知道的Chrome除錯工具技巧
控制檯上
$0 是當前選中的html $1 是我們上一次選擇的節點的應用...一直到$4
---
Array.from(document.querySelectorAll('dic'))===$$('div')
--
$_ 上一次執行的結果的引用
--
控制檯右鍵點選save 以檔案的形式檢視錯誤資訊
+ '123' // 123
+ 'ds' // NaN
+ '' // 0
+ null // 0
+ undefined // NaN
Boolean過濾陣列中的所有假值
雙位操作符替換Math.floor() 但是負數來說就不相同了
~~4.9
取整 |0
返回多行語句 需要用()包裹起來
clas=dir=>(
Math.PI* diameer;
)
bind()
bind方法與call/apply最大的不同是前端返回一個繫結上下文的函式,後兩個直接執行函式
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
return {
value: this.value,
name: name,
age: age
}
};
bar.call(foo, "Jack", 20); // 直接執行了函式
// {value: 1, name: "Jack", age: 20}
var bindFoo1 = bar.bind(foo, "Jack", 20); // 返回一個函式
bindFoo1();
// {value: 1, name: "Jack", age: 20}
可以指定this
返回一個函式
可以傳入引數
柯里化
this存在的問題
var nickname = "Kitty";
function Person(name){
this.nickname = name;
this.distractedGreeting = function() {
setTimeout(function(){
console.log("Hello, my name is " + this.nickname);
}, 500);
}
}
var person = new Person('jawil');
person.distractedGreeting();
//Hello, my name is Kitty
解決方案1: 快取this
var nickname = "Kitty";
function Person(name){
this.nickname = name;
this.distractedGreeting = function() {
var self = this; // added
setTimeout(function(){
console.log("Hello, my name is " + self.nickname); // changed
}, 500);
}
}
var person = new Person('jawil');
person.distractedGreeting();
// Hello, my name is jawil
第二種bind
var nickname = "Kitty";
function Person(name){
this.nickname = name;
this.distractedGreeting = function() {
setTimeout(function(){
console.log("Hello, my name is " + this.nickname);
}.bind(this), 500);
}
}
var person = new Person('jawil');
person.distractedGreeting();
// Hello, my name is jawil
驗證是否是陣列
var toStr = Function.prototype.call.bind(Object.prototype.toString);
function isArray(obj){
return toStr(obj) === '[object Array]';
}
isArray([1, 2, 3]);
// true
// 使用改造後的 toStr
toStr([1, 2, 3]); // "[object Array]"
toStr("123"); // "[object String]"
toStr(123); // "[object Number]"
toStr(Object(123)); // "[object Number]"
柯里化
只傳遞給函式一部分引數來呼叫,讓它返回一個函式去處理剩下的引數
var add=function(x){
return function(y){
return x+y
}
}
var increment=add(1);
increment(2)
這裡定義一個add函式,它接受一個引數並返回一個新的函式,呼叫add之後,返回的函式
就通過閉包的方法記住add的第一個引數,所有bind本身也是閉包的一種使用場景
this問題
一個繫結函式也能使用new操作符建立物件,這種就想把原函式當成建構函式
提供的this被忽略,同時呼叫時的引數被提供給模擬函式
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
var bindFoo = bar.bind(foo, 'Jack');
var obj = new bindFoo(20);
// undefined
// Jack
// 20
console.log(obj.habit);
// shopping
console.log(obj.friend);
// kevin
Array.prototype.slice.call()
將函式的實際引數轉換成陣列
Array.prototype.slice.call(arguments)
[].slice.call(arguments,0) //預設從0開始往後擷取
Array.from() //ES6
防止抖動
會遇到一些頻繁的事件觸發比如:
window: resize,scroll, mousedown mousermove keyup keydown...
當持續觸發事件是,一定時間內沒有再觸發事件,處理函式才會執行一次
//第一個引數是函式,第二個引數是定時器的時間
function debounce(func, wait) {
var timeOut;
return function () {
if(timeOut) {
clearTimeout(timeOut);
}
// 儲存this上下文,引數
var that = this, args = arguments; //event的問題
timeOut = setTimeout(function () {
func.apply(that, args);
}, wait)
}
}
toString和valueOf
toString(): 返回物件的字串表示
valueOf(): 返回物件的字串,數值或者布林值表示
//先看看toString()方法的結果
var a = 3;
var b = '3';
var c = true;
var d = {test:'123',example:123}
var e = function(){console.log('example');}
var f = ['test','example'];
a.toString();// "3"
b.toString();// "3"
c.toString();// "true"
d.toString();// "[object Object]"
e.toString();// "function (){console.log('example');}"
f.toString();// "test,example"
//再看看valueOf()方法的結果
var a = 3;
var b = '3';
var c = true;
var d = {test:'123',example:123}
var e = function(){console.log('example');}
var f = ['test','example'];
a.valueOf();// 3
b.valueOf();// "3"
c.valueOf();// true
d.valueOf();// {test:'123',example:123}
e.valueOf();// function(){console.log('example');}
f.valueOf();// ['test','example']
特殊的地方 表示物件的時候,toString-> [object Object]
而valueOf 返回自身
//例子一
var example = {test:'123'};
console.log(+example);// NaN
//例子二 同時改寫 toString 和 valueOf 方法
var example = {
toString:function(){
return '23';
},
valueOf:function(){
return '32';
}
};
console.log(+example);// 32
通過例子一和例子二中,在有一元加操作符,會先呼叫valueOf,再呼叫Number()
節流
不管操作多頻繁,我始終固定時間觸發一次
function throttle(func,wait) {
let timeout;
return function () {
let context=this;
if(!timeout){
timeout=setTimeout(function () {
timeout=null;
func.apply(context,args);
},wait)
}
}
}
陣列去重
function uniq(array){
var temp = []; //一個新的臨時陣列
for(var i = 0; i < array.length; i++){
if(temp.indexOf(array[i]) == -1){
temp.push(array[i]);
}
}
return temp;
}
//filter 第一個引數迭代的物件 第二個是下標,第三個陣列本身
array.filter((item, index, array) => array.indexOf(item)===index);
Array.from(new Set(array))
//排序去重
array.concat().sort((a,b)=>a-b)).filter((item,index,array)=>{
//a||b,a為true返回a
//a&&b,a為true返回b
return !index||item!==array[index-1]
})
[...new Set(array")]
型別判斷
typeof 檢測六種基礎型別+object+function
Undefined null Boolean String number Symbol function Object
Object.prototype.toString.call(obj)=> [object 型別]
最大值最小值
Maxs=arr=>{
let res=arr[0];
for(let i=0;i<arr.length;i++){
res=Math.max(res,arr[i]);
}
return res;
}
Math.max.apply(null,arr)
Math.max(...arr)
arr.reduce((r, v) => Math.max(r, v)))
arr.sort((a,b)=>b-a)[0]
arr.reduce((r,v)=r>v?r:v)
陣列扁平化
flatten=arr=>arr.reduce((r,s)=>{
return r.concat(Array.isArray(s)?flatten(s):s)
},[])
flatten=arr=>arr.toString().split(',').map(item=>Number(item));
flatten=arr=>arr.join('.').split(',').map(item=>parseInt(item));
flatten=arr=>{
let res=[];
return arr.map(item=>{
return arr.isArray(item)?res=res.concat(flatten(item)):res.push(item);
})
}
//some至少有一個
function flatten(arr) {
while(arr.some(item=>Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
//Array.from toString split
在陣列中查詢指定索引
indexOf
//ES6
findIndex 返回符合條件的第一個元素的索引
findLastIndex 倒序