Skip to content

ES6 之函数新特性 #40

@wangjing013

Description

@wangjing013

1. 支持参数的默认值

在 ES6 之前通常指定变量的默认值时, 采用方式如下:

function request(url, method, data, timeout) {
   method = method || 'get'; 
   data = data || {};
   timeout = timeout ||  2000;
   return ajax({
     url: url,
     method: method;
     data: data,
     timeout: timeout
  })
}

在 ES6 中支持参数默认值, 如下方式:

function request(url, method = 'get', data = {}, timeout = 2000) {
   return ajax({
     url: url,
     method: method;
     data: data,
     timeout: timeout
  })
}

触发默认值只有当函数调用时 method 参数值为 undefined 才会被触发, 如下:

request('www.shiguangkey.com')
request('www.shiguangkey.com', undefined, undefined)

这种方式相比前一种好处, 可以规避一些隐藏的 bug. 例如:

function request(url, method, data, timeout) {
   method = method || 'get'; 
   data = data || {};
   timeout = timeout ||  2000;
   return ajax({
     url: url,
     method: method;
     data: data,
     timeout: timeout
  })
}
request('www.shiguangkey.com', 'get', { name : 'J'}, 0); 

上面传入 timeout 为 0, 很遗憾它并不会生效. 因为 0 认为是 false, 所以 timeout 被设置为 2000.

函数使用参数默认值对 arguments 的影响

当函数使用默认值后, 不管那种模式下 arguments 中值的更改不会映射到参数.

// 非严格模式
function foo(name) {
   console.log(arguments[0] === name); //=> true
   arguments[0] = '李四';
   console.log(name); //=> 李四
}
foo('张三');

// 指定默认值时
function foo(name = '匿名好友') {
   console.log(arguments[0] === name); //=> true
   arguments[0] = '李四';
   console.log(name); //=> '张三'
}
foo('张三');

2. 不定参数

通常 JS 中在调用函数时允许传任意数量的参数, 想要获取完整参数列表时可以通过 arguments 变量.

下面通过简单的案例从指定对象中挑选给定属性列表并返回一个新的对象, 如下:

function pick(o) {
 let result = Object.create(null);
 // 从第二个参数开始遍历
 for(let i = 1, len = arguments.length; i < len; i++){
    result[arguments[i]] = o[arguments[i]]
 }
 return result;
}
const obj = {
   name: '张三',
   age: 20
}
const now = pick(obj, age);
console.log(now);

上面实现方式存在几个问题:

  • 从函数签名并不能直观知道函数可以接收多少个参数.
  • 遍历时需要记住开始遍历的索引位置

在ES6中引入不定参, 可以很方便解决上述存在的问题:

function pick(o, ...paths) {
  let result = Object.create(null);
  for(let i = 0, len = paths.length; i < len; i++){
     result[paths[i]] = o[paths[i]]
  }
  return result;
}
const obj = {
    name: '张三',
    age: 20
}
const now = pick(obj, 'age');
console.log(now);

⚠️ 注意

不定参数只能放在参数列表的最后位置

3. 加强的 name 属性

name 就是一个函数标识, 方便辨别. ES6 中针对这部分做了很多改进,确保每个函数都有一个合适的名字. 如下:

var doSomething = function (){}  //=> "doSomething"
var doSomething = function doSomethingElse(){} //=> 'doSomethingElse'
doSomething.bind().name; //=> 'bound doSomething'
(new Function()).name; //=> 'anonymous'.  

⚠️ 注意

函数的 name 属性的值不一定引用同名变量, 它只是协助调试用的额外信息, 所以不能使用 name 属性的值来获取对于函数的引用.

4. 明确函数的用途

JavaScript 不像其它的面相对象语言(e.g Java) 构造函数、方法有明确的区分. 在 JavaScript 中通常只有命名的区分, 如下:

function Person(){} //=> 构造函数
function getInfo(){} //=> 非构造函数

上面命名只是一种约定, 并不能去阻止每个人的用什么样的方式调用. 通常构造函数由 new 操作符去调用并生成相应的对象. 有时难免会把构造函数当作普通方法去调用. 如下:

function Person(name, age){
   this.name = name;
   this.age = age
}
var person = Person('张三', 20);
console.log(person); //=> undefined
console.log(window.name); //=> '张三' 

把构造函数当普通方法调用时, 此时 this 指向的 window. 相应的window上name被覆盖了. 导致意向不到的问题.

有什么方式去进行安全检测 ?

  • instanceof
function Person(name, age){
   if(this instanceof Person) {
      this.name = name;
      this.age = age
   }else {
      return new Person(name, age);
   }
}
console.log(Person('张三', 20)); //=> {name: "张三", age: 20}
console.log(new Person('张三', 20)); //=> {name: "张三", age: 20}

当使用 new 调用时, 此时 this 则为 Person 的实例, 否则手动添加 new. 但这种方式还存在问题的如下:

var person0 = new Person('张三', 20);
var person1 = Person.call(person0);    //=> 这样就绕过 instanceof 检测

从结果来看, 单纯通过 instanceof 判断构造函数通过 new 调用的显然做不到. 那怎么? new.target

function Person(name, age){
   if(new.target === Person) {
      this.name = name;
      this.age = age
   }else {
      throw new Error('必须通过 new 关键字调用 Person.')
   }
}
console.log(new Person('张三', 20)); //=> {name: "张三", age: 20}
console.log(Person('张三', 20)); //=> Uncaught Error: 必须通过 new 关键字调用 Person.

new.target 表示 new 操作符的目标(构造函数),

  • 当调用函数的 [[Construct]]方法时, new.target 被赋值为 new操作符的目标.
  • 当调用函数的[[Call]]方法时, 则 new.target 的值为 undefined.

总结

  • 参数的默认值
    • 当参数的值为 undefined 时, 默认值才会生效
    • 默认参数可以很好规避以往通过短路或带来问题
    • 当使用默认参数值时, 严格或非严格模式下 arguments 内容修改都不会同步到行参上
  • 不定参数只能放在参数列表最后
  • 明确函数调用
    • instanceof
    • new.target

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions