原文:http://blog.e10t.net/talk-abo…
牛客上有这么一道 JavaScript 的题目。
//填写内容让下面代码支撑a.name = “name1”; b.name = “name2”;
function obj(name){
【1】
}
obj.【2】 = "name2";
var a = obj("name1");
var b = new obj;
【1】和【2】是填写的内容,【2】的答案是 prototype.name
,没争议。
题目是【1】,参考答案居然是 if(name){ this.name = name;}return this;
,这么随便地玩弄 this
不就是明摆着污染全局变量吗?暴力赋值不可取。
下面的一些高票议论还说了一大堆诠释的空话,连他本身都说本身好罗嗦。对,你不只罗嗦,而且还没有改错。解释里都说了给 window 的属性赋值,还不自知出题目,真是误人子弟。
先来剖析一下题目,a 和 b 都从 obj 来,为何同名的属性值不一样?能够看出,是对 obj 这个函数的挪用体式格局不一样,a 是 obj 函数的挪用效果,而 b 则是 obj 作为组织函数挪用的效果。所以这题的重点应该是怎样辨别_函数挪用_和_组织函数挪用_。
一个关键字 new
决议了差别。new
的作用是什么呢?MDN 上说了,口试也会考你的,简朴来说是三步,new foo
:
天生一个继续于 foo.prototype 的对象
foo 会被挪用,个中的
this
值会被绑定为 1 中的对象假如 foo 没有返回一个对象(注重是对象!),则返回 1 的对象
从 2 就能够看出 this
值会被 new
绑定为一个肯定的对象,而不是像一般函数挪用中那样本身不可预感,要看上下文的历程。
因而就能够在这里做文章。先来推断 this
的值。
if (this instanceof obj) {}
instanceof
会搜检 this
的原型链上是不是存在 foo.prototype
。也就是说能推断是不是满足第 1 条,确保了对象能从 prototype
中读取到 name
属性。(毕竟代码中并没有给 b 的赋值中传入)
instanceof
并非圆满的推断要领,然则在这里足够了,背面会谈到这个题目。
if (this instanceof obj) {
// new 挪用
} else {
// 非 new 挪用
return {
name: name
}
}
非 new 挪用的情况下,直接返回一个新对象就 OK 了。
而在 new 挪用的情况下,能够看到 function obj(name)
定义的时刻是有参数的,挪用的时刻却没参数,这就要警惕了,为了平安起见,照样推断一下为妙。
if (this instanceof obj) {
// new 挪用
if (name !== undefined) {
this.name = name
}
} else {
// 非 new 挪用
return {
name: name
}
}
一般来说,推断会写成 if (name)
,然则遇到 null
、0
、false
就 GG 了,所以照样郑重点吧。
题目到这里就能够比较圆满地解答了。
bonus: instanceof 的题目
『instanceof
会搜检 this
的原型链上是不是存在 foo.prototype
』,为何说得这么拗口,是因为须要表达出 instanceof
原本就不是真的用来检测是不是挪用 new
的要领。
在题目内里,请求的是 a 须要从原型链上读取到特定的属性值,所以 instanceof
的作用刚好在这里能符合请求罢了。
函数挪用除了题目中的要领另有第三种要领,那就是 foo.call
、foo.apply
,而且也能为函数指定 this
的值(所以另有 bind
)。因此是存在要领调戏 instanceof
的。
foo.prototype.name = 'foo'
var midman = new foo('fake foo')
var a = foo.call(midman)
var b = foo.call(midman, 'b')
a // undefined, WTF?!
b // undefined, WTF?!
这里的 foo
挪用的体式格局是作为函数来挪用,然则为 this
绑定的值是从 foo
上 new
出来的,换句话说,其原型链上存在 foo.prototype
,因而就骗过了 instanceof
。
因而 ES2015 来援救你了,新增了一个 new.target
。因而修改成:
if (new.target !== undefined) {
// new 挪用
if (name !== undefined) {
this.name = name
}
} else {
// 非 new 挪用
return {
name: name
}
}