JavaScript中Object和Function及其prototype之间的关系

引子

在nodejs中执行如下代码

> Object instanceof Function
true

> Function.prototype instanceof Object
true

> Function instanceof Function
true

是不是有点迷惑?

instanceof的真实含义

在js中判断某个实例是否某个类的实例,我们可以是用如下代码:

function Test() {}
var t = new Test();
t instanceof Test;  // true

从字面上看,要表达的意思很直接。然而让我们hack一下代码,看看会发生什么:

var testPrototype = Test.prototype;
Test.prototype = {};
t instanceof Test;  // false

当Test类的原型更新为另一个对象时,之前用Test创建的实例其原型不变,依旧是Test上原先的prototype。从这个例子可以看出,我们可以大致这么理解instanceof这个语法:

t inheritsFrom Test.prototype

为了获得或判断t的真实原型,ES5提供了Object.getPrototypeOf()和Object.prototype.isPrototypeOf()方法:

Object.getPrototypeOf(t) === testPrototype;  // true
testPrototype.isPrototypeOf(t);  // true

构造函数只是实例和原型的连接器

当使用构造函数创建对象后,对象与其原型已经联系起来,之后构造函数对于这两者已经没有什么存在的意义了。我们用猎头撮合人才与公司签订劳动协议来举例子:

          劳动协议签订完毕
人才 --------------------------> 公司

       猎头:没我什么事儿了……

这个法则也适用于js对象的原型继承:

            继承关系建立
实例 --------------------------> 原型

       构造函数:没我什么事儿了……

构造函数就像中介,用来联系实例和原型。构造函数和原型虽然有prototype属性联系,但他们真的没什么大关系。

整理混乱的关系

在js中不污染prototype时具有以下规则:
– 所有function都继承自Function.prototype
– Object构造函数也是function,因此也继承自Function.prototype
– Function构造函数也是function,因此也继承自Function.prototype
– Function.prototype继承自Object.prototype,但增加了callable的支持,即可以在标识符后边使用()来触发调用

继承关系图

Object.prototype
        ^
        |
Function.prototype
    ^        ^
    |        |
 Object   Function

验证代码:

> Object.getPrototypeOf(Object.prototype) === null
true

> Object.getPrototypeOf(Function.prototype) === Object.prototype
true

> Object.getPrototypeOf(Object) === Function.prototype
true

> Object.getPrototypeOf(Function) === Function.prototype
true

《深入理解ES6》中文翻译及编排bug

第3章 函数

元属性(Metaproperty)new.target (55页)
通常是新创建对象实例,也就是函数体内this的构造函数
应为
通常是构造函数,它用于创建对象实例,作为函数体内的this
原文
https://github.com/nzakas/understandinges6/blob/master/manuscript/03-Functions.md#the-newtarget-metaproperty
oshotokill翻译版本
https://github.com/OshotOkill/understandinges6-simplified-chinese/blob/master/chapter_3.md#元属性-newtargetthe-newtarget-metaproperty

箭头函数语法 第二小节(60页)
即使没有显式的返回语句
根据原文,应保留return关键字
即使没有显式的return语句
原文
https://github.com/nzakas/understandinges6/blob/master/manuscript/03-Functions.md#arrow-function-syntax
oshotokill翻译版本
https://github.com/OshotOkill/understandinges6-simplified-chinese/blob/master/chapter_3.md#箭头函数语法arrow-function-syntax

第5章 解构

引言
解构是一种打破数据解构
应为
解构是一种打破数据结构

对象解构 第二小节(90页)
也是用来从options对象读取相应值的属性名称
应为
也是用来从node对象读取相应值的属性名称
原文
https://github.com/nzakas/understandinges6/blob/master/manuscript/05-Destructuring.md#object-destructuring

默认值 最后一小节 (93页)
此处没有node.value属性,因为value使用了预设的默认值
应为
此处没有node.value属性,因此value使用了预设的默认值

第6章 Symbol

Symbol.match, Symbol.replace, Symbol.search 和 Symbol.split Symbols 属性
示例代码 [Symbol.split] 函数
return value.length === 10 ? [, ] : [value];
应为
return value.length === 10 ? [””, “”] : [value];
原文
https://github.com/nzakas/understandinges6/blob/master/manuscript/06-Symbols.md#the-symbolmatch-symbolreplace-symbolsearch-and-symbolsplit-symbols

Symbol.match, Symbol.replace, Symbol.search 和 Symbol.split Symbols 属性
示例代码

let replace1 = message1.replace(hasLengthOf10),
    replace2 = message2.replace(hasLengthOf10);

console.log(replace1);          // "Hello world"
console.log(replace2);          // "Hello John"

应为

let replace1 = message1.replace(hasLengthOf10, "Howdy!"),
    replace2 = message2.replace(hasLengthOf10, "Howdy!");

console.log(replace1);          // "Hello world"
console.log(replace2);          // "Howdy!"

原文
https://github.com/nzakas/understandinges6/blob/master/manuscript/06-Symbols.md#the-symbolmatch-symbolreplace-symbolsearch-and-symbolsplit-symbols

第7章 Set集合与Map集合

Set集合的forEach()方法
forEach的第一个参数(134页)
Set集合中下一次索引的位置
应为
Set集合中下一个位置的
原文
https://github.com/nzakas/understandinges6/blob/master/manuscript/07-Sets-And-Maps.md#the-foreach-method-for-sets
142页Map集合的forEach()方法也存在同样的问题。

第8章 迭代器(Iterator)和生成器(Generator)

异步任务执行器
示例代码3之后 第一小节(179页)
如果没有错误产生,data被传入task.run()
应为
如果没有错误产生,data被传入task.next()
原文
https://github.com/nzakas/understandinges6/blob/master/manuscript/08-Iterators-And-Generators.md#asynchronous-task-runner

第9章 JavaScript中的类

为何使用类语法(184页)
中修改类名会导致程序报错
应为
类方法中修改类名会导致程序报错
原文
https://github.com/nzakas/understandinges6/blob/master/manuscript/09-Classes.md#why-to-use-the-class-syntax

为何使用类语法 最后一小节(185页)
尽管可以在不使用new语法的前提下实现类的所有功能
应为
尽管可以在不使用新语法的前提下实现类的所有功能

在类的构造函数中使用new.target
第二小节 (208页)
等价于Rectangle的nwe.target
应为
等价于Rectangle的new.target

第12章 代理(Proxy)和反射(Reflection)

原型代理陷阱 末小节(281页)
paroxy
应为
proxy

第13章 用模块封装代码

导出的基本语法 示例代码(317页)
export multiply;
应为
export { multiply };
原文
https://github.com/nzakas/understandinges6/blob/master/manuscript/13-Modules.md#basic-exporting

《JavaScript面向对象精要》中文翻译及编排bug

第1章

引言
开发者选择模仿……
应为
一些开发者选择模仿……

1.4.2节 定义数组的字面形式
用逗号区分的任意数量的值
应为
用逗号分隔的任意数量的值

第2章

2.4节 重载 第一小节
JavaScript语言根据实际传入的参数决定调用函数的哪个版本
应为
该类语言(指上文提到的其他面向对象语言)根据实际传入的参数决定调用函数的哪个版本

第3章

3.8节 总结 末小节
Object.prevent Extensions()
应为
Object.preventExtensions()

第4章

4.1节 第4、第8处示例代码
instanceofPerson
应为
instanceof Person

第6章

6.1.2节 代码示例2 getAge方法
returnage;
应为
return age;

JavaScript获取函数参数列表

参考了网上别人写的代码,大致是这样的:

function getArgNames(fn) {
    if (typeof(fn)==='function') {
        return fn.toString().match(/function\s*\w+\(\s*([^)]*)\)/)[1].replace(/\/\*.*?\*\/|\/\/[^\r\n]*(?:[\r\n]+|$)|\s+$/g,'').split(/\s*,\s*/);
    }
}

主要思路是调用Function.prototype.toString()获取函数的定义字符串,然后提取其中的参数列表,去除其中的注释和多余的空白,最后将他们按逗号分隔到数组里。

js分享到微博的简单URL

var title='分享标题'

var url=location.href;

var site = "示例网站";

var picUrls=['图片URL1', '图片URL2', '图片URL3'];



var sina_weibo = "http://v.t.sina.com.cn/share/share.php?title=" + encodeURIComponent(title) + "&url=" + url + "&pic=" + picUrls.join("||") + "&source=bookmark&appkey=&ralateUid=";


var tentcent_weibo = "http://share.v.t.qq.com/index.php?c=share&a=index&title=" + encodeURIComponent(title) + "&url=" + url + "&pic=" + picUrls.join("|") + "&site=" + site + "&appkey=";

var qzone = "http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?title=" + encodeURIComponent(title) + "&url=" + url + "&pics=" + picUrls.join("|") + "&summary=";

浏览器和元素的尺寸、位置有关的属性和方法

学习笔记,写的不是很全,兼容性尚未测试。

属性 作用 兼容性
screen.width
screen.heihgt
   
   
screen.availWidth
screen.availHeight
   
   
window.innerWidth
window.innerHeight
浏览器视口尺寸,去除浏览器菜单栏、工具栏、状态栏,但包含滚动条的尺寸,  
window.outerWidth
window.outerHeight
 浏览器窗口外边框尺寸  
 浏览器窗口外边框尺寸  
window.screenX
window.screenY
window.screenTop
window.screenLeft
窗口在屏幕中的位置  
window.pageXOffset
window.pageYOffset
   
   
Element.getBoundingClientRect() 返回元素对应border-box方框到视口的偏移坐标,  
Element.getClientRects() 返回类数组,对以inline方式显示的元素来说,每行行框都是一个方框,返回所有这些方框到视口的偏移坐标,对显示为块级的元素来说,效果同getBoundingClientRect()  
document.elementFromPoint()  指定相对于视口的坐标,返回该坐标处的元素。如果有定位元素,返回z-index最大(最上层)的,否则返回该处在文档树中最深的节点元素。  
Element.offsetWidth
Element.offsetHeight
Element.offsetLeft
Element.offsetTop
元素滚动可见部分内容border-box的尺寸。offsetLeft, offsetTop返回到offsetParent的距离  
Element.offsetParent 偏移的上级参照元素  
Element.clientWidth
Element.clientHeight
Element.clientLeft
Element.clientTop
元素滚动可见部分内容padding-box(不含滚动条)的尺寸,对显示为inline的元素来说,始终返回0。clientLeft,
clientTop返回padding-box外边缘到border-box外边缘的距离,通常来说就是边框宽度。但对于从右到左书写的语言来说,垂直滚动条出现在padding-box左侧,所以client-left还包括了滚动条的宽度。
 
Element.scrollWidth
Element.scrollHeight
Element.scrollLeft
Element.scrollTop
padding-box滚动整体的尺寸。在没有内容溢出时,scrollWidth, scrollHeight和clientWidth, clientHeight是相等的。 scrollLeft, scrollTop为视口相对元素左上角的滚动偏移量,且为可写属性。  
window.scroll()
window.scrollTo()
   
window.scrollBy()    
Element.scrollIntoView()    

JavaScript客户端document对象不常用属性

document.readyState

文档载入状态,按顺序分别为

  • loading,正在解析文档
  • interactive,文档解析完成,但文档的其它资源(如图片)尚未完全载入,此时document也会触发DOMContentLoaded事件
  • complete,文档资源完全载入完毕,此时也会触发window的load事件

document.compatMode

页面渲染模式,有标准模式和怪异模式,未声明doctype的文档将被以怪异模式渲染,以保持向后兼容,取值有

  • CSS1Compat,正在使用标准模式
  • BackCompat或该属性未定义,表示正在使用怪异模式。

document.domain

指定文档的同源策略,以便不同子域的脚本可以访问其他子域的资源。

document.URL

文档的初始URL,不同于location.href,为只读属性。

document.lastModified

文档的最后修改时间。

document.referrer

文档的来源URL,从哪个地址跳转到当前文档。

document.title

文档的标题。