ES6函数
@(ES6大法) 来自印象笔记的整理
1、在ES5中,要对一个参数赋默认值都要这样:
function makeRequest(url, timeout, callback) {
timeout = timeout || 2000;
callback = callback || function() {};
// 函数的剩余部分
}
当timeout、callback实参为undefined时,|| 后面的值就会成为它门的默认值。ES5的实现办法要写这么多代码,特别麻烦。
ES6对函数进行改造,可以对参数进行默认:
function makeRequest(url, timeout = 2000, callback = function() {}) {
// 函数的剩余部分
}
// 不使用默认值,传null会被采用,undefined不会
makeRequest("/foo", null, function(body) {
doSomething(body);
});
2、ES6函数中的arguments对象
ES5中,非严格模式: arguments 对象会反映出具名参数的变化(arguments可以在函数内由于对形参的重新赋值而会被改写),
严格模式,不再反映出具名参数的变化(在函数内对参数的重新赋值不会影响到arguments的值)
ES6函数的arguments对象表现与ES5严格模式一致。
还有一点要注意:ES6参数默认值的存在触发了 arguments 对象与
具名参数的分离,也就是说,虽然你指定了其中一个参数的默认值,但是,arguments对象还是以你调用传进来为准,不会把默认值放进arguments对象里去。
3、ES6函数的默认参数也可以是个表达式
let value = 5;
function getValue() {
return value++;
}
function add(first, second = getValue()) {
return first + second;
}
console.log(add(1, 1)); // 2
console.log(add(1)); // 6
console.log(add(1)); // 7
4、ES6函数的默认值可以是前面(后面不行)的参数,或者前面的参数的运算
function add(first, second = first) {
return first + second;
}
console.log(add(1, 1)); // 2
console.log(add(1)); // 2
function getValue(value) {
return value + 5;
}
function add(first, second = getValue(first)) {
return first + second;
}
console.log(add(1, 1)); // 2
console.log(add(1)); // 7
5、ES6的参数默认值的暂时性死区
与let/const 的暂时性死区相同,函数的每个参数都会创建一个新的标识,在初始化之前不允许访问。这体现在第4点,只能对前面的参数进行对后面参数的默认。
6、ES5的不具名参数和ES6的剩余参数
当不确定参数有几个时,ES5可以使用arguments对象来查看所有传进来的参数,并做一些逻辑处理:
function pick(object) {
let result = Object.create(null); // 传建一个原型为null的对象
// 从第二个参数开始处理
// 遍历arguments对象
// 根据第1个参数之后的参数,从第一个参数中挑出符合组成新对象(result)返回
for (let i = 1, len = arguments.length; i < len; i++) {
result[arguments[i]] = object[arguments[i]];
}
return result;
}
let book = {
title: "Understanding ES6",
author: "Nicholas C. Zakas",
year: 2015
};
let bookData = pick(book, "author", "year");
console.log(bookData.author); // "Nicholas C. Zakas"
console.log(bookData.year); // 2015
ES6引入了剩余参数解决以上对arguments操作不易于阅读的问题
剩余参数由三个点…与一个紧跟着的具名参数指定
function pick(object, ...keys) {
let result = Object.create(null);
for (let i = 0, len = keys.length; i < len; i++) {
result[keys[i]] = object[keys[i]];
}
return result;
}
…keys 与 arguments不同,它是除了第一个参数的剩下的参数组成的数组。
注意:
1)函数的length属性由具名参数个数决定,不包括剩余参数
上面的函数,pick.length = 1
2)一个函数只能有一个剩余函数
3)剩余参数必须放在最后一个
4)剩余参数不能在对象字面量的setter属性中使用,对象字面量的 setter 被限定只能使用单个参数;而剩余参数按照定义是不限制参数数量的,因此它在此处不被许可
let object = {
// 语法错误:不能在 setter 中使用剩余参数
set name(...value) {
// 一些操作
}
};
7、剩余参数的出现目的是替代掉之前的arguments
8、扩展运算符
剩余参数是为了把多个独立的参数合并到一个数组去,而扩展运算符用在调用函数时传入函数的参数中:
console.log(Math.max.apply(Math, [1,2,3,4,5]));
以上是ES6之前的写法,由于max函数接受的参数为单个元素,数组求最大值就只能改变max函数的this指向,而现在用ES6你可以这样:
console.log(Math.max(...[1,2,3,4,5]));
…扩展运算符, JS 引擎将会将该数组分割为独立参数并把它们传递进去
你还可以把扩展运算符和其他参数混合
console.log(Math.max(...[1,2,3,4,5], 10));
9、函数的new关键字的双重用途未使用new调用时,返回了undefined,并会在全部window对象添加添加name属性,此时this指向window
function Person(name) {
this.name = name;
}
// 使用new调用时,函数内部的this是一个新对象,并作为函数的返回值,此时this指向的是调用者person
var person = new Person("Nicholas");
// 未使用new关键字,返回undefined,并在全局window对象下添加name属性
// 指向window
var notAPerson = Person("Nicholas");
console.log(person); // "[Object object]"
console.log(notAPerson); // "undefined"
为啥?
答:
1)JS 为函数提供了两个不同的内部方法: [[Call]] 与 [[Construct]]
2)当函数未使用 new进行调用时, [[call]] 方法会被执行,call运行的是函数的函数体
3)而当函数使用 new进行调用时, [[Construct]] 方法则会被执行,负责创建一个被称为新目标的新的对象,且使用该新目标作为 this 去执行函数体,拥有[[Construct]]的函数被称为构造器
注意:不是所有函数都有[[Construct]] 方法,箭头函数就没有,用instanceof来判断一个函数的构造器是什么
所以,在 ES5 中判断函数是不是使用了 new 来调用(即作为构造器),下面来告诉调用者用Person必须用new关键字:
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"); // 奏效了!
用call改变this的指向为person,对于该函数来说,没有任何方法能将这种方式与使用 new 调用区分开来。
10、ES6登场:new.target,使用new的调用后,new.target为构造器,不使用new调用,则new.target 为 undefined
11、ES6的块级函数
ES5严格模式下,不允许在代码块中声明函数:
"use strict";
if (true) {
// 在 ES5 会抛出语法错误, ES6 则不会
function doSomething() {
// ...
}
}
ES6中则不会,与块级作用域相似,在代码块中声明的函数,只能都在块里被访问,而唯一与let/const的区别是,块级函数存在函数提升到代码块顶部,而let/const不能提升。
另外要注意的是:
在ES6非严格模式下,块级函数的提升不是提升到代码块的顶部,而是提升到函数或者全局环境的顶部(当前作用域顶部)
12、箭头函数 =>
与传统的js函数有何不同?
1)没有 this 、 super 、 arguments ,也没有 new.target 绑定,
2)不能被使用 new 调用
3)没有原型, 既然不能对箭头函数使用 new ,那么它也不需要原型,也就是没有
prototype 属性
4)不能更改 this
5)不允许重复的具名参数
13、箭头函数的语法:
参数只有一个,可以省略()
var reflect = value => value;
函数体只有一句,可以省略{},作为返回值
var sum = (num1, num2) => num1 + num2;
如果没有参数,就要用()
var getName = () => "Nicholas";
如果要创建一个空函数,必须有{}
var doNothing = () => {};
如果函数体不止一句,要加{}
如果函数返回值是对象,即使只有一句,也要加({})
var getTempItem = id => ({ id: id, name: "Temp" });
14、箭头函数没有 this 绑定,意味着箭头函数内部的 this 值只能通过查找作用域链来确定。如果箭头函数被包含在一个非箭头函数内,那么 this 值就会与该函数的相等;否则,this 值就会是全局对象(在浏览器中是 window ,在 nodejs 中是 global )
15、ES5中普通函数的this指向会发生变化:
1). this总是代表它的直接调用者(js的this是执行上下文), 例如 obj.func ,那么func中的this就是obj
2).在默认情况(非严格模式下,未使用 ‘use strict’),没找到直接调用者,则this指的是 window (约定俗成)
3).在严格模式下,没有直接调用者的函数中的this是 undefined
4).使用call,apply,bind(ES5新增)绑定的,this指的是 绑定的对象
16、尾调用优化
总结
A. ES6函数增加了默认参数,默认参数不会计入到arguments对象
B. ES6函数的arguments不允许在函数内改写,与ES5严格模式一致
C. ES6的默认参数可以是表达式
D. ES6函数是块级函数,只能在块级作用域下访问,严格模式下,会提升到代码块顶部,非严格模式下,提升到函数或者全局环境顶部
E. ES6剩余参数和扩展运算符,函数的length属性不包含剩余参数
F. ES6函数的语法
G. ES6函数this