重学js-函数隐式参数

222 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

对学过的知识进行二次学习,不仅能够复习学过的知识,还可能有不可思议的收获。

隐式函数参数

在函数中,除了在函数定义中显式声明的参数之外,函数调用会传递两个隐式的参数:argumentsthis

这些隐式参数在函数声明中没有明确定义,但会默认传递给函数并且可以在函数内正常访问。在函数内可以像其他明确定义的参数一样引用它们。

arguments参数

无论是否有明确定义对应的形参,通过它我们都可以访问到函数的所有参数。借此可以实现 原生 JavaScript 并不支持的函数重载特性,而且可以实现接收参数数量可变的可变函数。

arguments对象有一个名为length的属性,表示实参的确切个数。

function whatever(a, b, c) {
    console.log(arguments)
}
whatever(1, 2, 3, 4, 5) // 传递 5 个参数

注意:

  • arguments对象的主要作用是允许我们访问传递给函数的所有参数,即便部分参数没有和函数的形参关联也无妨。

  • arguments对象仅是一个类数组的结构,避免把arguments参数当作 数组

实现一个求和函数,来计算任意数量参数的和

/**
 * 使用arguments参数对所有函数参数执行操作
 * @returns {T}
 */
function sum() {
    // 将 arguments 转化成数组
    const arr = Array.prototype.slice.call(arguments)
    return arr.reduce((a, b) => {
        return a + b
    }, 0)
}

console.log(sum(1,2,3,4,5,6))
console.log(sum(1,2,3,4))

this 函数上下文

函数调用

function foo(name) {}
function bar(name) {}
foo('john')
(function (who) {
    return who
})('hello')

const hehe = {
    foo1: function (){}
}

hehe.foo1('123') // 对象的方法 调用
hehe = new bar('bar') // 作为构造函数调用
foo.call(hehe, 'hehe') // 通过call方法调用
foo.apply(hehe, ['hehe']) // 通过apply方法调用
1. 作为函数被调用

什么是作为函数被调用?几个例子

function test1(){}
const test2 = function (){}
test1() // 函数定义被调用
test2() // 函数作为表达式被调用
(function (){})() // 立即被调用的函数

作为函数被调用时,this 指向有两种:

  • 在非严格模式下,它将是全局上下文(window对象)。
  • 而在严格模式下,它将是 undefined。
function test1(){
    console.log(this)
}
function test2() {
    "use strict";
    console.log(this)
}
test1() // Window
test2() // undefined
2. 作为方法被调用

test 函数作为方法,被调用

const a = {
    test: function (){
        console.log(this) // { test: f }
    }
}
a.test()

这种调用方式,this 指向对象。

3. 作为构造函数调用
function Test() {
    this.a = function () {
        return this
    }
}
const b1 = new Test() 
const b2 = new Test() 
console.log(b1.a()) // Test {a: ƒ}
console.log(b2.a()) // Test {a: ƒ}

创建了一个名为 Test 的构造函数,通过 new 调用时候会创建一个空的对象,并将其作为函数上下文,也就是 this, 传递给函数

整个 new 的过程:

  • 创建一个新的空对象。
  • 该对象作为 this 参数传递给构造函数,从而成为构造函数的函数上下文。
  • (将方法属性添加到该对象)
  • 新构造的对象作为 new 运算符的返回值

构造函数返回值

测试一下:

const obj1 = {
    name: 'xiaoxxxxxxx'
}
function Person1() {
    this.name = 'xiaoming'
    return obj1
}
function Person2() {
    this.name = 'xiaoming'
    return 1
}
const p1 = new Person1()
console.log(p1) // {name: 'xiaoxxxxxxx'}
const p2 = new Person2()
console.log(p2) // Person2 {name: 'xiaoming'}

结论:

  • 如果构造函数返回一个对象,则该对象将作为整个表达式的值返回,而传入构造函数的 this 将被丢弃。
  • 但是,如果构造函数返回的是非对象类型,则忽略返回值,返回新创建的对象。
4. 使用apply和call方法调用

以上几种方法,this 指向,都存在各自的规律。

那如果想改变函数上下文怎么办呢?想要显式指定它怎么办?

例子

const obj3 = {
    foo: 1
}
function bar(str1, str2) {
    console.log(str1) // aaa
    console.log(str2) // bbb
    console.log(this.foo) // 1 
    console.log(this) // { foo: 1 }
}
bar.call(obj3, 'aaa', 'bbb') 
bar.apply(obj3, ['aaa', 'bbb'])

可以看出它们的 作用 是改变函数执行时的 this 指向,区别就是第二个参数格式不同。