资讯专栏INFORMATION COLUMN

箭头函数你想知道的都在这里

xiaoqibTn / 1098人阅读

摘要:没有箭头函数没有自己的对象,这不一定是件坏事,因为箭头函数可以访问外围函数的对象那如果我们就是要访问箭头函数的参数呢你可以通过命名参数或者参数的形式访问参数不能通过关键字调用函数有两个内部方法和。

1、基本语法回顾

我们先来回顾下箭头函数的基本语法。
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;
};

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

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

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

下面是一种特殊情况,虽然可以运行,但会得到错误的结果。

let foo = () => { a: 1 };
foo() // undefined

上面代码中,原始意图是返回一个对象{ a: 1 },但是由于引擎认为大括号是代码块,所以执行了一行语句a: 1。这时,a可以被解释为语句的标签,因此实际执行的语句是1;,然后函数就结束了,没有返回值。

关于作用域

箭头函数内定义的变量及其作用域

// 常规写法
var greeting = () => {let now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting();          //"Good day."
console.log(now);    // ReferenceError: now is not defined 标准的let作用域

// 参数括号内定义的变量是局部变量(默认参数)
var greeting = (now=new Date()) => "Good" + (now.getHours() > 17 ? " evening." : " day.");
greeting();          //"Good day."
console.log(now);    // ReferenceError: now is not defined

// 对比:函数体内{}不使用var定义的变量是全局变量
var greeting = () => {now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting();           //"Good day."
console.log(now);     // Fri Dec 22 2017 10:01:00 GMT+0800 (中国标准时间)

// 对比:函数体内{} 用var定义的变量是局部变量
var greeting = () => {var now = new Date(); return ("Good" + ((now.getHours() > 17) ? " evening." : " day."));}
greeting(); //"Good day."
console.log(now);    // ReferenceError: now is not defined
2、关于this 2.1、默认绑定外层this

箭头函数没有 this,所以需要通过查找作用域链来确定 this 的值。
这就意味着如果箭头函数被非箭头函数包含,this 绑定的就是最近一层非箭头函数的 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,绑定定义时所在的作用域,而不是指向运行时所在的作用域。

所以,箭头函数转成 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);
}
2.2、 不能用call()、apply()、bind()方法修改里面的this
(function() {
  return [
    (() => this.x).bind({ x: "inner" })() // 无效的bind,最终this还是指向外层
  ];
}).call({ x: "outer" });
// ["outer"]

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

3、没有 arguments

箭头函数没有自己的 arguments 对象,这不一定是件坏事,因为箭头函数可以访问外围函数的 arguments 对象:

function constant() {
    return () => arguments[0]
}

var result = constant(1);
console.log(result()); // 1

那如果我们就是要访问箭头函数的参数呢?

你可以通过命名参数或者 rest 参数的形式访问参数:

let nums = (...nums) => nums;
4、 不能通过 new 关键字调用

JavaScript 函数有两个内部方法:[[Call]][[Construct]]

当通过new调用函数时,执行[Construct]]方法,创建一个实例对象,然后再执行函数体,将 this 绑定到实例上。

当直接调用的时候,执行[[Call]]方法,直接执行函数体。

箭头函数并没有[[Construct]]方法,不能被用作构造函数,如果通过 new 的方式调用,会报错。

var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
5、没有原型

由于不能使用new调用箭头函数,所以也没有构建原型的需求,于是箭头函数也不存在prototype这个属性。

var Foo = () => {};
console.log(Foo.prototype); // undefined
5、不适用场合

第一个场合是定义函数的方法,且该方法内部包括this。

const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。
第二个场合是需要动态this的时候,也不应使用箭头函数。

var button = document.getElementById("press");
button.addEventListener("click", () => {
  this.classList.toggle("on");
});

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

6、使用场景

下面这个是我们开发经常遇到的。我们一般会通过this赋值给一个变量,然后再通过变量访问。

class Test {
    constructor() {
        this.birth = 10;  
    }
    submit(){
        let self = this;
        $.ajax({          
            type: "POST",
            dataType: "json",
            url: "xxxxx" ,//url
            data: "xxxxx",
            success: function (result) {
                console.log(self.birth);//10
            },
            error : function() {}                   
        });
    }
}
let test = new Test();
test.submit();//undefined 

这里我们就可以通过箭头函数来解决

...
success: (result)=> {
    console.log(this.birth);//10
},
...

箭头函数在react中的运用场景

class Foo extends Component {
  constructor(props) {
    super(props);
  }
  handleClick() {
    console.log("Click happened", this);
    this.setState({a: 1});
  }
  render() {
    return ;
  }
}

在react中我们这样直接调用方法是有问题的,在handleClick函数中的this是有问题,我们平时需要这么做

class Foo extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log("Click happened", this);
    this.setState({a: 1});
  }
  render() {
    return ;
  }
}

这里通过this.handleClick.bind(this)给函数绑定this。但是这样写起来有些麻烦,有没有简单的方法呢?这时候我们的箭头函数就出场了

class Foo extends Component {
  // Note: this syntax is experimental and not standardized yet.
  handleClick = () => {
    console.log("Click happened", this);
    this.setState({a: 1});
  }
  render() {
    return ;
  }
}

箭头函数中 this 的值是继承自 外围作用域,很好的解决了这个问题。
除此之外我们还可以用箭头函数传参(这个不是必须的),而且会有性能问题。更多信息请查看

const A = 65 // ASCII character code

class Alphabet extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.state = {
      justClicked: null,
      letters: Array.from({length: 26}, (_, i) => String.fromCharCode(A + i)).
    };
  }
  handleClick(letter) {
    this.setState({ justClicked: letter });
  }
  render() {
    return (
      
Just clicked: {this.state.justClicked}
    {this.state.letters.map(letter =>
  • this.handleClick(letter)}> {letter}
  • )}
) } }

最后
更多系列文章请看

ES6学习(一)之var、let、const

ES6学习(二)之解构赋值及其原理

ES6学习(三)之Set的模拟实现

ES6学习(四)之Promise的模拟实现

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star对作者也是一种鼓励。

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

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

相关文章

  • 关于 this 你想知道的一切都在这里

    摘要:如下在第一个例子中,被点击元素是通过,这个形式参数来代替的。它的作用和形式参数类似,其本质上是一个对象的引用,它的特殊性在于不需要手动传值,所以使用起来会更加简单和方便。 无论在 javascript 的日常使用中还是前端面试过程中,this 的出镜率都极高。这无疑说明了,this 的重要性。但是 this 非常灵活,导致很多人觉得 this 的行为难以理解。本文从为什么要有 this...

    Lemon_95 评论0 收藏0
  • 零基础 | 入行软件测试,你想知道都在这里

    摘要:应届生零基础可以学习软件测试吗俗话说,人怕入错行。霸哥这里分别从入行难度入行方式行业前景薪资待遇四个方面来分析一下。目前市场上的测试人员,一部分是企业自己培养的,另一部分是来自培训机构。软件测试的行业门槛低,市场需求量大。 ...

    neroneroffy 评论0 收藏0
  • 阿里云云上年中钜惠来啦!你想知道都在这里

    摘要:七月流火,燃情盛夏值此季节,阿里云又推出了年中钜惠,精选百款产品,助力创业新势力。阿里云云盾安全防护体系,不仅合规,更加安全。七月流火,燃情盛夏!值此季节,阿里云又推出了年中钜惠,精选百款产品,助力创业新势力。从7月26日开始,每天上午10点、下午4点将会放出爆款产品,进行限量秒杀,大家不要错过。注册登陆还可抽取 iPhone 12 Pro、Cherry 机械键盘、企业电子书大礼包等好礼。 ...

    TesterHome 评论0 收藏0
  • 浅谈箭头函数

    摘要:来看看箭头函数的情况可以发现在定时器这个匿名函数作用域里,所指向的对象,就是定义时所指的对象。对于构造函数来说,它就是的实例。如下据此,总结箭头函数的三个优点语法简洁,能够写出漂亮的单行函数。 ES6新引入了箭头函数,它是一种表达简洁的函数,下面我们来看看它有哪三个显著优势。假设定义了一个名为names的数组,如下: showImg(https://segmentfault.com/i...

    NoraXie 评论0 收藏0
  • 关于区块链通证模型,你想知道都在

    摘要:在以太坊出现后,进入了第二阶段。以太坊可以被视作区块链世界类似于和的底层操作系统。通证经济的设计方向模式的组织,是天然的生态型组织。区块链时代的生态组织,大致可以分成这几种类型。 简介   区块链最重要的应用就是将实物价值或虚拟资产映射成链上Token,通过资产上链,实现跨地域、低成本的进行资产交易与转移,本质上是权益再分配,核心是提高激励性和效益。  很多人把Token译为代币,我更...

    huangjinnan 评论0 收藏0

发表评论

0条评论

xiaoqibTn

|高级讲师

TA的文章

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