阮一峰ES6教程读书笔记(三)数值、函数的扩展

About

本文只记录了一些我比较感兴趣的新增方法,以及特性,更加详细的介绍请参考阮一峰的《ECMAScript 6 入门》

一、数值的扩展

1. 进制表示法

  • 0b 二进制
  • 0o 八进制
  • 0d 十进制
  • 0x 十六进制

如果要将其它进制字符串数值转为十进制,要使用Number方法。

Number('0b111')  // 7
Number('0o10')  // 8
Number(0x11) // 17

2. Number.parseInt() 和 Number.parseFloat()

ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。

// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45

// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45

3. Number.isInteger()

Number.isInteger()用来判断一个数值是否为整数。JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。

Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false

如果传入的参数不是数值,这里不会发生自动转换,而是直接返回false

Number.isInteger() // false
Number.isInteger(null) // false
Number.isInteger('15') // false
Number.isInteger(true) // false

4. Number.EPSILON

Number对象新增了属性EPSILON,这个属性其实是一个数字即2的-52次方,用来表示JavaScript的最小精度。我们可以把它用在浮点运算中用来设置运算精度。

function withinErrorMargin (left, right) {
  return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2) // 误差范围2的-50次方
}

0.1 + 0.2 === 0.3 // false
withinErrorMargin(0.1 + 0.2, 0.3) // true

1.1 + 1.3 === 2.4 // false
withinErrorMargin(1.1 + 1.3, 2.4) // true

二、Math对象上的扩展

ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用。

1. Math.trunc()

Math.trunc方法用于去除一个数的小数部分,返回整数部分。

Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0

如果参数不是数值,那么会先使用Number方法将其转为数值,然后在取整,如果无法转为数值,或者参数为空,就返回NaN,对于没有部署这个方法的环境,可以用下面的代码模拟。

Math.trunc = Math.trunc || function(x) {
  return x < 0 ? Math.ceil(x) : Math.floor(x);
};

2. Math.hypot()

Math.hypot方法返回所有参数的平方和的平方根。用来做勾股定理不要太爽~

Math.hypot(3, 4);        // 5
Math.hypot(3, 4, 5);     // 7.0710678118654755

如果传入的参数有一个无法转为数字就会返回NaN

3. Math.log10(),Math.log2()

分别是求以10为底的对数和以2为底的对数,可以用以下方法替代:

Math.log2 = Math.log2 || function(x) {
  return Math.log(x) / Math.LN2;
};

Math.log10 = Math.log10 || function(x) {
  return Math.log(x) / Math.LN10;
};

4. 新增指数运算符

ES2016 新增了一个指数运算符(**)。

2 ** 2 // 4
2 ** 3 // 8

let a = 1.5;
a **= 2;
// 等同于 a = a * a;

let b = 4;
b **= 3;
// 等同于 b = b * b * b;

三、函数的扩展

1. 函数参数的默认值

ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。比如在函数体内通过typeOf检测形参是否为空,如果为空就赋给形参默认值,这样写起来不仅代码非常臃肿且十分丑陋,其他语言都能给参数默认值,而在JavaScript中却不能,所以ES6 允许我们给函数参数传入默认值。

function print (x, y = 'world') {
    console.log(`${x} ${y}`)
}
print('hello') // hello world

1.1 使用参数默认值时,变量不能重名

例如:

function foo(x, x, y) {
  console.log(x,x,y)
}
foo(1,2,3) // 2 2 3

function foo(x, x, y = 3) {
  console.log(x,x,y)
}
foo(1,2,3) // Uncaught SyntaxError: Duplicate parameter name not allowed in this context

1.2 参数默认值是惰性求值的

参数默认值不是传值的,而是每次都重新计算默认值表达式的值。

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}

foo() // 100

x = 100;
foo() // 101

形参p虽然有默认值,但是并非定义时就已经计算出来,而是等到调用时才计算该默认值。

1.3 与解构赋值结合使用

如果想在形参上使用结构赋值,那么必须要给他们赋一个默认值{}

function foo({x, y}) {
  console.log(x, y)
}

foo() // 报错

function foo({x, y} = {}) {
  console.log(x, y)
}

foo() // undefined undefined
foo({x:1, z:2}) // 1 undefined
foo({x:1, y:2}) // 1 2

为进一步加深理解,我们来看这样一个例子:

function m1({x = 0, y = 0} = {}) {
  return [x, y];
}

// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

写法二与一不同的地方在于:写法一中虽然也使用了解构赋值,但是在解构之前形参xy都已经有默认值为0了,即使在调用函数时只传了一个值,那么另一个值也不会为undefined而是为0;而写法二中形参xy采用了惰性求值,也就是说其并没有真正的默认值,如果调用m2时,没有传入参数,那么启动解构赋值,形参xy0,但是如果只传了1个参数,那么就不会启动形参内的解构赋值,这样的话,另一个参数就为undefined

m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]

m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]

1.4 带有默认值的参数位置

通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。

1.5 函数的length属性

函数的length返回该函数的形参数量,但不包括带有默认值的参数,总的来说就是调用该函数时必须传入多少个参数。

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

1.5 默认参数的应用

利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。

function missparams () {
    throw new Error('Missing a paramater')
}

function foo (x = missparams(), y = 1) {
    console.log(x, y)
}

foo() // Error: Missing a paramater

2. rest参数

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

这其实很好理解,在其他语言中,无论是C还是Python中我们会在定义形参时为了提升函数的稳定性,会在形参后定义*args,如果有多的参数就会被放在数组中传入函数,而不会引发错误。

function add(...values) {
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

值得注意的是,...rest只能是作为形参的最后一个参数,正如*args一样,必须放在最后,否则会报错。

3. name属性

这个属性早就被浏览器广泛支持,但是直到 ES6,才将其写入了标准。

var f = function () {};

// ES5
f.name // ""

// ES6
f.name // "f"

4. 箭头函数

我们先看个例子:

// 不使用箭头函数
function greet () {
    console.log(`hello ${this.name}`)
}

var name = 'yan' //即 window.name = 'yan'
person = {
    'name': 'bing',
    'greet': greet
}
greet()  // hello yan
person.greet()  // hello bing

//使用箭头函数
var greet = () => console.log(`hello ${this.name}`)
person = {
    'name': 'bing',
    'greet': greet
}
greet()  // hello yan
person.greet()  // hello yan
greet.call(person) // hello yan

通过上面的例子我们可以得知箭头函数的this的指向是不会改变的,它在哪里定义,哪里就是它的执行上下文,由此我们可以得到使用箭头函数的注意点:

(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

出现上述情况的根本原因是箭头函数没有自己的this,它的this指向外层的this

不适用的场合:

  1. 定义对象的方法:
const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。

  1. 需要动态this的时候:
var button = document.getElementById('press');
button.addEventListener('click', () => {
  this.classList.toggle('on');
});

因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。

5. Function.prototype.toString()

toString()方法返回函数代码本身,以前会省略注释和空格。

function /* foo comment */ foo () {}

foo.toString()
// "function /* foo comment */ foo () {}"

参考链接

作者:阮一峰
链接:http://es6.ruanyifeng.com/#docs/destructuring

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容

  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,550评论 0 5
  • 函数参数的默认值 基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。 上面代码检查函数l...
    呼呼哥阅读 3,364评论 0 1
  • 函数参数的默认值 基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。 上面代码检查函数l...
    陈老板_阅读 447评论 0 1
  • [TOC] 参考阮一峰的ECMAScript 6 入门参考深入浅出ES6 let和const let和const都...
    郭子web阅读 1,773评论 0 1
  • 此文包含数值的扩展、函数的扩展,总结了一些我认为有可能会用到的,而去除了一些稍稍复杂一时难以去学习理解且目前不想去...
    六个周阅读 541评论 0 7