资讯专栏INFORMATION COLUMN

ES6(2)-各种类型的扩展(字符串、正则、数值、函数)

godruoyi / 700人阅读

摘要:返回布尔值,表示参数字符串是否在原字符串的头部。模板字符串之中还能调用函数。其他对字符串还有许多扩展,例如对字符表示的扩充以及为字符串提供了遍历方法详情请点击正则的扩展构造函数在中,构造函数的参数有两种情况。

ES6对各种基本类型都做了扩展,内容有些多,本章节挑选比较重要的扩展说明。

1 字符串的扩展 1.1 includes(), startsWith(), endsWith()

传统上,JavaScript只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。

includes():返回布尔值,表示是否找到了参数字符串。

startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。

endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。

var s = "Hello world!";

s.startsWith("Hello") // true
s.endsWith("!") // true
s.includes("o") // true

这三个方法都支持第二个参数,表示开始搜索的位置。

var s = "Hello world!";

s.startsWith("world", 6) // true
s.endsWith("Hello", 5) // true
s.includes("Hello", 6) // false

上面代码表示,使用第二个参数n时,endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束。

1.2 repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。

"x".repeat(3) // "xxx"
"hello".repeat(2) // "hellohello"
"na".repeat(0) // ""

参数如果是小数,会被取整。

"na".repeat(2.9) // "nana"

如果repeat的参数是负数或者Infinity,会报错。

"na".repeat(Infinity)
// RangeError
"na".repeat(-1)
// RangeError

但是,如果参数是0到-1之间的小数,则等同于0,这是因为会先进行取整运算。0到-1之间的小数,取整以后等于-0,repeat视同为0。

"na".repeat(-0.9) // ""

参数NaN等同于0。

"na".repeat(NaN) // ""

如果repeat的参数是字符串,则会先转换成数字。

"na".repeat("na") // ""
"na".repeat("3") // "nanana"
1.3 模板字符串

这个功能应该是最值得介绍的了,因为有了这个,我们可以抛弃之前用 + 号拼接字符串了。

传统的JavaScript语言,输出模板通常是这样写的。

$("#result").append(
  "There are " + basket.count + " " +
  "items in your basket, " +
  "" + basket.onSale +
  " are on sale!"
);

上面这种写法相当繁琐不方便,而且改动麻烦,ES6引入了模板字符串解决这个问题。

$("#result").append(`
  There are ${basket.count} items
   in your basket, ${basket.onSale}
  are on sale!
`);

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

// 普通字符串
`In JavaScript "
" is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

上面代码中的模板字符串,都是用反引号表示。如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。

var greeting = ``Yo` World!`;

如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。

$("#list").html(`
  • first
  • second
`);

上面代码中,所有模板字符串的空格和换行,都是被保留的,比如

    标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法消除它。

    $("#list").html(`
    
    • first
    • second
    `.trim());

    模板字符串中嵌入变量,需要将变量名写在${}之中。

    function authorize(user, action) {
      if (!user.hasPrivilege(action)) {
        //传统写法为
        //return "str:" + a + "XXXX"; 
        return  `str: ${a} XXXX`);
      }
    }
    

    大括号内部可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性。

    var x = 1;
    var y = 2;
    
    `${x} + ${y} = ${x + y}`
    // "1 + 2 = 3"
    
    `${x} + ${y * 2} = ${x + y * 2}`
    // "1 + 4 = 5"
    
    var obj = {x: 1, y: 2};
    `${obj.x + obj.y}`
    // "3"
    

    模板字符串之中还能调用函数。

    function fn() {
      return "Hello World";
    }
    
    `foo ${fn()} bar`
    // foo Hello World bar
    

    如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的toString方法。

    如果模板字符串中的变量没有声明,将报错。

    // 变量place没有声明
    var msg = `Hello, ${place}`;
    // 报错
    

    由于模板字符串的大括号内部,就是执行JavaScript代码,因此如果大括号内部是一个字符串,将会原样输出。

    `Hello ${"World"}`
    // "Hello World"
    

    模板字符串甚至还能嵌套。

    const tmpl = addrs => `
      
      ${addrs.map(addr => `
        
      `).join("")}
      
    ${addr.first}
    ${addr.last}
    `; //上面代码中,模板字符串的变量之中,又嵌入了另一个模板字符串,使用方法如下。 const data = [ { first: "", last: "Bond" }, { first: "Lars", last: "" }, ]; console.log(tmpl(data)); // // // // // // // // //
    Bond
    Lars
    1.4 其他

    ES6对字符串还有许多扩展,例如 对 字符Unicode表示的扩充以及为字符串提供了遍历方法(for ... of)
    详情请点击 http://es6.ruanyifeng.com/#do...

    2 正则的扩展 2.1 RegExp 构造函数

    在 ES5 中,RegExp构造函数的参数有两种情况。

    第一种情况是,参数是字符串,这时第二个参数表示正则表达式的修饰符(flag)。

    var regex = new RegExp("xyz", "i");
    // 等价于
    var regex = /xyz/i;
    

    第二种情况是,参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝。

    var regex = new RegExp(/xyz/i);
    // 等价于
    var regex = /xyz/i;
    

    但是,ES5 不允许此时使用第二个参数添加修饰符,否则会报错。

    var regex = new RegExp(/xyz/, "i");
    // Uncaught TypeError: Cannot supply flags when constructing one RegExp from another
    

    ES6 改变了这种行为。如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。

    new RegExp(/abc/ig, "i").flags
    // "i"
    

    上面代码中,原有正则对象的修饰符是ig,它会被第二个参数i覆盖。

    2.2 新的修饰符-u

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

    /^uD83D/u.test("uD83DuDC2A") // false
    /^uD83D/.test("uD83DuDC2A") // true
    

    上面代码中,uD83DuDC2A是一个四个字节的 UTF-16 编码,代表一个字符。但是,ES5 不支持四个字节的 UTF-16 编码,会将其识别为两个字符,导致第二行代码结果为true。加了u修饰符以后,ES6 就会识别其为一个字符,所以第一行代码结果为false。

    2.3 新的修饰符-y

    除了u修饰符,ES6 还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。

    y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。

    var s = "aaa_aa_a";
    var r1 = /a+/g;
    var r2 = /a+/y;
    
    r1.exec(s) // ["aaa"]
    r2.exec(s) // ["aaa"]
    
    r1.exec(s) // ["aa"]
    r2.exec(s) // null
    

    上面代码有两个正则表达式,一个使用g修饰符,另一个使用y修饰符。这两个正则表达式各执行了两次,第一次执行的时候,两者行为相同,剩余字符串都是_aa_a。由于g修饰没有位置要求,所以第二次执行会返回结果,而y修饰符要求匹配必须从头部开始,所以返回null。

    如果改一下正则表达式,保证每次都能头部匹配,y修饰符就会返回结果了。

    var s = "aaa_aa_a";
    var r = /a+_/y;
    
    r.exec(s) // ["aaa_"]
    r.exec(s) // ["aa_"]
    
    3 数值的扩展 3.1 Number.isFinite(), Number.isNaN()

    ES6 在Number对象上,新提供了Number.isFinite()和Number.isNaN()两个方法。

    Number.isFinite()用来检查一个数值是否为有限的(finite)。

    Number.isFinite(15); // true
    Number.isFinite(0.8); // true
    Number.isFinite(NaN); // false
    Number.isFinite(Infinity); // false
    Number.isFinite(-Infinity); // false
    Number.isFinite("foo"); // false
    Number.isFinite("15"); // false
    Number.isFinite(true); // false
    

    ES5 可以通过下面的代码,部署Number.isFinite方法。

    (function (global) {
      var global_isFinite = global.isFinite;
    
      Object.defineProperty(Number, "isFinite", {
        value: function isFinite(value) {
          return typeof value === "number" && global_isFinite(value);
        },
        configurable: true,
        enumerable: false,
        writable: true
      });
    })(this);
    

    Number.isNaN()用来检查一个值是否为NaN。

    Number.isNaN(NaN) // true
    Number.isNaN(15) // false
    Number.isNaN("15") // false
    Number.isNaN(true) // false
    Number.isNaN(9/NaN) // true
    Number.isNaN("true"/0) // true
    Number.isNaN("true"/"true") // true
    

    ES5 通过下面的代码,部署Number.isNaN()

    (function (global) {
      var global_isNaN = global.isNaN;
    
      Object.defineProperty(Number, "isNaN", {
        value: function isNaN(value) {
          return typeof value === "number" && global_isNaN(value);
        },
        configurable: true,
        enumerable: false,
        writable: true
      });
    })(this);
    

    它们与传统的全局方法isFinite()isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。

    isFinite(25) // true
    isFinite("25") // true
    Number.isFinite(25) // true
    Number.isFinite("25") // false
    
    isNaN(NaN) // true
    isNaN("NaN") // true
    Number.isNaN(NaN) // true
    Number.isNaN("NaN") // false
    Number.isNaN(1) // false
    
    3.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
    

    这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。

    Number.parseInt === parseInt // true
    Number.parseFloat === parseFloat // true
    
    3.3 Number.isInteger()

    Number.isInteger()用来判断一个值是否为整数。需要注意的是,在 JavaScript 内部,整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值。

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

    ES5 可以通过下面的代码,部署Number.isInteger()

    (function (global) {
      var floor = Math.floor,
        isFinite = global.isFinite;
    
      Object.defineProperty(Number, "isInteger", {
        value: function isInteger(value) {
          return typeof value === "number" &&
            isFinite(value) &&
            floor(value) === value;
        },
        configurable: true,
        enumerable: false,
        writable: true
      });
    })(this);
    
    4 函数的扩展 4.1 函数参数的默认值

    基本用法

    ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。

    function log(x, y) {
      y = y || "World";
      console.log(x, y);
    }
    
    log("Hello") // Hello World
    log("Hello", "China") // Hello China
    log("Hello", "") // Hello World
    

    上面代码检查函数log的参数y有没有赋值,如果没有,则指定默认值为World。这种写法的缺点在于,如果参数y赋值了,但是对应的布尔值为false,则该赋值不起作用。就像上面代码的最后一行,参数y等于空字符,结果被改为默认值。

    为了避免这个问题,通常需要先判断一下参数y是否被赋值,如果没有,再等于默认值。

    if (typeof y === "undefined") {
      y = "World";
    }
    

    ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

    function log(x, y = "World") {
      console.log(x, y);
    }
    
    log("Hello") // Hello World
    log("Hello", "China") // Hello China
    log("Hello", "") // Hello
    

    可以看到,ES6 的写法比 ES5 简洁许多,而且非常自然。

    除了简洁,ES6 的写法还有两个好处:首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码无法运行。

    参数变量是默认声明的,所以不能用let或const再次声明。

    function foo(x = 5) {
      let x = 1; // error
      const x = 2; // error
    }
    

    上面代码中,参数变量x是默认声明的,在函数体中,不能用let或const再次声明,否则会报错。

    使用参数默认值时,函数不能有同名参数。

    // 不报错
    function foo(x, x, y) {
      // ...
    }
    
    // 报错
    function foo(x, x, y = 1) {
      // ...
    }
    // SyntaxError: Duplicate parameter name not allowed in this context
    

    另外,一个容易忽略的地方是,参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。

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

    上面代码中,参数p的默认值是x + 1。这时,每次调用函数foo,都会重新计算x + 1,而不是默认p等于 100。

    参数默认值的位置

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

    // 例一
    function f(x = 1, y) {
      return [x, y];
    }
    
    f() // [1, undefined]
    f(2) // [2, undefined])
    f(, 1) // 报错
    f(undefined, 1) // [1, 1]
    
    // 例二
    function f(x, y = 5, z) {
      return [x, y, z];
    }
    
    f() // [undefined, 5, undefined]
    f(1) // [1, 5, undefined]
    f(1, ,2) // 报错
    f(1, undefined, 2) // [1, 5, 2]
    

    上面代码中,有默认值的参数都不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入undefined。

    如果传入undefined,将触发该参数等于默认值,null则没有这个效果。

    function foo(x = 5, y = 6) {
      console.log(x, y);
    }
    
    foo(undefined, null)
    // 5 null
    

    上面代码中,x参数对应undefined,结果触发了默认值,y参数等于null,就没有触发默认值。

    函数的 length 属性

    指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。

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

    上面代码中,length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。比如,上面最后一个函数,定义了3个参数,其中有一个参数c指定了默认值,因此length属性等于3减去1,最后得到2。

    这是因为length属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,后文的 rest 参数也不会计入length属性。

    (function(...args) {}).length // 0
    

    如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。

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

    作用域

    一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个多带带的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。

    var x = 1;
    
    function f(x, y = x) {
      console.log(y);
    }
    
    f(2) // 2
    

    上面代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个多带带的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2。

    再看下面的例子。

    let x = 1;
    
    function f(y = x) {
      let x = 2;
      console.log(y);
    }
    
    f() // 1
    

    上面代码中,函数f调用时,参数y = x形成一个多带带的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x影响不到默认值变量x。

    如果此时,全局变量x不存在,就会报错。

    function f(y = x) {
      let x = 2;
      console.log(y);
    }
    
    f() // ReferenceError: x is not defined
    

    下面这样写,也会报错。

    var x = 1;
    
    function foo(x = x) {
      // ...
    }
    
    foo() // ReferenceError: x is not defined
    

    上面代码中,参数x = x形成一个多带带作用域。实际执行的是let x = x,由于暂时性死区的原因,这行代码会报错”x 未定义“。

    4.2 rest 参数

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

    function add(...values) {
      let sum = 0;
    
      for (var val of values) {
        sum += val;
      }
    
      return sum;
    }
    
    add(2, 5, 3) // 10
    

    上面代码的add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数。

    下面是一个 rest 参数代替arguments变量的例子。

    // arguments变量的写法
    function sortNumbers() {
      return Array.prototype.slice.call(arguments).sort();
    }
    
    // rest参数的写法
    const sortNumbers = (...numbers) => numbers.sort();
    

    上面代码的两种写法,比较后可以发现,rest 参数的写法更自然也更简洁。

    rest 参数中的变量代表一个数组,所以数组特有的方法都可以用于这个变量。下面是一个利用 rest 参数改写数组push方法的例子。

    function push(array, ...items) {
      items.forEach(function(item) {
        array.push(item);
        console.log(item);
      });
    }
    
    var a = [];
    push(a, 1, 2, 3)
    

    注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

    // 报错
    function f(a, ...b, c) {
      // ...
    }
    

    函数的length属性,不包括 rest 参数。

    (function(a) {}).length  // 1
    (function(...a) {}).length  // 0
    (function(a, ...b) {}).length  // 1
    
    4.3 箭头函数

    基本用法

    ES6 允许使用“箭头”(=>)定义函数。

    var f = v => v;
    

    上面的箭头函数等同于:

    var f = function(v) {
      return v;
    };
    

    如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。

    var f = () => 5;
    // 等同于
    var f = function () { return 5 };
    
    var sum = (num1, num2) => num1 + num2;
    // 等同于
    var sum = function(num1, num2) {
      return num1 + num2;
    };
    

    如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

    var sum = (num1, num2) => { return num1 + num2; }
    

    由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

    // 报错
    let getTempItem = id => { id: id, name: "Temp" };
    
    // 不报错
    let getTempItem = id => ({ id: id, name: "Temp" });
    

    如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了。

    let fn = () => void doesNotReturn();
    

    箭头函数可以与变量解构结合使用。

    const full = ({ first, last }) => first + " " + last;
    
    // 等同于
    function full(person) {
      return person.first + " " + person.last;
    }
    

    箭头函数使得表达更加简洁。

    const isEven = n => n % 2 == 0;
    const square = n => n * n;
    

    上面代码只用了两行,就定义了两个简单的工具函数。如果不用箭头函数,可能就要占用多行,而且还不如现在这样写醒目。

    箭头函数的一个用处是简化回调函数。

    // 正常函数写法
    [1,2,3].map(function (x) {
      return x * x;
    });
    
    // 箭头函数写法
    [1,2,3].map(x => x * x);
    

    另一个例子是

    // 正常函数写法
    var result = values.sort(function (a, b) {
      return a - b;
    });
    
    // 箭头函数写法
    var result = values.sort((a, b) => a - b);
    下面是 rest 参数与箭头函数结合的例子。
    
    const numbers = (...nums) => nums;
    
    numbers(1, 2, 3, 4, 5)
    // [1,2,3,4,5]
    
    const headAndTail = (head, ...tail) => [head, tail];
    
    headAndTail(1, 2, 3, 4, 5)
    // [1,[2,3,4,5]]
    

    使用注意点

    箭头函数有几个使用注意点。

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

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

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

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

    上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。

    function foo() {
      setTimeout(() => {
        console.log("id:", this.id);
      }, 100);
    }
    
    var id = 21;
    
    foo.call({ id: 42 });
    // id: 42
    

    上面代码中,setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到100毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。

    箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域。下面是另一个例子。

    function Timer() {
      this.s1 = 0;
      this.s2 = 0;
      // 箭头函数
      setInterval(() => this.s1++, 1000);
      // 普通函数
      setInterval(function () {
        this.s2++;
      }, 1000);
    }
    
    var timer = new Timer();
    
    setTimeout(() => console.log("s1: ", timer.s1), 3100);
    setTimeout(() => console.log("s2: ", timer.s2), 3100);
    // s1: 3
    // s2: 0
    

    上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在的作用域(即全局对象)。所以,3100毫秒之后,timer.s1被更新了3次,而timer.s2一次都没更新。

    箭头函数可以让this指向固定化,这种特性很有利于封装回调函数。下面是一个例子,DOM 事件的回调函数封装在一个对象里面。

    var handler = {
      id: "123456",
    
      init: function() {
        document.addEventListener("click",
          event => this.doSomething(event.type), false);
      },
    
      doSomething: function(type) {
        console.log("Handling " + type  + " for " + this.id);
      }
    };
    

    上面代码的init方法中,使用了箭头函数,这导致这个箭头函数里面的this,总是指向handler对象。否则,回调函数运行时,this.doSomething这一行会报错,因为此时this指向document对象。

    this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

    所以,箭头函数转成 ES5 的代码如下。

    // ES6
    function foo() {
      setTimeout(() => {
        console.log("id:", this.id);
      }, 100);
    }
    
    // ES5
    function foo() {
      var _this = this;
    
      setTimeout(function () {
        console.log("id:", _this.id);
      }, 100);
    }
    

    上面代码中,转换后的ES5版本清楚地说明了,箭头函数里面根本没有自己的this,而是引用外层的this。

    除了this,arguments在箭头函数之中也是不存在的,它是指向外层函数的对应变量。

    function foo() {
      setTimeout(() => {
        console.log("args:", arguments);
      }, 100);
      setTimeout(function() {
        console.log("args:", arguments);
      }, 100);
    }
    
    foo(2, 4, 6, 8)
    // args: [2, 4, 6, 8]
    

    上面代码中,箭头函数内部的变量arguments,其实是函数foo的arguments变量。

    另外,由于箭头函数没有自己的this,所以当然也就不能用call()、apply()、bind()这些方法去改变this的指向。

    (function() {
      return [
        (() => this.x).bind({ x: "inner" })()
      ];
    }).call({ x: "outer" });
    // ["outer"]
    

    上面代码中,箭头函数没有自己的this,所以bind方法无效,内部的this指向外部的this。

    长期以来,JavaScript 语言的this对象一直是一个令人头痛的问题,在对象方法中使用this,必须非常小心。箭头函数”绑定”this,很大程度上解决了这个困扰。

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

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

相关文章

  • ES6学习笔记2—各扩展

    摘要:字符串的扩展字符的表示法允许采用形式表示一个字符,其中表示字符的码点。返回布尔值,表示参数字符串是否在源字符串的头部。使用和这两个常量,用来表示这个范围的上下限。对于那些无法用个二进制位精确表示的小数,方法返回最接近这个小数的单精度浮点数。 字符串的扩展 字符的 Unicode 表示法 JavaScript 允许采用uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码...

    Zoom 评论0 收藏0
  • ES6标准入门》读书笔记

    摘要:标准入门读书笔记和命令新增命令,用于声明变量,是块级作用域。用于头部补全,用于尾部补全。函数调用的时候会在内存形成一个调用记录,又称为调用帧,保存调用位置和内部变量等信息。等到执行结束再返回给,的调用帧才消失。 《ES6标准入门》读书笔记 @(StuRep) showImg(https://segmentfault.com/img/remote/1460000006766369?w=3...

    HollisChuang 评论0 收藏0
  • ES6

    摘要:情况一情况二这两种情况,根据的规定都是非法的。的作用域与命令相同只在声明所在的块级作用域内有效。因此,将一个对象声明为常量必须非常小心。顶层对象的属性与全局变量挂钩,被认为时语言最大的设计败笔之一。 这是ES6的入门篇教程的笔记,网址:链接描述,以下内容中粗体+斜体表示大标题,粗体是小标题,还有一些重点;斜体表示对于自身,还需要下功夫学习的内容。这里面有一些自己的见解,所以若是发现问题...

    AZmake 评论0 收藏0
  • 深入理解ES6(三)(符串扩展

    摘要:字符串的扩展一字符串的遍历器接口为字符串添加了遍历器接口,使得字符串可以被循环遍历。返回布尔值,表示参数字符串是否在源字符串的头部。三方法返回一个新字符串,表示将原字符串重复次。如果模板字符串中的变量没有声明,将报错。 字符串的扩展 一、 字符串的遍历器接口 ES6 为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历。 for (let codePoint of foo...

    Steve_Wang_ 评论0 收藏0
  • 深入理解ES6(三)(符串扩展

    摘要:字符串的扩展一字符串的遍历器接口为字符串添加了遍历器接口,使得字符串可以被循环遍历。返回布尔值,表示参数字符串是否在源字符串的头部。三方法返回一个新字符串,表示将原字符串重复次。如果模板字符串中的变量没有声明,将报错。 字符串的扩展 一、 字符串的遍历器接口 ES6 为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历。 for (let codePoint of foo...

    tainzhi 评论0 收藏0

发表评论

0条评论

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