前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >你真的了解JS的函数吗?

你真的了解JS的函数吗?

作者头像
Jou
发布2022-08-10 20:57:54
发布2022-08-10 20:57:54
80200
代码可运行
举报
文章被收录于专栏:前端技术归纳前端技术归纳
运行总次数:0
代码可运行

1.函数声明和函数表达式

我们先来看看常见形式

  • 函数声明: function 函数名称 (参数:可选){ 函数体 }
  • 函数表达式: function 函数名称(可选)(参数:可选){ 函数体 }

如果function foo(){}是作为赋值表达式的一部分的话,那它就是一个函数表达式

如果function foo(){}被包含在一个函数体内,或者位于程序的最顶部的话,那它就是一个函数声明。

比如

代码语言:javascript
代码运行次数:0
运行
复制
// 声明,因为它是程序的一部分
function foo(){} 

 //表达式,因为它是赋值表达式的一部分
var bar = function foo(){};

// 表达式,因为它是new表达式
new function bar(){}; 

(function(){
// 声明,因为它是函数体的一部分
function bar(){} 
})();

还有一种函数表达式不太常见,就是被括号括住的(function foo(){}),他是表达式的原因是因为括号 ()是一个分组操作符,它的内部只能包含表达式

差别
  • 函数声明存在着函数提升,函数表达式不会

2.立即执行函数

下面是我们常见的两种写法,因为JavaScript里括弧()里面不能包含语句,所以,解析器在解析function关键字的时候,会将相应的代码解析成function表达式,而不是function声明。

代码语言:javascript
代码运行次数:0
运行
复制
(function(){ 
    var a; 
    //code
}());

(function(){
    var a; 
    //code
})();

下面这种写法则不会被解析成立即执行函数

代码语言:javascript
代码运行次数:0
运行
复制
function(){
    var a; 
    //code
}(1);

它等价于,一个函数表达式和一个常量表达式

代码语言:javascript
代码运行次数:0
运行
复制
function(){
    var a; 
    //code
}

(1)

但是,括号有个缺点,那就是如果上一行代码不写分号,括号会被解释为上一行代码最末的函数调用,产生完全不符合预期,并且难以调试的行为,加号等运算符也有类似的问题。

所以一些推荐不加分号的代码风格规范,会要求在括号前面加上分号。

立即执行函数的另一种写法
代码语言:javascript
代码运行次数:0
运行
复制
void function () {
    var a = 100;
    console.log(a);

}();

语义上 void 运算表示忽略后面表达式的值,变成 undefined

3.this的指向

通常情况下
代码语言:javascript
代码运行次数:0
运行
复制
function showThis(){
    console.log(this);
}

var o = {
    showThis: showThis
}

showThis(); // global
o.showThis(); // o

调用函数时使用的引用,决定了函数执行时刻的 this 值。所以这里全局调用函数this指向global,使用对象调用函数,this指向对象

在strict模式下
代码语言:javascript
代码运行次数:0
运行
复制
"use strict"
function showThis(){
    console.log(this);
}

var o = {
    showThis: showThis
}

showThis(); // undefined
o.showThis(); // o

当严格模式时使用,this 严格按照调用时传入的值,可能为 null 或者 undefined。

箭头函数
代码语言:javascript
代码运行次数:0
运行
复制
const showThis = () => {
    console.log(this);
}

var o = {
    showThis: showThis
}

showThis(); // global
o.showThis(); // global

箭头函数的this只与定义时的作用域有关。

类的方法
代码语言:javascript
代码运行次数:0
运行
复制
class C {
    showThis() {
        console.log(this);
    }
}
var o = new C();
var showThis = o.showThis;

showThis(); // undefined
o.showThis(); // o

因为 class 设计成了默认按 strict 模式执行。

4.JavaScript 中,调用函数有哪几种方式?

  • 方法调用模式 Foo.foo(arg1, arg2);
  • 函数调用模式 foo(arg1, arg2);
  • 构造器调用模式 (new Foo())(arg1, arg2);
  • call/apply 调用模式 Foo.foo.call(that, arg1, arg2);
  • bind 调用模式 Foo.foo.bind(that)(arg1, arg2)();
1,call()

函数在执行的时候,实际上默认调用了call方法

代码语言:javascript
代码运行次数:0
运行
复制
test()  //   等价于test.call()

这是默认调用call方法。。但是当我给他加上参数。。它就完成一些很强大的功能。 函数调用call方法默认可以改变函数内部的this指向。 它的第一个参数,是this改变后指向的对象,后面的参数对应函数执行的参数。 举个例子:

代码语言:javascript
代码运行次数:0
运行
复制
//现在的this默认指向调用者
function Person(name){
  this.name=name;
}
//我们创建一个空对象
var person={};
//那么我们想让Person中的this指向person怎么办
//这样
Person.call(person,'jackson');
//最后打印出person
person={
  name:'jackson'
}

我们可以看到,调用了call后,实际上在Person执行的时候,this.name就变成了person.name。

2,apply()

apply的用法和call很像,它的第一个参数依旧是改变函数执行的时候的this指向,不同的是,函数执行的时候的各个形参,需要被放在一个数组里面,做为执行时候的第二个参数。

代码语言:javascript
代码运行次数:0
运行
复制
function Person(name,age){
    this.name=name;
    this.age=age;
}
var person={}
//第二个参数为数组
Person.apply(person,['Osborn',22])
3,bind()

bind的用法和前面两个都有所不同,它有延迟执行的特点,它返回一个新的函数。 bind()的第一个参数代表函数执行的this的指向,后面的参数可以用来执行函数执行时候的形参。

代码语言:javascript
代码运行次数:0
运行
复制
this.num = 9; 
var mymodule = {
  num: 81,
  getNum: function() { 
    console.log(this.num);
  }
};

mymodule.getNum(); // 81

var getNum = mymodule.getNum;
getNum(); // 9, 因为在这个例子中,"this"指向全局对象

var boundGetNum = getNum.bind(mymodule);
boundGetNum(); // 81

它还有一个很好的作用,就是预先指定参数, 只要将这些参数(如果有的话)作为bind()的参数写在this后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。

代码语言:javascript
代码运行次数:0
运行
复制
function list() {
    //这里相当于将函数参数放到一个数组中返回
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

// 预定义参数37
var leadingThirtysevenList = list.bind(undefined, 37);

var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
如果对一个函数进行多次 bind,那么上下文会是什么呢?
代码语言:javascript
代码运行次数:0
运行
复制
let a = {};
let fn = function () {
  console.log(this);
};
fn.bind().bind(a)(); // => ?

不管我们给函数 bind 几次,fn 中的 this 永远由第一次 bind 决定,所以结果永远是 window。

5.如何实现一个 new?

代码语言:javascript
代码运行次数:0
运行
复制
function _new(func, ...args) {
  let obj = Object.create(func.prototype); // 原型
  let res = func.apply(obj, args); // 初始化对象属性
  return res instanceof Object ? res : obj; // 返回值
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-11-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.函数声明和函数表达式
    • 差别
  • 2.立即执行函数
    • 立即执行函数的另一种写法
  • 3.this的指向
    • 通常情况下
    • 在strict模式下
    • 箭头函数
    • 类的方法
  • 4.JavaScript 中,调用函数有哪几种方式?
    • 1,call()
    • 2,apply()
    • 3,bind()
      • 如果对一个函数进行多次 bind,那么上下文会是什么呢?
  • 5.如何实现一个 new?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档