前言
本文为看完原文的读后感,非原文。
2020年3月31日,有空在掘金上看到一位字节跳动的面试者发的第一面的面试题,首先要求实现如下功能:
let a = new Foo(); // a.id -> 1
let b = new Foo(); // b.id -> 2
思路
- 方法1:
Foo是个方法,每次通过new方法调用Foo方法,里面的id值都会加1:
let i = 1;
function Foo() {
// i不放里面,因为这样每次new都会重新定义i,使得i不会递增
// 另外i++是先赋值然后再加,++i是先加再赋值
this.id = i++;
}
// ok,实现了
console.log(new Foo().id); // 1
console.log(new Foo().id); // 2
但面试官要求不能用全局变量:
- 方法2:
使用静态属性,把全局变量改为静态属性即可。
function Foo() {
this.id = Foo.i++;
}
// 为防止new方法每次初始i的值,所以不能放到方法中,要放外面
Foo.i = 1;
// ok,实现了
console.log(new Foo().id); // 1
console.log(new Foo().id); // 2
但是,你懂得,你并没有get到面试官的点,于是他提出,也别用静态属性。
- 方法3:
使用闭包,因为闭包可以访问函数外界的变量。不清楚闭包的可以看这篇文章:闭包
另外为什么这里要用立即执行函数(function(){})()
是因为这样可以创造一个函数作用域,这样i
就不会暴露全局,只能内部调用,这又涉及到作用域。
const Foo = (function() {
let i = 1;
return function() {
this.id = i++;
}
})();
// ok,实现了
console.log(new Foo().id); // 1
console.log(new Foo().id); // 2
之后面试官又把试题升级,希望得到这样的结果:
let a = Foo(); // a.id -> 1
let b = new Foo(); // b.id -> 2
let c = new Foo(); // c.id -> 1
let d = Foo(); // d.id -> 2
- 方法1:
刚好前段时间整理过关于直接调用fn()和new fn()的区别,详情见文章,直接调用方法,方法中的this指向全局window,通过new方法,方法中的this指向赋值对象。
这里考点主要是鉴别方法是否通过new方法调用,es5的话用instanceof
,es6的话用new.target
。
const Foo = (function() {
let i = 1;
return function() {
if (new.target == Foo) {
this.id = i++;
} else {
return new Foo();
}
}
})();
console.log(Foo().id); // 1
console.log(new Foo().id); // 2
console.log(new Foo().id); // 3
console.log(Foo().id); // 4
网友评论