New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sea.js 源码解析(三) #175

Open
lifesinger opened this Issue Jul 30, 2013 · 41 comments

Comments

Projects
None yet
@lifesinger
Copy link
Owner

lifesinger commented Jul 30, 2013

789px-mallard2

前一篇解析中,关于 undefined 的讨论挺有意思。jQuery 2.x 里,很可能会去掉这种写法。有兴趣的可以去看下原文评论

今天继续源码解析,说说 util-lang.js 文件。

语言增强

util-lang.js 经历丰富,一生大起大落,辉煌时有好几百行,到如今只剩下十几行。

function isType(type) {
  return function(obj) {
    return Object.prototype.toString.call(obj) === "[object " + type + "]"
  }
}

var isObject = isType("Object")
var isString = isType("String")
var isArray = Array.isArray || isType("Array")
var isFunction = isType("Function")

var _cid = 0
function cid() {
  return _cid++
}

辉煌时的几百行,提供了各种功能,今天暂且不表,以后有机会再说。剩下的功能,明眼人一看就明白,一个是类型判断,另一个 cid 生成器。先聊聊前者。

类型判断

以 isString 为例,这个故事不太复杂,也不那么简单。各位看官泡杯凉茶,听我道来。

判断一个变量是否字符串类型,最简单直接的写法是

function isString(obj) {
  return typeof obj == "string"
}

绝大部分情况下,以上代码就够用了。然而

typeof new String("xxx") // => "object"

当字符串是通过 new String 生成时,typeof 返回的是 "object",因为 new String 返回的的确是对象。可以参考这篇总结文:JavaScript's typeof operator

但我们才不管是字符串直接量,还是字符串对象呢,我们希望这两种情况下,isString 都能返回 true 。于是

function isString(obj) {
  return typeof obj == "string" || obj instanceof String
}

上面的写法,曾出现在各种流行类库的早期代码中,一直工作得好好的。直到有人在 iframe 中,写出以下代码

// 在 iframe 中
var foo = new String("bar")

if (top.isString(foo)) {
  // Do some cool things
}

上面的代码,是调用父页面的 isString 方法,来判断 iframe 中的变量是否字符串。由于 iframe 和 top 中的 String 全局对象并不相等,因此 obj instanceof String 会返回 false,于是 top.isString(foo) 华丽丽地挂了。

做前端真苦逼,但不能因为苦逼就撂挑子不干了。全世界范围内开始为这一「难题」想尽各种办法,后来有神人出山,轻松给出一段代码

function isString(obj) {
  Object.prototype.toString.call(obj) == "[object String]"
}

此代码一出,天下震惊,引各路类库竞折腰。这代码,可不仅仅解决了 isString 的问题,而是解决了 isXxx 一类问题。

神码原理很简单。简言之,是因为 ECMAScript 就是这么规定的,而各个浏览器都遵守了这一规定,因此就有了这一统天下的写法。有兴趣的,可以看这篇文章:instanceof considered harmful (or how to write a robust isArray)

下面说说另一端辛酸史。

鸭子判断

在神码出现前,isString 的问题还好说,甚至可以忽略,但 isArray、isFunction、isRegExp 等函数的问题更大,而且很难忽略。我们拿 isArray 来说说。

typeof [] // => "object"

不要去责怪 typeof,它没错。JavaScript 里,数组就是对象。

[] instanceof Array // => true

instanceof 还是挺不错的,但和 isString 一样,遇到跨页面操作时,就不行。和 isString 还不一样,对于 isArray 来说,这个问题更严重。(想想为什么?)

于是大家想起了一句谚语

If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
如果它看起来像鸭子,会像鸭子一样游泳,也会嘎嘎叫,那么它很可能就是一只鸭子。

比如题图中的动物,大部分人会猜这是一只鸭子。

人类的大脑,对世界的认识里,往往就是鸭子逻辑。这没什么不对,绝大部分情况下都运作良好。题图中的动物的确是只鸭子,我们的判断是对的。

这样,放到代码里,就有了

function isArray(object) {
  return object != null && typeof object === "object" &&
    'splice' in object && 'join' in object
}

上面的代码,在 Prototype 类库中真实存在过。直到神码出现后,才改成以下写法

function isArray(obj) {
  return Object.prototype.toString.call(obj) === "[object Array]"
}

对现代浏览器来说,上面的写法,依旧让各大浏览器引擎的实现者觉得很难受,于是直接有了

Array.isArray([]) // => true

Array 对象中直接有了 isArray 静态方法。当年的详情:Determining with absolute accuracy whether or not a JavaScript object is an array

可惜目前只有 isArray 方法,isFunction、isRegExp 等,依旧得靠我们自己去实现。

放在一起

看完了前人的辛酸史,我们直接拿来用就好。最简单的拿法是

var toString = Object.prototype.toString

function isObject(obj) {
  return toString.call(obj) === "[object Object]"
}

function isString(obj) {
  return toString.call(obj) === "[object String]"
}

function isArray(obj) {
  return toString.call(obj) === "[object Array]"
}

function isFunction(obj) {
  return toString.call(obj) === "[object Function]"
}

在绝大部分代码里,上面这样写,已经很完美。但在 jQuery 或 Sea.js 等类库代码里,考虑压缩后的大小,依旧可以改进

function type( obj ) {
  if ( obj == null ) {
    return String( obj );
  }

  return typeof obj === "object" || typeof obj === "function" ?
      class2type[ core_toString.call(obj) ] || "object" :
      typeof obj;
}

上面是 jQuery 里的写法,有兴趣的可以去研读下。特别是我没提及的 isObject 和 isFunction,这两个历史更曲折悠长,衍生讨论很多。

Sea.js 里,只做了个简单封装

function isType(type) {
  return function(obj) {
    return Object.prototype.toString.call(obj) === "[object " + type + "]"
  }
}

isType 是个返回函数的函数,这样就可以用来生成 isString 等各种方法。这写法主要是为了减少压缩后的大小,不是为了故意耍酷。耍酷的代码要尽量少,要见一个杀一个。

在 Sea.js 里就这么着了,是因为在 Sea.js 的使用场景下,isXxx 都是内部方法,调用处是可预测的,因此无需像 jQuery 那样考虑各种各样场景。

cid 生成

util-lang.js 里,还有一段代码

var _cid = 0
function cid() {
  return _cid++
}

cid 代表的是 client id,一般还会有

  1. uuid - 全宇宙唯一 id 。
  2. uid - 唯一 id 。
  3. cid - 在客户端保持唯一的 id 。

在 Sea.js 的场景下,cid 就足够用了。在够用的情况下,尽量保持简单,这是 Sea.js 的源码追求。

最后

上面的故事,在 JavaScript 开发中,算是很小很小的插曲。但每每回顾,依旧心生敬畏。 对正确性和简单性的追求,是优秀程序员心中的「根因」 ,有了这根,才有了繁花似锦的各种代码。

最后,留几个小作业:

  1. 我们知道 typeof new String("xxx") 返回 "object",请问 typeof String("xxx") 返回什么?为什么?

  2. 为什么我用的是 typeof obj == "string" 而不是 typeof obj === "string"

  3. 下面这种写法,有什么不妥?

      function isString(obj) {
        return obj.constructor === String
      }
  4. Object.prototype.toString.call(obj)({}).toString.call(obj) 的区别是什么?哪个好?

  5. 鸭子判断究竟好不好?那些异常情况,真的需要关注吗?

以上 5 个问题,当回复里都有思考过的回答后,我再更新 Sea.js 源码解析(四)。WTP 的更新频率,取决于大家的参与程度,靠大家了:)

(完)

题图:并不简单的鸭子。


欢迎订阅 WTP(Web 技术与产品交流)微信公众帐号。WTP 关注技术、产品、自由梦,会偶尔推送一些原创文字。欢迎扫描二维码订阅:

@switer

This comment has been minimized.

Copy link

switer commented Jul 30, 2013

逛着github就发现更新了

@suntiger035

This comment has been minimized.

Copy link

suntiger035 commented Jul 30, 2013

相当不错

@dafeizizhu

This comment has been minimized.

Copy link

dafeizizhu commented Jul 30, 2013

  1. 返回"string",因为直接调用String返回的是一个字符串,而new String返回的是一个String对象。
  2. 难道是少写一个=?实在想不出其他理由了。
  3. 这种写法要考虑obj是否null或者undefined的场景,typeof不用。
  4. 应该是前者好,因为不用引入一个新的对象。
  5. 就单纯讨论上述例子的鸭子判断应该存在某些问题,例如存在以下对象:
var fakeArray = {
  "splice": function () {},
  "join": function () {}
};

这样的对象也会被上述鸭子判断的方法当作是一个数组。感觉还是要针对特定的场景使用尽量安全的instanceof或者typeof来判断。

@switer

This comment has been minimized.

Copy link

switer commented Jul 30, 2013

@dafeizizhu 补充第二种情况 new String('xxx') !== 'xxx'。但是也看不出什么道理。

@zhaojinglun

This comment has been minimized.

Copy link

zhaojinglun commented Jul 30, 2013

  1. @dafeizizhu
  2. 猜想===会有一些原型链的读取判断,这里用==足够了
  3. obj是null会报错;且记得周爱民的书中提到,obj是基本类型时候,读取属性的操作会临时创建一个包装类对象,但该对象立即销毁;且constructor是可以被任意修改的,继承时候修改constructor指向
  4. underscore用的前者,jquery用的后者。后者要遍历到{}顶部Object中的toString方法,前者性能更好
@Gerhut

This comment has been minimized.

Copy link

Gerhut commented Jul 30, 2013

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof

Examples 12行

typeof (typeof 1) === 'string'; // typeof always return a string
@suntiger035

This comment has been minimized.

Copy link

suntiger035 commented Jul 30, 2013

1.typeof String("xxx")
String("xxx")是强制类型转换,相当于typeof 'xxx' 所以结果是'string'
2.typeof obj 返回的始终是字符串类型,所以,当与字符串比较的时候,===跟==是没区别的
3.
function func(){}
func.constructor = String;
console.log(typeof func); //返回 'function'
console.log(func.constructor);//返回String
因此,这个时候就不管用了
4.({}).toString Object.prototype.toString 是同一个方法,当运行 ({}).toString === Object.prototype.toString 返回true;因此两者木有区别
5.当页面有嵌套的iframe的时候,方法相互调用,就会出错

@naifen00

This comment has been minimized.

Copy link

naifen00 commented Jul 30, 2013

@dafeizizhu
第2 同疑惑,只是为节省一个字符?
另补充下 3
constructor可以被修改
如:

var arr = new Array();
arr.constructor = String; //当然还可以胡乱修改成任何东西 arr.constructor="abc";

arr.constructor === String;//true
@zzxadi

This comment has been minimized.

Copy link

zzxadi commented Jul 30, 2013

@dafeizizhu @zhaojinglun 2和4在玉伯给的链接JavaScript's typeof operator有答案,@dafeizizhu 是对的。

@sjpsega

This comment has been minimized.

Copy link

sjpsega commented Jul 30, 2013

涨姿势了,感觉自己比较浮躁,这些问题基本回答不上来啊……

@zhaojinglun

This comment has been minimized.

Copy link

zhaojinglun commented Jul 30, 2013

@zzxadi 哈,学习了

@owenXin

This comment has been minimized.

Copy link

owenXin commented Jul 30, 2013

1.String是一个构造函数的同时,本身也是一个函数。 查看了ESMAScript5.1,当将String座位函数调用,它执行一个类型转换。
String([value]),返回一个由于ToString(value)计算出来的字符串值(不是String对象)。
所以这里的typeof String("xxx")返回字"string".
2.我的理解是,typeof本身返回的就是字符串,所以没有必要加"==="进行强比较。"==="会增加类型比较,在这里会影响运行效率。
3.是不是还是因为"iframe 和 top 中的 String 全局对象并不相等"
4.本来以为Object的property属性不能被覆盖,尝试了一下结果是可以被重写的。没能想出来这么写的原因,看了一下@dafeizizhu的,觉得是有点道理的。望玉伯指导啊。
5.个人觉得鸭子判断不是一种很可取的方式,尤其是在软件编程过程中。代码的可扩展性会给这种判断带来更大的复杂性,代码也会比较混乱。如文中,还得增加一个判断头是否是绿色的来判断是否是鸳鸯。 不如直接在定义的时候将它的子类型定义成鸳鸯。

一直想看sea.js的代码,但是平时工作较忙,再者自己看感觉也理解不出那么多精妙的地方。玉伯最近开始写的解析,实在让我太高兴了。每一篇都细读,学到很多,谢谢分享!!

@Gaubee

This comment has been minimized.

Copy link

Gaubee commented Jul 30, 2013

说到类型判断,我一直纠结Object.create([])到底是Object还是Array,个人更倾向于后者。所以我觉得还是typeof判断Array和Object以外的类型,而Array和Object用instanceof来判断……虽然我自己也是用({}).toString.call……

@coolicer

This comment has been minimized.

Copy link

coolicer commented Jul 30, 2013

1.返回string。因为String('xxx'),是原始类型。
2.全等是不会转换类型。
3.constructor会被重写。
4.{}有可能被重写,object上的prototype一般不会吧。

@qiangtou

This comment has been minimized.

Copy link

qiangtou commented Jul 30, 2013

    1. @owenXin 已说得很详细,ESMAScript5.1这样规定的。附上链接 http://es5.github.io/#x15.5.1.1
    1. 这个问题的看法和@dafeizizhu 一致,真是为了节约一个字节?
    1. undefiend,null会出错,同时constructor很容易被重写
    1. 使用字面量虽然可节省字节,但是每次用的时候会多产生一个对象,而且调用toString方法时会访问上层原型链,也就是Object.prototype上的toString属性。性能上会损失不少吧
    1. 关于鸭子,总感觉是一种临时的解决方案,对于当下,也是没办法的办法,最好的情况就是能统一标准,W3C也好,各大浏览器厂商也好。希望代码 中尽量避免这种鸭子判断吧。
@willkan

This comment has been minimized.

Copy link

willkan commented Jul 30, 2013

1.new String() 返回的是String的包装类对象,String()返回的是String
2.typeof返回的就是string,在比较对象类型相同情况下,== 和 ===进行的运算次数是相同度,用==还能节省那么一个字符
3.constructor可以被重写
4.({})可以节省字节,但会在浏览器回收垃圾前保有这么个新建的对象,Object.prototype则无需创建新的对象
5.鸭子其实只是一种推测,准确率基于各宿主对象的实现

@yiminghe

This comment has been minimized.

Copy link

yiminghe commented Jul 30, 2013

这么纠结 isString 啊,isString 等函数(除了 isArray,所有才有了原生实现?)实际上一点用都没有,还耗性能。 new String() 等实际代码里根本不会出现,出现了是自讨苦吃。

所以 kissy 中只调用了 isArray 以及 typeof obj == 'string',类似还有从不调用 hasOwnProperty

@lifesinger

This comment has been minimized.

Copy link
Owner Author

lifesinger commented Jul 30, 2013

@yiminghe 我赞同你的。

@wxnet2013

This comment has been minimized.

Copy link

wxnet2013 commented Jul 30, 2013

仅回答第二个吧:
===全等于是判断值和类型的,这里typeof仅仅需要判断类型,不需要判断值的。故仅用逻辑判断即可,无需全等。

2013/7/30 lifesinger notifications@github.com

@yiminghe https://github.com/yiminghe 我赞同你的。


Reply to this email directly or view it on GitHubhttps://github.com//issues/175#issuecomment-21788445
.

@betarabbit

This comment has been minimized.

Copy link

betarabbit commented Jul 30, 2013

我们知道typeof new String("xxx") 返回 "object",请问 typeof String("xxx") 返回什么?为什么?

返回‘string'。
因为直接调用String()实际上相当于一个toString转换。

为什么我用的是 typeof obj == "string" 而不是 typeof obj === "string"

typeof的结果一定是String型,没必要用===了。

下面这种写法,有什么不妥?

  function isString(obj) {
    return obj.constructor === String
  }

首先,constructorinstanceof一样都存在mutil-frame问题。
其次,constructor和构造函数的prototype属性有关,如果改变构造函数的prototype属性,可能会造成混乱。

function A () {}
var a1 = new A()
A.prototype = {}
var a2 = new A()

a1.constructor === A // true
a2.constructor === A // false
a2.constructor === Object // true

最后,楼上的朋友也说了,constructor是可以被改变的。

Object.prototype.toString.call(obj)({}).toString.call(obj) 的区别是什么?哪个好?

这个还真不知道哪个好,求教。
({}).toString.call(obj) 会生成一个新的Object对象,而 Object.prototype.toString.call(obj)。但是似乎没有什么太大影响,而且用({}).toString.call(obj) 还能少打几个字母,倾向于这种写法……

鸭子判断究竟好不好?那些异常情况,真的需要关注吗?

一般来说不会有什么大问题,可以不用关注,尤其是在可控的情况下。当然,如果是jQuery这样需要处理各种奇葩对象的场合下,还是不要依赖鸭子判断的好,下面的对象很可能被当成一个Array

{
  splice: function () {},
  length: 1,
  join: function () {}
}
@luckydrq

This comment has been minimized.

Copy link

luckydrq commented Jul 31, 2013

  return function(obj) {
    return Object.prototype.toString.call(obj) === "[object " + type + "]"
  }
}

但是这里为什么用上了===?

@ourai

This comment has been minimized.

Copy link

ourai commented Jul 31, 2013

@luckydrq 代码规范,建议用全等。

@weijie789220

This comment has been minimized.

Copy link

weijie789220 commented Jul 31, 2013

来说说我的看法:
1.typeof String("xxx") 返回的是"string", 因为String全局函数内部返回的是一个字符串的字面量,而new String()则会返回一个对象。
2.== 和===的区别就是:一个是等于,一个是等同,前者在两参数进行比较时会视情况做类型转换,而后者则会对比较的参数做严格的类型检查。typeof 运算符的返回值即为String类型,故无需用等同来判断造成多余的开销。
3.不妥,因为constructor为构造函数的prototype对象的属性,并且其属性值为构造函数本身,该属性特性为可读写,即可修改为其他任意值,故不能用作判断。
4.区别是前者直接调用Object对象原型的toString方法,后者虽也是调此方法但多出了查找原型链的步骤,考虑到效率前者较好。
5.个人觉得存在就有他的道理,如类数组的应用:若对象有以数字为名的属性和length属性,即可认为是数组,同样,可以调用数组常用方法,但遍历的时候可能会得到一些不需要的属性 这是值得注意的。

@qiangtou

This comment has been minimized.

Copy link

qiangtou commented Aug 1, 2013

再来说说第二个问题,标准最靠谱

看看Ecma-262 Edition 5.1是怎么描述的吧

==的比较逻辑
===的比较逻辑

从规范看出,==和===在两边类型均相同的情况下,判断逻辑是完全一样的,并没有什么区别,大家可以去比较一下,规范里面的描述每个字符都是一样的。

我想@lifesinger 用==而不用===也是基于这样的考虑吧,既然能确定两边的类型都是string,使用==和===在效果上和性能消耗上都是一样的,那为什么不省一字节呢?

@canvasT

This comment has been minimized.

Copy link

canvasT commented Aug 5, 2013

其实我想说的是第二个问题根本无需纠结,==也好===也罢,都不会有任何影响,@lifesinger 为啥要提这个问题呢?害我为了知道答案看遍了所有评论。

@wndfly

This comment has been minimized.

Copy link

wndfly commented Aug 20, 2013

其实这么写博客真心很不错

@tobeyouth

This comment has been minimized.

Copy link

tobeyouth commented Aug 22, 2013

  1. String(xxx)可以被执行,证明这是一个函数,在控制台里试了一下:
    console.log(new String('abc'))返回的是 String {0: "d", 1: "d"}
    console.log(String('abc'))返回的就是 'abc'
    2.不清楚,先占位
    3.constructor只是个指向构造函数的指针,可以指向任何函数,所以用这个作为判断,多少不靠谱,至于有多不靠谱,上面已经有人说明了。
    4.使用 ({})每次都要新建一个对象,虽然没什么错误,但是频繁使用应该还是很会占用内存的吧...用在手机端,说不定会导致比较容易发热...
@jsw0528

This comment has been minimized.

Copy link

jsw0528 commented Sep 18, 2013

又一个月过去了,没下文了?

@jsw0528

This comment has been minimized.

@loveyrp

This comment has been minimized.

Copy link

loveyrp commented Nov 22, 2013

...........

@ICELI

This comment has been minimized.

Copy link

ICELI commented Feb 28, 2014

四呢。。。。。。。

@AndreLion

This comment has been minimized.

Copy link

AndreLion commented Apr 8, 2014

说好的10篇以上的系列连载呢?不会太监了吧?

@chemdemo

This comment has been minimized.

Copy link

chemdemo commented May 9, 2014

function isString(obj) {
    return obj.constructor === String
}

不是所有对象都有constructor属性的,比如传入undefined或null,这里会抛异常

@lifesinger

This comment has been minimized.

Copy link
Owner Author

lifesinger commented May 9, 2014

@chemdemo 很多时候我们并非需要「完全正确」的代码,而是需要在特定场景下的最优代码。你的疑问文中有解释的:

在 Sea.js 里就这么着了,是因为在 Sea.js 的使用场景下,isXxx 都是内部方法,调用处是可预测的,因此无需像 jQuery 那样考虑各种各样场景。

在 Sea.js 内部调用 isString 方法的地方,可以确保不会出现 undefined / null 等特殊值,因此 isString 的实现无需考虑这种场景。

@wh1100717

This comment has been minimized.

Copy link

wh1100717 commented May 11, 2014

第一个问题:

我们知道 typeof new String("xxx") 返回 "object",请问 typeof String("xxx") 返回什么?为什么?

new String('abc')为对象,输出为:

console.log(new String('abc')) //{ '0': 'a', '1': 'b', '2': 'c' }

String('abc')返回string类型字符串,输出为:

console.log(String('abc')) //abc

因为在JS里面,利用new关键字来生成构造函数,new具体的执行过程大体如下(以new A()举例来说):

  • 创建新的对象x
  • 判断A.prototype
  • 如果是object,则执行x.__proto__ = A.prototype
  • 如果不是object,则执行x.__proto__ = Object.prototype
  • 执行result = A.call(x)
  • 如果resultobject,则返回result
  • 如果result不是object,则返回x

由以上原则可以仿写一个构造函数如下:

var A = function(str){
    //我简单的做了一下初始化赋值,而实际String是对传入参数构造一个对象。
    this.p = 123
    return str
}
console.log(new A('abc'))         //{p:1}
console.log(A('abc'))                //abc

从这个代码可以看出,如果不用new关键字的话,String()实际上是作为一个函数来执行,得到的不过是函数return的返回值而已。

new关键字执行则复杂得多,如果深究的话,两者还有很多不同的地方。

@wh1100717

This comment has been minimized.

Copy link

wh1100717 commented May 11, 2014

第二个问题:

为什么我用的是 typeof obj == "string" 而不是 typeof obj === "string" ?

首先说一下===,其表示严格相等,首先比较类型,再比较值,如果都相同,则返回True

接着说一下==,其表示probably equal. 可以理解模糊判等。

在ECMA-262中对其做了如下定义:

The production EqualityExpression : EqualityExpression == RelationalExpression is evaluated as follows:

  1. Let lref be the result of evaluating EqualityExpression.
  2. Let lval be GetValue(lref).
  3. Let rref be the result of evaluating RelationalExpression.
  4. Let rval be GetValue(rref).
  5. Return the result of performing abstract equality comparison rval == lval. (see 11.9.3).

在11.9.3中具体定义了相等算法

The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:
1.If Type(x) is the same as Type(y), then
a. If Type(x) is Undefined, return true.
b. If Type(x) is Null, return true.
c. If Type(x) is Number, then
i. If x is NaN, return false.
ii. If y is NaN, return false.
iii. If x is the same Number value as y, return true.
iv. If x is +0 and y is -0, return true.
v. If x is -0 and y is +0, return true.
vi. Return false.
d. If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions). Otherwise, return false.
e. If Type(x) is Boolean, return true if x and y are both true or both false. Otherwise, return false.
f. Return true if x and y refer to the same object. Otherwise, return false.
2. If x is null and y is undefined, return true.
3. If x is undefined and y is null, return true.
4. If Type(x) is Number and Type(y) is String,
return the result of the comparison x == ToNumber(y).
5. If Type(x) is String and Type(y) is Number,
return the result of the comparison ToNumber(x) == y.
6. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
8. If Type(x) is either String or Number and Type(y) is Object,
return the result of the comparison x == ToPrimitive(y).
9. If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.
10. Return false.

NOTE 1 Given the above definition of equality:

  • String comparison can be forced by: "" + a == "" + b.
  • Numeric comparison can be forced by: +a == +b.
  • Boolean comparison can be forced by: !a == !b.

NOTE 2 The equality operators maintain the following invariants:

  • A != B is equivalent to !(A == B).
  • A == B is equivalent to B == A, except in the order of evaluation of A and B.

NOTE 3 The equality operator is not always transitive. For example, there might be two distinct String objects, each representing the same String value; each String object would be considered equal to the String value by the == operator, but the two String objects would not be equal to each other. For Example:

  • new String("a") == "a" and "a" == new String("a") are both true.
  • new String("a") == new String("a") is false.

NOTE 4 Comparison of Strings uses a simple equality test on sequences of code unit values. There is no attempt to use the more complex, semantically oriented definitions of character or string equality and collating order defined in the Unicode specification. Therefore Strings values that are canonically equal according to the Unicode standard could test as unequal. In effect this algorithm assumes that both Strings are already in normalised form.

再来看一下

typeof typeof Object    //string

因为typeof Obj 返回的string类型,因此是同类型进行比较,那么=====都能返回正确的结果,但是从执行过程来看,有细微的差别,==实际上比===要多一些边界判断,但是这些都是微乎其微的,性能上几乎没有影响。那么之所以用==的原因应该就是少写了一个=或者是压缩的时候省了一些地方。不过还是觉得应该用===,在CoffeeScript里面就已经严格把==给屏蔽掉了。==应该属于the bad parts of Javascript^_^

@wh1100717

This comment has been minimized.

Copy link

wh1100717 commented May 11, 2014

第三个问题

下面这种写法,有什么不妥?

function isString(obj) {
    return obj.constructor === String
}

1 . constructor可以更改,而typeof是关键字,可以真正返回类型

a = 123
console.log a.constructor      #[Function: Number]
a.constructor = String
console.log a.constructor      #[Function: String]
console.log typeof a           #number

2 . undefinednull所并没有constructor属性,因此当传入的值为undefined或者null的时候会报错

第四个问题

Object.prototype.toString.call(obj)({}).toString.call(obj) 的区别是什么?哪个好?

Object是构造函数,JS已经定义。那么Object.prototype.toString.call(obj)表示的是直接调用Object构造函数对应原型中的toString()方法。

{}是一个对象,等同于new Object(),那么({}).toString.call(obj)表示用new Object()生成一个对象,并且在生成对象的过程中将对象的__proto__指向Object.prototype,然后调用该对象的__proto__.toString函数。

显然第二种方式开销要大得多。

@catchstar

This comment has been minimized.

Copy link

catchstar commented Jul 25, 2014

深深被教育了,非常感谢。
对于上述5个问题的个人解答:
1.返回值为字符串,值为“xxx”,因为前者的String是作为构造函数来调用,返回object对象(虽然值也为xxx),而后者是作为普通函数调用,相当于做了次类型转换,返回值为xxx的字符串
2.首先此处代码是判断是否是字符串的条件之一。当typeof 操作符的对象是字符串(不是String对象)时,返回“string”,==此时不用再做类型转换,==和===接下来所做工作一样,都只是判断值是否相等,没必要多用一个字符,save the world by saving a character(from alexanderdickson)
3.因为constructor是一个可读写的属性,可进行修改,这种判断不稳妥
4.此处区别是后者是先创建一个Object对象,然后调用Object原型上的toString方法,而前者是直接利用Object原型上方法,不需创建
5.鸭子判断好处是,我们可以利用经验和直觉来判断事物,而且往往是正确的。但同时这种由经验和直觉得出的判断会让我们在特殊情况下得出错误结论。当有一种未知生物走起来像鸭子,但它却不是鸭子时候,我们这种判断就出问题

@ghost

This comment has been minimized.

Copy link

ghost commented Aug 4, 2014

@lifesinger 四呢?看完前三篇,收益很多~不再连载了吗?

@ligaofeng0901

This comment has been minimized.

Copy link

ligaofeng0901 commented Nov 29, 2014

同求四

@SansaZhang

This comment has been minimized.

Copy link

SansaZhang commented Feb 18, 2016

求续集

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment