目录
执行环境" >执行环境
一个新话题" >一个新话题
新的语法" >新的语法
箭头函数是怎么实现的
" >箭头函数是怎么实现的
立即执行函数(IIFE)" >立即执行函数(IIFE)
相关思考" >相关思考
作用域" >作用域
有趣和有用的使用" >有趣和有用的使用
推荐的链接" >推荐的链接
总结" >总结
首页 web前端 js教程 深入浅析ES6中的箭头函数及其作用域

深入浅析ES6中的箭头函数及其作用域

Oct 19, 2020 pm 06:02 PM
es6 javascript 作用域 箭头函数

深入浅析ES6中的箭头函数及其作用域

在ES6很多很棒的新特性中, 箭头函数 (或者大箭头函数)就是其中值得关注的一个! 它不仅仅是很棒很酷, 它很好的利用了作用域, 快捷方便的在现在使用以前我们用的技术, 减少了很多代码......但是如果你不了解箭头函数原理的话可能就有点难以理解. 所以,让我们来看下箭头函数, 就是现在!

执行环境

你可以自己去学习和尝试下, 你可以简单的把示例程序代码复制到你的浏览器控制台下. 现在, 推荐使用Firefox(22+)开发者工具, Firefox(22+)开发者工具现在支持箭头函数,你也可以使用谷歌浏览器. 如果你使用谷歌浏览器, 你必须要做下列两件事:

  • 在谷歌浏览器中地址栏中输入:about:flags, 找到 "使用体验性JavaScript"选项,开启使用。

  • 在函数的开头加上use strict,然后再在你的谷歌浏览中测试箭头函数吧(提示:请用谷歌浏览器v38,我当时就是被浏览器版本坑了):

(function(){
    "use strict";
    // use arrow functions here
}());
登录后复制

幸运的是后面会有越来越多的浏览器支持ES6特性. 现在你完成了所有准备工作, 让我们继续深入它吧!

一个新话题

最近大家在讨论关于ES6的一个话题:关于箭头函数, 像这样:

=>
登录后复制

新的语法

随着讨论产生了一个新的语法:

param => expression
登录后复制

新增的语法是作用在变量上, 可以在表达式中申明多个变量, 下面是箭头函数的使用模式:

//  一个参数对应一个表达式
param => expression;// 例如 x => x+2;

// 多个参数对应一个表达式
(param [, param]) => expression; //例如 (x,y) => (x + y);

// 一个参数对应多个表示式
param => {statements;} //例如 x = > { x++; return x;};

//  多个参数对应多个表达式
([param] [, param]) => {statements} // 例如 (x,y) => { x++;y++;return x*y;};

//表达式里没有参数
() => expression; //例如var flag = (() => 2)(); flag等于2
() => {statements;} //例如 var flag = (() => {return 1;})(); flag就等于1

//传入一个表达式,返回一个对象
([param]) => ({ key: value });
//例如  var fuc = (x) => ({key:x})
        var object = fuc(1);
        alert(object);//{key:1}
登录后复制

箭头函数是怎么实现的

我们可以把一个普通函数转换成用箭头函数来实现:

// 当前函数
var func = function (param) {
    return param.split(" ");
}
// 利用箭头函数实现
var func = param => param.split(" ");
登录后复制

从上面的例子中我们可以看出箭头函数的语法实际上是返回了一个新的函数, 这个函数有函数体和参数。

因此, 我们可以这样调用刚才我们创建的函数:

func("Felipe Moura"); // returns ["Felipe", "Moura"]
登录后复制

立即执行函数(IIFE)

你能在立即执行函数里使用箭头函数,例如:

( x => x * 2 )( 3 ); // 6
登录后复制

这行代码产生了一个临时函数,这个函数有一个形参x,函数的返回值为x*2,之后系统会马上执行这个临时函数, 将3赋值给形参x.

下面的例子描述了临时函数体里有多行代码的情况:

( (x, y) => {
    x = x * 2;
    return x + y;
})( 3, "A" ); // "6A"
登录后复制

相关思考

思考下面的函数:

var func = x => {
    return x++;
};
登录后复制

我们列出了一些常见的问题:

箭头函数创建的临时函数的arguments它不会被置:

console.log(arguments); // not defined
登录后复制

typeofinstanceof函数也能正常检查临时函数:

func instanceof Function; // true
typeof func; // function
func.constructor == Function; // true
登录后复制

把箭头函数放在括号内是无效的:

//  有效的常规语法
(function (x, y){
    x= x * 2;
    return x + y;
} (3, "B") );
// 无效的箭头函数语法
( (x, y) => {
    x= x * 2;
    return x + y;
} ( 3, "A" ) );
// 但是可以这样写就是有效的了:
( (x,y) => {
    x= x * 2;return x + y;
} )( 3,"A" );//立即执行函数
登录后复制

尽管箭头函数会产生一个临时函数,但是这个临时函数不是一个构造函数:

var instance= new func(); // TypeError: func is not a constructor
登录后复制

同样也没有原型对象:

func.prototype; // undefined
登录后复制

作用域

这个箭头函数的作用域和其他函数有一些不同,如果不是严格模式,this关键字就是指向window,严格模式就是undefined,在构造函数里的this指向的是当前对象实例,如果this在一个对象的函数内则this指向的是这个对象,this有可能指向的是一个DOM元素,例如当我们添加事件监听函数时,可能这个this的指向不是很直接,其实this(不止是this变量)变量的指向是根据一个规则来判断的:作用域流。下面我将演示this在事件监听函数和在对象函数内出现的情况:

在事件监听函数中:

document.body.addEventListener('click', function(evt){
    console.log(this); // the HTMLBodyElement itself
});
登录后复制

在构造函数里:

function Person () {
    let fullName = null;
    this.getName = function () {
        return fullName;
    };
    this.setName = function (name) {
        fullName = name;
        return this;
    };
}
let jon = new Person();
jon.setName("Jon Doe");
console.log(jon.getName()); // "Jon Doe"
//注:this关键字这里就不解释了,大家自己google,baidu吧。
登录后复制

在这个例子中,如果我们让Person.setName函数返回Person对象本身,我们就可以这样用:

jon.setName("Jon Doe")
.getName(); // "Jon Doe"
登录后复制

在一个对象里:

let obj = {
    foo: "bar",
    getIt: function () {
        return this.foo;
    }
};
console.log( obj.getIt() ); // "bar"
登录后复制

但是当执行流(比如使用了setTimeout)和作用域变了的时候,this也会变。

function Student(data){
    this.name = data.name || "Jon Doe";
    this.age = data.age>=0 ? data.age : -1;
    this.getInfo = function () {
        return this.name + ", " + this.age;
    };
    this.sayHi = function () {
        window.setTimeout( function () {
            console.log( this );
        }, 100 );
    }
}

let mary = new Student({
    name: "Mary Lou",
    age: 13
});
console.log( mary.getInfo() ); // "Mary Lou, 13"
mary.sayHi();
// window
登录后复制

当setTimeout函数改变了执行流的情况时,this的指向会变成全局对象,或者是在严格模式下就是undefine,这样在setTimeout函数里面我们使用其他的变量去指向this对象,比如self,that,当然不管你用什么变量,你首先应该在setTimeout访问之前,给self,that赋值,或者使用bind方法不然这些变量就是undefined。

这是后就是箭头函数登场的时候了,它可以保持作用域,this的指向就不会变了。

让我们看下上文起先的例子,在这里我们使用箭头函数:

function Student(data){
    this.name = data.name || "Jon Doe";
    this.age = data.age>=0 ? data.age : -1;
    this.getInfo = function () {
        return this.name + ", " + this.age;
    };
    this.sayHi = function () {
        window.setTimeout( ()=>{
            // the only difference is here
            console.log( this );
        }, 100 );
    }
}

let mary = new Student({
    name: "Mary Lou",
    age: 13
});
console.log( mary.getInfo() ); // "Mary Lou, 13"
mary.sayHi();
// Object { name: "Mary Lou", age: 13, ... }
登录后复制

分析:在sayHi函数中,我们使用了箭头函数,当前作用域是在student对象的一个方法中,箭头函数生成的临时函数的作用域也就是student对象的sayHi函数的作用域。所以即使我们在setTimeout调用了箭头函数生成的临时函数,这个临时函数中的this也是正确的指向。

有趣和有用的使用

创建一个函数很容易,我们可以利用它可以保持作用域的特征:

例如我们可以这么使用:Array.forEach()

var arr = ['a', 'e', 'i', 'o', 'u'];
arr.forEach(vowel => {
    console.log(vowel);
});
登录后复制

分析:在forEach里箭头函数会创建并返回一个临时函数 tempFun,这个tempFun你可以想象成这样的:function(vowel){ console.log(vowel);}但是Array.forEach函数会怎么去处理传入的tempFunc呢?在forEach函数里会这样调用它:tempFunc.call(this,value);所有我们看到函数的正确执行效果。

//在Array.map里使用箭头函数,这里我就不分析函数执行过程了。。。。

var arr = ['a', 'e', 'i', 'o', 'u'];
arr.map(vowel => {
    return vowel.toUpperCase();
});
// [ "A", "E", "I", "O", "U" ]
登录后复制

费布拉奇数列

var factorial = (n) => {
    if(n==0) {
        return 1;
    }
    return (n * factorial (n-1) );
}
factorial(6); // 720
登录后复制

我们也可以用在Array.sort方法里:

let arr = ['a', 'e', 'i', 'o', 'u'];
arr.sort( (a, b)=> a < b? 1: -1 );
登录后复制

也可以在事件监听函数里使用:

// EventObject, BodyElement
document.body.addEventListener('click', event=>console.log(event, this));
登录后复制

推荐的链接

下面列出了一系列有用的链接,大家可以去看一看

总结

尽管大家可能会认为使用箭头函数会降低你代码的可读性,但是由于它对作用域的特殊处理,它能让我们能很好的处理this的指向问题。箭头函数加上let关键字的使用,将会让我们JavaScript代码上一个层次!尽量多使用箭头函数,你可以再你的浏览器测试你写的箭头函数代码,大家可以再评论区留下你对箭头函数的想法和使用方案!我希望大家能享受这篇文章,就像你会不就的将来享受箭头函数带给你的快乐.

相关免费学习推荐:js视频教程

以上是深入浅析ES6中的箭头函数及其作用域的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

c语言中typedef struct的用法 c语言中typedef struct的用法 May 09, 2024 am 10:15 AM

typedef struct 在 C 语言中用于创建结构体类型别名,简化结构体使用。它通过指定结构体别名将一个新的数据类型作为现有结构体的别名。优点包括增强可读性、代码重用和类型检查。注意:在使用别名前必须定义结构体,别名在程序中必须唯一并且仅在其声明的作用域内有效。

java中的variable expected怎么解决 java中的variable expected怎么解决 May 07, 2024 am 02:48 AM

Java 中的变量期望值异常可以通过以下方法解决:初始化变量;使用默认值;使用 null 值;使用检查和赋值;了解局部变量的作用域。

js中闭包的优缺点 js中闭包的优缺点 May 10, 2024 am 04:39 AM

JavaScript 闭包的优点包括保持变量作用域、实现模块化代码、延迟执行和事件处理;缺点包括内存泄漏、增加了复杂性、性能开销和作用域链影响。

c++中的include什么意思 c++中的include什么意思 May 09, 2024 am 01:45 AM

C++ 中的 #include 预处理器指令将外部源文件的内容插入到当前源文件中,以复制其内容到当前源文件的相应位置。主要用于包含头文件,这些头文件包含代码中需要的声明,例如 #include <iostream> 是包含标准输入/输出函数。

C++ 智能指针:全面剖析其生命周期 C++ 智能指针:全面剖析其生命周期 May 09, 2024 am 11:06 AM

C++智能指针的生命周期:创建:分配内存时创建智能指针。所有权转移:通过移动操作转移所有权。释放:智能指针离开作用域或被明确释放时释放内存。对象销毁:所指向对象被销毁时,智能指针成为无效指针。

c++中函数的定义和调用可以嵌套吗 c++中函数的定义和调用可以嵌套吗 May 06, 2024 pm 06:36 PM

可以。C++ 允许函数嵌套定义和调用。外部函数可定义内置函数,内部函数可在作用域内直接调用。嵌套函数增强了封装性、可复用性和作用域控制。但内部函数无法直接访问外部函数的局部变量,且返回值类型需与外部函数声明一致,内部函数不能自递归。

js中this的指向有几种情况 js中this的指向有几种情况 May 06, 2024 pm 02:03 PM

JavaScript 中,this 的指向类型有:1. 全局对象;2. 函数调用;3. 构造函数调用;4. 事件处理程序;5. 箭头函数(继承外层 this)。此外,可以使用 bind()、call() 和 apply() 方法显式设置 this 的指向。

vue中let和var的区别 vue中let和var的区别 May 08, 2024 pm 04:21 PM

在 Vue 中,let 和 var 声明变量时在作用域上存在差异:作用域:var 具有全局作用域,let 具有块级作用域。块级作用域:var 不创建块级作用域,let 创建块级作用域。重新声明:var 允许在同一作用域内重新声明变量,let 不允许。

See all articles