常见到一些代码或笔试题会出现针对旧浏览器(主要是IE6/7/8)的 getElementsByClassName 方法的 polyfill,我在面试一些初学者时也常常问起,不过回答差不多者寥寥。其实想完整的实现与原生相同的功能基本不可能,原因之一即是不管该方法返回的是 HTMLCollection 还是 NodeList,它们都必须是 alive 的,意即针对 DOM 树的任何改动都会实时反应到其中。因此,非瞬时地缓存集合的长度是危险的。同时由于不能构造一个 HTMLCollectionNodeList ,我们一般会以数组代之,因此集合是不可变的。

所有 getElements* 方法返回的集合都是 live 的,只有 querySelector 返回的 static 的。测试参见这里;

引用

CSS3 对 text-decoration 进行了扩展,新的语法为:

text-decoration: text-decoration-style text-decoration-color text-decoration-line;

目前只有 blink 内核对其进行了支持,但很不完善,或者说是一个bug。描述为:无法设置新的格式,但是却可以获取。例如:

<p id="p" style="text-decoration:underline"></p>
<script>
    var $p = document.querySelector('#p');
    console.log($p.style.textDecoration);//underline solid rgba(0,0,0)
    $p.style.textDecoration = "overline solid dashed #fff";
    console.log($p.style.textDecoration);//underline solid rgba(0,0,0)
</script>

该问题从Chrome 31开始出现,我提了 bug 给 chromium ,在后来的Chrome 37 中修复了一部分,不再返回 text-decoration-colortext-decoration-line,同时仍然无法设置。

这个 bug ,jQuery 拒绝修复,理由是 1.x 与 2.x 都仅保证支持 Chrome 的最近两个版本,同时这也不是个严重的问题,当前,Chrome 39 已经发布了。

昨天 GitHub 上捷克共和国一哥们提出几个 issues,讲我的某个石器时代土著插件不支持 Sublime Text Command 并且 Context Menu 缺失。其实这两个功能入口仅需要编辑两个配置文件就可以了,但前提是命令数量是有限的,这样才可以直接写死配置文件。

只是这个插件提供的主要功能即是无限扩展命令完成各种插入操作,默认提供了4种常见插入,此外可以无限配置插入种类,如果全部都要配置快捷键,确实不合理。

Sublime 插件配置文件分为几种:

  • sublime-menu,右键菜单和主菜单
  • sublime-commands,文本命令
  • sublime-keymap,快捷键定义
  • sublime-settings,插件主要配置文件

前三种格式全为数组,最后一个为对象,也只有最后一个 Sublime 单独提供了 API 供读写:

sublime.load_settings()
sublime.save_settings()

一般地,我们只需要读取就行了。

Text CommandContext Menu 需要修改 menucommands 配置文件,因为它们是数组格式,不能通过上面的 API 读写,所以不得不使用 python 的原生API进行写入操作。在每次插件加载时,动态更新这两个文件,编辑器本身也能立即得到通知。

settings 配置文件被修改时理应重新进行一次上面的操作,但是 Sublime API 的 Settings.add_on_change 似乎不怎么起作用,因此不得不强制要求编辑器重启来刷新设置。

引用

下面是从http://justineo.github.io/slideshows/semantic-html摘录的关于大部分 HTML 标签语义化含义的内容,可简要理解各种标签的含义。

全局属性

id

标示符 (用于引用),不应依赖其语义处理相应元素

class

authors are encouraged to use values that describe the nature of the content

title
  • 链接 - 描述目标信息
  • 图片 - 版权 / 描述
  • 引用 - 来源信息
  • 交互元素 - 操作指南
lang

内容的语言

元数据 (metadata)

meta
  • 元数据
  • name 属性决定种类,content 属性表示内容
  • 标准元数据名 (application-name author description generator keywords)
  • 已注册的扩展元数据名 (WHATWG Wiki MetaExtensions)
链接(links)
链接类型
  • 外部资源链接
    指向用来组成当前文档的外部资源,通常由 UA 自动处理
  • 超链接
    用来「导航」到其他资源 (可以在 UA 中打开 下载 …)
  • 元数据,用来描述文档本身与其他资源的关系

  • 必须包含 rel 及 href 属性

link + rel + author link + rel + license 都有预定义的语义

  • rel=”stylesheet” 链接到样式表 (外部资源)

  • rel=”alternate” 链接到当前文档的其他形式 (超链接)

  • rel=”prev” rel=”next” 链接到文档的前一篇 / 后一篇 / 前一页 / 后一页 (超链接) 在生成站点目录、归档视图时很有帮助。

  • rel=”icon” 当前文档的 favicon (外部资源)

a元素
  • 存在 href 属性时为超链接

  • 缺少 href 属性时为链接占位符

与 link 元素不同,a 元素代表的超链接都是显式的。

a + rel
  • rel=”prev” rel=”next” 链接到文档的前一篇 / 后一篇 / 前一页 / 后一页 (超链接)
  • rel=”nofollow”

当前文档的作者并不推荐超链接指向的文档 (超链接标注)
由 Google 引入,他们认为适用场景有 (via):

  1. 不可信赖的内容
  2. 付费链接
  3. 按优先级别进行抓取 (比如通知 Googlebot 不要抓取「注册」或「登陆」页面)
rel 属性

其他在 HTML 规范中预定义的 rel 属性值及其含义参见 HTML5 草案中 Link types 一节

区块 (sections)

section 元素
  • 按主题将内容分组,通常会有标题 (heading)
  • 并非「语义化的 div」

何时使用?

一个简单的评判标准:当你希望这个元素的内容体现在文档的_提纲 (outline)_ 中时,用 section 是合适的。

可以帮助 UA 迅速获得导航内容,例如读屏器可以省去很多渲染直接跳到导航位置。

不一定要包含 ul,也可用自然文本进行导航。

article 元素
  • 独立的文档、页面、应用、站点
  • 可以单独发布、重用
  • 可以是…
  • 一篇帖子
  • 一篇报刊文章
  • 一则用户评论
  • 一个可交互的 widget
aside 元素
  • 表示与周围内容关系不太密切的内容 (eg. 广告)
  • 通常表现为侧边栏内容 (eg. 相关背景内容)、引述内容
h1–h6 元素
<body>
    <h1>Let's call it a draw(ing surface)</h1>
    <h2>Diving in</h2>
    <h2>Simple shapes</h2>
</body>

语义上等价于:

<body>
  <h1>Let's call it a draw(ing surface)</h1>
  <section>
    <h1>Diving in</h1>
  </section>
  <section>
    <h1>Simple shapes</h1>
  </section>
</body>
hgroup 元素
  • 标题的组合

  • 用于副标题、标语 (tagline) 等

    The Lord of the Rings

    The Return of the King

    The Lord of the Rings

    "One ring to rule them all."

hgroup 中级别最高的标题才出现在提纲中

header 元素
  • 一组介绍性描述或导航信息 (目录 / 搜索框 / logo / …)

  • 通常包含 h1–h6 hgroup

  • 不影响文档提纲的生成

    Welcome to...

    Voidwars!

  • 代表最近的父级区块内容的页脚

  • 作者信息 / 相关文档 / 版权信息

  • 不影响文档提纲的生成

address 元素

代表与最近的父级 article 或 body 关联的联系人信息

<address>
  <a href="../People/Raggett/">Dave Raggett</a> 
  <a href="../People/Arnaud/">Arnaud Le Hors</a> 
  contact persons for the <a href="Activity">W3C HTML Activity</a>
</address>

分组内容 (grouping content)

p 元素
  • 「段落」的显式表述
    段落是主题接近的若干句子组成的文本块 (via)
    -非优先考虑的选择
    例如 address 的内容也是一个段落,但有更准确的语义
hr 元素
  • 原意为「horizontal rule」(水平分隔线)
  • HTML5 中重定义为不同主题内容间的分隔符
  • 区块内容之间不需要用 hr 元素分隔
pre 元素
  • 表示已排版的内容
  • 代码片段 / ASCII art / …
blockquote 元素
  • 引用的来自其他来源的内容

  • cite 属性表示该来源的 URL

  • 署名必须放在 blockquote 外

    His next piece was the aptly named Sonnet 130:

    My mistress' eyes are nothing like the sun
    Coral is far more red than her lips red
    [...]

ol ul li 元素
  • 有序 / 无序列表

  • 改变列表项顺序是否影响表达

  • ol 下 li 元素的 value 属性代表该列表项的序号值

    Relegation zone:

    1. Bolton Wanderers
    2. Blackburn Rovers
    3. Wolverhampton Wanderers
dl dt dd 元素
  • 名值对的集合

  • 术语定义表 / 元数据 / FAQ / …

    happiness
    n.
    The state of being happy.
    Good fortune; success. Oh happiness! It worked!
    rejoice
    v.intr. To be delighted oneself .
    v.tr. To cause one to be delighted.
figure 元素
  • 比较独立的、被主要内容引用的部分
  • 插画 / 图表 / 照片 / 代码 / …
  • 通常会有一个标题 (figcaption)
figcaption 元素
  • 图表标题 / 图例 / 代码说明 / …
div 元素
  • 本身无语义
  • 可以和 class lang title 等属性结合,为一系列连续的内容增加语义
  • 最后考虑的选择

文本级语义 (text-level semantics)

em 元素
  • 表示侧重点的强调

  • 强调级别由 em 的嵌套个数决定

  • em 的位置不同,文本本身含义不同

  • 在可视化 UA 上一般渲染为斜体

    Bats can fly.

    Bats can fly.

    Bats can fly.

三句含义各不相同

strong 元素
  • 表示内容的重要性

  • 重要程度由 strong 的嵌套个数决定

  • strong 的位置不同,文本本身含义不变

  • 在可视化 UA 上一般渲染为粗体

    Warning. A huge wave of zombies is approaching.

i 元素
  • 不再只是「斜体」

  • 表示另一种叙述方式

  • 分类学名词 / 外来语片段 / 舞台指示 / 船名 / …

  • 建议与 class / lang 属性搭配使用

    Sunflower (Helianthus annuus) is an annual plant native to the Americas.

    There is a certain je ne sais quoi in the air.

    Titanic sank in the North Atlantic Ocean on 15 April 1912.

b 元素
  • 不再只是「粗体」

  • 表示某种需要引起注意却又没有其他额外语义的内容

  • 摘要中的关键词 / 评介中的产品名称 / 文章的开篇内容 …

  • 建议与 class 属性搭配使用

    Kittens 'adopted' by pet rabbit

    Six abandoned kittens have found an unexpected new mother figure — a pet rabbit.

    Veterinary nurse Melanie Humble took the three-week-old kittens to her Aberdeen home.

    [...]
small 元素
s 元素
  • 不再只是「带删除线的文字」

  • 表示不再准确或不再相关的内容

  • 与 del 元素含义不同

    Buy our Iced Tea and Lemonade!

    Recommended retail price: $3.99 per bottle

    Now selling for just $2.99 a bottle!

u 元素
  • 不再只是「带下划线的文字」

  • 表示用非文本进行的标注的内容

  • 中文专名 / 拼写检查的错误内容 / …

    屈原放逐,乃賦離騒左丘失明,厥有國語。(司馬遷《報任安書》)

cite 元素
  • 引述的作品标题

  • 书 / 论文 / 散文 / 电影 / 歌曲 / 电视节目 / 画作 / …

    My favorite movie is Transformers by Michael Bay.

q 元素
  • 引用的来自其他来源的段内内容

  • cite 属性表示该来源的 URL

  • 不用 q 而用引号亦正确

    The W3C page About W3C says the W3C's mission is To lead the World Wide Web to its full potential by developing protocols and guidelines that ensure long-term growth for the Web.

abbr 元素
  • abbreviation or acronym (区别?)

  • 其 title 属性的含义为所写的全称

    The WHATWG started working on HTML5 in 2004.

建议在用户不熟悉的缩写词汇第一次出现时用 abbr + title 进行语义标注,帮助其理解

dfn 元素
  • 用来展现一个术语的定义实例

  • 最接近的父级段落、定义列表组或区块内容必须包含 dfn 元素指定术语的定义

    The GDO is a device that allows off-world teams to open the iris.

很容易抽取出特定术语的含义,从而很容易回答「What is …?」类的问题

time 元素
  • 为表述的内容增加一个机器可读的时间数据

  • datetime 属性值必须是预定义的几种时间格式之一

  • 如果不含 datetime 属性,则会解析其文本内容值

    http://www.web2con.com/ Web 2.0 Conference: - at the Argent Hotel San Francisco CA
code samp kbd 元素
  • code - 代码片段
  • samp - 计算机程序的输出
  • kbd - 用户输入的内容 / 按键
mark 元素
  • 在引用的文字中使用,表示在当前文档中需要引起注意但原文中并没有强调的含义 (eg. 对一篇文章的分析中对原文的标注)

  • 表示与用户当前的行为相关的内容 (eg. 高亮显示搜索关键词)

    6月13日下午,一场大雨过后,正阳门箭楼被带着水雾的脚手架包裹得严严实 实。北京旧城中轴线上的这座标志性建筑,正经历着新中国成立后规模最大的一次修缮。

    [...]

    6月13日的那场大雨,将故宫端门外西朝房冲洗得干干净净。

    作者为什么两次提到6月13日的大雨?请谈谈你的看法。

ruby rt rp 元素
  • 注音标示,「ruby」来自日本印刷业

  • 主要于 CJK 文字

    ()(xié)(shè)(huì)

span 元素
  • 本身无语义

  • 可以和 class lang 等属性结合,为文本片段增加语义

  • 有更合适的元素时不应选择 span

    var greet = function() {
    console.log(“Hello world.”);
    }

更改记录 (edits)

ins del 元素
  • 表示对当前文档内容进行的增添与删改
  • cite 属性指向对某个修改的说明文档的 URL
  • datetime 属性表示了修改发生的时间 (取值规范)
  • 用来记录文档的编辑历史

嵌入内容 (embedded content)

img 元素
  • src alt 属性决定了图片的含义
  • 有 src 且 alt 为空字符串,代表装饰用图
  • 有 src 且 alt 为非空字符串,图为文档内容的一部分
  • 有 src 且无 alt,图为内容一部分但无等价的文本内容可用
  • 用 alt 文本替换图片,文档含义尽可能不变

    You are standing in an open field west of a house. A white houseThe house is white  with a boarded front door.    There is a small mailbox here.

iframe embed object param 元素
  • iframe - 内嵌的浏览上下文

  • embed - 外部应用或可交互内容的整合入口

  • object - 通用外部资源 根据具体内容可以被处理为图片、内嵌的浏览上下文、供插件调用的资源

  • param - 为 object 元素传递的参数


相当于 imgiframe 的效果

<embed src="catgame.swf" type="application/x-shockwave-flash" quality="high">

<object data="catgame.swf" type="application/x-shockwave-flash"> 
  <param name="quality" value="high">
  <p>Plugin needed.</p>
</object>

功能等价但 object 提供更好的回退策略

多媒体元素
  • video - 视频
  • audio - 音频

公共属性:src crossorigin preload autoplay mediagroup loop muted controls

source 元素
  • 表示所在多媒体元素的可替代资源 (可能不同格式 / 清晰度,读取失败或无法解码时可以依次尝试)

  • type 属性中除了 MIME 类型外,可使用 codecs= 来指定编码

track 元素
  • 用来为多媒体元素指定「文本轨」

  • kind 属性描述文本轨的类型,可用值包括 subtitles captions descriptions chapters metadata

表格数据 (tabular data)

table 元素
  • 用来表示超过一维的数据
caption 元素
  • 表示所处的 table 的标题

当所处的 table 是外部 figure 元素的唯一子元素,应首选 figcaption

tbody thead tfoot 元素
  • 均为一组表格行
  • thead 表示列头 (通常为列标题,单元格用 th 元素)
  • tfoot 表示列脚 (通常为列数据汇总)
col colgroup tr 元素
  • 列,列组,行
td th 元素
  • td - 数据单元格
  • th - 标题单元格

th 的 scope 属性表示标题对应的数据范围

类似于 Java 语言中将异常(Exception)分为 CheckedExceptionRuntimeException一样,ECMA262 定义 Error 分为“早期错误(early error)”与“运行时错误(runtime error)”。

“早期错误”意即能够在程序进行任意构造求值操作之前检测到并报出的错误,包含下面几类:

  1. 语法错误;
  2. 对同一个属性定义多个 setter 或多个 getter;
  3. 对同一个属性同时定义 value 和 setter/getter;
  4. 正则表达式语法错误;
  5. 严格模式中有 重复的属性赋值;
  6. 严格模式中使用 wi th 关键字;
  7. 独立严格模式下的 函数定义中具有重复的参数定义;
  8. returnbreakcontinue 的不合适使用;
  9. 向非引用赋值

除了这些错误以外都属于运行时错误,不同的错误类型将会对程序带来一定的影响,例如:

<script>var i =0  j = 0;</script>
<script>
    ++i;
    syntax error
</script>
<script>
    ++j;
    throw new Error('runtime error');
</script>
<script>
    console.log(i + '/' + j);//0/1
</script>

内建的 Error 包含下面几种类型:

  • EvalError
  • InternalError
  • RangeError
  • ReferenceError
  • SyntaxError
  • TypeError
  • URIError
参考

List of ES3 Incompatibilities introduced by ES5.

From Annex E:

  • 7.1: Unicode format control characters are no longer stripped from ECMAScript source text before processing. In Edition 5 if such a character appears in a StringLiteral or RegularExpressionLiteral the character will be incorporated into the literal where in Edition 3 the character would not be incorporated into the literal.

    (function () {
    return eval(‘“\u200C\u200D\uFEFF”.length == 3’);
    })();

  • 7.2: Unicode character is now treated as whitespace and its presence in the middle of what appears to be an identifier could result in a syntax error which would not have occurred in Edition 3.

    (function () {
    try {
    eval(‘var foo\uFEFFbar’);
    } catch (e) {
    return e instanceof SyntaxError;
    }
    })()

  • 7.3: Line terminator characters that are preceded by an escape sequence are now allowed within a string literal token. In Edition 3 a syntax error would have been produced.

    (function () {
    try {
    eval(“‘foo\\nbar’;”);
    return true;
    } catch (e) {
    return false;
    }
    })();

  • 7.3: Regular expression literals now return a unique object each time the literal is evaluated. This change is detectable by any programs that test the object identity of such literal values or that are sensitive to the shared side effects.

    (function () {
    function re(){ return /(?:)/; }
    return re() !== re();
    })();

  • 7.8.5: Edition 5 requires early reporting of any possible RegExp constructor errors that would be produced when converting a RegularExpressionLiteral to a RegExp object. Prior to Edition 5 implementations were permitted to defer the reporting of such errors until the actual execution time creation of the object.

  • 7.8.5: In Edition 5 unescaped “/“ characters may appear as a CharacterClass in a regular expression literal. In Edition 3 such a character would have been interpreted as the final character of the literal.

    (function () {
    try {
    var re = eval(‘/[/]/‘);
    return re.test(‘/‘);
    } catch (e) {
    return false;
    }
    })();

  • 10.4.2: In Edition 5 indirect calls to the eval function use the global environment as both the variable environment and lexical environment for the eval code. In Edition 3 the variable and lexical environments of the caller of an indirect eval was used as the environments for the eval code.

    (function (global) {
    //TODO: maybe try other types of indirect calls
    return (function () { return global === (0 eval)(‘this’); }).call({});
    })(this);

  • 15.4.4: In Edition 5 all methods of Array.prototype are intentionally generic. In Edition 3 toString and toLocaleString were not generic and would throw a TypeError exception if applied to objects that were not instances of Array.

    (function () {
    try {
    Array.prototype.toString.call({});
    Array.prototype.toLocaleString.call({});
    return true;
    } catch (e) {
    return false;
    }
    });

  • 10.6: In Edition 5 the array indexed properties of argument objects that correspond to actual formal parameters are enumerable. In Edition 3 such properties were not enumerable.

    (function () {
    return arguments.propertyIsEnumerable(‘0’);
    })(0);

  • 10.6: In Edition 5 the value of the [[Class]] internal property of an arguments object is “Arguments”. In Edition 3 it was “Object”. This is observable if toString is called as a method of an arguments object.

    (function () {
    return ({}).toString.call(arguments) == “[object Arguments]”;
    })();

  • 12.6.4: for-in statements no longer throw a TypeError if the in expression evaluates to null or undefined. Instead the statement behaves as if the value of the expression was an object with no enumerable properties.

    (function () {
    try {
    for(var prop in null);
    for(prop in undefined);
    } catch (e) {
    return false;
    }
    return true;
    })();

  • 15: Implementations are now required to ignore extra arguments to standard built-in methods unless otherwise explicitly specified. In Edition 3 the handling of extra arguments was unspecified and implementations were explicitly allowed to throw a TypeError exception.

  • 15.1.1: The value properties NaN Infinity and undefined of the Global Object have been changed to be read-only properties.

    (function (_NaN _Infinity _undefined) {
    NaN = Infinity = undefined = null;
    if (!isNaN(NaN) || Infinity != _Infinity || undefined !== _undefined) {
    //TODO: restore values
    return false;
    }
    return true;
    })(NaN Infinity);

  • 15.1.2.1: Implementations are no longer permitted to restrict the use of eval in ways that are not a direct call. In addition any invocation of eval that is not a direct call uses the global environment as its variable environment rather than the caller’s variable environment.

    (function (global) {
    try {
    return [eval]0 === global;
    } catch (e) {
    return false;
    }
    }).call({} this);

  • 15.1.2.2: The specification of the function parseInt no longer allows implementations to treat Strings beginning with a 0 character as octal values.

    (function () {
    return parseInt(‘010’) === 10;
    })();

  • 15.3.4.3: In Edition 3 a TypeError is thrown if the second argument passed to Function.prototype.apply is neither an array object nor an arguments object. In Edition 5 the second argument may be any kind of generic array-like object that has a valid length property.

    (function () {
    try {
    return (function (a b) { return a + b == 10; }).apply({} {0:5 1:5 length:2});
    } catch (e) {
    return false;
    }
    })();

  • 15.3.4.3 15.3.4.4: In Edition 3 passing undefined or null as the first argument to either Function.prototype.apply or Function.prototype.call causes the global object to be passed to the indirectly invoked target function as the this value. If the first argument is a primitive value the result of calling ToObject on the primitive value is passed as the this value. In Edition 5 these transformations are not performed and the actual first argument value is passed as the this value. This difference will normally be unobservable to existing ECMAScript Edition 3 code because a corresponding transformation takes place upon activation of the target function. However depending upon the implementation this difference may be observable by host object functions called using apply or call. In addition invoking a standard built-in function in this manner with null or undefined passed as the this value will in many cases cause behaviour in Edition 5 implementations that differ from Edition 3 behaviour. In particular in Edition 5 built-in functions that are specified to actually use the passed this value as an object typically throw a TypeError exception if passed null or undefined as the this value.

    (function () {
    try {
    //Maybe test ({}).toString.call(null) == ‘[object Null]’
    return ({}).hasOwnProperty.call(null ‘’) false;
    } catch (e) {
    //ToObject in hasOwnProperty should throw TypeError if null
    return true;
    }
    })();

  • 15.3.5.2: In Edition 5 the prototype property of Function instances is not enumerable. In Edition 3 this property was enumerable.

    !Function.propertyIsEnumerable(‘prototype’);

  • 15.5.5.2: In Edition 5 the individual characters of a String object’s [[PrimitiveValue] may be accessed as array indexed properties of the String object. These properties are non-writable and non-configurable and shadow any inherited properties with the same names. In Edition 3 these properties did not exist and ECMAScript code could dynamically add and remove writable properties with such names and could access inherited properties with such names.

    (function () {
    String.prototype[1] = ‘x’;
    var foo = new String(‘foo’);
    foo[0] = ‘y’; //non-writable
    delete foo[0]; //non-configurable
    return foo[0] == ‘f’ && foo[1] == ‘o’;
    })();

  • 15.9.4.2: Date.parse is now required to first attempt to parse its argument as an ISO format string. Programs that use this format but depended upon implementation specific behaviour (including failure) may behave differently.

    (function () {
    try{
    return !!Date.parse(“2014-09-07T15:24:08.011Z”);
    }catch(e){
    return false;
    }
    })();

  • 15.10.2.12: In Edition 5 \s now additionally matches <BOM>.

    (function () {
    return /\s/.test(‘\uFEFF’);
    })();

  • 15.10.4.1: In Edition 3 the exact form of the String value of the source property of an object created by the RegExp constructor is implementation defined. In Edition 5 the String must conform to certain specified requirements and hence may be different from that produced by an Edition 3 implementation.

  • 15.10.6.4: In Edition 3 the result of RegExp.prototype.toString need not be derived from the value of the RegExp object’s source property. In Edition 5 the result must be derived from the source property in a specified manner and hence may be different from the result produced by an Edition 3 implementation.

  • 15.11.2.1 15.11.4.3: In Edition 5 if an initial value for the message property of an Error object is not specified via the Error constructor the initial value of the property is the empty String. In Edition 3 such an initial value is implementation defined.

    (function () {
    var error = new Error();
    return typeof error.message == ‘string’ && error.message.length == 0;
    })();

  • 15.11.4.4: In Edition 3 the result of Error.prototype.toString is implementation defined. In Edition 5 the result is fully specified and hence may differ from some Edition 3 implementations.

    (function () {
    var foo = new Error bar = new Error;
    foo.name = ‘Foo’;
    foo.message = bar.name = ‘Bar’;
    return foo.toString() == ‘Foo: Bar’ && bar.toString() == ‘Bar’;
    })();

  • 15.12: In Edition 5 the name JSON is defined in the global environment. In Edition 3 testing for the presence of that name will show it to be undefined unless it is defined by the program or implementation.

    (function (global) {
    return typeof global.JSON != ‘undefined’;
    })(this);

From Annex D:

  • 11.8.2 11.8.3 11.8.5: ECMAScript generally uses a left to right evaluation order however the Edition 3 specification language for the > and <= operators resulted in a partial right to left order. The specification has been corrected for these operators such that it now specifies a full left to right evaluation order. However this change of order is potentially observable if side-effects occur during the evaluation process.

    (function(){
    var i = 1 j = 1;
    (i*=2) > 1 > (i+=1);
    (j*=2) <= 1 <= (j+=1);
    return 3 === i && 3 === j;
    })();

  • 11.1.4: Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitializer does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this.

    (function () {
    return [1 ].length === 1;
    })();

  • 11.2.3: Edition 5 reverses the order of steps 2 and 3 of the algorithm. The original order as specified in Editions 1 through 3 was incorrectly specified such that side-effects of evaluating Arguments could affect the result of evaluating MemberExpression.

  • 12.4: In Edition 3 an object is created as if by new Object() to serve as the scope for resolving the name of the exception parameter passed to a catch clause of a try statement. If the actual exception object is a function and it is called from within the catch clause the scope object will be passed as the this value of the call. The body of the function can then define new properties on its this value and those property names become visible identifiers bindings within the scope of the catch clause after the function returns. In Edition 5 when an exception parameter is called as a function undefined is passed as the this value.

  • 13: In Edition 3 the algorithm for the production FunctionExpression with an Identifier adds an object created as if by new Object() to the scope chain to serve as a scope for looking up the name of the function. The identifier resolution rules (Section 10.1.4 in Edition 3) when applied to such an object will if necessary follow the object’s prototype chain when attempting to resolve an identifier. This means all the properties of Object.prototype are visible as identifiers within that scope. In practice most implementations of Edition 3 have not implemented this semantics. Edition 5 changes the specified semantics by using a Declarative Environment Record to bind the name of the function.

  • 15.10.6: RegExp.prototype is now a RegExp object rather than an instance of Object. The value of its [[Class]] internal property which is observable using Object.prototype.toString is now “RegExp” rather than “Object”.

    (function () {
    return ({}).toString.call(RegExp.prototype) == ‘[object RegExp]’;
    })();

Other changes:

  • 11.5.1: A PropertyName in a PropertyAssignment can consist of an
    IdentifierName this makes possible to use Reserved Words.

    (function () {
    var obj;
    try {
    eval(‘obj = {if:1}’);
    return obj[‘if’] == 1;
    } catch (e) {
    return false;
    }
    })();

hasOwnPropertyin 都可以用来判断一个对象的成员是否存在,但有很大的区别,前者不会搜索对象的原型链中的成员,但后者会;前者是 Object 原型中的函数,后者是 Javascript 操作符等。关于第一中区别,可以通过阅读 ECMAScript 规范来了解其细节。

hasOwnProperty

[ECMA-262 3rd edition](http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262 %203rd%20edition %20December%201999.pdf) 对 hasOwnProperty 的描述非常简单:“返回对象(不包括原型链)中是否有该成员”。该版本针对对象成员定义了四个属性:ReadOnlyDontEnumDontDeleteInternal

ECMA-262 5.1rd edition 重新定义了成员属性,提供了更灵活的访问权限。在该版本中,对象成员分为“数据成员(Data Property)”和“存取成员(Accessor Property)”两种。前者包含属性:ValueWritableEnumerableConfigurable,后者包含GetSetEnumerableConfigurable。对于不同的成员定义方式,其类型自然也不同。

var o = {
    a:1 //Data Property
    set b(){} //Accessor Property
    get c(){} //Accessor Property
};
o.d = 1;//Data Property
o.__defineSetter('m'  function(m){});//Accessor Property
o.__defineGetter('n'  function(n){});//Accessor Property
Object.defineProperty(o 'e' {});//Data Property
Object.defineProperty(o 'e' {set:function(){}});//Accessor Property
Object.create(null {f:{}});//Data Property
Object.create(null {f:{set:function(){}}});//Accessor Property

需要注意的是不能同时定义“value”与“set/get”。

ES5 中的 hasOwnProperty 需要访问一个内部方法:GetOwnProperty,该方法返回一个新的属性描述符(Property Descriptor ),或者是“数据成员”或者是“存取成员”。对于字符串来说,它有一个自己的 GetOwnProperty方法,允许以数字作为 property name,代表在指定位置上是否存在字符,该值不超过字符串长度减一。该方法仅返回“数据成员”格式,因为字符串是不可变的。

in

in 操作符只能用于对象而非简单类型,它调用内部方法 HasProperty,该方法递归搜索原型链,直到找到对应的成员。在 ES5 中,该方法还涉及另一个内部方法:GetOwnProperty。因此对于字符串,下面的表达式返回真:

2 in new String('abc')
总结

判断一个对象 O 是否携带有成员 P,一般可以:

undefined === O.P

但对于值为 undefined 的成员无效,这时就需要使用 in 操作符:

var Class = function(){this.P = undefined;};
Class.prototype = {Q:undefined;};
var O = new Class();

!!O.P;//false
!!O.Q;//false
'P' in O;//true
'Q' in O;//true
O.hasOwnProperty('P');//true
O.hasOwnProperty('Q');//false

JavaScript 中的==运算符用以比较两侧的值是否“近似”相等,区别于===的严格相等。

==可以达到以下效果:

null==undefined //true
[]==false //true
[]=='' //true
[1]=='1' //true

要说明 JavaScript 引擎在计算 == 运算符时做了什么,先要了解几个内部概念和方法。

Type

ECMAScript 规范规定了六种变量类型:null undefined string number boolean object。Type 不同于运算符 typeof ,它可以分辨出 null 和 object,但不能分辨 function 和 object,当然事实上并没有 function 这样一种类型。

可以这样模拟 Type 的行为:

function Type(e) {
    if (undefined === e) {
        return 'undefined';
    } else if (null === e) {
        return 'null';
    } else if ('number' === typeof e) {
        return 'number';
    } else if ('string' === typeof e) {
        return 'string';
    } else if ('boolean' === typeof e) {
        return 'boolean';
    } else return 'object';
}
0的符号

另外需要说明的是0本身包括两个值,正零:+0,和负零:-0。一般不会对这两个值进行区分,甚至使用 === 运算符也分辨不出:

+0===-0 //true

在ECMAScript中有内部内部方法可以区别出这两个值,当然我们也可以做到这一点:

function isPositiveZero(e) {
    return 0 === e && 1 / e > 0;
}

function isNegativeZero(e) {
    return 0 === e && 1 / e < 0;
}

关于这两点将会在后面的侦断中用到。

ToNumber

转换为数字,规则如下:

输入类型 结果
Undefined NaN
Null +0
Boolean 真返回1,假返回+0
Number 直接返回
String 字面意义
Object 调用ToNumber(toPrimitive),hint:Number
toPrimitive

该内部方法将一个对象转换为原始类型,在上面提到的六种类型中,前五种都属于原始类型。对于非原始类型,将根据一个成为 hint 的值访问该对象的 Default Value 属性来获取原始值。hint 取值只能为 “string” 和 “number”(默认)。如果为 “string”,将依次调用对象的 toStringvalueOf 来获取原始值,如果为 “number”,将依次调用 valueOftoString 方法,可见顺序依赖于 hint 值。

==

== 操作x,y两个值时要经过一系列的类型和值的侦断,在 ECMAScript 内部称之为 The Abstract Equality Comparison Algorithm

  1. 如果 Type(x) 不同于 Type(y) 执行第 14 步。
  2. 如果 Type(x) 为 Undefined 返回真。
  3. 如果 Type(x) 为 Null 返回真。
  4. 如果 Type(x) 不是 Number 执行第 11 步。
  5. 如果 x 为 NaN 返回假。
  6. 如果 y 为 NaN 返回假。
  7. 如果 x 与 y 有相同的值 返回真。
  8. 如果 x 为 +0 并且 y 为 −0 返回真。
  9. 如果 x 为 −0 并且 y 为 +0 返回真。
  10. 返回假。
  11. 如果 Type(x) 为 String 那么如果 x 和 y 具有相同的字符序列(等长并且对应位置字符相同)返回真,否则,返回假。
  12. 如果 Type(x) 为 Boolean 如果 x 和 y 都是真或者都是假则返回真,否则 返回假。
  13. 如果x和y引用相同的对象返回真,否则 返回假。
  14. 如果 x 为 null 并且 y 为 undefined 返回真。
  15. 如果 x 为 undefined 并且 y 为 null 返回真。
  16. 如果 Type(x) 为 Number 并且 Type(y) 为 String 返回 x == ToNumber(y) 的结果。
  17. 如果 Type(x) 为 String 并且 Type(y) 为 Number 返回 ToNumber(x) == y 的结果。
  18. 如果 Type(x) 为 Boolean 返回 ToNumber(x) == y 的结果。
  19. 如果 Type(y) 为 Boolean 返回 x == ToNumber(y) 的结果。
  20. 如果 Type(x) 为 String 或者 Number 并且 Type(y) 为 Object 返回 x == ToPrimitive(y) 的结果。
  21. 如果 Type(x) 为 Object 并且 Type(y) 为 String 或者 Number 返回 ToPrimitive(x) == y 的结果。
  22. 返回假。

有了这个流程,就可以知道上面提到的几个式子成立的原理:

[]==false

任意数组转换为布尔值时都为真,但与布尔值进行== 运算操作时,首先会将布尔转换为数字,即 false=>0,接着再与数组进行 == 运算。这时,需要进行 toPrimitive([] Number) 运算了,返回0,所以式子返回真。

[1]=='1'

数组与字符串比较,直接转换为 toPrimitive([1] String) == '1',显然为真。

总结

在使用 == 进行操作之前,一定要明确两边值类型所带来的结果差异,必要时,直接强转为布尔值进行计算。

测试

该页面展示了比较两个不同类型值得过程中所发生的事情。

参考

Node Packaged Modules(NPM) 中使用的版本号系统遵循语义化版本2.0.0

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

  1. 主版本号:当你做了不兼容的API 修改,
  2. 次版本号:当你做了向下兼容的功能性新增,
  3. 修订号:当你做了向下兼容的问题修正。

NPM中所依赖的其它模块可以指定为特定的版本,也可以指定一个版本号范围,为此 Node-semver 引擎提供了一套灵活的语法:

>

大于某版本,如 “>0.1.2”

<

小于某版本,如 “<0.2.1”

>=

大于等于某版本,如 “>=0.1.2”

<=

小于等于某版本,如 “>=0.1.2”

-

两个版本范围之内,包含边界,如 “0.1.2 - 0.1.9”,相当于 “>=0.1.2” and “<=0.1.9”

^

当前版本至下一个主版本,如 “^1.1.2”,相当于 “>=1.1.2” and “<2.0.0”

~

当前版本至下一个次版本,如 “~1.1.2”,相当于 “>=1.1.2” and “<1.2.0”

另外,* x和空代表任意,下面几个的含义相同:
“1.2.x”,”1.2.*”,”1.2”

选用合适的版本范围选择符,避免依赖模块的小版本升级导致的连锁强迫升级。

参考

过宽的大段文字内容对读者不够友好,尝试阅读一下这里的第一段文字,是否在换行时遇到困难。

像报纸一样,web 设计时遇到大段文字一般都会尝试将文字内容分成多列显示,在技术上可以通过加入表格(table)或任意其它块级元素标签解决。显然这样的设计欠缺灵活性,需要手动分割文字内容,过多或过少的文字都将影响分割算法。CSS 2.1 后带来了新的布局功能————多列布局,很好地解决了此问题。

#####语法

多列布局

多列布局适用于非替换 block 元素(除 table 外),table-cellinline-block 元素。

article{
    columns:3;
}

这段 CSS 代码即定义所有 article 元素分为3列进行排版。这是最大列数定义,当文字不足时,也可能按2列或1列显示。

也可以这样定义:

article{
    columns:10em;
}

这意味着每列最多显示10个字符宽度。

事实上,columnscolumn-countcolumn-width 的缩写。大多数时候仅需给出一个值,当两个值都给出时,一般仅有一个能影响最终排版,column-count 代表可以分割的最大列数。

######列间隙

使用 column-gap 设置列与列之间的间隙,如:

article{
    column-gap:3em;
}

######列分割线

列分割线由 column-rule-colorcolumn-rule-widthcolumn-rule-style 定义,语法类似与 border ,如:

article{
    column-rule-color:#28b;
    column-rule-style:dashed;
    column-rule-width:1px;
}

或者简写为:

article{
    column-rule:1px #28b dashed;
}

######分割位置

可以定义一些分割位置的建议,如不能在某些元素的中间分割:

article p{
    break-before:avoid;
    break-after:column;
    break-inside:avoid;
}

这些属性仅对块级元素有效,不过好像目前还没有浏览器支持它们。

######夸列元素

可以定义夸列元素,如:

article h1{
    column-span:all|none;
}

######列填充策略

两种策略:平衡和非平衡的,目前只有 Firefox 支持:

article{
    column-fill:balance|auto;
}

#####浏览器支持

虽然多列布局一直处于 Candidate Recommendation 阶段,但是现代 PC 和 Mobile 浏览器几乎已经全部实现, IE 从 10 开始支持。部分浏览器需要加厂商前缀。

  • IE 10+
  • Firefox 5+
  • Chrome 12+
  • Safari 3.2+
  • Opera 11.1+
  • Android 2.1+
  • iOS 3.2+

下面是多列布局所需的所有 CSS 属性的 LESS

.column(@w){
    -webkit-columns:@w;
    -moz-columns:@w;
    columns:@w;
}
.column-gap(@len){
    -webkit-column-gap:@len;
    -moz-column-gap:@len;
    column-gap:@len;
}
.column-rule-color(@color){
    -webkit-column-rule-color:@color;
    -moz-column-rule-color:@color;
    column-rule-color:@color;
}
.column-rule-style(@style:solid){
    -webkit-column-rule-style:@style;
    -moz-column-rule-style:@style;
    column-rule-style:@style;
}
.column-rule-width(@width){
    -webkit-column-rule-width:@width;
    -moz-column-rule-width:@width;
    column-rule-width:@width;
}
.break-before(@break){
    -webkit-break-before:@break;
    -moz-break-before:@break;
    break-before:@break;
}
.break-after(@break){
    -webkit-break-after:@break;
    -moz-break-after:@break;
    break-after:@break;
}
.break-inside(@break){
    -webkit-break-inside:@break;
    -moz-break-inside:@break;
    break-inside:@break;
}
.column-span(@span){
    -webkit-column-span:@span;
    -moz-column-span:@span;
    column-span:@span;
}
.column-fill(@fill:balance){
    -webkit-column-fill:@fill;
    -moz-column-fill:@fill;
    column-fill:@fill;
}

查看多列布局的实际效果,点击这里

#####总结

使用 CSS 多列布局不仅大大提高了布局灵活性,对屏幕阅读器、打印机和 SEO 也更友好。但需要针对旧浏览器设计功能降级。

#####参考

0%