正则、数值、函数、数组、数值、对象

(一)正则的扩展

  • 1、添加了 u 修饰符,含义为“Unicode 模式”,用来正确处理大于 \uFFFF 的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。

  • 2、新增 unicode 属性,表示是否设置了 u 修饰符。

  • 3、加了 y 修饰符,叫做“粘连”(sticky)修饰符

  • 4、与 y 修饰符相匹配,ES6 的正则实例对象多了 sticky 属性,表示是否设置了 y 修饰符

  • 5、新增了 flags 属性,会返回正则表达式的修饰符。

  • 6、ES2018 引入 s 修饰符,使得 . 可以匹配任意单个字符

四个字符属于“行终止符”。

U+000A 换行符( \

U+000D 回车符( \\r )
U+2028 行分隔符(line separator)
U+2029 段分隔符(paragraph separator)

/foo.bar/.test('foo\
bar')
// false

( 二 )数值的扩展

  • 1、二进制和八进制数值的新的写法,分别用前缀 0b (或 0B )和 0o (或 0O )表示。

  • 2、新提供了 Number.isFinite() 和 Number.isNaN() 两个方法
    注传统方法先调用 Number() 将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效, Number.isFinite() 对于非数值一律返回 false , Number.isNaN() 只有对于 NaN 才返回 true ,非 NaN 一律返回 false 。

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

  • 4、 Number.isInteger() 用来判断一个数值是否为整数。

  • 5、新增一个极小的常量 Number.EPSILON 。根据规格,它表示 1 与大于 1 的最小浮点数之间的差。

  • 6、JavaScript 能够准确表示的整数范围在 -2^53 到 2^53 之间(不含两个端点) Number.isSafeInteger() 则是用来判断一个整数是否落在这个范围之内

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

  • 8、 Math.sign 方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。 它会返回五种值。

    • 参数为正数,返回 +1 ;
    • 参数为负数,返回 -1 ;
    • 参数为 0,返回 0 ;
    • 参数为-0,返回 -0 ;
    • 其他值,返回 NaN
  • 9、 Math.cbrt 方法用于计算一个数的立方根。

  • 10、 新增了一个指数运算符( ** )

    2 ** 3
    // 8

    (三)函数的扩展

  • 1、参数默认值可以与解构赋值的默认值,结合起来使用。
    指定了默认值以后,函数的 length 属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后, length 属性将失真。

    function fetch(url, { body = '', method = 'GET', headers = {} }) {
    console.log(method);
    }
  • 2、引入 rest 参数(形式为 …变量名 ),用于获取函数的多余参数,这样就不需要使用 arguments 对象了

  • 3、ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

  • 4、ES6 对这个属性的行为做出了一些修改。如果将一个匿名函数赋值给一个变量,ES5 的 name 属性,会返回空字符串,而 ES6 的 name 属性会返回实际的函数名。
    Function 构造函数返回的函数实例, name 属性的值为 anonymous 。

    (new Function).name
    // "anonymous"

    bind 返回的函数, name 属性值会加上 bound 前缀。

    function foo() {};
    foo.bind({}).name
    // "bound foo"
    (function(){}).bind({}).name
    // "bound "
  • 5、允许使用“箭头”( => )定义函数。
    箭头函数有几个使用注意点。
    (1)函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
    (2)不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。
    (3)不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
    (4)不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。

  • 6、尾调用优化
    ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。

    function f() {
    let m = 1;
    let n = 2;
    return g(m + n);
    }
    f();
    // 等同于
    function f() {
    return g(3);
    }
    f();
    // 等同于
    g(3);

    上面代码中,如果函数 g 不是尾调用,函数 f 就需要保存内部变量 m 和 n 的值、 g 的调用位置等信息。但由于调用 g 之后,函数 f 就结束了,所以执行到最后一步,完全可以删除 f(x) 的调用帧,只保留 g(3) 的调用帧。

  • 7、尾递归优化

     function factorial(n) {
    if (n === 1) return 1;
    return n * factorial(n - 1);
    }

    factorial(5)
    // 120

    // 上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。
    // 如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。
    function factorial(n, total) {
    if (n === 1) return total;
    return factorial(n - 1, n * total);
    }

    factorial(5, 1)
    // 120

    8、ES2017 允许函数的最后一个参数有尾逗号(trailing comma)

(四)数组的扩展

  • 1、扩展运算符(spread)是三个点( … )。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
    任何定义了遍历器(Iterator)接口的对象(参阅 Iterator 一章),都可以用扩展运算符转为真正的数组。

  • 2、 Array.from 方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)

  • 3、 Array.of 方法用于将一组值,转换为数组。

  • 4、数组实例的 copyWithin 方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。

     Array.prototype.copyWithin(target, start = 0, end = this.length)

    // target(必需):从该位置开始替换数据。如果为负值,表示倒数。
    // start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示倒数。
    // end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。
    // 这三个参数都应该是数值,如果不是,会自动转为数值。
  • 5、数组实例的 find、 findIndex方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为 true 的成员,然后返回该成员。如果没有符合条件的成员,则返回 undefined 。

    [1, 4, -5, 10].find((n) => n < 0)
    // -5
  • 6、 fill 方法使用给定值,填充一个数组。

  • 7、ES6 提供三个新的方法—— entries() , keys() 和 values() ——用于遍历数组。

  • 8、 Array.prototype.includes 方法返回一个布尔值,表示某个数组是否包含给定的值,与 字符串的 includes 方法类似。

  • 9、数组的成员有时还是数组, Array.prototype.flat(number) 用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。number: 表示要“拉平”number层的嵌套数组
    flatMap() 方法对原数组的每个成员执行一个函数(相当于执行 Array.prototype.map() ),然后对返回值组成的数组执行 flat() 方法。该方法返回一个新数组,不改变原数组。

      // 相当于 [[2, 4], [3, 6], [4, 8]].flat()
    [2, 3, 4].flatMap((x) => [x, x * 2])
    // [2, 4, 3, 6, 4, 8]
    // 注意
    // 将数组的空位,转为 undefined

    Array.from(['a',,'b'])
    // [ "a", undefined, "b" ]
    [...['a',,'b']]
    // [ "a", undefined, "b" ]
    [,'a','b',,].copyWithin(2,0) // [,"a",,"a"]
    new Array(3).fill('a') // ["a","a","a"]
    let arr = [, ,];
    for (let i of arr) {
    console.log(1);
    }
    // 1
    // 1

    (五)对象的扩展

  • 1、ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

    const baz = {
    foo,
    method() {
    return "Hello!";
    }
    };
    baz // {foo: "bar",method:function method(){...}}
  • 2、可枚举性与遍历:
    对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。es5的方法: Object.getOwnPropertyDescriptor 方法可以获取该属性的描述对象。

     let obj = { foo: 123 };
    Object.getOwnPropertyDescriptor(obj, 'foo')
    // {
    // value: 123,
    // writable: true,
    // enumerable: true,
    // configurable: true
    // }

    描述对象的 enumerable 属性,称为“可枚举性”,如果该属性为 false ,就表示某些操作会忽略当前属性。
    目前,有四个操作会忽略 enumerable 为 false 的属性(不遍历)。

    • for…in 循环:只遍历对象自身的和继承的可枚举的属性。
    • Object.keys() :返回对象自身的所有可枚举的属性的键名。
    • JSON.stringify() :只串行化对象自身的可枚举的属性。
    • Object.assign() : 忽略 enumerable 为 false 的属性,只拷贝对象自身的可枚举的属性。
      前三个是 ES5 就有的,最后一个 Object.assign() 是 ES6 新增的

      ES6 规定,所有 Class 的原型的方法都是不可枚举的。

      属性的遍历

      ES6 一共有 5 种方法可以遍历对象的属性。
  • for…in

    循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

  • Object.keys(obj)

    Object.keys 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

  • Object.getOwnPropertyNames(obj)

    Object.getOwnPropertyNames 返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

  • Object.getOwnPropertySymbols(obj)

    Object.getOwnPropertySymbols 返回一个数组,包含对象自身的所有 Symbol 属性的键名。

  • Reflect.ownKeys(obj)

    Reflect.ownKeys 返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。

  • 首先遍历所有数值键,按照数值升序排列。

  • 其次遍历所有字符串键,按照加入时间升序排列。

  • 最后遍历所有 Symbol 键,按照加入时间升序排列。

    Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
    // ['2', '10', 'b', 'a', Symbol()]
  • 3、 this 关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字 super ,指向当前对象的原型对象。

  • 4、ES2018 将扩展运算符( …)引入对象。

  • 5、函数的 name 属性,返回函数名。对象方法也是函数,因此也有 name 属性。
    如果对象的方法使用了取值函数( getter )和存值函数( setter ),则 name 属性不是在该方法上面,而是该方法的属性的描述对象的 get 和 set 属性上面,返回值是方法名前加上 get 和 set 。

     const person = {
    get foo() {},
    set foo(x) {},
    sayName() {
    console.log('hello!');
    },
    };
    person.sayName.name // "sayName"
    obj.foo.name
    // TypeError: Cannot read property 'name' of undefined

    const descriptor = Object.getOwnPropertyDescriptor(obj, 'foo');

    descriptor.get.name // "get foo"
    descriptor.set.name // "set foo"

    // 有两种特殊情况:
    // bind方法创造的函数,name属性返回bound加上原函数的名字;
    // Function构造函数创造的函数,name属性返回anonymous

    (new Function()).name // "anonymous"
    var doSomething = function() {
    // ...
    };
    doSomething.bind().name // "bound doSomething"
    // 如果对象的方法是一个 Symbol 值,那么name属性返回的是这个 Symbol 值的描述。
    const key1 = Symbol('description');
    const key2 = Symbol();
    let obj = {
    [key1]() {},
    [key2]() {},
    };
    obj[key1].name // "[description]"
    obj[key2].name // ""

    对象的新增方法

  • 1、 Object.is 。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
    不同之处只有两个:一是 +0 不等于 -0 ,二是 NaN 等于自身。

    +0 === -0 //true
    NaN === NaN // false

    Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true
  • 2、 Object.assign 方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

  • 3、ES5 的 Object.getOwnPropertyDescriptor() 方法会返回某个对象属性的描述对象(descriptor)。ES2017 引入了 Object.getOwnPropertyDescriptors() 方法,返回指定对象所有自身属性(非继承属性)的描述对象。

  • 4、 Object.setPrototypeOf 方法的作用与 proto 相同,用来设置一个对象的 prototype 对象,返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法(接收两个参数)
    如果第一个参数不是对象,会自动转为对象( undefined 和 null 无法转为对象会报错)。但是由于返回的还是第一个参数,所以这个操作不会产生任何效果。

    Object.setPrototypeOf(1, {}) === 1 // true
    Object.setPrototypeOf('foo', {}) === 'foo' // true
    Object.setPrototypeOf(true, {}) === true // true

    Object.getPrototypeOf(obj);与 Object.setPrototypeOf 方法配套,用于读取一个对象的原型对象。如果参数不是对象,会被自动转为对象。如果参数是 undefined 或 null ,它们无法转为对象,所以会报错。

  • 5、ES5 引入了 Object.keys 方法,返回一个数组、ES2017 引入了跟 Object.keys 配套的 Object.values 和 Object.entries ,作为遍历一个对象的补充手段,供 for…of 循环使用。

    const obj = { 100: 'a', 2: 'b', 7: 'c' };
    Object.values(obj)
    // ["b", "c", "a"] 属性名为数值的属性(同上文属性的遍历规则),是按照数值大小,从小到大遍历的,因此返回的顺序是 b、c、a

    Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)
    所有可遍历(enumerable)属性的键值对数组。如果原对象的属性名是一个 Symbol 值,该属性会被忽略。

    const obj = { foo: 'bar', baz: 42 };
    Object.entries(obj)
    // [ ["foo", "bar"], ["baz", 42] ]
  • 6、 Object.fromEntries() 方法是 Object.entries() 的逆操作,用于将一个键值对数组转为对象。

拓展:

ES5 中 Object.create

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

语法:
Object.create(proto, [propertiesObject])

ES5 中 Object.defineProperties()

语法:

Object.defineProperties(obj, props)

obj
在其上定义或修改属性的对象。
props
要定义其可枚举属性或修改的属性描述符的对象。对象中存在的属性描述符主要有两种:数据描述符和访问器描述符(更多详情,请参阅Object.defineProperty())。描述符具有以下键:
configurable
对象的属性是否可以被删除,以及除 value 和 writable 特性外的其他特性是否可以被修改。
默认为 false
enumerable
true 当且仅当在枚举相应对象上的属性时该属性显现。
默认为 false
value
与属性关联的值。可以是任何有效的JavaScript值(数字,对象,函数等)。
默认为 undefined .
writable
true 当且仅当与该属性相关联的值可以用assignment operator改变时。
默认为 false
get
作为该属性的 getter 函数,如果没有 getter 则为 undefined 。函数返回值将被用作属性的值。
默认为 undefined
set
作为属性的 setter 函数,如果没有 setter 则为 undefined 。函数将仅接受参数赋值给该属性的新值。
默认为 undefined

 var obj = {};

var bValue;Object.defineProperties(obj, {
'property1': {
get : function() {
return bValue;
},
set : function(newValue){
bValue = newValue;
}, // value: true, 数据描述符和存取描述符不能混合使用
writable: true
},
'property2': {
value: 'Hello',
writable: false
}
// etc. etc.
});
// 显式
Object.defineProperty(obj, "key", {
enumerable: false,
configurable: false,
writable: false,
value: "static"
});