UDN-企业互联网技术人气社区

板块导航

浏览  : 772
回复  : 3

[面试] 说说牛客上的一道 JavaScript 题目

[复制链接]
cat77的头像 楼主
发表于 2016-12-15 15:23:36 | 显示全部楼层 |阅读模式
  牛客上有这么一道 JavaScript 的题目
  1. //填写内容让下面代码支持a.name = “name1”; b.name = “name2”;
  2. function obj(name){  
  3.     【1】
  4. }
  5. obj.【2】 = "name2";  
  6. var a = obj("name1");  
  7. 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 并不是完美的判断方法,但是在这里足够了,后面会谈到这个问题。
  1. if (this instanceof obj) {  
  2.     // new 调用
  3. } else {
  4.     // 非 new 调用
  5.     return {
  6.         name: name
  7.     }
  8. }
复制代码

  非 new 调用的情况下,直接返回一个新对象就 OK 了。

  而在 new 调用的情况下,可以看到 function obj(name) 定义的时候是有参数的,调用的时候却没参数,这就要小心了,为了安全起见,还是判断一下为妙。
  1. if (this instanceof obj) {  
  2.     // new 调用
  3.     if (name !== undefined) {
  4.         this.name = name
  5.     }
  6. } else {
  7.     // 非 new 调用
  8.     return {
  9.         name: name
  10.     }
  11. }
复制代码

  一般来说,判断会写成 if (name) ,但是碰到 null 、 0 、 false 就 GG 了,所以还是谨慎点吧。

  问题到这里就可以比较完美地解答了。

  bonus: instanceof 的问题

  『 instanceof 会检查 this 的原型链上是否存在 foo.prototype 』,为什么说得这么拗口,是因为需要表达出 instanceof 本来就不是真的用来检测是否调用 new 的方法。

  在题目里面,要求的是 a 需要从原型链上读取到特定的属性值,所以 instanceof 的作用刚好在这里能符合要求而已。

  函数调用除了题目中的方法还有第三种方法,那就是 foo.call 、 foo.apply ,而且也能为函数指定 this 的值(所以还有 bind )。因此是存在方法调戏 instanceof 的。
  1. foo.prototype.name = 'foo'  
  2. var midman = new foo('fake foo')  
  3. var a = foo.call(midman)  
  4. var b = foo.call(midman, 'b')  
  5. a  // undefined, WTF?!  
  6. b  // undefined, WTF?!
复制代码

  这里的 foo 调用的方式是作为函数来调用,但是为 this 绑定的值是从 foo 上 new 出来的,换句话说,其原型链上存在 foo.prototype ,于是就骗过了 instanceof 。

  于是 ES2015 来搭救你了,新增了一个 new.target 。于是修改成:
  1. if (new.target !== undefined) {  
  2.     // new 调用
  3.     if (name !== undefined) {
  4.         this.name = name
  5.     }
  6. } else {
  7.     // 非 new 调用
  8.     return {
  9.         name: name
  10.     }
  11. }
复制代码

相关帖子

发表于 2016-12-19 11:12:18 | 显示全部楼层
js框架越来越火了, 不过本人还是喜欢原生的好, 自己写类库, 自己来封装,用着方便。。。。
使用道具 举报

回复

发表于 2016-12-19 11:12:18 | 显示全部楼层
回帖支持下楼主,请眼熟我,我叫“弑杀天下“
使用道具 举报

回复

发表于 2016-12-19 11:12:22 | 显示全部楼层
Very Good!
使用道具 举报

回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部