前端基础(三):函数

简介: 在最新的ES规范中,声明函数有4中方法:-函数声明-函数表达式-构造函数Function-生成器函数

函数定义

在最新的ES规范中,声明函数有4中方法:

-函数声明

-函数表达式

-构造函数Function

-生成器函数

1.函数声明

语法:


function name([param[, param2 [...]]]){

	[statements]

}

name:函数名称

param:需要传递给函数的参数的名称。有最大参数数量限制,不同引擎限制不同。

statements:包含函数体的语句

函数的最基本用法,注意会有声明提升机制。

2.函数表达式

语法:

var name = function(param [,param [...]]){

        [statements]

}

或

let name = function(param [,param [...]]){

        [statements]

}

用法同函数声明,使用let时,是块级作用域。

如下代码,我们就能看出函数声明与函数表达式的差异:


testFun(); //testFun
function testFun () {
    console.log('testFun');
}

console.log(typeof testFun1); //undefined
var testFun1 = function () {
    console.log('testFun1');
};

以第一个是函数申明提升,所以我们可以在声明之前使用函数。第二个是var变量声明提升,在声明之前它的值是undefined,所以无法直接使用。

3.Funcion构造函数

语法:


new Function([arg1 [ ,arg2 [,arg3 [...]]] ,] functionBody);

参数:


arg1, arg2, ... argN


​ 函数使用的参数列表


functionBody


​ 一个包含函数定义的语句字符串

JS中,所有的函数都是Function构造函数的实例。Object内置对象实质也是一个函数,所以,Object也是继承自Function的。而且,Function本身也是一个函数,因此,它自己也继承自己。

Function.prototype === Function.__proto__;	//true

使用这种方法创建函数有以下三点缺点:

1.使用Function创建的函数只有在运行到 new Function()语句时,才会解析函数。上面两种方式创建的函数,是与JS代码一起解析的。所以,使用Function函数创建的函数效率更低。

2.函数都是在全局作用域中创建的,无法使用创建时的上下文作用域。


let msg = 'testFun3 global';
function testFun3 () {
    let msg = 'testFun3 inner';
    let testFun4 = new Function('console.log(msg);');
    testFun4();
}
testFun3(); //testFun3 global

3.代码书写在字符串中,不利于维护。

所以,尽量不要使用该方式定义函数。

4.function*

语法:

function* name([param[, param[, ... param]]]) { statements }

参数同 1

定义一个生成器函数。

基础用法:


function * generator (i) {
    yield i;
    yield i + 1;
    let y = yield i + 2;
    yield y;
}
let gen1 = generator(0);
console.log(gen1.next()); //{value: 0, done: false}
console.log(gen1.next()); //{value: 1, done: false}
console.log(gen1.next()); //{value: 2, done: false}
console.log(gen1.next(100)); //{value: 100, done: false}
console.log(gen1.next()); //{value: undefined, done: true}

调用next函数时,传入的值是赋予上一个yield的返回值。

有浏览器兼容性问题,IE所有版本都不支持该用法。

函数调用

###直接调用

语法:


funName([arg1 [,arg2 , [arg3 [...]]]]);

最常用方式,不予赘述。

apply

语法:

func.apply(thisArg [ ,argsArray])

参数:

thisArg

​ 可选参数,将参数作为函数执行的this值使用。

​ 如果处于非严格模式下,传值为null或者undefined时,就将全局对象绑定到函数的this上。如果传值为原始值(数字、字符串、布尔),则会将this指向原始值的包装对象(Number、String、Boolean)

argsArray

​ 可选参数,传入一个数组或者类数组的值,作为函数的参数列表。

一般情况下,我们直接调用函数即可,在一些特殊的应用场景下,我们才会需要用到apply函数。例:


let Obj = {
    name: 'obj',
    print: function () {
        console.log(this.name);
    }
};
function success (callback) {
    this.name = 'success obj';
    callback(); 	//success obj
    callback.apply(Obj); 	//obj
}
success(Obj.print);

如上,如果我们将函数作为参数传递时,为了保证this的指向,就可能需要使用apply方法。

call


fun.call(thisArg, arg1, arg2, ...)

用法同apply,只是参数数组改为了参数列表。

作为构造函数调用


new constructor[([args])]

参数:

constructor

​ 一个类或者函数。

args

​ 类或者函数的参数列表,new constructor 等同于 new constructor().

当使用new Foo(...)来调用一个函数时,其实发生了如下三件事。

①创建一个继承自Foo.prototype的新对象。

②调用Foo函数,并将新建的对象作为this绑定到当前函数的执行上下文中。

③如果函数有返回值,就返回该值;否则,直接返回第一步创建的对象。

举个栗子:


function Foo (name) {
    this.name = name;
}

var foo1 = new Foo('Tom');
var foo2 = Object.create(Foo.prototype);
Foo.call(foo2, 'Tom');
console.log(foo1 instanceof Foo); //true
console.log(foo2 instanceof Foo); //true

对象方法调用

语法:

obj.foo([args])


var pFoo = obj.foo;
pFoo([args]);

这种方式就是对象的属性值为函数的时候,我们调用函数的情况。

用法基本和直接调用一致,其实通过第一种方式调用,实质就是在全局对象上声明了一个函数变量,然后通过全局对象来调用这个函数,所以他们的表象和使用方法类似。

但是这里,我们需要注意一点,就是我们通过对象属性调用和声明一个变量来获取函数值而后调用有使用上的区别。

举个栗子:


var obj = {
    foo: function (name) {
        this.name = name;
        console.log(this);
    }
};

obj.foo('Tom'); //obj
var pFoo = obj.foo;
pFoo(); //window

如上栗子,直接调用,this指向的是 obj 对象,但如果通过变量赋值后,this指向的是当前函数调用语句的上下文环境,这里我是浏览器环境,并且在顶层作用域中执行,所以this指向的是window对象。

总结

函数定义有函数声明、函数表达式、Function构造函数、function*四种方式。前面两种最常用,但是要注意this的指向问题(由于函数声明的作用域问题,我建议尽量使用函数表达式)。Function构造函数官方推荐不要使用,funtion*生成器函数是ES2015出现的新特性,使用时需要注意兼容性。

函数调用有函数直接调用、apply、call、作为构造函数调用、对象方法调用。他们各有针对的使用场景,需要注意的是对象方法调用中的this指向问题。

原文发布时间为:2018年06月19日
原文作者:老司机带你撸代码

本文来源:开源中国 如需转载请联系原作者

 









目录
相关文章
|
3月前
|
前端开发 JavaScript 开发者
揭秘JavaScript魔法三剑客:call、apply、bind,解锁函数新世界,你的前端之路因它们而精彩!
【8月更文挑战第23天】在 JavaScript 的世界里,`call`、`apply` 和 `bind` 这三个方法常常让新手感到困惑。它们都能改变函数执行时的上下文(即 `this` 的指向),但各有特点:`call` 接受一系列参数并直接调用函数;`apply` 则接收一个参数数组,在处理不确定数量的参数时特别有用;而 `bind` 不会立即执行函数,而是创建一个新版本的函数,其 `this` 上下文已被永久绑定。理解这三个方法能帮助开发者更好地运用函数式编程技巧,提升代码灵活性和可维护性。
38 0
|
1月前
|
前端开发 JavaScript
前端:实现一个 sleep 函数
在前端开发中,实现一个 `sleep` 函数可以用来暂停代码执行,模拟延迟效果,常用于测试或控制异步操作的节奏。该函数通常基于 `Promise` 和 `setTimeout` 实现,简单易用。
|
2月前
|
存储 前端开发 JavaScript
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
本文介绍了JavaScript中作用域的概念,包括全局变量和局部变量的区别,预解析机制(变量提升),以及函数返回值的使用和类型。通过具体示例讲解了变量的作用域、函数的返回值、以及如何通过return关键字从函数中返回数据。
23 1
前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值
|
2月前
|
存储 前端开发 JavaScript
前端基础(十一)_函数声明及调用、函数的形参与实参、arguments参数、函数的参数类型、函数中的问题
本文介绍了JavaScript中函数的声明及调用、形参与实参的概念、arguments对象的使用、函数参数的类型以及函数中this的作用。通过示例代码详细解释了函数如何接收参数、如何处理参数个数不匹配的情况,以及函数在不同上下文中this的指向。
24 1
|
3月前
|
JSON 前端开发 API
构建前端防腐策略问题之更新getMemoryUsagePercent函数以适应新的API返回格式的问题如何解决
构建前端防腐策略问题之更新getMemoryUsagePercent函数以适应新的API返回格式的问题如何解决
构建前端防腐策略问题之更新getMemoryUsagePercent函数以适应新的API返回格式的问题如何解决
|
2月前
|
JavaScript 前端开发
前端JS函数
【9月更文挑战第4天】前端JS函数
28 6
|
4月前
|
开发框架 前端开发 JavaScript
循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中
循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中
|
4月前
|
开发框架 JSON 前端开发
循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数
循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数
|
4月前
|
JavaScript 前端开发
前端框架原理自测题:根据 JSX / Vue 模板写出 render 函数 / VNode
前端框架原理自测题:根据 JSX / Vue 模板写出 render 函数 / VNode
27 0
|
4月前
|
前端开发 JavaScript
前端 JS 经典:箭头函数的意义
前端 JS 经典:箭头函数的意义
42 0
下一篇
无影云桌面