重要知识点有:函数参数默许值、盈余参数、扩大运算符、new.target属性、块级函数、箭头函数以及尾挪用优化
《深切明白ES6》笔记 目次
函数的默许参数在ES5中,我们给函数传参数,然后在函数体内设置默许值,以下面这类体式格局。
function a(num, callback) {
num = num || 6
callback = callback || function (data) {console.log('ES5: ', data)}
callback(num * num)
}
a() //ES5: 36,不传参输出默许值
//你还能够如许运用callback
a(10, function(data) {
console.log(data * 10) // 1000, 传参输出新数值
})
弊病:此处的 num 的有用值现实上有多是 0 ,但因为 0 是假值,就会致使 num 的值在这类情况下会被替换为 6;
能够用 typeof 来检测参数的范例:
function a(num, callback) {
num = (typeof num!== "undefined") ? num: 6;
callback = (typeof callback !== "undefined") ? callback : function (data) {console.log('ES5: ', data)};
callback(num * num)
}
虽然这类要领更平安,但依旧为完成一个基础需求而书写了过量的代码。它代表了一种大众
形式,而盛行的 JS 库中都充溢着相似的形式。
function a(num = 6, callback = function (data) {console.log('ES6: ', data)}) {
callback(num * num)
}
a() //ES6: 36, 不传参输出默许值
a(10, function(data) {
console.log(data * 10) // 1000,传参输出新数值
})
运用ES6的默许值写法能够让函数体内部的代码越发简约文雅
function mixArgs(first, second) {
console.log(first === arguments[0]);
console.log(secOnd=== arguments[1]);
first = "c";
secOnd= "d";
console.log(first === arguments[0]);
console.log(secOnd=== arguments[1]);
}
mixArgs("a", "b");
//输出
true
true
true
true
function mixArgs(first, second) {
"use strict";
console.log(first === arguments[0]);
console.log(secOnd=== arguments[1]);
first = "c";
secOnd= "d"
console.log(first === arguments[0]);
console.log(secOnd=== arguments[1]);
}
mixArgs("a", "b");
//输出
true
true
false
false
arguments 对象的表现老是会与 ES5 的严厉形式一致,不管此时函数是不是明白运转在严厉形式下。
// 非严厉形式
function mixArgs(first, secOnd= "b") {
console.log(arguments.length);
console.log(first === arguments[0]);
console.log(secOnd=== arguments[1]);
first = "c";
secOnd= "d"
console.log(first === arguments[0]);
console.log(secOnd=== arguments[1]);
}
mixArgs("a");
//输出
1
true
false
false
false
此时arguments.length =1 ,因为只给 mixArgs() 通报了一个参数。这也意味着arguments[1] 的值是 undefined ,相符将单个参数通报给函数时的预期;这同时意味着first 与 arguments[0] 是相称的。转变 first 和 second 的值不会对 arguments 对象形成影响,不管是不是在严厉形式下,所以你能够一直根据 arguments 对象来反应初始挪用状况。
参数不仅能够设置默许值为字符串,数字,数组或许对象,还能够是一个函数。
function add() {
return 10
}
function a(num = add()){
console.log(num)
}
a() // 10
第一章我们提到了let和const什么变量的暂时死区(TDZ),默许参数既然是参数,那末也一样有暂时死区,函数的作用域是自力的,a函数不能同享b函数的作用域参数。
//这是个默许参数暂时死区的例子,当初始化a时,b还没有声明,所以第一个参数对b来讲就是暂时死区。
处置惩罚无定名参数
function add(a = b, b){
console.log(a + b)
}
add(undefined, 2) // b is not define
上面说的参数都是定名参数,而无定名参数也是函数传参时经常用到的。当传入的参数是一个对象,不是一个详细的参数名,则是无定名参数。
function add(object){
console.log(object.a + object.b)
}
let obj = {
a: 1,
b: 2
}
add(obj) // 3
运用…(睁开运算符)的参数就是不定参数,它示意一个数组。
function add(...arr){
console.log(a + b)
}
let a = 1,b = 2
add(a, b) // 3
不定参数的运用限定:
//毛病的写法1
function add(...arr, c){
console.log(a + b)
}
let a = 1,b = 2,c = 3
add(a, b, c)
//毛病的写法2
let obj = {
set add(...arr) {
}
}
盈余参数怎样影响 arguments 对象
arguments 对象在函数被挪用时反应了传入的参数,与盈余参数能协同事情,就像以下顺序所演示的:
function checkArgs(...args) {
console.log(args.length);
console.log(arguments.length);
console.log(args[0], arguments[0]);
console.log(args[1], arguments[1]);
}
checkArgs("a", "b");
//输出
2
2
a a
b b
arguments 对象总能准确反应被传入函数的参数,而疏忽盈余参数的运用。
ES6中的组织函数Function新增了支撑默许参数和不定参数。
扩大运算符考虑一下Math.max()要领,它接收恣意数目的参数,并会返回个中的最大值。
//两个值举行比较
let value1 = 25,
value2 = 50;
console.log(Math.max(value1, value2)); // 50
//一个数组中找到最大值(es5)
let values = [25, 50, 75, 100]
console.log(Math.max.apply(Math, values));
//es6
let values = [25, 50, 75, 100]
// 等价于 console.log(Math.max(25, 50, 75, 100));
console.log(Math.max(...values)); // 100
扩大运算符通报参数
//假定你想让 Math.max() 返回的最小值为 0 (以防数组中混入了负值),你能够将参数 0 零丁传入,并继承为其他参数运用扩大运算符
ES6 的称号属性
let values = [-25, -50, -75, -100]
console.log(Math.max(...values, 0)); // 0
ES6 给一切函数添加了 name 属性。
//函数声明
function doSomething() {
// ...
}
//匿名函数表达式
var doAnotherThing = function() {
// ...
};
console.log(doSomething.name); // "doSomething"
console.log(doAnotherThing.name); // "doAnotherThing"
//doSomethingElse的优先级高于doSomething
var doSomething = function doSomethingElse() {
// ...
};
//person.firstName 现实是个 getter 函数,因而它的称号是 "get firstName"
var person = {
get firstName() {
return "Nicholas"
},
sayName: function() {
console.log(this.name);
}
}
console.log(doSomething.name); // "doSomethingElse"
console.log(person.sayName.name); // "sayName"
var descriptor = Object.getOwnPropertyDescriptor(person, "firstName");
console.log(descriptor.get.name); // "get firstName"
var doSomething = function() {
明白函数的两重用处
// ...
};
console.log(doSomething.bind().name); // "bound doSomething"
console.log((new Function()).name); // "anonymous"
JS 为函数供应了两个差别的内部要领: [[Call]] 与 [[Construct]] 。当函数未运用 new举行挪用时, [[call]] 要领会被实行,运转的是代码中显现的函数体。而当函数运用 new举行挪用时, [[Construct]] 要领则会被实行,担任建立一个被称为新目标的新的对象,并
且运用该新目标作为 this 去实行函数体。具有 [[Construct]] 要领的函数被称为组织器。
运用instanceof
function Person(name) {
if (this instanceof Person) {
this.name = name; // 运用 new
} else {
throw new Error("You must use new with Person.")
}
}
var person = new Person("Nicholas");
var notAPerson = Person("Nicholas"); // 抛出毛病
但这类情况下并不牢靠:
function Person(name) {
if (this instanceof Person) {
this.name = name; // 运用 new
} else {
throw new Error("You must use new with Person.")
}
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); // 见效了!
经由过程搜检 new.target 是不是被定义,这个新的元属性就让你能平安地推断函数是不是被运用new举行了挪用。
function Person(name) {
if (typeof new.target !== "undefined") {
this.name = name; // 运用 new
} else {
throw new Error("You must use new with Person.")
}
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); // 失足!
也能够搜检 new.target 是不是被运用特定组织器举行了挪用,比方以下代码:
function Person(name) {
if (new.target === Person) {
this.name = name; // 运用 new
} else {
throw new Error("You must use new with Person.")
}
}
function AnotherPerson(name) {
Person.call(this, name);
}
var person = new Person("Nicholas");
var anotherPerson = new AnotherPerson("Nicholas"); // 失足!
块级函数正告:在函数以外运用 new.target 会有语法毛病。
"use strict";
if (true) {
// 在 ES5 会抛出语法毛病, ES6 则不会
function doSomething() {
// ...
}
}
块级函数会被提拔到定义地点的代码块的顶部:
"use strict";
if (true) {
console.log(typeof doSomething); // "function"
function doSomething() {
// ...
}
doSomething();
}
console.log(typeof doSomething); // "undefined"
let 函数表达式:
"use strict";
if (true) {
console.log(typeof doSomething); // 抛出毛病
let doSomething = function () {
// ...
}
doSomething();
}
console.log(typeof doSomething);
ES6 在非严厉形式下一样许可运用块级函数,但行动有纤细差别。块级函数的作用域会被提拔到地点函数或全局环境的顶部,而不是代码块的顶部。
// ES6 behavior
箭头函数
if (true) {
console.log(typeof doSomething); // "function"
function doSomething() {
// ...
}
doSomething();
}
console.log(typeof doSomething); // "function"
箭头函数与传统的 JS 函数区分:
var getName = () => "Nicholas";
// 有用等价于:
var getName = function() {
return "Nicholas";
};
var reflect = value => value;
// 有用等价于:
var reflect = function(value) {
return value;
};
var sum = (num1, num2) => num1 + num2;
// 有用等价于:
var sum = function(num1, num2) {
return num1 + num2;
};
var sum = (num1, num2) => {
return num1 + num2;
};
// 有用等价于:
var sum = function(num1, num2) {
return num1 + num2;
};
//将对象字面量包裹在括号内,标示了括号内是一个字面量而不是函数体。
var getTempItem = id => ({ id: id, name: "Temp" });
// 有用等价于:
var getTempItem = function(id) {
return {
id: id,
name: "Temp"
};
};
let person = function(name) {
return {
getName: function() {
return name;
}
};
}("Nicholas");
console.log(person.getName()); // "Nicholas"
let person = ((name) => {
return {
getName: function() {
return name;
}
};
})("Nicholas");
console.log(person.getName()); // "Nicholas"
尾挪用优化译注:运用传统函数时, (function(){/
函数体/})(); 与 (function(){/
函数体/}());这两种体式格局都是可行的。
但若运用箭头函数,则只要下面的写法是有用的: (() => {/
函数体/})();
尾挪用是指在函数return的时刻挪用一个新的函数,因为尾挪用的完成须要存储到内存中,在一个循环体中,假如存在函数的尾挪用,你的内存能够爆满或溢出。
ES6中,引擎会帮你做好尾挪用的优化事情,你不须要本身优化,但须要满足下面3个请求:
1、函数不是闭包
2、尾挪用是函数末了一条语句
3、尾挪用效果作为函数返回
一个满足以上请求的函数以下所示:
"use strict";
function a() {
return b();
}
下面的都是不满足的写法:
//没有return不优化
"use strict";
function a() {
b();
}
//不是直接返回函数不优化
"use strict";
function a() {
return 1 + b();
}
//尾挪用是函数不是末了一条语句不优化
"use strict";
function a() {
const s = b();
return s
}
//闭包不优化
"use strict";
function a() {
const num = 1
function b() {
return num
}
return b
}
尾挪用现实用处——递归函数优化
在ES5时期,我们不引荐运用递归,因为递归会影响机能。
但是有了尾挪用优化以后,递归函数的机能有了提拔。
//新型尾优化写法
"use strict";
function a(n, p = 1) {
if(n <= 1) {
return 1 * p
}
let s = n * p
return a(n - 1, s)
}
//求 1 x 2 x 3的阶乘
let sum = a(3)
console.log(sum) // 6