ES6的新特性??_ES6是什麼+30分鐘帶你瞭解ES6核心內容(上)
文章目錄
一.函數參數的擴展
默認參數
基本用法
function fn(name,age=17){
console.log(name+","+age);
}
fn("Amy",18); // Amy,18
fn("Amy",""); // Amy,
fn("Amy"); // Amy,17
注意點:使用函數默認參數時,不允許有同名參數。
// 不報錯
function fn(name,name){
console.log(name);
}
// 報錯
//SyntaxError: Duplicate parameter name not allowed in this context
function fn(name,name,age=17){
console.log(name+","+age);
}
只有在未傳遞參數,或者參數爲 undefined 時,纔會使用默認參數,null 值被認爲是有效的值傳遞。
function fn(name,age=17){
console.log(name+","+age);
}
fn("Amy",null); // Amy,null
函數參數默認值存在暫時性死區,在函數參數默認值表達式中,還未初始化賦值的參數值無法作爲其他參數的默認值。
function f(x,y=x){
console.log(x,y);
}
f(1); // 1 1
function f(x=y){
console.log(x);
}
f(); // ReferenceError: y is not defined
不定參數
不定參數用來表示不確定參數個數,形如,…變量名,由…加上一個具名參數標識符組成。具名參數只能放在參數組的最後,並且有且只有一個不定參數。
基本用法
function f(...values){
console.log(values.length);
}
f(1,2); //2
f(1,2,3,4); //4
箭頭函數
箭頭函數提供了一種更加簡潔的函數書寫方式。基本語法是:
參數 => 函數體
基本用法:
var f = v => v;
//等價於
var f = function(a){
return a;
}
f(1); //1
當箭頭函數沒有參數或者有多個參數,要用 () 括起來。
var f = (a,b) => a+b;
f(6,2); //8
當箭頭函數函數體有多行語句,用 {} 包裹起來,表示代碼塊,當只有一行語句,並且需要返回結果時,可以省略 {} , 結果會自動返回。
var f = (a,b) => {
let result = a+b;
return result;
}
f(6,2); // 8
當箭頭函數要返回對象的時候,爲了區分於代碼塊,要用 () 將對象包裹起來
// 報錯
var f = (id,name) => {id: id, name: name};
f(6,2); // SyntaxError: Unexpected token :
// 不報錯
var f = (id,name) => ({id: id, name: name});
f(6,2); // {id: 6, name: 2}
注意點:沒有 this、super、arguments 和 new.target 綁定。
var func = () => {
// 箭頭函數裏面沒有 this 對象,
// 此時的 this 是外層的 this 對象,即 Window
console.log(this)
}
func(55) // Window
var func = () => {
console.log(arguments)
}
func(55); // ReferenceError: arguments is not defined
箭頭函數體中的 this 對象,是定義函數時的對象,而不是使用函數時的對象。
function fn(){
setTimeout(()=>{
// 定義時,this 綁定的是 fn 中的 this 對象
console.log(this.a);
},0)
}
var a = 20;
// fn 的 this 對象爲 {a: 19}
fn.call({a: 18}); // 18
不可以作爲構造函數,也就是不能使用 new 命令,否則會報錯
適合使用的場景
ES6之前,使用普通函數把其中每個名字轉換爲大寫形式:
const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map(function(name) {
return name.toUpperCase();
});
箭頭函數表示:
const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map(
name => name.toUpperCase()
);
普通函數可以是函數聲明或者函數表達式, 但是箭頭函數始終都是表達式, 全程是箭頭函數表達式, 因此因此僅在表達式有效時才能使用,包括:
- 存儲在變量中,
- 當做參數傳遞給函數,
- 存儲在對象的屬性中。
所以,當我們需要維護一個 this 上下文的時候,就可以使用箭頭函數。
不適合使用的場景
定義函數的方法,且該方法中包含 this
var Person = {
'age': 18,
'sayHello': ()=>{
console.log(this.age);
}
};
var age = 20;
Person.sayHello(); // 20
// 此時 this 指向的是全局對象
var Person1 = {
'age': 18,
'sayHello': function () {
console.log(this.age);
}
};
var age = 20;
Person1.sayHello(); // 18
// 此時的 this 指向 Person1 對象
需要動態 this 的時候
var button = document.getElementById('userClick');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
button 的監聽函數是箭頭函數,所以監聽函數裏面的 this 指向的是定義的時候外層的 this 對象,即 Window,導致無法操作到被點擊的按鈕對象。
二.javascript標準函數this
new 對象
const mySundae = new Sundae('Chocolate', ['Sprinkles', 'Hot Fudge']);
sundae這個構造函數內的this的值是實例對象, 因爲他使用new被調用.
指定的對象
const result = obj1.printName.call(obj2);
函數使用call/apply被調用,this的值指向指定的obj2,因爲call()第一個參數明確設置this的指向
上下文對象
data.teleport();
函數是對象的方法, this指向就是那個對象,此處this就是指向data.
全局對象或 undefined
teleport();
此處是this指向全局對象,在嚴格模式下,指向undefined.
javascript中this是很複雜的概念, 要詳細判斷this,請參考this你不懂JS: this 與對象原型
三.箭頭函數和this
對於普通函數, this的值基於函數如何被調用, 對於箭頭函數,this的值基於函數週圍的上下文, 換句話說,this的值和函數外面的this的值是一樣的.
function IceCream() {
this.scoops = 0;
}
// 爲 IceCream 添加 addScoop 方法
IceCream.prototype.addScoop = function() {
setTimeout(function() {
this.scoops++;
console.log('scoop added!');
console.log(this.scoops); // undefined+1=NaN
console.log(dessert.scoops); //0
}, 500);
};
const dessert = new IceCream();
dessert.addScoop();
傳遞給 setTimeout() 的函數被調用時沒用到 new、call() 或 apply(),也沒用到上下文對象。意味着函數內的 this 的值是全局對象,不是 dessert 對象。實際上發生的情況是,創建了新的 scoops 變量(默認值爲 undefined),然後遞增(undefined + 1 結果爲 NaN);
解決此問題的方式之一是使用閉包(closure):
// 構造函數
function IceCream() {
this.scoops = 0;
}
// 爲 IceCream 添加 addScoop 方法
IceCream.prototype.addScoop = function() {
const cone = this; // 設置 `this` 給 `cone`變量
setTimeout(function() {
cone.scoops++; // 引用`cone`變量
console.log('scoop added!');
console.log(dessert.scoops);//1
}, 0.5);
};
const dessert = new IceCream();
dessert.addScoop();
箭頭函數的作用正是如此, 將setTimeOut()的函數改爲剪頭函數:
// 構造函數
function IceCream() {
this.scoops = 0;
}
// 爲 IceCream 添加 addScoop 方法
IceCream.prototype.addScoop = function() {
setTimeout(() => { // 一個箭頭函數被傳遞給setTimeout
this.scoops++;
console.log('scoop added!');
console.log(dessert.scoops);//1
}, 0.5);
};
const dessert = new IceCream();
dessert.addScoop();
四.Class類
在ES6中,class (類)作爲對象的模板被引入,可以通過 class 關鍵字定義類。
class 的本質是 function。
它可以看作一個語法糖,讓對象原型的寫法更加清晰、更像面向對象編程的語法。
基礎用法
類定義
類表達式可以爲匿名或命名。
// 匿名類
let Example = class {
constructor(a) {
this.a = a;
}
}
// 命名類
let Example = class Example {
constructor(a) {
this.a = a;
}
}
類聲明
class Example {
constructor(a) {
this.a = a;
}
}
注意要點:不可重複聲明。
class Example{}
class Example{}
// Uncaught SyntaxError: Identifier 'Example' has already been
// declared
let Example1 = class{}
class Example{}
// Uncaught SyntaxError: Identifier 'Example' has already been
// declared
注意要點
類定義不會被提升,這意味着,必須在訪問前對類進行定義,否則就會報錯
類中方法不需要 function 關鍵字。
方法間不能加分號。
new Example();
class Example {}
類的主體
屬性
prototype
ES6 中,prototype 仍舊存在,雖然可以直接自類中定義方法,但是其實方法還是定義在 prototype 上的。 覆蓋方法 / 初始化時添加方法
Example.prototype={
//methods
}
添加方法
Object.assign(Example.prototype,{
//methods
})
靜態屬性
靜態屬性:class 本身的屬性,即直接定義在類內部的屬性( Class.propname ),不需要實例化。 ES6 中規定,Class 內部只有靜態方法,沒有靜態屬性。
class Example {
// 新提案
static a = 2;
}
// 目前可行寫法
Example.b = 2;
公共屬性
class Example{}
Example.prototype.a = 2;
實例屬性
實例屬性:定義在實例對象( this )上的屬性。
class Example {
a = 2;
constructor () {
console.log(this.a);
}
}
name 屬性
返回跟在 class 後的類名(存在時)。
let Example=class Exam {
constructor(a) {
this.a = a;
}
}
console.log(Example.name); // Exam
let Example=class {
constructor(a) {
this.a = a;
}
}
console.log(Example.name); // Example
方法
constructor 方法
constructor 方法是類的默認方法,創建類的實例化對象時被調用。
class Example{
constructor(){
console.log('我是constructor');
}
}
new Example(); // 我是constructor
返回對象
class Test {
constructor(){
// 默認返回實例對象 this
}
}
console.log(new Test() instanceof Test); // true
class Example {
constructor(){
// 指定返回對象
return new Test();
}
}
console.log(new Example() instanceof Example); // false
靜態方法
class Example{
static sum(a, b) {
console.log(a+b);
}
}
Example.sum(1, 2); // 3
原型方法
class Example {
sum(a, b) {
console.log(a + b);
}
}
let exam = new Example();
exam.sum(1, 2); // 3
實例方法
class Example {
constructor() {
this.sum = (a, b) => {
console.log(a + b);
}
}
}
類的實例化
new
class 的實例化必須通過 new 關鍵字。
class Example {}
let exam1 = Example();
// Class constructor Example cannot be invoked without 'new'
實例化對象共享原型對象
class Example {
constructor(a, b) {
this.a = a;
this.b = b;
console.log('Example');
}
sum() {
return this.a + this.b;
}
}
let exam1 = new Example(2, 1);
let exam2 = new Example(3, 1);
console.log(exam1._proto_ == exam2._proto_); // true
exam1._proto_.sub = function() {
return this.a - this.b;
}
console.log(exam1.sub()); // 1
console.log(exam2.sub()); // 2
decorator
decorator 是一個函數,用來修改類的行爲,在代碼編譯時產生作用。
類修飾
一個參數
第一個參數 target,指向類本身。
function testable(target) {
target.isTestable = true;
}
@testable
class Example {}
Example.isTestable; // true
多個參數——嵌套實現
function testable(isTestable) {
return function(target) {
target.isTestable=isTestable;
}
}
@testable(true)
class Example {}
Example.isTestable; // true
實例屬性
上面兩個例子添加的是靜態屬性,若要添加實例屬性,在類的 prototype 上操作即可。
方法修飾
3個參數:target(類的原型對象)、name(修飾的屬性名)、descriptor(該屬性的描述對象)。
class Example {
@writable
sum(a, b) {
return a + b;
}
}
function writable(target, name, descriptor) {
descriptor.writable = false;
return descriptor; // 必須返回
}
修飾器執行順序
由外向內進入,由內向外執行。
class Example {
@logMethod(1)
@logMthod(2)
sum(a, b){
return a + b;
}
}
function logMethod(id) {
console.log('evaluated logMethod'+id);
return (target, name, desctiptor) => console.log('excuted logMethod '+id);
}
// evaluated logMethod 1
// evaluated logMethod 2
// excuted logMethod 2
// excuted logMethod 1
封裝與繼承
getter / setter
class Example{
constructor(a, b) {
this.a = a; // 實例化時調用 set 方法
this.b = b;
}
get a(){
console.log('getter');
return this.a;
}
set a(a){
console.log('setter');
this.a = a; // 自身遞歸調用
}
}
let exam = new Example(1,2); // 不斷輸出 setter ,最終導致 RangeError
class Example1{
constructor(a, b) {
this.a = a;
this.b = b;
}
get a(){
console.log('getter');
return this._a;
}
set a(a){
console.log('setter');
this._a = a;
}
}
let exam1 = new Example1(1,2); // 只輸出 setter , 不會調用
getter 方法
console.log(exam._a); // 1, 可以直接訪問
getter 不可單獨出現
class Example {
constructor(a) {
this.a = a;
}
get a() {
return this.a;
}
}
let exam = new Example(1); // Uncaught TypeError: Cannot set property // a of #<Example> which has only a getter
getter 與 setter 必須同級出現
class Father {
constructor(){}
get a() {
return this._a;
}
}
class Child extends Father {
constructor(){
super();
}
set a(a) {
this._a = a;
}
}
let test = new Child();
test.a = 2;
console.log(test.a); // undefined
class Father1 {
constructor(){}
// 或者都放在子類中
get a() {
return this._a;
}
set a(a) {
this._a = a;
}
}
class Child1 extends Father1 {
constructor(){
super();
}
}
let test1 = new Child1();
test1.a = 2;
console.log(test1.a); // 2
extends
通過 extends 實現類的繼承。
class Child extends Father { ... }
super
子類 constructor 方法中必須有 super ,且必須出現在 this 之前。
class Father {
constructor() {}
}
class Child extends Father {
constructor() {}
// or
// constructor(a) {
// this.a = a;
// super();
// }
}
let test = new Child(); // Uncaught ReferenceError: Must call super
// constructor in derived class before accessing 'this' or returning
// from derived constructor
調用父類構造函數,只能出現在子類的構造函數。
class Father {
test(){
return 0;
}
static test1(){
return 1;
}
}
class Child extends Father {
constructor(){
super();
}
}
class Child1 extends Father {
test2() {
super(); // Uncaught SyntaxError: 'super' keyword unexpected
// here
}
}
調用父類方法, super 作爲對象,在普通方法中,指向父類的原型對象,在靜態方法中,指向父類
class Child2 extends Father {
constructor(){
super();
// 調用父類普通方法
console.log(super.test()); // 0
}
static test3(){
// 調用父類靜態方法
return super.test1+2;
}
}
Child2.test3(); // 3
注意要點
不可繼承常規對象。
var Father = {
// ...
}
class Child extends Father {
// ...
}
// Uncaught TypeError: Class extends value #<Object> is not a constructor or null
// 解決方案
Object.setPrototypeOf(Child.prototype, Father);
本文參考:
https://www.jianshu.com/p/87008f4f8513
https://www.runoob.com/w3cnote/es6-tutorial.html
本次介紹就到這,學習愉快! 😃