一个前端开发者的救赎之路-JS基础回顾(三)-Function函数
函数的声明
- 赋值式:var func = function() {}
- 声明式:function func() {}
- 箭头函数:()=>{}
- 三者之间的区别
a. 声明式可以在函数声明前去调用,赋值式不可以
b. 箭头函数,他们从定义自己的环境继承上下文,而不是像以其他方式定义的函数那样定义自己的调用上下文
c. 箭头函数没有prototype属性,所以它不能作为新类的构造函数
函数的调用
- 作为函数:
- 注意这个说法,函数是通过调用表达式被作为函数或方法调用的。
- 调用表达式包括求值为函数对象的函数表达式,后跟一对圆括号,圆括号中是逗号分隔的零或多个参数表达式列表
- this指向:
- 非严格模式:全局对象
- 严格模式:undefined
-
注意
: 箭头函数又有不同:它们总是继承自身定义所在环境的this值(这里即使是严格模式,this也是window) - 下面的代码可以用来判断是不是处于严格模式
// 定义并调用函数,以确定当前是不是严格模式 const strict = (function() { return !this }())
- 我有一篇文章,这里面有一个自己遇到的有趣的this指向和作用域的问题
- 这里注意条件式调用:
- 在Es2020中,可以在函数表达式的后面、圆括号的前面插入
?.
,从而只在函数不是null或undefined的情况下调用。 - 在没有副作用的前提下:f?.()<=>(f !== null && f !== undefined ? f() : undefined)
- 在Es2020中,可以在函数表达式的后面、圆括号的前面插入
- 作为方法:
-
this关键字不具有变量那样的作用域机制,除了箭头函数,嵌套函数不会继承包含函数的this值。如果嵌套函数被当做方法来用,那它的this就是调用它的对象。如果嵌套函数(不是箭头函数)被当做函数来调用,则它的this值要么是全局对象(非严格模式),要么是undefined(严格模式)
let o = { a: 1, b: 2, m: function() { let self = this; console.log(this.a); // 1 f(); function f() { console.log(this.b); // undefined } const g = () => { console.log(this.b); // 2 } g(); } } o.m();
-
- 作为构造函数
- 作为构造函数调用,上下文是这个新对象
- 通过call()或apply()方法调用
- 这两个方法允许我们指定调用时的this,这意味着可以将任意函数作为任意对象的方法来调用
- call()方法使用自己的参数列表作为函数的参数,而apply()方法则期待数组值作为参数。
- 通过JavaScript语言隐式调用
- 这里在开发中排查bug,很关键,就是我们引用了别人的三方库或者代码都有可能出现这个隐式调用
函数的参数
- 函数调用时所传的实参,可以少于形参也可以多于形参,
- 函数定义式可以用一个剩余参数来接收多余的参数,
剩余参数必须作为最后一个参数
,并且是...args
这种形式,剩余参数永远是一个数组,不会是undefined,即使没有传对应的实参,因此不需要给剩余参数默认值
JS预解析
- 对于函数的预解析和普通变量不一样,函数预解析是直接把整个函数提到顶部作用域,在预解析时会提前定义,只是不会立即执行。
- 普通变量只是把声明提升到所在作用域顶部,而不进行初始化。
- 因此,上面函数定义的时候,如果使用字面量的方式会把这个变量提升到顶部作用域并赋值undefined,所以在定义前调用的时候会报错。
-
let
和const
会提升(hoisting),但由于 TDZ(暂时性死区) 机制,在声明前访问会报错。
作用域
- 作用域在定义的时候,一个函数就会形成一个内部作用域,后来引入了let/const又有了块级作用域
- 作用域在访问的时候,是从下往上找,一直找到顶级作用域,找不到就报is not defined
- 在赋值的时候不是,如果一直到最顶级都未声明,那他就是直接在全局定义域声明并且赋值这个变量。所以即使在vue中的某一个函数直接使用xxx=来进行操作,也是会在全局作用域添加一个全局变量xxx(
内存泄漏
),千万不要这样干
函数的方法和属性
- length属性:返回声明时声明的形参的个数(不包含剩余参数),只读
- name属性:表示定义函数时使用的名字(如果是用名字定义的),如果是未命名的函数,表示在第一次创建这个函数时赋给该函数的变量名或属性名。这个属性主要用于记录调试或排错信息。只读
- prototype属性:箭头函数没有
- call()和apply()方法:
箭头函数的this不会被修改
- bind()方法:bind()方法主要目的是把函数绑定到对象,
箭头函数不起作用
- toString()方法:
- ECMAScript规定返回一个符合函数声明语句的字符串
- 实际上,多数(不是全部),都是返回函数完整源代码
- 内置函数返回的字符串中通常包含“[native code]”,表示函数体
- Function()构造函数:
- Function()构造函数可以接收
任意多个
字符串参数,其中最后一个参数
是函数体的文本。这个函数体 文本中可以包含任意JavaScript语句,相互以分号分隔。传给构造函数的其他字符串参数,将作为这个新函数的的形参。 - 注意:Function()构造函数
不接受任何指定新函数名字的参数
。与函数字面量一样,Function()构造函数创建的也是匿名函数
- Function()构造函数可以接收