关于前端:ES6语法你了解多少

4次阅读

共计 11566 个字符,预计需要花费 29 分钟才能阅读完成。

ES6 语法

模板字面量

在 ES6 之前, 将字符串连贯到一起的办法是 + 或者 concat() 办法, 如

const student = {
  name: 'Richard Kalehoff',
  guardian: 'Mr. Kalehoff'
};

const teacher = {
  name: 'Mrs. Wilson',
  room: 'N231'
}

let message = student.name + 'please see' + teacher.name + 'in' + teacher.room + 'to pick up your report card.'; 

模板字面量实质上是蕴含嵌入式表达式的字符串字面量.
模板字面量用倒引号 (` )(而不是单引号 (”) 或双引号(“”))示意,能够蕴含用 ${expression}` 示意的占位符

let message = `${student.name} please see ${teacher.name} in ${teacher.room} to pick up your report card.`; 

解构

在 ES6 中, 能够应用 解构 从数组和对象提取值并赋值给独特的变量

解构数组的值:

const point = [10, 25, -34];
const [x, y, z] = point;
console.log(x, y, z); 

Prints: 10 25 -34

[]示意被解构的数组, x,y,z示意要将数组中的值存储在其中的变量, 在解构数组是, 还能够疏忽值, 例如 const[x,,z]=point, 疏忽y 坐标.

解构对象中的值:

const gemstone = {
  type: 'quartz',
  color: 'rose',
  karat: 21.29
};
const {type, color, karat} = gemstone;
console.log(type, color, karat); 

花括号 {} 示意被解构的对象,typecolorkarat 示意要将对象中的属性存储到其中的变量

对象字面量简写法

let type = 'quartz';
let color = 'rose';
let carat = 21.29;

const gemstone = {
  type: type,
  color: color,
  carat: carat
};

console.log(gemstone); 

应用和所调配的变量名称雷同的名称初始化对象时 如果属性名称和所调配的变量名称一样,那么就能够从对象属性中删掉这些反复的变量名称。

let type = 'quartz';
let color = 'rose';
let carat = 21.29;
const gemstone = {type,color,carat};
console.log(gemstone); 

简写办法的名称:

const gemstone = {
  type,
  color,
  carat,
  calculateWorth: function() {// 将依据类型 (type),色彩(color) 和克拉 (carat) 计算宝石 (gemstone) 的价值
  }
}; 

匿名函数被调配给属性 calculateWorth,然而真的须要 function 关键字吗?在 ES6 中不须要!

let gemstone = {
  type,
  color,
  carat,
  calculateWorth() { ...}
}; 

for…of 循环

for...of循环是最新增加到 JavaScript 循环系列中的循环。
它联合了其兄弟循环模式 for 循环和 for...in 循环的劣势,能够循环任何可迭代(也就是恪守可迭代协定)类型的数据。默认状况下,蕴含以下数据类型:StringArrayMapSet,留神不蕴含 Object 数据类型(即 {})。默认状况下,对象不可迭代

for 循环

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i = 0; i < digits.length; i++) {console.log(digits[i]);
} 

for 循环的最大毛病是须要跟踪计数器和退出条件。
尽管 for 循环在循环数组时确实具备劣势,然而某些数据结构不是数组,因而并非始终适宜应用 loop 循环。

for…in 循环

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

for (const index in digits) {console.log(digits[index]);
} 

仍然须要应用 index 来拜访数组的值
当你须要向数组中增加额定的办法(或另一个对象)时,for...in 循环会带来很大的麻烦。因为 for...in 循环循环拜访所有 可枚举的属性 ,意味着如果向数组的 原型中增加任何其余属性,这些属性也会呈现在循环中。

Array.prototype.decimalfy = function() {for (let i = 0; i < this.length; i++) {this[i] = this[i].toFixed(2);
  }
};

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

for (const index in digits) {console.log(digits[index]);
} 

forEach 循环 是另一种模式的 JavaScript 循环。然而,forEach() 实际上是数组办法,因而只能用在数组中。也无奈进行或退出 forEach 循环。如果心愿你的循环中呈现这种行为,则须要应用根本的 for 循环。

for…of 循环
for...of 循环用于循环拜访任何可迭代的数据类型。
for...of 循环的编写形式和 for...in 循环的根本一样,只是将 in 替换为 of,能够疏忽索引。

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

for (const digit of digits) {console.log(digit);
} 

倡议应用复数对象名称来示意多个值的汇合。这样,循环该汇合时,能够应用名称的复数版本来示意汇合中的单个值。例如,for (const button of buttons) {…}

for...of 循环还具备其余劣势,解决了 for 和 for…in 循环的不足之处。你能够随时进行或退出 for…of 循环。

for (const digit of digits) {if (digit % 2 === 0) {continue;}
  console.log(digit);
} 

不必放心向对象中增加新的属性。for…of 循环将只循环拜访对象中的值。

Array.prototype.decimalfy = function() {for (i = 0; i < this.length; i++) {this[i] = this[i].toFixed(2);
  }
};

const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

for (const digit of digits) {console.log(digit);
} 

开展运算符

开展运算符(用三个间断的点 (...) 示意)是 ES6 中的新概念,使你可能将字面量对象开展为多个元素

const books = ["Don Quixote", "The Hobbit", "Alice in Wonderland", "Tale of Two Cities"];
console.log(...books); 

Prints: Don Quixote The Hobbit Alice in Wonderland Tale of Two Cities

开展运算符的一个用处是联合数组。

如果你须要联合多个数组,在有开展运算符之前,必须应用 Arrayconcat() 办法。

const fruits = ["apples", "bananas", "pears"];
const vegetables = ["corn", "potatoes", "carrots"];
const produce = fruits.concat(vegetables);
console.log(produce); 

Prints: [“apples”, “bananas”, “pears”, “corn”, “potatoes”, “carrots”]

应用开展符来联合数组

const fruits = ["apples", "bananas", "pears"];
const vegetables = ["corn", "potatoes", "carrots"];
const produce = [...fruits,...vegetables];
console.log(produce); 

残余参数(可变参数)

应用开展运算符将数组开展为多个元素, 应用残余参数能够将多个元素绑定到一个数组中.
残余参数也用三个间断的点 (... ) 示意,使你可能将不定数量的元素示意为数组.

用处 1: 将变量赋数组值时:

const order = [20.17, 18.67, 1.50, "cheese", "eggs", "milk", "bread"];
const [total, subtotal, tax, ...items] = order;
console.log(total, subtotal, tax, items); 

用处 2: 可变参数函数
对于参数不固定的函数,ES6 之前是应用 参数对象 (arguments) 解决:

function sum() {
  let total = 0;  
  for(const argument of arguments) {total += argument;}
  return total;
} 

在 ES6 中应用残余参数运算符则更为简洁, 可读性进步:

function sum(...nums) {
  let total = 0;  
  for(const num of nums) {total += num;}
  return total;
} 

ES6 箭头函数

ES6 之前, 应用一般函数把其中每个名字转换为大写模式:

const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map(function(name) {return name.toUpperCase();
}); 

箭头函数示意:

const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map(name => name.toUpperCase()
); 

一般函数能够是 函数申明 或者 函数表达式 , 然而箭头函数始终都是 表达式 , 全程是 箭头函数表达式, 因而因而仅在表达式无效时能力应用,包含:

  • 存储在变量中,
  • 当做参数传递给函数,
  • 存储在对象的属性中。
const greet = name => `Hello ${name}!`; 

能够如下调用:

greet('Asser'); 

如果函数的参数只有一个, 不须要应用 () 包起来, 然而只有一个或者多个, 则必须须要将参数列表放在圆括号内:

// 空参数列表须要括号
const sayHi = () => console.log('Hello Udacity Student!');

// 多个参数须要括号
const orderIceCream = (flavor, cone) => console.log(`Here's your ${flavor} ice cream in a ${cone} cone.`);
orderIceCream('chocolate', 'waffle'); 

个别箭头函数都只有一个表达式作为函数主题:

const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map(name => name.toUpperCase()
); 

这种函数表达式模式称为 简写主体语法:

  • 在函数主体四周没有花括号,
  • 主动返回表达式

然而如果箭头函数的主体内须要多行代码, 则须要应用 惯例主体语法:

  • 它将函数主体放在花括号内
  • 须要应用 return 语句来返回内容。
const upperizedNames = ['Farrin', 'Kagure', 'Asser'].map( name => {name = name.toUpperCase();
  return `${name} has ${name.length} characters in their name`;
}); 

javascript 规范函数 this

  1. new 对象
const mySundae = new Sundae('Chocolate', ['Sprinkles', 'Hot Fudge']); 

sundae这个构造函数内的 this 的值是实例对象, 因为他应用 new 被调用.

  1. 指定的对象
const result = obj1.printName.call(obj2); 

函数应用 call/apply 被调用,this 的值指向指定的 obj2, 因为 call() 第一个参数明确设置 this 的指向

  1. 高低 ` 文对象
data.teleport(); 

函数是对象的办法, this 指向就是那个对象, 此处 this 就是指向 data.

  1. 全局对象或 undefined
teleport(); 

此处是 this 指向全局对象, 在严格模式下, 指向 undefined.

javascript 中 this 是很简单的概念, 要具体判断 this, 请参考 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() 的函数被调用时没用到 newcall()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(); 

默认参数函数

function greet(name, greeting) {name = (typeof name !== 'undefined') ?  name : 'Student';
  greeting = (typeof greeting !== 'undefined') ?  greeting : 'Welcome';

  return `${greeting} ${name}!`;
}

greet(); // Welcome Student!
greet('James'); // Welcome James!
greet('Richard', 'Howdy'); // Howdy Richard! 

greet() 函数中凌乱的前两行的作用是什么?它们的作用是当所需的参数未提供时,为函数提供默认的值。然而看起来很麻烦, ES6 引入一种新的形式创立默认值, 他叫默认函数参数:

function greet(name = 'Student', greeting = 'Welcome') {return `${greeting} ${name}!`;
}

greet(); // Welcome Student!
greet('James'); // Welcome James!
greet('Richard', 'Howdy'); // Howdy Richard! 

默认值与解构

  1. 默认值与解构数组
function createGrid([width = 5, height = 5]) {return `Generates a ${width} x ${height} grid`;
}

createGrid([]); // Generates a 5 x 5 grid
createGrid([2]); // Generates a 2 x 5 grid
createGrid([2, 3]); // Generates a 2 x 3 grid
createGrid([undefined, 3]); // Generates a 5 x 3 grid 

createGrid() 函数预期传入的是数组。它通过解构将数组中的第一项设为 width,第二项设为 height。如果数组为空,或者只有一项,那么就会应用默认参数,并将缺失的参数设为默认值 5。

然而存在一个问题:

createGrid(); // throws an error 

Uncaught TypeError: Cannot read property ‘Symbol(Symbol.iterator)’ of undefined

呈现谬误,因为 createGrid() 预期传入的是数组,而后对其进行解构。因为函数被调用时没有传入数组,所以呈现问题。然而,咱们能够应用默认的函数参数!

function createGrid([width = 5, height = 5] = []) {return `Generating a grid of ${width} by ${height}`;
}
createGrid(); // Generates a 5 x 5 grid 

Returns: Generates a 5 x 5 grid

  1. 默认值与解构函数

就像应用数组默认值解构数组一样,函数能够让对象成为一个默认参数,并应用对象解构:

function createSundae({scoops = 1, toppings = ['Hot Fudge']}={}) {
  const scoopText = scoops === 1 ? 'scoop' : 'scoops';
  return `Your sundae has ${scoops} ${scoopText} with ${toppings.join('and')} toppings.`;
}

createSundae({}); // Your sundae has 1 scoop with Hot Fudge toppings.
createSundae({scoops: 2}); // Your sundae has 2 scoops with Hot Fudge toppings.
createSundae({scoops: 2, toppings: ['Sprinkles']}); // Your sundae has 2 scoops with Sprinkles toppings.
createSundae({toppings: ['Cookie Dough']}); // Your sundae has 1 scoop with Cookie Dough toppings.
createSundae(); // Your sundae has 1 scoop with Hot Fudge toppings. 
  1. 数组默认值与对象默认值

默认函数参数只是个简略的增加内容,然而却带来很多便当!与数组默认值相比,对象默认值具备的一个劣势是可能解决跳过的选项。看看上面的代码:

function createSundae({scoops = 1, toppings = ['Hot Fudge']} = {}) {…} 

createSundae() 函数应用对象默认值进行解构时,如果你想应用 scoops 的默认值,然而更改 toppings,那么只需应用 toppings 传入一个对象:

createSundae({toppings: ['Hot Fudge', 'Sprinkles', 'Caramel']}); 

将上述示例与应用数组默认值进行解构的同一函数绝对比。

function createSundae([scoops = 1, toppings = ['Hot Fudge']] = []) {…} 

对于这个函数,如果想应用 scoops 的默认数量,然而更改 toppings,则必须以这种奇怪的形式调用你的函数:

createSundae([undefined, ['Hot Fudge', 'Sprinkles', 'Caramel']]); 

因为数组是基于地位的,咱们须要传入 undefined 以跳过第一个参数(并应用默认值)来达到第二个参数。

Javascript 类

ES5 创立类:

function Plane(numEngines) {
  this.numEngines = numEngines;
  this.enginesActive = false;
}

// 由所有实例 "继承" 的办法
Plane.prototype.startEngines = function () {console.log('starting engines...');
  this.enginesActive = true;
}; 

ES6 类只是一个语法糖, 原型持续实际上在底层暗藏起来, 与传统类机制语言有些区别.

class Plane {
  //constructor 办法尽管在类中, 但不是原型上的办法, 只是用来生成实例的.
  constructor(numEngines) {
    this.numEngines = numEngines;
    this.enginesActive = false;
  }
  // 原型上的办法, 由所有实例对象共享.
  startEngines() {console.log('starting engines…');
    this.enginesActive = true;
  }
}

console.log(typeof Plane); //function 

javascript 中类其实只是 function, 办法之间不能应用,, 不必逗号辨别属性和办法.

静态方法
要增加静态方法,请在办法名称后面加上关键字 static

class Plane {constructor(numEngines) {
    this.numEngines = numEngines;
    this.enginesActive = false;
  }
  static badWeather(planes) {for (plane of planes) {plane.enginesActive = false;}
  }
  startEngines() {console.log('starting engines…');
    this.enginesActive = true;
  }
} 
  • 关键字 class 带来其余基于类的语言的很多思维, 然而没有向 javascript 中增加此性能
  • javascript 类实际上还是原型继承
  • 创立 javascript 类的新实例时必须应用 new 关键字

super 和 extends

应用新的 super 和 extends 关键字扩大类:

class Tree {constructor(size = '10', leaves = {spring: 'green', summer: 'green', fall: 'orange', winter: null}) {
    this.size = size;
    this.leaves = leaves;
    this.leafColor = null;
  }

  changeSeason(season) {this.leafColor = this.leaves[season];
    if (season === 'spring') {this.size += 1;}
  }
}

class Maple extends Tree {constructor(syrupQty = 15, size, leaves) {super(size, leaves); //super 用作函数
    this.syrupQty = syrupQty;
  }

  changeSeason(season) {super.changeSeason(season);//super 用作对象
    if (season === 'spring') {this.syrupQty += 1;}
  }

  gatherSyrup() {this.syrupQty -= 3;}
} 

应用 ES5 编写同样性能的类:

function Tree(size, leaves) {
  this.size = size || 10;
  this.leaves = leaves || {spring: 'green', summer: 'green', fall: 'orange', winter: null};
  this.leafColor;
}

Tree.prototype.changeSeason = function(season) {this.leafColor = this.leaves[season];
  if (season === 'spring') {this.size += 1;}
}

function Maple (syrupQty, size, leaves) {Tree.call(this, size, leaves);
  this.syrupQty = syrupQty || 15;
}

Maple.prototype = Object.create(Tree.prototype);
Maple.prototype.constructor = Maple;

Maple.prototype.changeSeason = function(season) {Tree.prototype.changeSeason.call(this, season);
  if (season === 'spring') {this.syrupQty += 1;}
}

Maple.prototype.gatherSyrup = function() {this.syrupQty -= 3;} 

super 必须在 this 之前被调用

在子类构造函数中,在应用 this 之前,必须先调用超级类。

class Apple {}
class GrannySmith extends Apple {constructor(tartnessLevel, energy) {
    this.tartnessLevel = tartnessLevel; // 在 'super' 之前会抛出一个谬误!super(energy); 
  }
}

const 与 let 变量

应用 var 带来的麻烦:

function getClothing(isCold) {if (isCold) {var freezing = 'Grab a jacket!';} else {
    var hot = 'It's a shorts kind of day.';
    console.log(freezing);
  }
} 

运行 getClothing(false) 后输入的是 undefined, 这是因为执行function 函数之前, 所有变量都会被 晋升, 晋升到函数作用域顶部.

letconst 申明的变量解决了这种问题, 因为他们是块级作用域, 在 代码块 (用{} 示意)中应用 letconst申明变量, 该变量会陷入 暂时性死区 直到该变量的申明被解决.

function getClothing(isCold) {if (isCold) {const freezing = 'Grab a jacket!';} else {
    const hot = 'It's a shorts kind of day.';
    console.log(freezing);
  }
} 

运行 getClothing(false) 后输入的是ReferenceError: freezing is not defined, 因为 freezing 没有在 else 语句、函数作用域或全局作用域内申明,所以抛出 ReferenceError

对于应用 letconst规定:

  • 应用 let 申明的变量能够从新赋值, 然而不能在同一作用域内从新申明
  • 应用 const 申明的变量必须赋值初始化, 然而不能在同一作用域类从新申明也无奈从新赋值.
正文完
 0