面试准备-函数

125 阅读21分钟

介绍

首先我们数据类型过程中已经学习到,函数属于对象的一种特殊表达方式。

function fn1 (...argument) {
    // doSomeThing
    ...
}
const fn2 = (...params) => {
    // doSomeThing
    ...
}
const obj = {
    fn3 (...params) {
         // doSomeThing
         ...
    }
}
const fn4 = (...params) => // i am some return

以上4种都是比较常见的声明方式,其中f1f2分别代表的是不同的函数类型,这里暂时称之为普通函数箭头函数,我们先看看官方文档是如何定义普通函数箭头函数

ECMAScript 函数对象

ECMAScript 函数对象封装了封闭在词法环境中的参数化 ECMAScript 代码,并支持对该代码的动态评估。 ECMAScript 函数对象是一个普通对象,与其他普通对象具有相同的内部插槽和相同的内部方法。 ECMAScript 函数对象的代码可以是严格模式代码非严格代码。代码为严格模式代码的 ECMAScript 函数对象称为严格函数。代码不是严格模式代码的代码称为非严格函数。

除了 [[Extensible]] 和 [[Prototype]],ECMAScript 函数对象还具有下表中列出的内部插槽

  • 函数对象内部插槽 |内部插槽 | 类型| 叙述| | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [[Environment]] | an Environment Record | The Environment Record 该功能已关闭。在评估函数的代码时用作外部环境。| | [[PrivateEnvironment]] | a PrivateEnvironment Record or null | 该函数已关闭 PrivateEnvironment 记录对Private Names的记录。如果此函数在语法上不包含在类中,则为 null。在评估函数代码时,用作内部类的外部 PrivateEnvironment。| | [[FormalParameters]]| a Parse Node | 定义函数形式参数列表的源文本的根解析节点。 | | [[ECMAScriptCode]]| a Parse Node | 定义函数主体的源文本的根解析节点。| | [[ConstructorKind]] | base or derived | 该函数是否是派生类 constructor.| | [[Realm]]| a Realm Record |创建函数并提供在评估函数时访问的任何内在对象的领域。 | [[ScriptOrModule]] | a Script Record or a Module Record | 如果这是一个严格函数,则为 true;如果这是一个非严格函数,则为 false。 | [[ThisMode]]| lexical, strict, or global | 定义如何在函数的形式参数和代码主体中解释此引用。词法意味着 this 指的是词法封闭函数的 this 值。严格意味着 this 值完全按照函数调用所提供的方式使用。全局意味着 undefined 或 null 的 this 值被解释为对全局对象的引用,并且任何其他 this 值首先传递给 ToObject。| | [[Strict]]| a Boolean| 如果这是一个严格函数,则为 true;如果这是一个非严格函数,则为 false。 | | [[HomeObject]] | an Object | 如果函数使用 super,则此对象的 [[GetPrototypeOf]] 提供了开始supre 属性查找的对象。 | | [[SourceText]] | a sequence of Unicode code points| 定义函数的源文本 | | [[Fields]] | a List of ClassFieldDefinition Records | I如果函数是一个类,则这是一个记录列表,表示该类的非静态字段和相应的初始值设定项。 | | [[PrivateMethods]] | a List of PrivateElements | 如果函数是一个类,这是一个表示类的非静态私有方法和访问器的列表。| | [[ClassFieldInitializerName]] | a String, a Symbol, a Private Name, or empty |如果该函数是作为类字段的初始化程序创建的,则用于该字段的 NamedEvaluation 的名称;否则为空。 | | [[IsClassConstructor]] | a Boolean | 指示函数是否是类构造函数。 (如果为 true,调用函数的 [[Call]] 将立即引发 TypeError 异常。)|

所有 ECMAScript 函数对象都有此处定义的 [[Call]] 内部方法。此外,也是构造函数的 ECMAScript 函数具有 [[Construct]] 内部方法。

[[Call]]( thisArgumentargumentsList )

ECMAScript 函数对象 F 的 [[Call]] 内部方法接受参数 thisArgument(ECMAScript 语言值)和 argumentsList(ECMAScript 语言值列表)并返回包含 ECMAScript 语言值的正常完成或突然完成。它在调用时执行以下步骤:

  1. 让 callerContext 为正在运行的执行上下文

  2. 让 calleeContext 为 PrepareForOrdinaryCall(F, undefined)。

  3. 断言:calleeContext 现在是正在运行的执行上下文。

  4. 如果 F.[[IsClassConstructor]] (也就是说当前对象为class的构造函数) 为真,则

    4.1设 error 为新创建的 TypeError 对象。

    4.2注意:在 calleeContext 中使用 F 的关联[[Realm]]记录创建错误。

    4.3从执行上下文堆栈中移除 calleeContext 并将 callerContext 恢复为正在运行的执行上下文。

    4.4返回 ThrowCompletion(错误)。

  5. 执行 OrdinaryCallBindThis(F, calleeContext, thisArgument) (判断绑定的this对象)。

  6. 让结果为 Completion(OrdinaryCallEvaluateBody(F, argumentsList)) (OrdinaryCallEvaluateBody(...)相当于真正开始执行函数,Completion(...)用于记录函数的完成情况)。

  7. 从执行上下文栈中移除calleeContext,将callerContext恢复为正在运行的执行上下文

  8. 如果result.[[Type]]为return,则返回result.[[Value]]。

  9. ReturnIfAbrupt(result)。

  10. 返回未定义。

PrepareForOrdinaryCall (F, newTarget)

主要用来为函数F提供函数执行上下文,步骤如下

  1. 让 callerContext 为正在运行的执行上下文
  2. 让 calleeContext 成为一个新的 ECMAScript 代码Execution Contexts
  3. 设置calleeContext的Function为F
  4. 设 calleeRealm 为 F.[[Realm]]
  5. 设置calleeContext的Realm为calleeRealm
  6. 设置calleeContext的ScriptOrModule为F.[[ScriptOrModule]]
  7. 设 localEnv 为 NewFunctionEnvironment(F, newTarget) (创建新的函数环境)
  8. 设置calleeContext的LexicalEnvironment为localEnv (执行上下文的词法环境设置成新的函数环境记录)
  9. 设置calleeContext的VariableEnvironment为localEnv(执行上下文的变量环境设置为新建的函数环境记录)
  10. 设置calleeContext的PrivateEnvironment为F.[[PrivateEnvironment]] (执行上下文的私有环境设置为F.[[PrivateEnvironment]]内置插槽
  11. 如果 callerContext 尚未挂起,则挂起 callerContext
  12. 将calleeContext压入执行上下文栈; calleeContext 现在是正在运行的执行上下文
  13. 注意:在此之后产生的任何异常对象都与 calleeRealm 相关联
  14. 返回被调用者上下文。

在这里我们分别来看一下执行上下文函数环境的定义以及创建方法的定义

Execution Contexts

执行上下文是一种规范设置,用于跟踪 ECMAScript 实现对代码的运行时评估。在任何时间点,每个实际执行代码的agents最多有一个执行上下文。这称为agents运行时执行上下文。本规范中对运行执行上下文的所有引用都表示围绕agents运行时执行上下文

执行上下文堆栈用于跟踪执行上下文。正在运行的执行上下文始终是此堆栈的顶部元素。每当控制从与当前运行的执行上下文相关联的可执行代码转移到与该执行上下文无关的可执行代码时,就会创建一个新的执行上下文。新创建的执行上下文被压入堆栈,成为正在运行的执行上下文

执行上下文包含跟踪其关联代码的执行进度所需的任何实现特定状态。每个执行上下文至少下表所示的状态组件

  • 执行上下文的状态组件 |组件 | 目的 | | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | code evaluation state | 执行、暂停和恢复与execution context相关的代码评估所需的任何状态 | | Function| 如果此执行上下文正在评估函数对象的代码,则该组件的值就是该函数对象。如果执行上下文正在评估脚本或模块的代码,则该值为 null。| | Realm | 关联代码从中访问 ECMAScript 资源的 Realm Record  | | ScriptOrModule |模块记录脚本记录,来自哪个关联的代码起源。如果没有始发脚本或模块,则原始执行上下文InitializeHostDefinedRealm中创建,值为null。|

正在运行的执行上下文对代码的评估可以在本规范中定义的各个点暂停。一旦正在运行的执行上下文被挂起,一个不同的执行上下文可能成为正在运行的执行上下文并开始评估其代码。在稍后的某个时间,挂起的执行上下文可能会再次成为正在运行的执行上下文,并在其先前被挂起的位置继续评估其代码。正在运行的执行上下文状态在执行上下文之间的转换通常以栈式后进先出的方式发生。但是,某些 ECMAScript 功能需要运行执行上下文non-LIFO转换。

正在运行的执行上下文的 Realm 组件的值也称为当前 Realm Record。 正在运行的执行上下文的 Function 组件的值也称为活动函数对象

ECMAScript 代码的执行上下文具有在下表状态组建

  • ECMAScript 代码执行上下文的状态组件
组件目的
LexicalEnvironment(词法环境)标识用于解析此执行上下文中的代码所做的标识符引用的环境记录
VariableEnvironment(变量环境)标识保存由此执行上下文中的 VariableStatements 创建的绑定的环境记录。
PrivateEnvironment (私有环境)标识在最近的包含类中保存由 ClassElements 创建的私有名称的 PrivateEnvironment 记录。如果没有包含类,则为 null。

执行上下文的 LexicalEnvironment 和 VariableEnvironment 组件始终是环境记录

表示生成器(Generator)评估的执行上下文具有下表中列出的其他状态组件。

  • 生成器(Generator)额外的状态组件 | 组件 | 目的 | | --------- | ----------------------------------------------------------------------------------------------------------- | | Generator | 此执行上下文正在评估的生成器。|

在大多数情况下,只有正在运行的执行上下文(执行上下文堆栈的顶部)由本规范中的算法直接操作。因此,当没有限定使用术语“LexicalEnvironment”和“VariableEnvironment”时,它们是指正在运行的执行上下文的那些组件。

执行上下文纯粹是一种规范机制,不需要对应于 ECMAScript 实现的任何特定实现。 ECMAScript 代码不可能直接访问或观察执行上下文。

NewFunctionEnvironment (F, newTarget)

抽象操作 NewFunctionEnvironment 接受参数 F(一个 ECMAScript 函数)和 newTarget(一个对象或未定义)并返回一个 Function Environment Record

  • Function Environment Record Function Environment Record 是一种Declarative Environment Records 就是用来function的顶级别范围(执行访问范围),如果function不是一个ArrowFunction,提供this的绑定。 如果一个function不是一个ArrowFunction,并且它包含对于super的引用,那么它的Function Environment Record也包括它通过super所引用的那个函数

函数环境包含下列内部插槽

内部插槽解释
[[ThisValue]]an ECMAScript language value[[ThisValue]]的值为this, 它的值是调用当前函数的值
[[ThisBindingStatus]]lexical, initialized, or uninitialized如果这个的值为lexical,那么这个函数为ArrowFunction, 并且没有this
[[FunctionObject]]an Object其调用导致创建此环境记录的函数对象(也就是函数F)。
[[NewTarget]]an Object or undefined如果此环境记录是由 [[Construct]] 内部方法创建的,则 [[NewTarget]] 是 [[Construct]] newTarget 参数的值。否则,它的值是未定义的。(也就是指的如果当前函数是构造函数,[[NewTarget]] 就是新构造出来的对象)

Function Environment Record 支持下表中列出的所有声明性Environment Records方法,并且对除 HasThisBinding (...)HasSuperBinding(...) 之外的所有这些方法共享相同的规范。另外,Environment Records 函数支持中列出的方法

  • Function Environment Record 的附加方法 |方法 | 目的| | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | BindThisValue(V) | 设置 [[ThisValue]] 并记录它已被初始化。| | GetThisBinding() | 返回 Environment Record'所记录的 this 绑定的值。如果 this 绑定尚未初始化,则引发 ReferenceError。 | | GetSuperBase() | 返回基于Environment Record记录的super所关联的对象. 如果为返回作为绑定在此那么super会引发运行时错误.

那么在这里我们找到了一些跟this指向的记录相关的方法,我们在阅读完Function Environment RecordExecution Contexts发现都提到了一个关键的名词叫做 Environment Record (环境记录),那么我们就一起来看看 Environment Record是什么?

Environment Record

环境记录是一种规范类型,用于根据 ECMAScript 代码的词法嵌套结构定义标识符与特定变量和函数的关联。通常,环境记录与 ECMAScript 代码的某些特定语法结构相关联,例如 FunctionDeclaration、BlockStatement 或 TryStatement 的 Catch 子语句。每次评估此类代码时,都会创建一个新的环境记录,以记录该代码创建的标识符绑定。

每个环境记录都有一个 [[OuterEnv]] 字段,该字段为 null 或是对外部环境记录的引用。这用于对环境记录值的逻辑嵌套进行建模。(内部)环境记录的外部引用是对内部环境记录在逻辑上围绕的环境记录的引用。当然,外部环境记录可以有自己的外部环境记录。环境记录可以用作多个内部环境记录的外部环境。例如,如果函数声明包含两个嵌套的函数声明,则每个嵌套函数的环境记录将具有作为其外部环境记录周围函数的当前计算的环境记录。

环境记录是纯粹的规范机制,不需要对应于 ECMAScript 实现的任何特定实现。ECMAScript 程序不可能直接访问或操作这些值。

Environment Record 的种类结构

环境记录可以被视为存在于一个简单的面向对象的层次结构中,其中环境记录是一个抽象类,具有三个具体的子类:声明性环境记录、对象环境记录和全局环境记录。函数环境记录和模块环境记录是声明性环境记录的子类。

  • Environment Record (abstract) 声明性环境记录用于定义 ECMAScript 语言句法元素的影响,例如 FunctionDeclarations、VariableDeclarations 和 Catch 子语句,它们直接将标识符绑定与 ECMAScript 语言值相关联。

  • 函数环境记录对应于 ECMAScript 函数对象的调用,并包含该函数中顶级声明的绑定。它可能会建立一个新的this绑定。它还捕获支持super方法调用所需的状态。

  • 模块环境记录包含模块的顶级声明的绑定。它还包含由模块显式导入的绑定。它的[[OuterEnv]]是全局环境记录。

  • 对象环境记录用于定义 ECMAScript 元素(如 WithStatement)的效果,这些元素将标识符绑定与某些对象的属性相关联。

  • 全局环境记录用于编写全局声明的脚本。它没有外部环境;它的 [[OuterEnv]] 为空。它可能预填充了标识符绑定,并且它包括一个关联的全局对象,其属性提供一些全局环境的标识符绑定。在执行 ECMAScript 代码时,可以将其他属性添加到全局对象中,并且可以修改初始属性。

环境记录抽象类包括下表中定义的抽象规范方法。这些抽象方法对于每个具体的子类都有不同的具体算法。

  • 环境记录中的抽象方法
方法目的
HasBinding(N)确定环境记录是否具有字符串值 N 的绑定。如果绑定,则返回 true;如果没有,则返回 false。
CreateMutableBinding(N, D)在环境记录中创建新但未初始化的可变绑定。字符串值 N 是绑定名称的文本。如果布尔参数 D 为 true,则随后可能会删除绑定。
CreateImmutableBinding(N, S)在环境记录中创建新但未初始化的不可变绑定。字符串值 N 是绑定名称的文本。如果 S 为 true,则在初始化后尝试设置它时,无论引用该绑定的操作的严格模式设置如何,都将始终引发异常。
InitializeBinding(N, V)在环境记录中设置已存在但未初始化的绑定的值。字符串值 N 是绑定名称的文本。V 是绑定的值,也是任何 ECMAScript 语言类型的值。
SetMutableBinding(N, V, S)在环境记录中设置已存在的可变绑定的值。字符串值 N 是绑定名称的文本。V 是绑定的值,可以是任何 ECMAScript 语言类型的值。S 是一个布尔标志。如果 S 为 true 并且无法设置绑定,则引发 TypeError 异常。
GetBindingValue(N, S)从环境记录返回已存在的绑定的值。字符串值 N 是绑定名称的文本。S 用于标识源自严格模式代码的引用,或者需要严格模式引用语义的引用。如果 S 为 true 并且绑定不存在,则引发 ReferenceError 异常。如果绑定存在但未初始化,则抛出引用错误,而不考虑 S 的值。
DeleteBinding(N)从环境记录中删除绑定。字符串值 N 是绑定名称的文本。如果 N 的绑定存在,请删除该绑定并返回 true。如果绑定存在但无法删除,则返回 false。如果绑定不存在,则返回 true。
HasThisBinding()确定环境记录是否建立了this绑定。如果这样做,则返回 true;如果不这样做,则返回 false。
HasSuperBinding()确定环境记录是否建立了super方法绑定。如果这样做,则返回 true;如果不这样做,则返回 false。
WithBaseObject()如果此环境记录与 with 语句相关联,则返回 with 对象。否则,返回未定义。.
Declarative Environment Records

每个声明性环境记录都与包含varconstletclassmoduleimport函数声明的 ECMAScript 程序作用域相关联。声明性环境记录绑定由其作用域中包含的声明定义的标识符集。

到这里函数执行的相关知识大致是结束了,接下来我们看看构造函数的内部方法[[Construct]]执行过程

[[Construct]]( argumentsListnewTarget )

ECMAScript 函数对象 F 的 [[Construct]] 内部方法接受参数 argumentsList(ECMAScript 语言值列表)和 newTarget(构造函数),并返回 Object 的正常完成或突然完成。

它在调用时执行以下步骤:

  1. 让 callerContext 为正在运行的执行上下文

  2. 设 kind 为 F.[[ConstructorKind]]。

  3. 如果 kind 是 base,那么

    3.1让 thisArgument 成为 ? OrdinaryCreateFromConstructor(newTarget, '%Object.prototype%')(这里主要是新建了一个新的对象,并且为对象进行原型引用的判断)。

  4. 让 calleeContext 为 PrepareForOrdinaryCall(F, newTarget)(创建新的执行上下文,上文有提到)。

  5. 断言:calleeContext 现在是正在运行的执行上下文。

  6. 如果 kind 是 base,那么

    6.1执行 OrdinaryCallBindThis(F, calleeContext, thisArgument)(把刚才新构建的对象,作为构造函数的this执行,尽心设置)。

    6.2 让 initializeResult 为 Completion(InitializeInstanceElements(thisArgument, F))(这里主要针对class 如果构造函数是class那么就把定义的变量跟函数,先赋值到this上)。

    6.3 如果 initializeResult 是突然完成,则

    6.3.1 从执行上下文堆栈中移除 calleeContext 并将 callerContext 恢复为正在运行的执行上下文。
    
    6.3.2 返回 ?初始化结果。
    
  7. 令constructorEnv为calleeContext的LexicalEnvironment。

  8. 让结果为 Completion(OrdinaryCallEvaluateBody(F, argumentsList))(执行构造函数,这里可以参考上面的函数执行)。

9、从执行上下文堆栈中移除calleeContext,将callerContext恢复为正在运行的执行上下文。

10.如果result.[[Type](如果完成记录的返回是正常放回,那么)是return,那么

10.1 如果 Type(result.[[Value]])Object,则返回 result.[[Value]]

10.2 如果 kind 是base,则返回 thisArgument。

10.3 如果 result.[[Value]] 不是undefined的,则抛出 TypeError 异常。

  1. 否则,ReturnIfAbrupt(result)。
  2. 让 thisBinding 成为 ?构造函数Env.GetThisBinding()。
  3. 断言:类型(thisBinding)是对象。
  4. 返回此绑定。

到这里我们就基本了解完了ES定义的函数的两大内部方法,接下来我们基于刚学习的ES函数来看一下,函数常见的一些使用场景以及技巧

函数常见场景,使用

私有变量封装

用于部分环境变量不希望外部直接访问,而通过标准接口的形式进行操作的场景

function getNewFn () {
    let somePrivateVariable = null
    function setPrivateVariableValue (val) {
        somePrivateVariable = val
    }
    function getPrivateVariableValue () {
        return somePrivateVariable
    }
    return {
        setPrivateVariableValue,
        getPrivateVariableValue
    }
}
const fn = getNewFn()
fn.setPrivateVariableValue(1)
console.log(fn.getPrivateVariableValue()) // 1
console.log(fn.somePrivateVariable)       // undefined

参数缓存(伪函数柯里化)

用于部分参数可进行预设置或参数缓存的场景(比如某个函数发现参数过多,可以通过预设参数来减少调用时的函数减少重复代码)

// 需要预设参数的函数
function targetFn (paramA, paramB, paramC) {
    console.log('paramA: ', paramA)
    console.log('paramB: ', paramB)
    console.log('paramC: ', paramC)
}
function getNewFn (paramA, paramB) {
    return function runFn (paramC) {
        return targetFn (paramA, paramB, paramC)
    }
}

const runHandler = getNewFn(1, 2)
runHandler(3)
// paramA:  1
// paramB:  2
// paramC:  3
runHandler(4)
// paramA:  1
// paramB:  2
// paramC:  4

链式调用

增加代码的可读性,让操作调用过程更加直观

const someObj = {
    a: 1,
    add () {
        this.a++
        return this
    },
    reduce () {
        this.a--
        return this
    },
    get () {
        return this.a
    }
}
var a = someObj
        .add()
        .add()
        .reduce()
        .get()
console.log(a) // 2

构造函数

通常构造对象,继承父对象的属性

function someCtro (param) {
    this.param = param
}
someCtro.prototype.parentVal = 'I am parentVal!'
const childObj = new someCtro('I am param!')

console.log(childObj.param) // 'I am param!'
console.log(childObj.parentVal) // 'I am parentVal!'

当然基于我们上面对于构造函数的学习,其实我们知道构造函数的返回值可以是对象类型的任何数据(比如函数,数组等着另一类的标准的怪异对象)

this指向

关于this的指向问题,我相信已经有足够多的文章做了很细致的总结,这里我打算直接通过官方文档中的OrdinaryCallBindThis(...)函数来讲解这个问题

OrdinaryCallBindThis( FcalleeContextthisArgument )

抽象操作 OrdinaryCallBindThis 接受参数 F(函数对象)、calleeContext(执行上下文)和 thisArgument(ECMAScript 语言值)并返回unused的参数。它在调用时执行以下步骤:

  1. 设 thisMode 为 F.[[ThisMode]]。

  2. 如果thisMode是lexical的,返回unused的(这里我们也在上文中的函数环境记录中了解到,如果这个值是lexical(词法),也就意味着这是一个剪头函数,它本身可能并不具备thisValue, 同时也无法在当前的函数环境记录中进行BindThisValue操作)。

  3. 设 calleeRealm 为 F.[[Realm]]。

  4. 设localEnv为calleeContext的LexicalEnvironment(也就是新增的函数环境记录)。

  5. 如果 thisMode 是strict的,让 thisValue 为 thisArgument。

  6. 否则,

    6.1 如果 thisArgument undefined或为null,则

     i. 设 globalEnv 为 calleeRealm.[[GlobalEnv]]。
     
     ii.断言:globalEnv 是一个全局环境记录。
     
     iii.设 thisValue 为 globalEnv.[[GlobalThisValue]]

    6.2 否则

     i. 让 thisValue 成为! ToObject(thisArgument)。
     
     ii.注意:ToObject 使用 calleeRealm 生成包装对象。
     
    
  7. 断言:localEnv是一个函数Environment Record。

  8. 断言:下一步永远不会返回突然完成,因为 localEnv.[[ThisBindingStatus]] 没有初始化。

  9. 执行! localEnv.BindThisValue(thisValue)(将数据绑定为当前环境记录的[[ThisValue]])。

  10. return unused

这里我们可以看到关键信息有:

  1. 实际上是开启strict, 那么thisArgumentundefined 就是 undefined,而不是原本指向全局转成的undefined
  2. 箭头函数 是 不具备this值的,所以我们在箭头函数中所使用的this实际上是当前函数的.[[OuterEnv]]中的环境记录中的[[ThisValue]],而不是当前函数环境下的[[ThisValue]]
  3. 剩下的场景基本只跟thisArgument

那么我们在什么环境下会调用我们的OrdinaryCallBindThis(...)方法,针对文档的搜索,我们仅在[[Call]] 跟 [[Construct]] 有关,也就是只有函数在执行以及构造时才会引起this指向的确定(反过来说也就是改动的可能性)。

常见的this指向的确立场景

  • 对象绑定 / 默认指向 当函数属于某个对象的属性值时(非严格模式下)
var a = 1
const obj = {
    a: 2,
    getVal: function () {
        console.log(this.a)
    } 
}
obj.getVal() // 2
var outObjFn = obj.getVal
outObjFn()   // 1

如果函数是被对象所调用的,那么this会默认的指向当前对象,如果取出来作为全局变量声明,那么可以理解为找不到调用对象,返回全局对象

  • 显式绑定 通过call,apply,bind,来进行重新指定this的指向

  • new表达式 构造函数中的this指向,这里就不赘述了,上面的[[Construct]]已经有提及

那么简单的聊完了this指向 我们来看看函数自带的一些属性方法

函数常见属性方法

// 第一种情况
function someFn (a) {
    // doSomeThing
    ...
}
console.log(someFn.length) // 1

// 第二种情况
function someFn (a, b = 1) {
    // doSomeThing
    ...
}
console.log(someFn.length) // 依旧是:1

// 第三种情况
function someFn (a, b = 1, c) {
    // doSomeThing
    ...
}
console.log(someFn.length) // 依旧是:1

// 第四种情况
function someFn (a, b, c = 1) {
    // doSomeThing
    ...
}
console.log(someFn.length) // 变成: 2
  • 绑定三兄弟call, apply, bind 绑定指定的对象作为this指向,并 执行/等待执行 方法

  • toString() 方法

用于返回方法的源代码,如果是原生方法,是没有办法看到代码实现的

当然由于Function也继承于Object,属于Object的标准怪异对象,所以我们也可以调用继承自Object原型上的方法,那么我们今天就讲到这里!

完美!

下一章:面试准备-对象