前言
过去的两篇文章我们介绍了JavaScript
的面向对象编程,今天的这篇文章我们来通过几道面试题深入理解一下JavaScript
的面向对象编程。
题目一:实现new操作符
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function () {
console.log('wangwang');
}
Dog.prototype.sayName = function () {
console.log('my name is ' + this.name);
}
/*
let sanmao = new Dog('三毛');
sanmao.sayName();
sanmao.bark();
*/
//=>基于内置的new关键词,我们可以创建Dog的一个实例sanmao,实例可以调取原型上的属性和方法,现在的需求是:自己实现一个_new方法,也能模拟出内置new后的结果
function _new() {
//=>完成你的代码
}
let sanmao = _new(Dog, '三毛');
sanmao.bark(); //=>"wangwang"
sanmao.sayName(); //=>"my name is 三毛"
console.log(sanmao instanceof Dog); //=>true
解答
function _new(Func, ...args) {
//=>完成你的代码
let obj = Object.create(Func.prototype);
let result = Func.call(obj, ...args);
return result !== null && (typeof result === "object" || typeof result === "function") ? result : obj;
}
题目二:基于内置类原型扩展实现数组去重
解答
首先说一些数组去重的方法:
for循环
function myDeduplication() {
let obj = {};
for(let i = 0;i <= this.length;i++) {
let item = this[i];
if(obj[item] !== undefined) {
this[i] = this[this.length - 1];
this.length--;
i--;
continue;
}
obj[item] = item;
}
return this;
}set方法
function myDeduplication(){
return Array.from(new Set(this));
}拓展运算符
function myDeduplication(this){
return [...new Set(this)];
}接下来,我们将
myDeduplication
方法拓展到Array
上,并使用
Array.prototype.myDeduplication = myDeduplication;
[1, 2, 88, 3, 4, 6, 4, 8, 10, 88].myDeduplication();
题目三:实现(4).minus(3).add(4) => 5
解答
看一眼题目,这个题目是让我们实现基于Number
扩展实现链式加减操作,下面开始实现:
Number.prototype.add = function(num) {
if(typeof num !== 'number') {
throw new Error(`num's type is not a number`);
}
return this + num;
}
Number.prototype.minus = function(num) {
if(typeof num !== 'number') {
throw new Error(`num's type is not a number`);
}
return this - num;
}
console.log((4).minus(3).add(4)); // 5
console.log((4).minus('3').add(4)) // "num's type is not a number"
题目四:下面的代码片段输出什么?
解答
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
};
Foo.prototype.getName = function () {
console.log(3);
};
var getName = function () {
console.log(4);
};
function getName() {
console.log(5);
}
Foo.getName(); // 2
getName(); // 4
Foo().getName(); // 1
getName(); // 1
new Foo.getName(); // 2
new Foo().getName(); // 3
new new Foo().getName(); // 3
本题的首先定义了Foo
函数,然后在Foo
上创建了名为getName
的静态属性创建了一个函数,之后在Foo
的原型上创建了getName
匿名函数,然后又通过函数变量表达式创建了一个getName
的函数,最后声明一个名为getName
的函数。
第一问:直接访问Foo
函数上的getName
静态属性,所以直接输出2
第二问:我们可以把上述代码拆分一下
var getName;
function getName() {
// ...
}
getName = function() {
// ...
}
所以输出了1
第三问:先执行了Foo
函数,后调用Foo
函数返回值对象的getName
属性函数,所以输出1
第四问:在第三问中,我们注意到,Foo
函数返回值中的getName
属性是一句函数赋值语句,所以原有的getName
已被改变,这个时候输出的结果是1
第五问:.
运算符优先级高于new
,所以此问相当于new (Foo.getName)()
,此时输出2
第六问:()
的优先级高于new
,此问相当于(new Foo()).getName()
,此时输出结果为3
第七问:根据运算优先级,此问改写为new ((new Foo()).getName)()
,最后输出结果为3
题目五:下面的代码片段输出什么?
解答
let obj = {
2: 3,
3: 4,
length: 2,
push: Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);
这是一个类数组,其中push
方法的内部实现为:
Array.prototype.push = function(target) {
this[this.length] = target;
this.length++;
}
根据push
方法的原理,最终输出了这样的结果。
题目六:a如何继承b?
解答
利用原型链继承
function A() {
// ...
}
function B() {
// ...
}
A.prototype = new B();构造函数实现
function B() {
// ...
}
function A() {
B.call(this)
}原型链+构造函数
function A() {
// ...
}
function B() {
// ...
}
A.prototype = new B();
A.prototype.constructor = A;Object.create()[推荐此方法]
function A() {
// ...
}
function B() {
// ...
}
A.prototype = Object.create(B.prototype);
A.prototype.constructor = A;class方法
class B {
constructor() {
// ...
}
}
class A extends B {
constructor() {
super()
}
}
推荐阅读
总结
上面的几道有关于面向对象的题,是我近期遇到的,然后将其整理分享给大家~如果大家有其他的好的题,也希望能够在评论区分享给我,分享给大家~如果上面整理的有问题,也希望大家在评论区指点出来,我们共同进步~
最后,分享一下我的个人公众号「web前端日记」,欢迎大家积极关注~