资讯专栏INFORMATION COLUMN

ES6对函数的改动

kk_miles / 963人阅读

摘要:改动函数的改变不算太大,都是一些其他语言早就有的功能,而一直比较欠缺的,比如函数参数默认值,任意参数的表示法,最大的变化应该是支持箭头函数其他语言称之为表达式,一种对匿名函数的一种简写方式,以下来探讨一下函数在中的一些改变默认参数任意参数操

ES6 functions改动

    ES6函数的改变不算太大,都是一些其他语言早就有的功能,而Javascript一直比较欠缺的,比如函数参数默认值,任意参数的表示法,最大的变化应该是支持箭头函数(其他语言称之为LAMBDA表达式),一种对匿名函数的一种简写方式,以下来探讨一下函数在ES6中的一些改变:

1. 默认参数(default parameters)
2. 任意参数(rest parameters ...)
3. spread 操作符(...)
4. new.target 元属性
5. 箭头函数( => )
6. 其他一些改动(miscellaneous)
1.默认参数

    ES6之前一直是通过其他方法来模拟默认参数的,例如逻辑或||符号,ES6版本真正意义上支持这种便利的写法。

// ES5模拟默认参数
function person(name, age) {
    name = name || "James";
    age = age || "18";
    console.log(name + " " + age);
}
// 一般情况下这种写法是没问题的,当逻辑或前面的值为falsy值,整个表达式返回后面的值
// 例如:
  person("Louis"); // ok
person(); // ok
person(undefined, 20); // ok
person("baby", 0); // "baby 18" error, 0为falsy值

上面可以看出这种写法是有一定问题的,各种JS库给出了另一种写法

function person(name, age) {
    if (typeof name === "undefined") {
        name = name || "James"; 
    }
    if (typeof age === "undefined") {
        age = age || "18";
    }
    console.log(name + " " + age);
}
person(undefined, 0); // ok "James 0"

ES6写法

function person(name = "James", age = 18) {
    console.log(name + " " + age);
}

// 1各种写法 默认参数出现在中间
function getRequest(url, timeout = 2000, callback) {
    // do something
}
gerRequest("/foo", undefined, function() {

});

// 2默认参数表达式
let value = 5;
function getValue() {
    return value++;
}
function add(first, second = getValue()) {
    return first + second;
}
add(3); // 8
add(3); // 9
add(1, 1); // 2

// 3后面参数引用前面参数
function add(first, second = first) {
    return first + second;
}
add(2); // 4
add(3, 4); // 7

默认参数TDZ(暂时死区)情况:

// 上面的第三种写法,若写成第一个参数引用第二个参数
function add(first = second, second) {
    return first + second;
}
add(1, 1); // ok 2
add(undefined, 4); // THROW AN ERROR 第二个参数未声明就引用就会抛出错误
// 就相当于
let first = second; // error
let second = 4;
2.任意参数

    ES6任意参数用 ... 表示,任意参数和arguments之间的差别
ES5使用arguments参数来实现对象属性拷贝:

function pick(object) {
    var result = Object.create(null); // 创建一个对象
    // 从第二个参数开始
    for (var i = 1, len = arguments.length; i < len; i++) {
        result[arguments[i]] = object[arguments[i]];
    }
    return result;
}
// arguments将object也计入,所以除开第一个参数要减1
var book = {
    title: "understanding ES6",
    author: "Nicholas C.Zakes",
    year: 2016
};
var o = pick(book, "author", "year");
o.author; // "Nicholas C.Zakes"
o.year; // 2016

上面的pick函数看上去不够直观,因为除第一个参数外不知道要添加几个参数,使用新语法

function pick(object, ...keys) {
    var result = Object.create(null); // 创建一个对象
    for (var i = 0, len = keys.length; i < len; i++) {
        result[keys[i]] = object[keys[i]];
    }
    return result;
}
// keys将object不计入在其内
var book = {
    title: "understanding ES6",
    author: "Nicholas C.Zakes",
    year: 2016
};
var o = pick(book, "author", "year");
o.author; // "Nicholas C.Zakes"
o.year; // 2016

使用rest parameters注意事项

1.要将任意参数放到函数的最后,不能放在中间位置

2.不能用于对象字面量setter中

function pick(object, ...keys, last) { //... }
// 语法错误

let object = {
    set name(...value) {
        // do something
    }
};
// 语法错误
3.spread操作符

spread操作符和rest parameters一样,都使用 ... 表示,spread操作符允许我们将数组中的参数一个一个传入函数中
例如:

// Math.max()函数, 一般可以加入任意个参数
Math.max(12, 13, 14, 15); // 15

// 以数组的形式
var arr = [1, 2, 3, 4];
Math.max.apply(null, arr); // 4

// 使用 "..."
Math.max(...arr); // 4
// 还可以加入其它的一些参数
Math.max(...arr, 5, 10); // 10

将一个数组去重:

var arr = [1, 2, 2, 4, 4];
// 使用Set将重复的去掉,然后将set对象转变为数组
var mySet = new Set(arr); // mySet {1, 2, 4}

// 方法1,使用Array.from转变为数组
// var arr = Array.from(mySet); // [1, 2, 4]

// 方法2,使用spread操作符
var arr = [...arr]; // [1, 2, 4]

// 方法3, 传统forEach
var arr2 = [];
mySet.forEach(v => arr2.push(v)); 
4.new.target元属性

    函数内部有两个方法 [[call]][[construct]] (箭头函数没有这个方法),当使用new 操作符时, 函数内部调用 [[construct]], 创建一个新实例,this指向这个实例; 不使用new 操作符时, 函数内部调用 [[call]]

    判断一个函数是否使用new操作符,ES5的方法:

function Person(name) {
    if (this instanceof Person) {
        this.name = name;
    } else {
        throw new Error("You must use new operator");
    }
}
var p = new Person("James"); // ok
var p = Person("James"); // error
// 但是可以通过其他方式绕过这种错误
var notPerson = Person.call(p, "Nicholas"); // works

ES6 通过new.target 来判断是否使用new,元属性 是指一个提供目标相关额外信息(比如new)的非对象属性。

function Person(name) {
    if (typeof new.target !== "undefined") {
        this.name = name;
    } else {
        throw new Errow("You must use new operator");
    }
}
var p = new Person("James"); // ok
var notPerson = Person.call(p, "Louis"); // error 
5.箭头函数

箭头函数有以下几个方面的特点:

this, super, arguments和arguments的值由最近一个包含它的非箭头函数定义。(No this, superm arguments and new.target bindings);

箭头函数内部没有 [[construct]]方法, 因此不能当作构造器,使用new操作符;

不存在原型(No prototype);

不能改变this, 在整个箭头函数生命周期this值保持不变;

不存在arguments对象,不过包含它的函数存在,箭头函数依靠命名参数和rest parameters;

不能拥有重复的命名参数,ES5只有严格模式下才不允许

1.箭头函数语法
// 语法很简单
let sum = (n1, n2) => n1 + n2;
// 相当于
let sum = function(n1, n2) {
    return n1 + n2;
};

let getTempItem = id => ({ id: id, name: "Temp" });
// 相当于
let getTempItem = function(id) {
    return {
        id: id,
        name: "Temp"
    };
};
2.没有this绑定
let PageHandler = {
    id: "123456",
    init: function() {
        document.addEventListener("click", function(event) {
            this.doSomething(event.type); // error
        }, false);
    },
    doSomething: function(type) {
        console.log("Handling " + type + " for " + this.id);
    }
};
// init函数中的this.doSomething,this指向的是函数内部document对象,
// 而不是PageHandler对象

使用箭头函数改写:


let PageHandler = {
  id: "123456",
  init: function() {
      document.addEventListener("click", event => this.doSomething(evnet.type)
        }, false);
    },
  doSomething: function(type) {
      console.log("Handling " + type + " for " + this.id);
  }
};
// 此处箭头函数this指向包含它的函数,即init,init为PageHandler的方法,
// this指向PageHandler对象实例
3.不能使用new
var MyType = () => {};
var obj = new MyType(); // Error
4.没有arguments对象

箭头函数没有arguments对象,但是可以使用包含函数中的arguments对象

function createArrowFunctionReturningFirstArg() {
    // arguments 为 createArrowFunctionReturningFirstArg中的对象
    return  () => arguments[0]; 
}
var arrowFunction = createArrowFunctionReturningFirstArg(10);
arrFunction(); // 10
6.其他(misllanceous)

其他的一些变化:

添加name属性用来判断函数名,用于调试;

规范块级别函数(block-level functions),当执行流结束块级别函数退出,块级别函数提升变量到块顶部;

对尾部调用(Tail call, 值一个函数返回另一个函数对象)性能进行优化,尤其是都递归函数性能提升很大

总结

ES6对默认参数的支持;

任意参数和spread操作符

箭头函数

    总体来说,这些改动都是为编写程序提供了极大的便利,不用再使用workaround来解决语法存在的问题,整体来讲,更加符合语言的书写习惯。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90988.html

相关文章

  • ES6(一): let,const

    摘要:明确规定,如果区块中存在和命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。暂时性死区意味着不再是一个百分百的安全操作了不允许重复声明不允许在相同作用域内,重复声明同一个变量。 let,const共同拥有的特点 提供了代码块级作用域;(代码块是用{}来实现的) 不存在变量提升,先声明再使用; 存在暂时性死区; 不允许重复声明; ...

    blair 评论0 收藏0
  • es6之箭头函数

    摘要:箭头函数没有绑定,意味着箭头函数内部的值只能通过查找作用域链来确定。无论此后箭头函数在何处执行,该对象都是可用的。 箭头函数 es6的箭头函数,顾名思义箭头函数是使用一个箭头( => )来定义的函数,这很容易理解但是它有很多行为与传统的js函数不同: 没有 this 、 super 、 arguments 。 不能被使用 new 调用: 箭头函数没有 [[Construct]] 方法...

    songjz 评论0 收藏0
  • ES6简记

    摘要:圆括号问题可以使用圆括号的情况只有一种赋值语句的非模式部分,可以使用圆括号。正确正确正确上面三行语句都可以正确执行,因为首先它们都是赋值语句,而不是声明语句其次它们的圆括号都不属于模式的一部分。 let 和 const 命令 1.let 概念: 暂时性死区(TDZ) 在作用域内,let变量已经存在;并绑定该作用域:不受外界影响,外界也不可获取let作用域以代码块划分,var作用域...

    renweihub 评论0 收藏0
  • ES6学习 第一章 let 和 const 命令

    摘要:外层作用域不报错正常输出块级作用域与函数声明规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。规定,块级作用域之中,函数声明语句的行为类似于,在块级作用域之外不可引用。同时,函数声明还会提升到所在的块级作用域的头部。 前言:最近开始看阮一峰老师的《ECMAScript 6 入门》(以下简称原...

    番茄西红柿 评论0 收藏2637
  • 你需要了解ES6新特性(前沿技术)

    摘要:年月,的创造者公司,决定将提交给国际标准化组织,希望这种语言能够成为国际标准。这表示外层代码块不受内层代码块的影响。也可以运用于函数及其他文中就简单介绍这么多,想更加了解新特性的可以自寻查看一下阮一峰的一本入门 ES6新特性 最近在项目中遇到了很多ES6的语法,遇到了不少坑坑洼洼,因此,在这里就简单介绍一下ES6中的一些新特性 如果想真正的了解ES6和ES5有什么不同,这里推荐看一下...

    Nino 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<