(5).add(2).minus(3)如何让浏览器通过?

339 阅读4分钟

前言

Hello~大家好。我是秋天的一阵风

在JavaScript的面试中,面试官可能会提出一些看似不合常规的问题,比如 “(5).add(2).minus(3)如何让浏览器通过?” 这个问题不仅考验你对JavaScript的理解,还考察了你对原型链和函数调用的掌握。接下来,我们将进行详细讲解。

一、引出题目

JavaScript是一种基于原型的语言,这意味着它的对象和函数都可以通过原型链进行扩展。在JavaScript中,函数也是对象,因此它们可以拥有自己的属性和方法。这种特性使得JavaScript在实现链式调用时非常灵活。

“(5).add(2).minus(3)如何让浏览器通过?” 这个问题实际上是在问,如何在不使用new关键字的情况下,让数字5调用add方法,然后调用minus方法,并得到正确的结果。这里的关键是要理解如何在数字对象上添加addminus方法,并使得这些方法返回的对象能够继续进行链式调用。

二、讲解

要实现这个功能,我们可以利用JavaScript的原型链和函数调用的特性。我们可以在Number的原型上添加addminus方法,这样所有的数字都可以使用这些方法:

1. 添加自定义add和minus方法

Number.prototype.add = function(num) {
};

Number.prototype.minus = function(num) {
};

我们第一步直接在Number的原型上添加了addminus方法。这样,任何数字都可以调用这两个方法,而不需要创建一个新的对象实例。

2. 搞清this指向

我们的addminus方法接收了一个num参数,这个就是需要相加或者相减的值。那么当前被操作的数,也就是 5 如何获得呢?其实很简单。函数中的this指向就是这个“当前被操作的数”

Tips: 如果你对this指向不熟悉,可以看看我的这两篇文章:

可能是你看过最完整的this指向总结!

改变This指向的三种方式及实现---call、apply、bind

Number.prototype.add = function(num){
    console.log('add:',this)
}
image.png

3. add 和 minus 接收多个参数

上面的代码中,我们只给add和minus方法写死了一个参数,考虑到方法还可以接收多个参数,如下图所示:

Number.prototype.add = function(num){
    console.log('add:',this)
}

Number.prototype.minus = function (num) {
    console.log('minus:',this)
}


const result = (5).add(2,2,5,5,1).minus(3,1,2,4)
console.log(result)

所以我们需要改一下形参接收的方式:

Number.prototype.add = function(...args){
    console.log('add:',args)
}

Number.prototype.minus = function (...args) {
    console.log('minus:',args)
}


const result = (5).add(2,2,5,5,1).minus(3,1,2,4)
console.log(result)
image.png

4. reduce实现逻辑

Tips: 关于reduce的用法和源码实现,你可以看我的这篇文章:

给我十分钟,手把手教你实现Javascript数组原型对象上的七个方法!

最后我们再用reduce来实现具体逻辑:

      Number.prototype.add = function (...args) {
        return (
          Number(this) + args.reduce((pre, cur) => Number(pre) + Number(cur))
        );
      };

      Number.prototype.minus = function (...args) {
        return (
          Number(this) - args.reduce((pre, cur) => Number(pre) + Number(cur))
        );
      };

      const result = (5).add(2, 5).minus(3, 4);
      console.log(result); // 5

三、知识补充

JavaScript的底层实现中,对象的原型链是通过内部属性[[Prototype]](在大多数现代浏览器中可以通过__proto__属性访问)来实现的。当我们访问一个对象的属性时,如果该对象本身没有这个属性,JavaScript解释器就会沿着原型链向上查找,直到找到该属性或到达原型链的末端。

此外,JavaScript中的函数也是对象,它们有自己的原型对象。这意味着函数可以有方法,这些方法可以通过函数的原型链来实现继承。这就是为什么我们可以在函数的原型上添加方法,从而实现链式调用的原因。

四、总结

通过这个问题,我们不仅学习了如何在JavaScript中实现链式调用,还深入理解了JavaScript的原型链和函数原型的底层实现。这些知识对于深入理解JavaScript语言特性和编写高效、可维护的代码至关重要。同时,这种方法也展示了JavaScript语言的灵活性和动态性,允许我们在运行时动态地扩展对象和函数的行为。