共计 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);
花括号 {}
示意被解构的对象,type
、color
和 karat
示意要将对象中的属性存储到其中的变量
对象字面量简写法
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
循环的劣势,能够循环任何可迭代(也就是恪守可迭代协定)类型的数据。默认状况下,蕴含以下数据类型:String
、Array
、Map
和 Set
,留神不蕴含 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
开展运算符的一个用处是联合数组。
如果你须要联合多个数组,在有开展运算符之前,必须应用 Array
的 concat()
办法。
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
- 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 恍然大悟
箭头函数和 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();
默认参数函数
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!
默认值与解构
- 默认值与解构数组
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
- 默认值与解构函数
就像应用数组默认值解构数组一样,函数能够让对象成为一个默认参数,并应用对象解构:
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.
- 数组默认值与对象默认值
默认函数参数只是个简略的增加内容,然而却带来很多便当!与数组默认值相比,对象默认值具备的一个劣势是可能解决跳过的选项。看看上面的代码:
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
函数之前, 所有变量都会被 晋升
, 晋升到函数作用域顶部.
let
与 const
申明的变量解决了这种问题, 因为他们是块级作用域, 在 代码块 (用{}
示意)中应用 let
或const
申明变量, 该变量会陷入 暂时性死区 直到该变量的申明被解决.
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
。
对于应用 let
与const
规定:
- 应用
let
申明的变量能够从新赋值, 然而不能在同一作用域内从新申明 - 应用
const
申明的变量必须赋值初始化, 然而不能在同一作用域类从新申明也无奈从新赋值.