前言
Hello~大家好。我是秋天的一阵风
在JavaScript的面试中,面试官可能会提出一些看似不合常规的问题,比如 “(5).add(2).minus(3)如何让浏览器通过?” 这个问题不仅考验你对JavaScript的理解,还考察了你对原型链和函数调用的掌握。接下来,我们将进行详细讲解。
一、引出题目
JavaScript
是一种基于原型的语言,这意味着它的对象和函数都可以通过原型链进行扩展。在JavaScript
中,函数也是对象,因此它们可以拥有自己的属性和方法。这种特性使得JavaScript
在实现链式调用时非常灵活。
“(5).add(2).minus(3)如何让浏览器通过?” 这个问题实际上是在问,如何在不使用new
关键字的情况下,让数字5调用add
方法,然后调用minus
方法,并得到正确的结果。这里的关键是要理解如何在数字对象上添加add
和minus
方法,并使得这些方法返回的对象能够继续进行链式调用。
二、讲解
要实现这个功能,我们可以利用JavaScript的原型链和函数调用的特性。我们可以在Number
的原型上添加add
和minus
方法,这样所有的数字都可以使用这些方法:
1. 添加自定义add和minus方法
Number.prototype.add = function(num) {
};
Number.prototype.minus = function(num) {
};
我们第一步直接在Number
的原型上添加了add
和minus
方法。这样,任何数字都可以调用这两个方法,而不需要创建一个新的对象实例。
2. 搞清this指向
我们的add
和minus
方法接收了一个num
参数,这个就是需要相加或者相减的值。那么当前被操作的数,也就是 5 如何获得呢?其实很简单。函数中的this
指向就是这个“当前被操作的数”
。
Tips: 如果你对this指向不熟悉,可以看看我的这两篇文章:
Number.prototype.add = function(num){
console.log('add:',this)
}
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)
4. reduce实现逻辑
Tips: 关于reduce的用法和源码实现,你可以看我的这篇文章:
最后我们再用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
语言的灵活性和动态性,允许我们在运行时动态地扩展对象和函数的行为。